Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.example
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ DB_USERNAME=username
DB_PASSWORD=password
DB_DATABASE=database_name
DB_HOST=server
SECRET_KEY=strong_random_bytes
DEBUG=starter:server
JWT_SECRET_KEY=strong_random_bytes
JWT_EXPIRES_IN=604800
42 changes: 25 additions & 17 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ This is the backend for the Solo React project.
2. Install dependencies (`npm install`)
3. Create a **.env** file based on the example with proper settings for your
development environment
4. Setup your PostgreSQL user, password and database and make sure it matches your **.env** file with CREATEDB privileges
4. Setup your PostgreSQL user, password and database and make sure it matches
your **.env** file with CREATEDB privileges

5. Run
* `npm run db:create`
Expand All @@ -19,20 +20,27 @@ This is the backend for the Solo React project.
## Deploy to Heroku

1. Create a new project
2. Under Resources click "Find more add-ons" and add the add on called "Heroku Postgres"
3. Install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-command-line)
2. Under Resources click "Find more add-ons" and add the add on called "Heroku
Postgres"
3. Install the [Heroku CLI]
4. Run `$ heroku login`
5. Add heroku as a remote to this git repo `$ heroku git:remote -a <project_name>`
6. Push the project to heroku `$ git push heroku master`
7. Connect to the heroku shell and prepare your database

```bash
$ heroku run bash
$ sequelize-cli db:migrate
$ sequelize-cli db:seed:all
```
(You can interact with your database this way as youd like, but beware that `db:drop` should not be run in the heroku environment)

8. Add a `REACT_APP_BASE_URL` config var. This should be the full URL of your react app: i.e. "https://solo-react.herokuapp.com"

9. profit
5. Add Heroku as a remote to this git repo `$ heroku git:remote -a <project_name>`
6. Push the project to Heroku `$ git push heroku master`
7. Connect to the Heroku shell and prepare your database

```bash
$ heroku run bash
$ sequelize-cli db:migrate
$ sequelize-cli db:seed:all
```

(You can interact with your database this way as you'd like, but beware that
`db:drop` should not be run in the Heroku environment. If you want to drop
and create the database, you need to remove and add back the "Heroku
Postgres" add-on.)

8. Add environment variables on the Heroku environment using the Heroku
dashboard. [Setting Heroku Config Vars]

[Heroku CLI]: https://devcenter.heroku.com/articles/heroku-command-line
[Setting Heroku Config Variables]: https://devcenter.heroku.com/articles/config-vars
83 changes: 63 additions & 20 deletions app.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const path = require('path');
const logger = require('morgan');
const csurf = require('csurf');
const routes = require('./routes');
const { ValidationError } = require("sequelize");
const { AuthenticationError } = require('./routes/util/auth');

const app = express();

Expand All @@ -15,46 +17,87 @@ app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));

app.use(cookieParser())

app.use(cookieParser());

// Security Middleware
app.use(cors({ origin: true }));
app.use(helmet({ hsts: false }));
app.use(csurf({
cookie: {
secure: process.env.NODE_ENV === 'production',
sameSite: process.env.NODE_ENV === 'production',
httpOnly: true
}
}));

if (process.env.NODE_ENV !== 'production') {
// enable cors only in development
app.use(cors());
}
// helmet helps set a variety of headers to better secure your app
app.use(helmet());
app.use(
csurf({
cookie: {
secure: process.env.NODE_ENV === 'production',
sameSite: process.env.NODE_ENV === 'production',
httpOnly: true,
},
})
);

// connect the routes from the /routes folder
app.use(routes);

// Serve React Application
// This should come after routes, but before 404 and error handling.
if (process.env.NODE_ENV === "production") {
if (process.env.NODE_ENV === 'production') {
// Serve the client's index.html file at the root route
app.get('/', (req, res) => {
res.cookie("XSRF-TOKEN", req.csrfToken());
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});

// Serve the static assets in the client's build folder
app.use(express.static("client/build"));

// Serve the client's index.html file at all other routes NOT starting with /api
app.get(/\/(?!api)*/, (req, res) => {
res.cookie("XSRF-TOKEN", req.csrfToken());
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}


// create a 404 error for any requests that reach this middleware
// (requests will not reach this middleware if the response has been resolved)
app.use(function(_req, _res, next) {
next(createError(404));
});

// If error is a sequelize error, make the errors look pretty
app.use((err, _req, _res, next) => {
// check if error is a Sequelize error:
if (err instanceof ValidationError) {
err.errors = err.errors.map((e) => e.message);
err.title = "Sequelize Error";
}
err.status = 422;
next(err);
});

// Error handler
app.use(function(err, _req, res, _next) {
res.status(err.status || 500);
if (err.status === 401) {
res.set('WWW-Authenticate', 'Bearer');

// If there is an authentication error, clear the token
if (err instanceof AuthenticationError) {
res.clearCookie('token');
}

// If in production, don't show the error stack trace
if (process.env.NODE_ENV === 'production') {
res.json({
message: err.message,
error: { errors: err.errors },
});
} else {
console.log(err.stack);
res.json({
message: err.message,
stack: err.stack,
error: JSON.parse(JSON.stringify(err)),
});
}
res.json({
message: err.message,
error: JSON.parse(JSON.stringify(err)),
});
});

module.exports = app;
21 changes: 16 additions & 5 deletions bin/www
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,38 @@
const app = require('../app');
const debug = require('debug')('starter:server');
const http = require('http');
const db = require("../db/models");
const config = require("../config");

/**
* Get port from environment and store in Express.
*/

const port = normalizePort(process.env.PORT || '5000');
const port = normalizePort(config.port || '5000');
app.set('port', port);

/**
* Create HTTP server.
*/

const server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
server.on("error", onError);
server.on("listening", onListening);

db.sequelize
.authenticate()
.then(() => {
debug("Connected to database successfully");
server.listen(port);
})
.catch(() => {
console.error("Error connecting to database");
process.exit(1);
});

/**
* Normalize a port into a number, string, or false.
Expand Down
58 changes: 54 additions & 4 deletions client/README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,8 +1,58 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
# Client

Your React App will live here. While is development, run this application from this location using `npm start` or `yarn start`.
This project was bootstrapped with [Create React App].

Your React App will live here.

No environment variables are needed to run this application in development, but be sure to set the REACT_APP_BASE_URL environment variable in heroku!
## Start Up in Development

This app will be automatically built when you deploy to heroku, please see the `heroku-postbuild` script in your `express.js` applications `package.json` to see how this works.
While is development, run this application from this location using `npm start`
or `yarn start`.

No environment variables are needed to run this application in development,
but be sure to edit the "proxy" in the `package.json` to the reflect port of
your backend server.

## Deploy to Production

This app will be automatically built when you deploy to Heroku, please see the
`heroku-postbuild` script in your `express.js` application's `package.json`,
**NOT** React's `package.json` to see how this works.

## Using Redux

If you are using Redux, then run:

`npm install redux react-redux redux-thunk`

## CSRF Protection

On all request methods besides `GET`, you need to define a `CSRF-TOKEN` header
that has a value of the `XSRF-TOKEN` cookie.

Example of a fetch request with CSRF header:

```js
import Cookies from 'js-cookie';

const login = async () => {
const csrfToken = Cookies.get("XSRF-TOKEN");
const res = await fetch("/api/session", {
method: "put",
headers: {
"Content-Type": "application/json",
"CSRF-TOKEN": csrfToken,
},
body: JSON.stringify({
username: "Demo-lition",
password: "password"
}),
});
res.data = await res.json(); // current user info
if (res.ok) {
return res.data;
}
};
```

[Create React App]: https://github.com/facebook/create-react-app
5 changes: 5 additions & 0 deletions client/package-lock.json
100644 → 100755

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"http-proxy-middleware": "^1.0.5",
"js-cookie": "^2.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
Expand Down
40 changes: 21 additions & 19 deletions client/src/App.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import React from 'react';
import { BrowserRouter, Switch, Route, NavLink } from 'react-router-dom';
import React, { useState, useEffect } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';

import UserList from './components/UsersList';
function App() {
const [loading, setLoading] = useState(true);

// Check to see if there is a user logged in before loading the application
useEffect(() => {
const loadUser = async () => {
const res = await fetch("/api/session");
if (res.ok) {
res.data = await res.json(); // current user info
console.log(res.data);
// if using Redux, add current user info to the store
}
setLoading(false);
}
loadUser();
}, []);

function App() {
if (loading) return null;

return (
<BrowserRouter>
<nav>
<ul>
<li><NavLink to="/" activeClass="active">Home</NavLink></li>
<li><NavLink to="/users" activeClass="active">Users</NavLink></li>
</ul>
</nav>
<Switch>
<Route path="/users">
<UserList />
</Route>

<Route path="/">
<h1>My Home Page</h1>
</Route>
</Switch>
<Route path="/">
<h1>My Home Page</h1>
</Route>
</BrowserRouter>
);
}
Expand Down
13 changes: 0 additions & 13 deletions client/src/components/User.js

This file was deleted.

Loading