diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/README.md b/README.md index cd9d694..c047a3f 100644 --- a/README.md +++ b/README.md @@ -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 -)](https://www.serverless-fullstack-app.com) - -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) - -## Quick Start - -Install the latest version of the Serverless Framework: - - -``` -npm i -g serverless -``` - -Then, initialize the `fullstack-app` template: - -``` -serverless init fullstack-app -cd fullstack-app -``` - -Then, add your AWS credentials in the `.env` file in the root directory, like this: - -```text -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 - -# 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` - -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`. - -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! - -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: - domain: ${env:domain} -``` - -```text -domain=serverless-fullstack-app.com -``` - -Support for stages is built in. - -You can deploy everything or individual components to different stages via the `--stage` flag, like this: - -`serverless deploy --stage prod` - -Or, you can hardcode the stage in `serverless.yml` (not recommended): - -```yaml -app: fullstack -component: express@0.0.20 -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: - -```bash -.env # Any stage -.env.dev # "dev" stage only -.env.prod # "prod" stage only -``` - -Then simply reference those environment variables using Serverless Variables in your YAML: - -```yaml -app: fullstack -component: express@0.0.20 -name: fullstack-api - -inputs: - domain: api.${env:domain} -``` - -And deploy! - -`serverless deploy --stage prod` - -Enjoy! This is a work in progress and we will continue to add funcitonality to this. - -## Other 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 Website](https://github.com/serverless-components/website) -* [Serverless AWS DynamoDB](https://github.com/serverless-components/aws-dynamodb) -* [Serverless AWS IAM Role](https://github.com/serverless-components/aws-iam-role) - -## Guides - -### 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) +[![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) + +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) + +## Quick Start + +Install the latest version of the Serverless Framework: + +``` +npm i -g serverless +``` + +Then, initialize the `fullstack-app` template: + +``` +serverless init fullstack-app +cd fullstack-app +``` + +Then, add your AWS credentials in the `.env` file in the root directory, like this: + +```text +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 + +# 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` + +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`. + +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! + +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: + domain: ${env:domain} +``` + +```text +domain=serverless-fullstack-app.com +``` + +Support for stages is built in. + +You can deploy everything or individual components to different stages via the `--stage` flag, like this: + +`serverless deploy --stage prod` + +Or, you can hardcode the stage in `serverless.yml` (not recommended): + +```yaml +app: fullstack +component: express@0.0.20 +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: + +```bash +.env # Any stage +.env.dev # "dev" stage only +.env.prod # "prod" stage only +``` + +Then simply reference those environment variables using Serverless Variables in your YAML: + +```yaml +app: fullstack +component: express@0.0.20 +name: fullstack-api + +inputs: + domain: api.${env:domain} +``` + +And deploy! + +`serverless deploy --stage prod` + +Enjoy! This is a work in progress and we will continue to add funcitonality to this. + +## Other 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 Website](https://github.com/serverless-components/website) +- [Serverless AWS DynamoDB](https://github.com/serverless-components/aws-dynamodb) +- [Serverless AWS IAM Role](https://github.com/serverless-components/aws-iam-role) + +## Guides + +### 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) diff --git a/api/app.js b/api/app.js index d478c04..3e250bf 100644 --- a/api/app.js +++ b/api/app.js @@ -1,16 +1,17 @@ -const express = require('express') -const app = express() -const passport = require('passport') -const { - users -} = require('./controllers') +const express = require("express"); +const app = express(); +const passport = require("passport"); +const { users } = require("./controllers"); /** * Configure Passport */ -try { require('./config/passport')(passport) } -catch (error) { console.log(error) } +try { + require("./config/passport")(passport); +} catch (error) { + console.log(error); +} /** * Configure Express.js Middleware @@ -18,26 +19,24 @@ catch (error) { console.log(error) } // Enable CORS app.use(function (req, res, next) { - res.header('Access-Control-Allow-Origin', '*') - res.header('Access-Control-Allow-Methods', '*') - res.header('Access-Control-Allow-Headers', '*') - res.header('x-powered-by', 'serverless-express') - next() -}) + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "*"); + res.header("Access-Control-Allow-Headers", "*"); + res.header("x-powered-by", "serverless-express"); + next(); +}); // Initialize Passport and restore authentication state, if any, from the session -app.use(passport.initialize()) -app.use(passport.session()) +app.use(passport.initialize()); +app.use(passport.session()); // Enable JSON use -app.use(express.json()) +app.use(express.json()); // Since Express doesn't support error handling of promises out of the box, // this handler enables that -const asyncHandler = fn => (req, res, next) => { - return Promise - .resolve(fn(req, res, next)) - .catch(next); +const asyncHandler = (fn) => (req, res, next) => { + return Promise.resolve(fn(req, res, next)).catch(next); }; /** @@ -45,37 +44,43 @@ const asyncHandler = fn => (req, res, next) => { */ 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) => { - res.status(200).send('Request received') -}) + res.status(200).send("Request received"); +}); /** * 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 */ app.get(`/*`, (req, res) => { - res.status(404).send('Route not found') -}) + res.status(404).send("Route not found"); +}); /** * Error Handler */ app.use(function (err, req, res, next) { - console.error(err) - res.status(500).json({ error: `Internal Serverless Error - "${err.message}"` }) -}) + console.error(err); + res + .status(500) + .json({ error: `Internal Serverless Error - "${err.message}"` }); +}); -module.exports = app \ No newline at end of file +module.exports = app; diff --git a/api/config/passport.js b/api/config/passport.js index d96ee55..fc9389e 100644 --- a/api/config/passport.js +++ b/api/config/passport.js @@ -2,26 +2,30 @@ * Config: Passport.js */ -const StrategyJWT = require('passport-jwt').Strategy -const ExtractJWT = require('passport-jwt').ExtractJwt -const { users } = require('../models') -const { comparePassword } = require('../utils') +const StrategyJWT = require("passport-jwt").Strategy; +const ExtractJWT = require("passport-jwt").ExtractJwt; +const { users } = require("../models"); +const { comparePassword } = require("../utils"); 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 = {} - options.jwtFromRequest = ExtractJWT.fromAuthHeaderAsBearerToken() - options.secretOrKey = process.env.tokenSecret || 'secret_j91jasf0j1asfkl' // Change this to only use your own secret token + passport.use( + new StrategyJWT(options, async (jwtPayload, done) => { + 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) => { - let user - try { user = await users.getById(jwtPayload.id) } - catch (error) { - console.log(error) - return done(error, null) - } - - if (!user) { return done(null, false) } - return done(null, user) - })) -} \ No newline at end of file + if (!user) { + return done(null, false); + } + return done(null, user); + }) + ); +}; diff --git a/api/controllers/index.js b/api/controllers/index.js index 34f82d6..ed1d48e 100644 --- a/api/controllers/index.js +++ b/api/controllers/index.js @@ -1,5 +1,5 @@ -const users = require('./users') +const users = require("./users"); module.exports = { users, -} \ No newline at end of file +}; diff --git a/api/controllers/users.js b/api/controllers/users.js index 4a042df..10990dc 100644 --- a/api/controllers/users.js +++ b/api/controllers/users.js @@ -2,80 +2,85 @@ * Controllers: Users */ -const jwt = require('jsonwebtoken') -const { users } = require('../models') -const { comparePassword } = require('../utils') +const jwt = require("jsonwebtoken"); +const { users } = require("../models"); +const { comparePassword } = require("../utils"); /** * Save - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res * @param {*} next */ const register = async (req, res, next) => { - try { - await users.register(req.body) + await users.register(req.body); } catch (error) { - return res.status(400).json({ error: error.message }) + return res.status(400).json({ error: error.message }); } - let user + let user; try { - user = await users.getByEmail(req.body.email) + user = await users.getByEmail(req.body.email); } catch (error) { - console.log(error) - return next(error, null) + console.log(error); + return next(error, null); } 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 - * @param {*} req - * @param {*} res - * @param {*} next + * @param {*} req + * @param {*} res + * @param {*} next */ const login = async (req, res, next) => { - - let user - try { user = await users.getByEmail(req.body.email) } - catch (error) { return done(error, null) } - - if (!user) { - return res.status(404).send({ error: 'Authentication failed. User not found.' }) + let user; + try { + user = await users.getByEmail(req.body.email); + } catch (error) { + return done(error, null); } - 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) { - 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, { - expiresIn: 604800 // 1 week - }) + expiresIn: 604800, // 1 week + }); - res.json({ message: 'Authentication successful', token }) -} + res.json({ message: "Authentication successful", token }); +}; /** * Get a user - * @param {*} req - * @param {*} res - * @param {*} next + * @param {*} req + * @param {*} res + * @param {*} next */ const get = async (req, res, next) => { - const user = users.convertToPublicFormat(req.user) - res.json({ user }) -} + const user = users.convertToPublicFormat(req.user); + res.json({ user }); +}; module.exports = { register, login, get, -} \ No newline at end of file +}; diff --git a/api/models/index.js b/api/models/index.js index 34f82d6..ed1d48e 100644 --- a/api/models/index.js +++ b/api/models/index.js @@ -1,5 +1,5 @@ -const users = require('./users') +const users = require("./users"); module.exports = { users, -} \ No newline at end of file +}; diff --git a/api/models/users.js b/api/models/users.js index b29e60f..b73422f 100644 --- a/api/models/users.js +++ b/api/models/users.js @@ -2,135 +2,132 @@ * Model: Users */ -const AWS = require('aws-sdk') -const shortid = require('shortid') -const utils = require('../utils') +const AWS = require("aws-sdk"); +const shortid = require("shortid"); +const utils = require("../utils"); const dynamodb = new AWS.DynamoDB.DocumentClient({ - region: process.env.AWS_REGION -}) + region: process.env.AWS_REGION, +}); /** * Register user * @param {string} user.email User email * @param {string} user.password User password */ -const register = async(user = {}) => { - +const register = async (user = {}) => { // Validate if (!user.email) { - throw new Error(`"email" is required`) + throw new Error(`"email" is required`); } if (!user.password) { - throw new Error(`"password" is required`) + throw new Error(`"password" is required`); } 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 - const existingUser = await getByEmail(user.email) + const existingUser = await getByEmail(user.email); 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 const params = { TableName: process.env.db, Item: { hk: user.email, - sk: 'user', + sk: "user", sk2: shortid.generate(), createdAt: Date.now(), updatedAt: Date.now(), password: user.password, - } - } + }, + }; - await dynamodb.put(params).promise() -} + await dynamodb.put(params).promise(); +}; /** * Get user by email address * @param {string} email */ -const getByEmail = async(email) => { - +const getByEmail = async (email) => { // Validate if (!email) { - throw new Error(`"email" is required`) + throw new Error(`"email" is required`); } 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 const params = { TableName: process.env.db, - KeyConditionExpression: 'hk = :hk', - ExpressionAttributeValues: { ':hk': email } - } + KeyConditionExpression: "hk = :hk", + 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) { - user.id = user.sk2 - user.email = user.hk + user.id = user.sk2; + user.email = user.hk; } - return user -} + return user; +}; /** * Get user by id * @param {string} id */ -const getById = async(id) => { - +const getById = async (id) => { // Validate if (!id) { - throw new Error(`"id" is required`) + throw new Error(`"id" is required`); } // Query const params = { TableName: process.env.db, IndexName: process.env.dbIndex1, - KeyConditionExpression: 'sk2 = :sk2 and sk = :sk', - ExpressionAttributeValues: { ':sk2': id, ':sk': 'user' } - } - let user = await dynamodb.query(params).promise() + KeyConditionExpression: "sk2 = :sk2 and sk = :sk", + ExpressionAttributeValues: { ":sk2": id, ":sk": "user" }, + }; + 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) { - user.id = user.sk2 - user.email = user.hk + user.id = user.sk2; + user.email = user.hk; } - return user -} + return user; +}; /** * Convert user record to public format * This hides the keys used for the dynamodb's single table design and returns human-readable properties. - * @param {*} user + * @param {*} user */ const convertToPublicFormat = (user = {}) => { - user.email = user.hk || null - user.id = user.sk2 || null - if (user.hk) delete user.hk - if (user.sk) delete user.sk - if (user.sk2) delete user.sk2 - if (user.password) delete user.password - return user -} + user.email = user.hk || null; + user.id = user.sk2 || null; + if (user.hk) delete user.hk; + if (user.sk) delete user.sk; + if (user.sk2) delete user.sk2; + if (user.password) delete user.password; + return user; +}; module.exports = { register, getByEmail, getById, convertToPublicFormat, -} +}; diff --git a/api/package.json b/api/package.json index ea75df5..2574195 100644 --- a/api/package.json +++ b/api/package.json @@ -1,7 +1,6 @@ { - "name": "serverless-fullstack-app-api", + "name": "forgetmenot", "version": "1.0.0", - "description": "", "main": "app.js", "dependencies": { "bcryptjs": "^2.4.3", @@ -15,6 +14,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "author": "", - "license": "ISC" + "author": "Ben Ramey ", + "license": "ISC", + "description": "" } diff --git a/api/utils/index.js b/api/utils/index.js index 12a7749..c17f2ad 100644 --- a/api/utils/index.js +++ b/api/utils/index.js @@ -1,35 +1,36 @@ /** - * Utils + * Utils */ -const bcrypt = require('bcryptjs') +const bcrypt = require("bcryptjs"); /** * Validate email address */ 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,}))$/ - return re.test(String(email).toLowerCase()) -} + 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,}))$/; + return re.test(String(email).toLowerCase()); +}; /** * Hash password - * @param {*} user + * @param {*} user */ const hashPassword = (password) => { - const salt = bcrypt.genSaltSync(10) - return bcrypt.hashSync(password, salt) -} + const salt = bcrypt.genSaltSync(10); + return bcrypt.hashSync(password, salt); +}; /** * Compare password */ const comparePassword = (candidatePassword, trustedPassword) => { - return bcrypt.compareSync(candidatePassword, trustedPassword) -} + return bcrypt.compareSync(candidatePassword, trustedPassword); +}; module.exports = { hashPassword, comparePassword, - validateEmailAddress -} \ No newline at end of file + validateEmailAddress, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..be0e86e --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/serverless.yml b/serverless.yml index 73162f4..f55cf73 100644 --- a/serverless.yml +++ b/serverless.yml @@ -1,2 +1,2 @@ app: forgetmenot -org: yemarnn \ No newline at end of file +org: yemarnn diff --git a/site/README.md b/site/README.md index 1b16a82..65b7601 100644 --- a/site/README.md +++ b/site/README.md @@ -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: | Command | Action | -|:----------------|:--------------------------------------------| +| :-------------- | :------------------------------------------ | | `npm install` | Installs dependencies | | `npm run dev` | Starts local dev server at `localhost:3000` | | `npm run build` | Build your production site to `./dist/` | diff --git a/site/astro.config.mjs b/site/astro.config.mjs index 9d9f9e1..ca0c593 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -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 don’t need processing. buildOptions: { // 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: { // hostname: 'localhost', // The hostname 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' }, - renderers: [ - "@astrojs/renderer-react" - ], + renderers: ["@astrojs/renderer-react"], }; diff --git a/site/public/style/global.css b/site/public/style/global.css index e1a3a6b..b8840cd 100644 --- a/site/public/style/global.css +++ b/site/public/style/global.css @@ -4,10 +4,15 @@ } :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; --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 { diff --git a/site/public/style/home.css b/site/public/style/home.css index 39de76a..841cfcf 100644 --- a/site/public/style/home.css +++ b/site/public/style/home.css @@ -1,6 +1,8 @@ :root { - --font-mono: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono', - 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace; + --font-mono: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", + "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", + "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, + monospace; --color-light: #f3f4f6; } diff --git a/site/src/components/ReactCounter.jsx b/site/src/components/ReactCounter.jsx index ca34627..a2449eb 100644 --- a/site/src/components/ReactCounter.jsx +++ b/site/src/components/ReactCounter.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState } from "react"; export default function ReactCounter() { const [count, setCount] = useState(0); diff --git a/site/src/config.js b/site/src/config.js index dbadd03..98b75f0 100644 --- a/site/src/config.js +++ b/site/src/config.js @@ -2,10 +2,10 @@ * Global Config */ -const config = {} +const config = {}; // Domains -config.domains = {} +config.domains = {}; /** * API Domain @@ -13,6 +13,6 @@ config.domains = {} * 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') */ -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 \ No newline at end of file +export default config; diff --git a/site/src/utils/api.js b/site/src/utils/api.js index a27600a..e05b2e2 100644 --- a/site/src/utils/api.js +++ b/site/src/utils/api.js @@ -2,70 +2,69 @@ * Utils: Back-end */ -import config from '../config' +import config from "../config"; /** * Register a new user */ 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 */ export const userLogin = async (email, password) => { - return await requestApi('/users/login', 'POST', { email, password }) -} + return await requestApi("/users/login", "POST", { email, password }); +}; /** * userGet */ export const userGet = async (token) => { - return await requestApi('/user', 'POST', null, { - Authorization: `Bearer ${token}` - }) -} + return await requestApi("/user", "POST", null, { + Authorization: `Bearer ${token}`, + }); +}; /** * API request to call the backend */ export const requestApi = async ( - path = '', - method = 'GET', + path = "", + method = "GET", data = null, - headers = {}) => { - + headers = {} +) => { // Check if API URL has been set 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 - if (!path.startsWith('/')) { - path = `/${path}` + if (!path.startsWith("/")) { + path = `/${path}`; } - const url = `${config.domains.api}${path}` + const url = `${config.domains.api}${path}`; // Set headers - headers = Object.assign( - { 'Content-Type': 'application/json' }, - headers - ) + headers = Object.assign({ "Content-Type": "application/json" }, headers); // Default options are marked with * const response = await fetch(url, { method: method.toUpperCase(), - mode: 'cors', - cache: 'no-cache', + mode: "cors", + cache: "no-cache", headers, - body: data ? JSON.stringify(data) : null - }) + body: data ? JSON.stringify(data) : null, + }); if (response.status < 200 || response.status >= 300) { - const error = await response.json() - throw new Error(error.error) + const error = await response.json(); + throw new Error(error.error); } - return await response.json() -} \ No newline at end of file + return await response.json(); +}; diff --git a/site/src/utils/index.js b/site/src/utils/index.js index 201fc0f..d158c57 100644 --- a/site/src/utils/index.js +++ b/site/src/utils/index.js @@ -1 +1 @@ -export * from './api' \ No newline at end of file +export * from "./api";