Install and use prettier

This commit is contained in:
2021-09-24 20:24:39 -05:00
parent 996e3610ac
commit d4d904d165
21 changed files with 358 additions and 323 deletions

2
.prettierignore Normal file
View File

@@ -0,0 +1,2 @@
node_modules
dist

1
.prettierrc.json Normal file
View File

@@ -0,0 +1 @@
{}

222
README.md
View File

@@ -1,112 +1,110 @@
[![Serverless Fullstack Application Express React DynamoDB AWS Lambda AWS HTTP API](https://s3.amazonaws.com/assets.github.serverless/components/readme-serverless-framework-fullstack-application.png [![Serverless Fullstack Application Express React DynamoDB AWS Lambda AWS HTTP API](https://s3.amazonaws.com/assets.github.serverless/components/readme-serverless-framework-fullstack-application.png)](https://www.serverless-fullstack-app.com)
)](https://www.serverless-fullstack-app.com)
A complete, serverless, full-stack application built on AWS Lambda, AWS HTTP API, Express.js, React and DynamoDB.
A complete, serverless, full-stack application built on AWS Lambda, AWS HTTP API, Express.js, React and DynamoDB.
#### Live Demo: [https://www.serverless-fullstack-app.com](https://www.serverless-fullstack-app.com)
#### Live Demo: [https://www.serverless-fullstack-app.com](https://www.serverless-fullstack-app.com)
## Quick Start
## Quick Start
Install the latest version of the Serverless Framework:
Install the latest version of the Serverless Framework:
```
npm i -g serverless
``` ```
npm i -g serverless
``` Then, initialize the `fullstack-app` template:
Then, initialize the `fullstack-app` template: ```
serverless init fullstack-app
``` cd fullstack-app
serverless init fullstack-app ```
cd fullstack-app
``` Then, add your AWS credentials in the `.env` file in the root directory, like this:
Then, add your AWS credentials in the `.env` file in the root directory, like this: ```text
AWS_ACCESS_KEY_ID=JAFJ89109JASFKLJASF
```text AWS_SECRET_ACCESS_KEY=AJ91J9A0SFA0S9FSKAFLASJFLJ
AWS_ACCESS_KEY_ID=JAFJ89109JASFKLJASF
AWS_SECRET_ACCESS_KEY=AJ91J9A0SFA0S9FSKAFLASJFLJ # This signs you JWT tokens used for auth. Enter a random string in here that's ~40 characters in length.
tokenSecret=yourSecretKey
# This signs you JWT tokens used for auth. Enter a random string in here that's ~40 characters in length.
tokenSecret=yourSecretKey # Only add this if you want a custom domain. Purchase it on AWS Route53 in your target AWS account first.
domain=serverless-fullstack-app.com
# Only add this if you want a custom domain. Purchase it on AWS Route53 in your target AWS account first. ```
domain=serverless-fullstack-app.com
``` In the root folder of the project, run `serverless deploy`
In the root folder of the project, run `serverless deploy` Lastly, you will need to add your API domain manually to your React application in `./site/src/config.js`, so that you interact with your serverless Express.js back-end. You can find the your API url by going into `./api` and running `serverless info` and copying the `url:` value. It should look something like this `https://9jfalnal19.execute-api.us-east-1.amazonaws.com` or it will look like the custom domain you have set.
Lastly, you will need to add your API domain manually to your React application in `./site/src/config.js`, so that you interact with your serverless Express.js back-end. You can find the your API url by going into `./api` and running `serverless info` and copying the `url:` value. It should look something like this `https://9jfalnal19.execute-api.us-east-1.amazonaws.com` or it will look like the custom domain you have set. **Note:** Upon the first deployment of your website, it will take a 2-3 minutes for the Cloudfront (CDN) URL to work. Until then, you can access it via the `bucketUrl`.
**Note:** Upon the first deployment of your website, it will take a 2-3 minutes for the Cloudfront (CDN) URL to work. Until then, you can access it via the `bucketUrl`. After initial deployment, we recommend deploying only the parts you are changing, not the entire thing together (why risk deploying your database with a code change?). To do this, `cd` into a part of the application and run `serverless deploy`.
After initial deployment, we recommend deploying only the parts you are changing, not the entire thing together (why risk deploying your database with a code change?). To do this, `cd` into a part of the application and run `serverless deploy`. When working on the `./api` we highly recommend using `serverless dev`. This command watches your code, auto-deploys it, and streams `console.log()` statements and errors directly to your CLI in real-time!
When working on the `./api` we highly recommend using `serverless dev`. This command watches your code, auto-deploys it, and streams `console.log()` statements and errors directly to your CLI in real-time! If you want to add custom domains to your landing pages and API, either hardcode them in your `serverless.yml` or reference them as environment variables in `serverless.yml`, like this:
If you want to add custom domains to your landing pages and API, either hardcode them in your `serverless.yml` or reference them as environment variables in `serverless.yml`, like this: ```yaml
inputs:
```yaml domain: ${env:domain}
inputs: ```
domain: ${env:domain}
``` ```text
domain=serverless-fullstack-app.com
```text ```
domain=serverless-fullstack-app.com
``` Support for stages is built in.
Support for stages is built in. You can deploy everything or individual components to different stages via the `--stage` flag, like this:
You can deploy everything or individual components to different stages via the `--stage` flag, like this: `serverless deploy --stage prod`
`serverless deploy --stage prod` Or, you can hardcode the stage in `serverless.yml` (not recommended):
Or, you can hardcode the stage in `serverless.yml` (not recommended): ```yaml
app: fullstack
```yaml component: express@0.0.20
app: fullstack name: fullstack-api
component: express@0.0.20 stage: prod # Put the stage in here
name: fullstack-api ```
stage: prod # Put the stage in here
``` Lastly, you can add separate environment variables for each stage using `.env` files with the stage name in them:
Lastly, you can add separate environment variables for each stage using `.env` files with the stage name in them: ```bash
.env # Any stage
```bash .env.dev # "dev" stage only
.env # Any stage .env.prod # "prod" stage only
.env.dev # "dev" stage only ```
.env.prod # "prod" stage only
``` Then simply reference those environment variables using Serverless Variables in your YAML:
Then simply reference those environment variables using Serverless Variables in your YAML: ```yaml
app: fullstack
```yaml component: express@0.0.20
app: fullstack name: fullstack-api
component: express@0.0.20
name: fullstack-api inputs:
domain: api.${env:domain}
inputs: ```
domain: api.${env:domain}
``` And deploy!
And deploy! `serverless deploy --stage prod`
`serverless deploy --stage prod` Enjoy! This is a work in progress and we will continue to add funcitonality to this.
Enjoy! This is a work in progress and we will continue to add funcitonality to this. ## Other Resources
## Other Resources For more details on each part of this fullstack application, check out these resources:
For more details on each part of this fullstack application, check out these resources: - [Serverless Components](https://github.com/serverless/components)
- [Serverless Express](https://github.com/serverless-components/express)
* [Serverless Components](https://github.com/serverless/components) - [Serverless Website](https://github.com/serverless-components/website)
* [Serverless Express](https://github.com/serverless-components/express) - [Serverless AWS DynamoDB](https://github.com/serverless-components/aws-dynamodb)
* [Serverless Website](https://github.com/serverless-components/website) - [Serverless AWS IAM Role](https://github.com/serverless-components/aws-iam-role)
* [Serverless AWS DynamoDB](https://github.com/serverless-components/aws-dynamodb)
* [Serverless AWS IAM Role](https://github.com/serverless-components/aws-iam-role) ## Guides
## Guides ### How To Debug CORS Errors
### How To Debug CORS Errors If you are running into CORS errors, see our guide on debugging them [within the Express Component's repo](https://github.com/serverless-components/express/blob/master/README.md#how-to-debug-cors-errors)
If you are running into CORS errors, see our guide on debugging them [within the Express Component's repo](https://github.com/serverless-components/express/blob/master/README.md#how-to-debug-cors-errors)

View File

@@ -1,16 +1,17 @@
const express = require('express') const express = require("express");
const app = express() const app = express();
const passport = require('passport') const passport = require("passport");
const { const { users } = require("./controllers");
users
} = require('./controllers')
/** /**
* Configure Passport * Configure Passport
*/ */
try { require('./config/passport')(passport) } try {
catch (error) { console.log(error) } require("./config/passport")(passport);
} catch (error) {
console.log(error);
}
/** /**
* Configure Express.js Middleware * Configure Express.js Middleware
@@ -18,26 +19,24 @@ catch (error) { console.log(error) }
// Enable CORS // Enable CORS
app.use(function (req, res, next) { app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*') res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', '*') res.header("Access-Control-Allow-Methods", "*");
res.header('Access-Control-Allow-Headers', '*') res.header("Access-Control-Allow-Headers", "*");
res.header('x-powered-by', 'serverless-express') res.header("x-powered-by", "serverless-express");
next() next();
}) });
// Initialize Passport and restore authentication state, if any, from the session // Initialize Passport and restore authentication state, if any, from the session
app.use(passport.initialize()) app.use(passport.initialize());
app.use(passport.session()) app.use(passport.session());
// Enable JSON use // Enable JSON use
app.use(express.json()) app.use(express.json());
// Since Express doesn't support error handling of promises out of the box, // Since Express doesn't support error handling of promises out of the box,
// this handler enables that // this handler enables that
const asyncHandler = fn => (req, res, next) => { const asyncHandler = (fn) => (req, res, next) => {
return Promise return Promise.resolve(fn(req, res, next)).catch(next);
.resolve(fn(req, res, next))
.catch(next);
}; };
/** /**
@@ -45,37 +44,43 @@ const asyncHandler = fn => (req, res, next) => {
*/ */
app.options(`*`, (req, res) => { app.options(`*`, (req, res) => {
res.status(200).send() res.status(200).send();
}) });
app.post(`/users/register`, asyncHandler(users.register)) app.post(`/users/register`, asyncHandler(users.register));
app.post(`/users/login`, asyncHandler(users.login)) app.post(`/users/login`, asyncHandler(users.login));
app.get(`/test/`, (req, res) => { app.get(`/test/`, (req, res) => {
res.status(200).send('Request received') res.status(200).send("Request received");
}) });
/** /**
* Routes - Protected * Routes - Protected
*/ */
app.post(`/user`, passport.authenticate('jwt', { session: false }), asyncHandler(users.get)) app.post(
`/user`,
passport.authenticate("jwt", { session: false }),
asyncHandler(users.get)
);
/** /**
* Routes - Catch-All * Routes - Catch-All
*/ */
app.get(`/*`, (req, res) => { app.get(`/*`, (req, res) => {
res.status(404).send('Route not found') res.status(404).send("Route not found");
}) });
/** /**
* Error Handler * Error Handler
*/ */
app.use(function (err, req, res, next) { app.use(function (err, req, res, next) {
console.error(err) console.error(err);
res.status(500).json({ error: `Internal Serverless Error - "${err.message}"` }) res
}) .status(500)
.json({ error: `Internal Serverless Error - "${err.message}"` });
});
module.exports = app module.exports = app;

View File

@@ -2,26 +2,30 @@
* Config: Passport.js * Config: Passport.js
*/ */
const StrategyJWT = require('passport-jwt').Strategy const StrategyJWT = require("passport-jwt").Strategy;
const ExtractJWT = require('passport-jwt').ExtractJwt const ExtractJWT = require("passport-jwt").ExtractJwt;
const { users } = require('../models') const { users } = require("../models");
const { comparePassword } = require('../utils') const { comparePassword } = require("../utils");
module.exports = (passport) => { module.exports = (passport) => {
const options = {};
options.jwtFromRequest = ExtractJWT.fromAuthHeaderAsBearerToken();
options.secretOrKey = process.env.tokenSecret; // Change this to only use your own secret token
const options = {} passport.use(
options.jwtFromRequest = ExtractJWT.fromAuthHeaderAsBearerToken() new StrategyJWT(options, async (jwtPayload, done) => {
options.secretOrKey = process.env.tokenSecret || 'secret_j91jasf0j1asfkl' // Change this to only use your own secret token let user;
try {
user = await users.getById(jwtPayload.id);
} catch (error) {
console.log(error);
return done(error, null);
}
passport.use(new StrategyJWT(options, async (jwtPayload, done) => { if (!user) {
let user return done(null, false);
try { user = await users.getById(jwtPayload.id) } }
catch (error) { return done(null, user);
console.log(error) })
return done(error, null) );
} };
if (!user) { return done(null, false) }
return done(null, user)
}))
}

View File

@@ -1,5 +1,5 @@
const users = require('./users') const users = require("./users");
module.exports = { module.exports = {
users, users,
} };

View File

@@ -2,80 +2,85 @@
* Controllers: Users * Controllers: Users
*/ */
const jwt = require('jsonwebtoken') const jwt = require("jsonwebtoken");
const { users } = require('../models') const { users } = require("../models");
const { comparePassword } = require('../utils') const { comparePassword } = require("../utils");
/** /**
* Save * Save
* @param {*} req * @param {*} req
* @param {*} res * @param {*} res
* @param {*} next * @param {*} next
*/ */
const register = async (req, res, next) => { const register = async (req, res, next) => {
try { try {
await users.register(req.body) await users.register(req.body);
} catch (error) { } catch (error) {
return res.status(400).json({ error: error.message }) return res.status(400).json({ error: error.message });
} }
let user let user;
try { try {
user = await users.getByEmail(req.body.email) user = await users.getByEmail(req.body.email);
} catch (error) { } catch (error) {
console.log(error) console.log(error);
return next(error, null) return next(error, null);
} }
const token = jwt.sign(user, process.env.tokenSecret, { const token = jwt.sign(user, process.env.tokenSecret, {
expiresIn: 604800 // 1 week expiresIn: 604800, // 1 week
}) });
res.json({ message: 'Authentication successful', token }) res.json({ message: "Authentication successful", token });
} };
/** /**
* Sign a user in * Sign a user in
* @param {*} req * @param {*} req
* @param {*} res * @param {*} res
* @param {*} next * @param {*} next
*/ */
const login = async (req, res, next) => { const login = async (req, res, next) => {
let user;
let user try {
try { user = await users.getByEmail(req.body.email) } user = await users.getByEmail(req.body.email);
catch (error) { return done(error, null) } } catch (error) {
return done(error, null);
if (!user) {
return res.status(404).send({ error: 'Authentication failed. User not found.' })
} }
const isCorrect = comparePassword(req.body.password, user.password) if (!user) {
return res
.status(404)
.send({ error: "Authentication failed. User not found." });
}
const isCorrect = comparePassword(req.body.password, user.password);
if (!isCorrect) { if (!isCorrect) {
return res.status(401).send({ error: 'Authentication failed. Wrong password.' }) return res
.status(401)
.send({ error: "Authentication failed. Wrong password." });
} }
const token = jwt.sign(user, process.env.tokenSecret, { const token = jwt.sign(user, process.env.tokenSecret, {
expiresIn: 604800 // 1 week expiresIn: 604800, // 1 week
}) });
res.json({ message: 'Authentication successful', token }) res.json({ message: "Authentication successful", token });
} };
/** /**
* Get a user * Get a user
* @param {*} req * @param {*} req
* @param {*} res * @param {*} res
* @param {*} next * @param {*} next
*/ */
const get = async (req, res, next) => { const get = async (req, res, next) => {
const user = users.convertToPublicFormat(req.user) const user = users.convertToPublicFormat(req.user);
res.json({ user }) res.json({ user });
} };
module.exports = { module.exports = {
register, register,
login, login,
get, get,
} };

View File

@@ -1,5 +1,5 @@
const users = require('./users') const users = require("./users");
module.exports = { module.exports = {
users, users,
} };

View File

@@ -2,135 +2,132 @@
* Model: Users * Model: Users
*/ */
const AWS = require('aws-sdk') const AWS = require("aws-sdk");
const shortid = require('shortid') const shortid = require("shortid");
const utils = require('../utils') const utils = require("../utils");
const dynamodb = new AWS.DynamoDB.DocumentClient({ const dynamodb = new AWS.DynamoDB.DocumentClient({
region: process.env.AWS_REGION region: process.env.AWS_REGION,
}) });
/** /**
* Register user * Register user
* @param {string} user.email User email * @param {string} user.email User email
* @param {string} user.password User password * @param {string} user.password User password
*/ */
const register = async(user = {}) => { const register = async (user = {}) => {
// Validate // Validate
if (!user.email) { if (!user.email) {
throw new Error(`"email" is required`) throw new Error(`"email" is required`);
} }
if (!user.password) { if (!user.password) {
throw new Error(`"password" is required`) throw new Error(`"password" is required`);
} }
if (!utils.validateEmailAddress(user.email)) { if (!utils.validateEmailAddress(user.email)) {
throw new Error(`"${user.email}" is not a valid email address`) throw new Error(`"${user.email}" is not a valid email address`);
} }
// Check if user is already registered // Check if user is already registered
const existingUser = await getByEmail(user.email) const existingUser = await getByEmail(user.email);
if (existingUser) { if (existingUser) {
throw new Error(`A user with email "${user.email}" is already registered`) throw new Error(`A user with email "${user.email}" is already registered`);
} }
user.password = utils.hashPassword(user.password) user.password = utils.hashPassword(user.password);
// Save // Save
const params = { const params = {
TableName: process.env.db, TableName: process.env.db,
Item: { Item: {
hk: user.email, hk: user.email,
sk: 'user', sk: "user",
sk2: shortid.generate(), sk2: shortid.generate(),
createdAt: Date.now(), createdAt: Date.now(),
updatedAt: Date.now(), updatedAt: Date.now(),
password: user.password, password: user.password,
} },
} };
await dynamodb.put(params).promise() await dynamodb.put(params).promise();
} };
/** /**
* Get user by email address * Get user by email address
* @param {string} email * @param {string} email
*/ */
const getByEmail = async(email) => { const getByEmail = async (email) => {
// Validate // Validate
if (!email) { if (!email) {
throw new Error(`"email" is required`) throw new Error(`"email" is required`);
} }
if (!utils.validateEmailAddress(email)) { if (!utils.validateEmailAddress(email)) {
throw new Error(`"${email}" is not a valid email address`) throw new Error(`"${email}" is not a valid email address`);
} }
// Query // Query
const params = { const params = {
TableName: process.env.db, TableName: process.env.db,
KeyConditionExpression: 'hk = :hk', KeyConditionExpression: "hk = :hk",
ExpressionAttributeValues: { ':hk': email } ExpressionAttributeValues: { ":hk": email },
} };
let user = await dynamodb.query(params).promise() let user = await dynamodb.query(params).promise();
user = user.Items && user.Items[0] ? user.Items[0] : null user = user.Items && user.Items[0] ? user.Items[0] : null;
if (user) { if (user) {
user.id = user.sk2 user.id = user.sk2;
user.email = user.hk user.email = user.hk;
} }
return user return user;
} };
/** /**
* Get user by id * Get user by id
* @param {string} id * @param {string} id
*/ */
const getById = async(id) => { const getById = async (id) => {
// Validate // Validate
if (!id) { if (!id) {
throw new Error(`"id" is required`) throw new Error(`"id" is required`);
} }
// Query // Query
const params = { const params = {
TableName: process.env.db, TableName: process.env.db,
IndexName: process.env.dbIndex1, IndexName: process.env.dbIndex1,
KeyConditionExpression: 'sk2 = :sk2 and sk = :sk', KeyConditionExpression: "sk2 = :sk2 and sk = :sk",
ExpressionAttributeValues: { ':sk2': id, ':sk': 'user' } ExpressionAttributeValues: { ":sk2": id, ":sk": "user" },
} };
let user = await dynamodb.query(params).promise() let user = await dynamodb.query(params).promise();
user = user.Items && user.Items[0] ? user.Items[0] : null user = user.Items && user.Items[0] ? user.Items[0] : null;
if (user) { if (user) {
user.id = user.sk2 user.id = user.sk2;
user.email = user.hk user.email = user.hk;
} }
return user return user;
} };
/** /**
* Convert user record to public format * Convert user record to public format
* This hides the keys used for the dynamodb's single table design and returns human-readable properties. * This hides the keys used for the dynamodb's single table design and returns human-readable properties.
* @param {*} user * @param {*} user
*/ */
const convertToPublicFormat = (user = {}) => { const convertToPublicFormat = (user = {}) => {
user.email = user.hk || null user.email = user.hk || null;
user.id = user.sk2 || null user.id = user.sk2 || null;
if (user.hk) delete user.hk if (user.hk) delete user.hk;
if (user.sk) delete user.sk if (user.sk) delete user.sk;
if (user.sk2) delete user.sk2 if (user.sk2) delete user.sk2;
if (user.password) delete user.password if (user.password) delete user.password;
return user return user;
} };
module.exports = { module.exports = {
register, register,
getByEmail, getByEmail,
getById, getById,
convertToPublicFormat, convertToPublicFormat,
} };

View File

@@ -1,7 +1,6 @@
{ {
"name": "serverless-fullstack-app-api", "name": "forgetmenot",
"version": "1.0.0", "version": "1.0.0",
"description": "",
"main": "app.js", "main": "app.js",
"dependencies": { "dependencies": {
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
@@ -15,6 +14,7 @@
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "Ben Ramey <benramey@fastmail.com>",
"license": "ISC" "license": "ISC",
"description": ""
} }

View File

@@ -1,35 +1,36 @@
/** /**
* Utils * Utils
*/ */
const bcrypt = require('bcryptjs') const bcrypt = require("bcryptjs");
/** /**
* Validate email address * Validate email address
*/ */
const validateEmailAddress = (email) => { const validateEmailAddress = (email) => {
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ var re =
return re.test(String(email).toLowerCase()) /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
} return re.test(String(email).toLowerCase());
};
/** /**
* Hash password * Hash password
* @param {*} user * @param {*} user
*/ */
const hashPassword = (password) => { const hashPassword = (password) => {
const salt = bcrypt.genSaltSync(10) const salt = bcrypt.genSaltSync(10);
return bcrypt.hashSync(password, salt) return bcrypt.hashSync(password, salt);
} };
/** /**
* Compare password * Compare password
*/ */
const comparePassword = (candidatePassword, trustedPassword) => { const comparePassword = (candidatePassword, trustedPassword) => {
return bcrypt.compareSync(candidatePassword, trustedPassword) return bcrypt.compareSync(candidatePassword, trustedPassword);
} };
module.exports = { module.exports = {
hashPassword, hashPassword,
comparePassword, comparePassword,
validateEmailAddress validateEmailAddress,
} };

18
package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "forgetmenot-serverless",
"version": "1.0.0",
"description": "[![Serverless Fullstack Application Express React DynamoDB AWS Lambda AWS HTTP API](https://s3.amazonaws.com/assets.github.serverless/components/readme-serverless-framework-fullstack-application.png\r )](https://www.serverless-fullstack-app.com)",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://grimfere@dev.azure.com/grimfere/ForgetMeNot/_git/forgetmenot-serverless"
},
"author": "",
"license": "ISC",
"devDependencies": {
"prettier": "2.4.1"
}
}

View File

@@ -1,2 +1,2 @@
app: forgetmenot app: forgetmenot
org: yemarnn org: yemarnn

View File

@@ -32,7 +32,7 @@ Any static assets, like images, can be placed in the `public/` directory.
All commands are run from the root of the project, from a terminal: All commands are run from the root of the project, from a terminal:
| Command | Action | | Command | Action |
|:----------------|:--------------------------------------------| | :-------------- | :------------------------------------------ |
| `npm install` | Installs dependencies | | `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` | | `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` | | `npm run build` | Build your production site to `./dist/` |

View File

@@ -5,14 +5,12 @@ export default {
// public: './public', // A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that dont need processing. // public: './public', // A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that dont need processing.
buildOptions: { buildOptions: {
// site: 'http://example.com', // Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. // site: 'http://example.com', // Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs.
sitemap: true, // Generate sitemap (set to "false" to disable) sitemap: true, // Generate sitemap (set to "false" to disable)
}, },
devOptions: { devOptions: {
// hostname: 'localhost', // The hostname to run the dev server on. // hostname: 'localhost', // The hostname to run the dev server on.
// port: 3000, // The port to run the dev server on. // port: 3000, // The port to run the dev server on.
// tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js' // tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js'
}, },
renderers: [ renderers: ["@astrojs/renderer-react"],
"@astrojs/renderer-react"
],
}; };

