Remove node.js app

This commit is contained in:
2021-02-23 20:29:53 -06:00
parent 9d98ad4e84
commit 7220990f6d
50 changed files with 0 additions and 1258 deletions

View File

@@ -1,22 +0,0 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js}]
charset = utf-8
# 4 space indentation
[*.js]
indent_style = tab
indent_size = 4
# Matches the exact files either package.json or .travis.yml
[{package.json,serverless.yml}]
indent_style = tab
indent_size = 4

28
.gitignore vendored
View File

@@ -1,28 +0,0 @@
.DS_Store
*.sublime-project
*.sublime-workspace
*.log
.serverless
v8-compile-cache-*
jest/*
coverage
testProjects/*/package-lock.json
testProjects/*/yarn.lock
.serverlessUnzipped
node_modules
.vscode/
.eslintcache
dist
.idea
build/
.env*
.cache*
.serverless
.serverless_nextjs
.serverless_plugins
env.js
tmp
package-lock.json
yarn.lock
test
logs/

112
README.md
View File

@@ -1,112 +0,0 @@
[![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)

View File

@@ -1,12 +0,0 @@
{
"extends": ["eslint:recommended", "prettier"],
"env": {
"node": true,
"browser": false,
"es6": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2018
}
}

View File

@@ -1,2 +0,0 @@
package.json
package-lock.json

View File

@@ -1,105 +0,0 @@
const express = require(`express`);
const pino = require(`pino`);
const pinoHttp = require("pino-http");
const app = express();
const passport = require(`passport`);
const { users } = require(`./controllers`);
/**
* logging
*/
let logger = pino();
if (process.env.name === `test`) {
logger = pino(pino.destination({ dest: `../logs/api.json`, sync: false }));
}
app.use(
pinoHttp({
logger: logger,
})
);
/**
* Configure Passport
*/
try {
require(`./config/passport`)(passport);
} catch (error) {
console.log(error);
}
/**
* Configure Express.js Middleware
*/
// 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();
});
// Initialize Passport and restore authentication state, if any, from the session
app.use(passport.initialize());
app.use(passport.session());
// Enable JSON use
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);
};
/**
* Routes - Public
*/
app.options(`*`, (req, res) => {
res.status(200).send();
});
app.post(`/users/register`, asyncHandler(users.register));
app.post(`/users/login`, asyncHandler(users.login));
app.get(`/test/`, (req, res) => {
res.status(200).send(`Request received`);
});
/**
* Routes - Protected
*/
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`);
});
/**
* Error Handler
*/
app.use(function (err, req, res, next) {
if (res.headersSent) {
return next(err);
}
req.log.error(err);
res.status(err.code || 500);
res.json({
error: err.message || `Internal Server Error - "${err.message}"`,
});
});
module.exports = app;

View File

@@ -1,31 +0,0 @@
/**
* Config: Passport.js
*/
const StrategyJWT = require(`passport-jwt`).Strategy;
const ExtractJWT = require(`passport-jwt`).ExtractJwt;
const userUtils = require(`../controllers/users/utils`);
const db = require(`../db`);
module.exports = (passport) => {
const options = {};
options.jwtFromRequest = ExtractJWT.fromAuthHeaderAsBearerToken();
options.secretOrKey = process.env.tokenSecret;
passport.use(
new StrategyJWT(options, async (jwtPayload, done) => {
let user;
try {
user = await db.getById(`user`, jwtPayload.id);
} catch (error) {
console.log(error);
return done(error, null);
}
if (!user) {
return done(null, false);
}
return done(null, userUtils.convertToPublicFormat(user));
})
);
};

View File

@@ -1,61 +0,0 @@
/*
* Controller base actions
*
* This module contains base actions that follow
* a template for CRUD operations on a DB and (optionally)
* do custom things afterward to return a response.
*/
const validation = require(`../../validation`);
const db = require(`../../db`);
const createAction = async (req, res, config) => {
validation.validateOrThrow(config.validationSchema, req.body);
const key = config.getObjectKey();
let existingObject = await db.getByKey(key);
if (existingObject) {
throw customError(
`An object with key "${key}" already exists in the database.`,
400
);
}
const dbObject = config.buildDbObject(req.body);
await db.put(config.dbKind, dbObject);
const createdObject = await db.getByKey(key);
const responseBody = config.buildResponseBody(createdObject);
res.json(responseBody);
};
const getByKeyAction = async (req, res, config) => {
validation.validateOrThrow(config.validationSchema, req.body);
const key = config.getObjectKey();
let existingObject = await db.getByKey(key);
if (!existingObject) {
return res
.status(404)
.send({ error: config.notFoundMessage });
}
const responseBody = config.buildResponseBody(existingObject);
res.json(responseBody);
};
/**
* Build a custom error
* @param {string} message Error message
* @param {number} code Error code, should be HTTP status code
*/
const customError = (message = "An error occurred.", code = 500) => {
const error = new Error(message);
error.code = code;
return error;
};
module.exports = {
createAction,
getByKeyAction,
};

View File

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

View File

@@ -1,77 +0,0 @@
/**
* Controllers: Users
*/
const schemas = require(`./schemas`);
const base = require(`../base`);
const userUtils = require(`./utils`);
/**
* Save
* @param {*} req
* @param {*} res
*/
const register = async (req, res) => {
const config = {
validationSchema: schemas.create,
getObjectKey: () => req.body.email,
buildDbObject: () => {
return {
hk: req.body.email,
password: userUtils.hashPassword(req.body.password)
};
},
dbKind: `user`,
buildResponseBody: createdUser => {
return {
message: `Authentication successful`,
token: userUtils.getToken(createdUser),
};
}
};
await base.createAction(req, res, config);
};
/**
* Sign a user in
* @param {*} req
* @param {*} res
*/
const login = async (req, res) => {
const config = {
validationSchema: schemas.login,
getObjectKey: () => req.body.email,
notFoundMessage: `Authentication failed. User not found.`,
buildResponseBody: user => {
const isCorrect = userUtils.comparePassword(req.body.password, user.password);
if (!isCorrect) {
return res
.status(401)
.send({ error: `Authentication failed. Wrong password.` });
}
return {
message: `Authentication successful`,
token: userUtils.getToken(user),
};
}
};
await base.getByKeyAction(req, res, config);
};
/**
* Get a user
* @param {*} req
* @param {*} res
*/
const get = async (req, res) => {
const user = userUtils.convertToPublicFormat(req.user);
res.json({ user });
};
module.exports = {
register,
login,
get,
};

View File

@@ -1,36 +0,0 @@
const commonSchemas = require(`../../validation/commonSchemas`);
const login = {
$schema: "http://json-schema.org/draft-07/schema#",
title: "Login User",
description: "An object to log-in a new user",
type: "object",
properties: {
email: commonSchemas.email,
password: {
type: "string",
},
},
required: ["email", "password"],
};
const create = {
$schema: "http://json-schema.org/draft-07/schema#",
title: "Create User",
description: "An object to create a new user",
type: "object",
properties: {
email: commonSchemas.email,
password: {
type: "string",
minLength: 10,
maxLength: 50,
},
},
required: ["email", "password"],
};
module.exports = {
login,
create
};

View File

@@ -1,59 +0,0 @@
/*
* User utilities
*/
const bcrypt = require(`bcryptjs`);
const jwt = require(`jsonwebtoken`);
const getToken = (user) => {
return jwt.sign(
convertToPublicFormat(user),
process.env.tokenSecret,
{
expiresIn: 604800, // 1 week
}
);
};
const convertToPublicFormat = (user = {}) => {
user.email = user.email || user.hk || null;
user.id = 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;
};
/**
* Hash password
* @param {string} password Password to hash
*/
const hashPassword = (password) => {
const salt = bcrypt.genSaltSync(10);
return bcrypt.hashSync(password, salt);
};
/**
* Compare password
* @param {string} candidatePassword Hashed password supplied by user
* @param {string} trustedPassword Hashed password on record for user
*/
const comparePassword = (candidatePassword, trustedPassword) => {
return bcrypt.compareSync(candidatePassword, trustedPassword);
};
module.exports = {
getToken,
convertToPublicFormat,
hashPassword,
comparePassword,
}

View File

@@ -1,61 +0,0 @@
const AWS = require(`aws-sdk`);
const shortid = require(`shortid`);
const commonSchemas = require(`../validation/commonSchemas`);
const validation = require(`../validation`);
const dynamodb = new AWS.DynamoDB.DocumentClient({
region: process.env.AWS_REGION,
});
const defaultParams = {
TableName: process.env.db,
};
const put = async (kind, item) => {
const params = {
...defaultParams,
Item: {
...item,
sk: kind,
sk2: shortid.generate(),
createdAt: Date.now(),
updatedAt: Date.now(),
},
};
await dynamodb.put(params).promise();
};
const getByKey = async (key) => {
const params = {
...defaultParams,
KeyConditionExpression: `hk = :hk`,
ExpressionAttributeValues: { ":hk": key },
};
const obj = await dynamodb.query(params).promise();
return obj.Items && obj.Items[0] ? obj.Items[0] : null;
};
const getById = async (kind, id) => {
validation.validateOrThrow(commonSchemas.id, id);
const params = {
...defaultParams,
IndexName: process.env.dbIndex1,
KeyConditionExpression: `sk2 = :sk2 and sk = :sk`,
ExpressionAttributeValues: {
":sk2": id,
":sk": kind,
},
};
const obj = await dynamodb.query(params).promise();
return obj.Items && obj.Items[0] ? obj.Items[0] : null;
};
module.exports = {
put,
getByKey,
getById,
};

View File

@@ -1,33 +0,0 @@
{
"name": "forgetmenot-api",
"version": "1.0.0",
"description": "",
"main": "app.js",
"dependencies": {
"ajv": "^6.12.6",
"aws-sdk": "^2.792.0",
"bcryptjs": "^2.4.3",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"passport": "^0.4.1",
"passport-jwt": "^4.0.0",
"pino-http": "^5.3.0",
"shortid": "^2.2.15"
},
"devDependencies": {
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"eslint": "^7.12.1",
"eslint-config-prettier": "^6.15.0",
"faker": "^5.1.0",
"mocha": "^8.2.1",
"prettier": "^2.1.2"
},
"scripts": {
"pretest": "eslint --ignore-path ../.gitignore .",
"test": "mocha",
"format": "prettier --write \"**/*.{js,json,md,yml}\""
},
"author": "",
"license": "ISC"
}

View File

@@ -1,18 +0,0 @@
component: express
name: api
inputs:
# Express application source code.
src: ./
# Permissions required for the AWS Lambda function to interact with other resources
roleName: ${output:permissions.name}
# Enable this when you want to set a custom domain.
# domain: api.${env:domain}
# Environment variables
env:
# AWS DynamoDB Table name. Needed for the code to access it.
db: ${output:database.name}
# AWS DynamoDB Table Index name. Needed for the code to access it.
dbIndex1: ${output:database.indexes.gsi1.name}
# A secret token to sign the JWT tokens with.
tokenSecret: ${env:tokenSecret} # Change to secret via environment variable: ${env:tokenSecret}

View File

@@ -1,10 +0,0 @@
module.exports = {
"email": {
"type": "string",
"format": "email"
},
"id": {
"type": "string",
"minLength": 1
}
}

View File

@@ -1,29 +0,0 @@
/*
* Validation
*/
const Ajv = require(`ajv`);
const validateOrThrow = (schema, data) => {
const ajv = new Ajv();
const valid = ajv.validate(schema, data);
if (!valid) {
throw validationError(ajv.errors);
}
};
/**
* Build a custom validation error
* @param {array[string]} validationErrors Validation errors
*/
const validationError = (validationErrors = []) => {
let error = new Error("Validation error(s)");
error.validationErrors = validationErrors;
error.code = 400;
return error;
};
module.exports = {
validateOrThrow,
};

View File

@@ -1,30 +0,0 @@
component: aws-dynamodb
name: database
inputs:
name: ${name}-${stage}
region: us-east-1
# Don't delete the Database Table if "serverless remove" is run
deletionPolicy: retain
# Simple, single-table design
attributeDefinitions:
- AttributeName: hk
AttributeType: S
- AttributeName: sk
AttributeType: S
- AttributeName: sk2
AttributeType: S
keySchema:
- AttributeName: hk
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
globalSecondaryIndexes:
- IndexName: gsi1
KeySchema:
- AttributeName: sk2
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
Projection:
ProjectionType: ALL

View File

@@ -1,28 +0,0 @@
component: aws-iam-role
name: permissions
inputs:
name: ${name}-${stage}
region: us-east-1
service: lambda.amazonaws.com
policy:
# AWS Lambda function containing Express Logs and Assume Role access
- Effect: Allow
Action:
- sts:AssumeRole
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
# AWS DynamoDB Table access
- Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:Query
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- ${output:database.arn}
- ${output:database.arn}/index/*

View File

@@ -1,2 +0,0 @@
app: forget-me-not
org: benjaminramey

View File

@@ -1,11 +0,0 @@
{
"extends": ["eslint:recommended", "prettier"],
"env": {
"node": false,
"browser": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 2018
}
}

69
site/.gitignore vendored
View File

@@ -1,69 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# dotenv environment variable files
.env*
# gatsby files
.cache/
public
# Mac files
.DS_Store
# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity

View File

@@ -1,4 +0,0 @@
.cache
package.json
package-lock.json
public

View File

@@ -1,14 +0,0 @@
The BSD Zero Clause License (0BSD)
Copyright (c) 2020 Gatsby Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@@ -1,99 +0,0 @@
<!-- AUTO-GENERATED-CONTENT:START (STARTER) -->
<p align="center">
<a href="https://www.gatsbyjs.com">
<img alt="Gatsby" src="https://www.gatsbyjs.com/Gatsby-Monogram.svg" width="60" />
</a>
</p>
<h1 align="center">
Gatsby's hello-world starter
</h1>
Kick off your project with this hello-world boilerplate. This starter ships with the main Gatsby configuration files you might need to get up and running blazing fast with the blazing fast app generator for React.
_Have another more specific idea? You may want to check out our vibrant collection of [official and community-created starters](https://www.gatsbyjs.com/docs/gatsby-starters/)._
## 🚀 Quick start
1. **Create a Gatsby site.**
Use the Gatsby CLI to create a new site, specifying the hello-world starter.
```shell
# create a new Gatsby site using the hello-world starter
gatsby new my-hello-world-starter https://github.com/gatsbyjs/gatsby-starter-hello-world
```
1. **Start developing.**
Navigate into your new sites directory and start it up.
```shell
cd my-hello-world-starter/
gatsby develop
```
1. **Open the source code and start editing!**
Your site is now running at `http://localhost:8000`!
_Note: You'll also see a second link: _`http://localhost:8000/___graphql`_. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the [Gatsby tutorial](https://www.gatsbyjs.com/tutorial/part-five/#introducing-graphiql)._
Open the `my-hello-world-starter` directory in your code editor of choice and edit `src/pages/index.js`. Save your changes and the browser will update in real time!
## 🧐 What's inside?
A quick look at the top-level files and directories you'll see in a Gatsby project.
.
├── node_modules
├── src
├── .gitignore
├── .prettierrc
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── gatsby-ssr.js
├── LICENSE
├── package-lock.json
├── package.json
└── README.md
1. **`/node_modules`**: This directory contains all of the modules of code that your project depends on (npm packages) are automatically installed.
2. **`/src`**: This directory will contain all of the code related to what you will see on the front-end of your site (what you see in the browser) such as your site header or a page template. `src` is a convention for “source code”.
3. **`.gitignore`**: This file tells git which files it should not track / not maintain a version history for.
4. **`.prettierrc`**: This is a configuration file for [Prettier](https://prettier.io/). Prettier is a tool to help keep the formatting of your code consistent.
5. **`gatsby-browser.js`**: This file is where Gatsby expects to find any usage of the [Gatsby browser APIs](https://www.gatsbyjs.com/docs/browser-apis/) (if any). These allow customization/extension of default Gatsby settings affecting the browser.
6. **`gatsby-config.js`**: This is the main configuration file for a Gatsby site. This is where you can specify information about your site (metadata) like the site title and description, which Gatsby plugins youd like to include, etc. (Check out the [config docs](https://www.gatsbyjs.com/docs/gatsby-config/) for more detail).
7. **`gatsby-node.js`**: This file is where Gatsby expects to find any usage of the [Gatsby Node APIs](https://www.gatsbyjs.com/docs/node-apis/) (if any). These allow customization/extension of default Gatsby settings affecting pieces of the site build process.
8. **`gatsby-ssr.js`**: This file is where Gatsby expects to find any usage of the [Gatsby server-side rendering APIs](https://www.gatsbyjs.com/docs/ssr-apis/) (if any). These allow customization of default Gatsby settings affecting server-side rendering.
9. **`LICENSE`**: This Gatsby starter is licensed under the 0BSD license. This means that you can see this file as a placeholder and replace it with your own license.
10. **`package-lock.json`** (See `package.json` below, first). This is an automatically generated file based on the exact versions of your npm dependencies that were installed for your project. **(You wont change this file directly).**
11. **`package.json`**: A manifest file for Node.js projects, which includes things like metadata (the projects name, author, etc). This manifest is how npm knows which packages to install for your project.
12. **`README.md`**: A text file containing useful reference information about your project.
## 🎓 Learning Gatsby
Looking for more guidance? Full documentation for Gatsby lives [on the website](https://www.gatsbyjs.com/). Here are some places to start:
- **For most developers, we recommend starting with our [in-depth tutorial for creating a site with Gatsby](https://www.gatsbyjs.com/tutorial/).** It starts with zero assumptions about your level of ability and walks through every step of the process.
- **To dive straight into code samples, head [to our documentation](https://www.gatsbyjs.com/docs/).** In particular, check out the _Guides_, _API Reference_, and _Advanced Tutorials_ sections in the sidebar.
## 💫 Deploy
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/gatsbyjs/gatsby-starter-hello-world)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/gatsbyjs/gatsby-starter-hello-world)
<!-- AUTO-GENERATED-CONTENT:END -->

View File

@@ -1,18 +0,0 @@
/**
* Global Config
*/
const config = {};
// Domains
config.domains = {};
/**
* API Domain
* Add the domain from your serverless express.js back-end here.
* 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://13gtp0hrak.execute-api.us-east-1.amazonaws.com";
export default config;

View File

@@ -1,10 +0,0 @@
/**
* Configure your Gatsby site with this file.
*
* See: https://www.gatsbyjs.com/docs/gatsby-config/
*/
module.exports = {
/* Your site config here */
plugins: [],
};

View File

@@ -1,37 +0,0 @@
{
"name": "forget-me-not-site",
"private": true,
"description": "Forget Me Not site for saving bookmarks",
"version": "0.1.0",
"license": "0BSD",
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md,yml}\"",
"start": "npm run develop",
"serve": "gatsby serve",
"clean": "gatsby clean",
"test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
},
"dependencies": {
"gatsby": "^2.25.3",
"js-cookie": "^2.2.1",
"moment": "^2.24.0",
"react": "^16.12.0",
"react-bootstrap": "^1.4.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2"
},
"devDependencies": {
"eslint": "^7.12.1",
"eslint-config-prettier": "^6.15.0",
"prettier": "^2.1.2"
},
"repository": {
"type": "git",
"url": "https://github.com/gatsbyjs/gatsby-starter-hello-world"
},
"bugs": {
"url": "https://github.com/gatsbyjs/gatsby/issues"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1 +0,0 @@
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta http-equiv="x-ua-compatible" content="ie=edge"/><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/><meta name="note" content="environment=development"/><script src="/socket.io/socket.io.js"></script></head><body><div id="___gatsby"></div><script src="/polyfill.js" nomodule=""></script><script src="/commons.js"></script></body></html>

View File

@@ -1,25 +0,0 @@
{
"short_name": "Forget Me Not",
"name": "Forget Me Not",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "android-chrome-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "android-chrome-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -1,11 +0,0 @@
component: website
name: site
inputs:
# React application. "hook" runs before deployment to build the source code. "dist" is the built artifact directory which is uploaded.
src:
src: ./
hook: npm run build
dist: public
# Enable this when you want to set a custom domain.
# domain: ${env:domain}

View File

@@ -1,14 +0,0 @@
import React from "react";
import { Container, Row, Col } from "react-bootstrap";
export default function Home() {
return (
<Container>
<Row>
<Col>
<p>Hello world!</p>
</Col>
</Row>
</Container>
);
}

View File

@@ -1,70 +0,0 @@
/**
* Utils: Back-end
*/
import config from "../../config";
/**
* Register a new user
*/
export const userRegister = async (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 });
};
/**
* userGet
*/
export const userGet = async (token) => {
return await requestApi("/user", "POST", null, {
Authorization: `Bearer ${token}`,
});
};
/**
* API request to call the backend
*/
export const requestApi = async (
path = "",
method = "GET",
data = null,
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.`
);
}
// Prepare URL
if (!path.startsWith("/")) {
path = `/${path}`;
}
const url = `${config.domains.api}${path}`;
// Set 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",
headers,
body: data ? JSON.stringify(data) : null,
});
if (response.status < 200 || response.status >= 300) {
const error = await response.json();
throw new Error(error.error);
}
return await response.json();
};

View File

@@ -1,81 +0,0 @@
import Cookies from "js-cookie";
/**
* Format Org and Username correctly for the Serverless Platform backend
*/
export const formatOrgAndUsername = (name = "") => {
name = name
.toString()
.toLowerCase()
.replace(/[^a-z\d-]+/gi, "-");
// Remove multiple instances of hyphens
name = name.replace(/-{2,}/g, "-");
if (name.length > 40) {
name = name.substring(0, 40);
}
return name;
};
/**
* Parse query parameters in a URL
* @param {*} searchString
*/
export const parseQueryParams = (searchString = null) => {
if (!searchString) {
return null;
}
// Clone string
let clonedParams = (" " + searchString).slice(1);
return clonedParams
.substr(1)
.split("&")
.filter((el) => el.length)
.map((el) => el.split("="))
.reduce(
(accumulator, currentValue) =>
Object.assign(accumulator, {
[decodeURIComponent(
currentValue.shift()
)]: decodeURIComponent(currentValue.pop()),
}),
{}
);
};
/**
* Parse hash fragment parameters in a URL
*/
export const parseHashFragment = (hashString) => {
const hashData = {};
let hash = decodeURI(hashString);
hash = hash.split("&");
hash.forEach((val) => {
val = val.replace("#", "");
hashData[val.split("=")[0]] = val.split("=")[1];
});
return hashData;
};
/**
* Save session in browser cookie
*/
export const saveSession = (userId, userEmail, userToken) => {
Cookies.set("serverless", { userId, userEmail, userToken });
};
/**
* Get session in browser cookie
*/
export const getSession = () => {
const data = Cookies.get("serverless");
return data ? JSON.parse(data) : null;
};
/**
* Delete session in browser cookie
*/
export const deleteSession = () => {
Cookies.remove("serverless");
};

View File

@@ -1,2 +0,0 @@
export * from "./helpers";
export * from "./api";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,25 +0,0 @@
{
"short_name": "Forget Me Not",
"name": "Forget Me Not",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "android-chrome-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "android-chrome-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow: