Fix most eslint violations

This commit is contained in:
2020-11-01 20:44:07 -06:00
parent 67149fc661
commit 33aaddad9c
11 changed files with 281 additions and 220 deletions

22
.editorconfig Normal file
View File

@@ -0,0 +1,22 @@
# 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}]
indent_style = space
indent_size = 2

33
api/.eslintrc.json Normal file
View File

@@ -0,0 +1,33 @@
{
"extends": "eslint:recommended",
"env": {
"node": true,
"browser": false,
"es6": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"semi": [
"error"
],
"no-trailing-spaces": [
"error"
],
"object-curly-newline": [
"error"
],
"object-property-newline": [
"error"
],
"quotes": [
"error",
"backtick"
],
"indent": [
"error",
"tab"
]
}
}

View File

@@ -1,16 +1,16 @@
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 users
} = require('./controllers') } = require(`./controllers`);
/** /**
* Configure Passport * Configure Passport
*/ */
try { require('./config/passport')(passport) } try { require(`./config/passport`)(passport); }
catch (error) { console.log(error) } catch (error) { console.log(error); }
/** /**
* Configure Express.js Middleware * Configure Express.js Middleware
@@ -18,19 +18,19 @@ 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
@@ -45,37 +45,37 @@ 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) {
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,25 @@
* 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')
module.exports = (passport) => { module.exports = (passport) => {
const options = {} const options = {};
options.jwtFromRequest = ExtractJWT.fromAuthHeaderAsBearerToken() options.jwtFromRequest = ExtractJWT.fromAuthHeaderAsBearerToken();
options.secretOrKey = process.env.tokenSecret options.secretOrKey = process.env.tokenSecret;
passport.use(new StrategyJWT(options, async (jwtPayload, done) => { passport.use(new StrategyJWT(options, async (jwtPayload, done) => {
let user let user;
try { user = await users.getById(jwtPayload.id) } try { user = await users.getById(jwtPayload.id); }
catch (error) { catch (error) {
console.log(error) console.log(error);
return done(error, null) return done(error, null);
} }
if (!user) { return done(null, false) } if (!user) { return done(null, false); }
return done(null, user) 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,9 +2,9 @@
* 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
@@ -13,27 +13,29 @@ const { comparePassword } = require('../utils')
* @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
@@ -41,27 +43,29 @@ const register = async (req, res, next) => {
* @param {*} res * @param {*} res
* @param {*} next * @param {*} next
*/ */
const login = async (req, res, next) => { const login = async (req, res) => {
let user;
let user try { user = await users.getByEmail(req.body.email); }
try { user = await users.getByEmail(req.body.email) } catch (error) { return done(error, null); }
catch (error) { return done(error, null) }
if (!user) { if (!user) {
return res.status(404).send({ error: 'Authentication failed. User not found.' }) return res.status(404).send({ error: `Authentication failed. User not found.` });
} }
const isCorrect = comparePassword(req.body.password, user.password) 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
@@ -69,13 +73,13 @@ const login = async (req, res, next) => {
* @param {*} res * @param {*} res
* @param {*} next * @param {*} next
*/ */
const get = async (req, res, next) => { const get = async (req, res) => {
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

@@ -1,14 +1,15 @@
"use strict";
/** /**
* 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
@@ -18,39 +19,37 @@ const dynamodb = new AWS.DynamoDB.DocumentClient({
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
@@ -61,28 +60,28 @@ 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
@@ -93,25 +92,26 @@ 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
@@ -119,18 +119,18 @@ const getById = async(id) => {
* @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,5 +1,5 @@
{ {
"name": "serverless-fullstack-app-api", "name": "forgetmenot-api",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "app.js", "main": "app.js",
@@ -11,8 +11,11 @@
"passport-jwt": "^4.0.0", "passport-jwt": "^4.0.0",
"shortid": "^2.2.15" "shortid": "^2.2.15"
}, },
"devDependencies": {}, "devDependencies": {
"eslint": "^7.12.1"
},
"scripts": { "scripts": {
"pretest": "eslint --ignore-path ../.gitignore .",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",

0
api/utils/db.js Normal file
View File

View File

@@ -2,34 +2,34 @@
* 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 = /^(([^<>()[\]\\.,;:\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()) 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
} };