View File

@@ -4,10 +4,15 @@
} }
:root { :root {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji;
font-size: 1rem; font-size: 1rem;
--user-font-scale: 1rem - 16px; --user-font-scale: 1rem - 16px;
font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem); font-size: clamp(
0.875rem,
0.4626rem + 1.0309vw + var(--user-font-scale),
1.125rem
);
} }
body { body {

View File

@@ -1,6 +1,8 @@
:root { :root {
--font-mono: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono', --font-mono: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console",
'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace; "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono",
"Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier,
monospace;
--color-light: #f3f4f6; --color-light: #f3f4f6;
} }

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState } from "react";
export default function ReactCounter() { export default function ReactCounter() {
const [count, setCount] = useState(0); const [count, setCount] = useState(0);

View File

@@ -2,10 +2,10 @@
* Global Config * Global Config
*/ */
const config = {} const config = {};
// Domains // Domains
config.domains = {} config.domains = {};
/** /**
* API Domain * API Domain
@@ -13,6 +13,6 @@ config.domains = {}
* This will enable your front-end to communicate with your back-end. * This will enable your front-end to communicate with your back-end.
* (e.g. 'https://api.mydomain.com' or 'https://091jafsl10.execute-api.us-east-1.amazonaws.com') * (e.g. 'https://api.mydomain.com' or 'https://091jafsl10.execute-api.us-east-1.amazonaws.com')
*/ */
config.domains.api = 'https://1t8da6s66e.execute-api.us-east-1.amazonaws.com' config.domains.api = "https://1t8da6s66e.execute-api.us-east-1.amazonaws.com";
export default config export default config;

View File

@@ -2,70 +2,69 @@
* Utils: Back-end * Utils: Back-end
*/ */
import config from '../config' import config from "../config";
/** /**
* Register a new user * Register a new user
*/ */
export const userRegister = async (email, password) => { export const userRegister = async (email, password) => {
return await requestApi('/users/register', 'POST', { email, password }) return await requestApi("/users/register", "POST", { email, password });
} };
/** /**
* Login a new user * Login a new user
*/ */
export const userLogin = async (email, password) => { export const userLogin = async (email, password) => {
return await requestApi('/users/login', 'POST', { email, password }) return await requestApi("/users/login", "POST", { email, password });
} };
/** /**
* userGet * userGet
*/ */
export const userGet = async (token) => { export const userGet = async (token) => {
return await requestApi('/user', 'POST', null, { return await requestApi("/user", "POST", null, {
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`,
}) });
} };
/** /**
* API request to call the backend * API request to call the backend
*/ */
export const requestApi = async ( export const requestApi = async (
path = '', path = "",
method = 'GET', method = "GET",
data = null, data = null,
headers = {}) => { headers = {}
) => {
// Check if API URL has been set // Check if API URL has been set
if (!config?.domains?.api) { if (!config?.domains?.api) {
throw new Error(`Error: Missing API Domain Please add the API domain from your serverless Express.js back-end to this front-end application. You can do this in the "site" folder, in the "./config.js" file. Instructions are listed there and in the documentation.`) throw new Error(
`Error: Missing API Domain Please add the API domain from your serverless Express.js back-end to this front-end application. You can do this in the "site" folder, in the "./config.js" file. Instructions are listed there and in the documentation.`
);
} }
// Prepare URL // Prepare URL
if (!path.startsWith('/')) { if (!path.startsWith("/")) {
path = `/${path}` path = `/${path}`;
} }
const url = `${config.domains.api}${path}` const url = `${config.domains.api}${path}`;
// Set headers // Set headers
headers = Object.assign( headers = Object.assign({ "Content-Type": "application/json" }, headers);
{ 'Content-Type': 'application/json' },
headers
)
// Default options are marked with * // Default options are marked with *
const response = await fetch(url, { const response = await fetch(url, {
method: method.toUpperCase(), method: method.toUpperCase(),
mode: 'cors', mode: "cors",
cache: 'no-cache', cache: "no-cache",
headers, headers,
body: data ? JSON.stringify(data) : null body: data ? JSON.stringify(data) : null,
}) });
if (response.status < 200 || response.status >= 300) { if (response.status < 200 || response.status >= 300) {
const error = await response.json() const error = await response.json();
throw new Error(error.error) throw new Error(error.error);
} }
return await response.json() return await response.json();
} };

View File

@@ -1 +1 @@
export * from './api' export * from "./api";