diff --git a/.env b/.env index c3b28c2..74c3547 100644 --- a/.env +++ b/.env @@ -2,3 +2,4 @@ CLIENT_ID = your_client_id_here CLIENT_SECRET = your_client_secret_here SESSION_SECRET = 'your_session_secret_string_here' PORT = 9001 +TRUST_PROXY = 1 diff --git a/.env.development b/.env.development index 912f800..49b4ed7 100644 --- a/.env.development +++ b/.env.development @@ -1,7 +1,6 @@ REDIRECT_URI = http://localhost:9001/api/auth/callback -TRUST_PROXY = 1 -PG_USER = your_postgres_username -PG_PASSWD = your_postgres_password -PG_DATABASE = postgres_database_name -PG_HOST = localhost -PG_PORT = postgres_instance_port +DB_USER = your_database_username +DB_PASSWD = your_database_password +DB_NAME = your_database_name +DB_HOST = localhost +DB_PORT = your_database_port diff --git a/.env.production b/.env.production index c2892a6..136b72d 100644 --- a/.env.production +++ b/.env.production @@ -1,2 +1 @@ REDIRECT_URI = https://domain.for.this.app/api/auth/callback -TRUST_PROXY=1 diff --git a/.gitignore b/.gitignore index f20e8ae..1976619 100644 --- a/.gitignore +++ b/.gitignore @@ -70,9 +70,7 @@ typings/ # dotenv environment variables file .env.local -.env.development.local -.env.production.local -.env.test.local +.env.*.local # parcel-bundler cache (https://parceljs.org/) .cache diff --git a/boilerplates/route.js b/boilerplates/route.js index 75ac8d8..95d4636 100644 --- a/boilerplates/route.js +++ b/boilerplates/route.js @@ -1,6 +1,6 @@ const router = require('express').Router(); -const validator = require("../validators"); +const { validate } = require("../validators"); router.get( diff --git a/config/sequelize.js b/config/sequelize.js index 54d2bec..a43c492 100644 --- a/config/sequelize.js +++ b/config/sequelize.js @@ -1,33 +1,28 @@ const logger = require("../utils/logger")(module); -let connConfigs = { +const connConfigs = { development: { - username: process.env.PG_USER, - password: process.env.PG_PASSWD, - database: process.env.PG_DATABASE, - host: process.env.PG_HOST, - port: process.env.PG_PORT, + username: process.env.DB_USER || 'postgres', + password: process.env.DB_PASSWD || '', + database: process.env.DB_NAME || 'postgres', + host: process.env.DB_HOST || '127.0.0.1', + port: process.env.DB_PORT || 5432, }, - test: { - username: process.env.PG_USER, - password: process.env.PG_PASSWD, - database: process.env.PG_DATABASE, - host: process.env.PG_HOST, - port: process.env.PG_PORT, + staging: { + use_env_variable: "DB_URL", // use connection string for non-dev env }, production: { - username: process.env.PG_USER, - password: process.env.PG_PASSWD, - database: process.env.PG_DATABASE, - host: process.env.PG_HOST, - port: process.env.PG_PORT, - }, + use_env_variable: "DB_URL", // use connection string for non-dev env + // dialectOptions: { + // ssl: true, + // }, + } } // common config for (const conf in connConfigs) { connConfigs[conf]['logging'] = (msg) => logger.debug(msg); - connConfigs[conf]['dialect'] = 'postgres'; + connConfigs[conf]['dialect'] = process.env.DB_DIALECT || 'postgres'; } -module.exports = connConfigs; \ No newline at end of file +module.exports = connConfigs; diff --git a/constants.js b/constants.js index 5ac49c6..5c36fc0 100644 --- a/constants.js +++ b/constants.js @@ -22,4 +22,4 @@ module.exports = { sessionName, stateKey, scopes -} \ No newline at end of file +}; diff --git a/index.js b/index.js index 4d94ed7..abd7a33 100644 --- a/index.js +++ b/index.js @@ -7,10 +7,11 @@ const session = require("express-session"); const cors = require('cors'); const cookieParser = require('cookie-parser'); const helmet = require("helmet"); - const SQLiteStore = require("connect-sqlite3")(session); -const db = require("./models"); + const { sessionName } = require('./constants'); +const db = require("./models"); + const { isAuthenticated } = require('./middleware/authCheck'); const logger = require("./utils/logger")(module); @@ -40,12 +41,10 @@ app.use(session({ })); app.use(cors()); -app.use(cookieParser()); - -// Configure helmet app.use(helmet()); -app.disable('x-powered-by') +app.disable('x-powered-by'); +app.use(cookieParser()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); @@ -58,13 +57,15 @@ app.use("/api/playlists", isAuthenticated, require("./routes/playlists")); app.use("/api/operations", isAuthenticated, require("./routes/operations")); // Fallbacks -app.use((_req, res) => { - return res.status(404).send( +app.use((req, res) => { + res.status(404).send( "Guess the cat's out of the bag!" ); + logger.info("Unrecognized URL", { url: req.url }); + return; }); -const port = process.env.PORT || 3000; +const port = process.env.PORT || 5000; const server = app.listen(port, () => { logger.info(`App Listening on port ${port}`); diff --git a/models/index.js b/models/index.js index ef7de7a..ec69f22 100644 --- a/models/index.js +++ b/models/index.js @@ -2,14 +2,12 @@ const fs = require("fs"); const path = require("path"); const Sequelize = require("sequelize"); -const typedefs = require("../typedefs"); const logger = require("../utils/logger")(module); const basename = path.basename(__filename); const env = process.env.NODE_ENV || "development"; const config = require(__dirname + "/../config/sequelize.js")[env]; const db = {}; -/** @type {typedefs.Sequelize} */ let sequelize; if (config.use_env_variable) { sequelize = new Sequelize(process.env[config.use_env_variable], config); @@ -48,4 +46,4 @@ Object.keys(db).forEach(modelName => { db.sequelize = sequelize; db.Sequelize = Sequelize; -module.exports = db; \ No newline at end of file +module.exports = db; diff --git a/package.json b/package.json index 92db2e3..ee3d8b4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "node index.js", - "dev": "cross-env NODE_ENV=development nodemon index.js" + "dev": "cross-env NODE_ENV=development nodemon --exitcrash index.js" }, "repository": { "type": "git", diff --git a/typedefs.js b/typedefs.js index 5ebe5e9..b7b1275 100644 --- a/typedefs.js +++ b/typedefs.js @@ -5,12 +5,6 @@ * @typedef {import('express').Response} Res * @typedef {import('express').NextFunction} Next * - * @typedef {import("sequelize").Sequelize} Sequelize - * @typedef {import("sequelize").Model} Model - * @typedef {import("sequelize").QueryInterface} QueryInterface - * - * @typedef {import('winston').Logger} Logger - * * @typedef {{ * type: string, * is_local: boolean, diff --git a/utils/jsonTransformer.js b/utils/jsonTransformer.js index 16e3110..3f88461 100644 --- a/utils/jsonTransformer.js +++ b/utils/jsonTransformer.js @@ -1,10 +1,11 @@ /** - * Returns a single string of the values of all keys in the given JSON object, even nested ones. + * Stringifies only values of a JSON object, including nested ones * - * @param {*} obj + * @param {any} obj JSON object + * @param {string} delimiter Delimiter of final string * @returns {string} */ -const getNestedValuesString = (obj) => { +const getNestedValuesString = (obj, delimiter = ', ') => { let values = []; for (key in obj) { if (typeof obj[key] !== "object") { @@ -14,7 +15,7 @@ const getNestedValuesString = (obj) => { } } - return values.join(); + return values.join(delimiter); } module.exports = { diff --git a/utils/logger.js b/utils/logger.js index 3b46dce..5030cff 100644 --- a/utils/logger.js +++ b/utils/logger.js @@ -1,7 +1,7 @@ const path = require("path"); const { createLogger, transports, config, format } = require('winston'); -const { colorize, combine, label, timestamp, printf, errors } = format; +const { combine, label, timestamp, printf, errors } = format; const typedefs = require("../typedefs"); @@ -36,10 +36,9 @@ const logFormat = printf(({ level, message, label, timestamp, ...meta }) => { /** * Creates a curried function, and call it with the module in use to get logs with filename * @param {typedefs.Module} callingModule The module from which the logger is called - * @returns {typedefs.Logger} */ -const logger = (callingModule) => { - let tmpLogger = createLogger({ +const curriedLogger = (callingModule) => { + let winstonLogger = createLogger({ levels: config.npm.levels, format: combine( errors({ stack: true }), @@ -48,7 +47,7 @@ const logger = (callingModule) => { logFormat, ), transports: [ - new transports.Console({ level: 'debug' }), + new transports.Console({ level: 'info' }), new transports.File({ filename: __dirname + '/../logs/debug.log', level: 'debug', @@ -57,12 +56,12 @@ const logger = (callingModule) => { new transports.File({ filename: __dirname + '/../logs/error.log', level: 'error', - maxsize: 10485760, + maxsize: 1048576, }), ] }); - tmpLogger.on('error', (error) => tmpLogger.crit("Error inside logger", { error })); - return tmpLogger; + winstonLogger.on('error', (error) => winstonLogger.error("Error inside logger", { error })); + return winstonLogger; } -module.exports = logger; \ No newline at end of file +module.exports = curriedLogger; diff --git a/validators/index.js b/validators/index.js index 6d5d3b5..e889ebe 100644 --- a/validators/index.js +++ b/validators/index.js @@ -1,9 +1,10 @@ const { validationResult } = require("express-validator"); -const typedefs = require("../typedefs"); const { getNestedValuesString } = require("../utils/jsonTransformer"); const logger = require("../utils/logger")(module); +const typedefs = require("../typedefs"); + /** * Refer: https://stackoverflow.com/questions/58848625/access-messages-in-express-validator * @@ -16,10 +17,21 @@ const validate = (req, res, next) => { if (errors.isEmpty()) { return next(); } - const extractedErrors = [] - errors.array().map(err => extractedErrors.push({ - [err.path]: err.msg - })); + + const extractedErrors = []; + errors.array().forEach(err => { + if (err.type === 'alternative') { + err.nestedErrors.forEach(nestedErr => { + extractedErrors.push({ + [nestedErr.path]: nestedErr.msg + }); + }); + } else if (err.type === 'field') { + extractedErrors.push({ + [err.path]: err.msg + }); + } + }); res.status(400).json({ message: getNestedValuesString(extractedErrors), @@ -31,4 +43,4 @@ const validate = (req, res, next) => { module.exports = { validate -} \ No newline at end of file +};