Setup logger, configloader, middleware & endpoints
This commit is contained in:
		
							
								
								
									
										45
									
								
								api/config/configuration.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								api/config/configuration.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| const path = require('path'); | ||||
| const Field = require('./field.js'); | ||||
|  | ||||
| let instance = null; | ||||
|  | ||||
| class Config { | ||||
|    constructor() { | ||||
|       this.location = Config.determineLocation(); | ||||
|       this.fields = require(`${this.location}`); | ||||
|    } | ||||
|  | ||||
|    static getInstance() { | ||||
|       if (instance == null) { | ||||
|          instance = new Config(); | ||||
|       } | ||||
|       return instance; | ||||
|    } | ||||
|  | ||||
|    static determineLocation() { | ||||
|       if (process.env.NODE_ENV === "production") | ||||
|          return path.join(__dirname, "../../config/env/production.json"); | ||||
|       return path.join(__dirname, "../../config/env/development.json"); | ||||
|    } | ||||
|  | ||||
|    get(section, option) { | ||||
|       if (this.fields[section] === undefined || this.fields[section][option] === undefined) { | ||||
|          throw new Error(`Field "${section} => ${option}" does not exist.`); | ||||
|       } | ||||
|  | ||||
|       const field = new Field(this.fields[section][option]); | ||||
|  | ||||
|       if (field.value === '') { | ||||
|          const envField = process.env[[section.toUpperCase(), option.toUpperCase()].join('_')]; | ||||
|          if (envField !== undefined && envField.length !== 0) { return envField; } | ||||
|       } | ||||
|  | ||||
|       if (field.value === undefined) { | ||||
|          throw new Error(`${section} => ${option} is empty.`); | ||||
|       } | ||||
|  | ||||
|       return field.value; | ||||
|    } | ||||
| } | ||||
|  | ||||
| module.exports = Config; | ||||
							
								
								
									
										15
									
								
								api/config/environmentVariables.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								api/config/environmentVariables.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| class EnvironmentVariables { | ||||
|    constructor(variables) { | ||||
|       this.variables = variables || process.env; | ||||
|    } | ||||
|  | ||||
|    get(variable) { | ||||
|       return this.variables[variable]; | ||||
|    } | ||||
|  | ||||
|    has(variable) { | ||||
|       return this.get(variable) !== undefined; | ||||
|    } | ||||
| } | ||||
|  | ||||
| module.exports = EnvironmentVariables; | ||||
							
								
								
									
										49
									
								
								api/config/field.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								api/config/field.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| const Filters = require('./filters.js'); | ||||
| const EnvironmentVariables = require('./environmentVariables.js'); | ||||
|  | ||||
| class Field { | ||||
|    constructor(rawValue, environmentVariables) { | ||||
|       this.rawValue = rawValue; | ||||
|       this.filters = new Filters(rawValue); | ||||
|       this.valueWithoutFilters = this.filters.removeFiltersFromValue(); | ||||
|       this.environmentVariables = new EnvironmentVariables(environmentVariables); | ||||
|    } | ||||
|  | ||||
|    get value() { | ||||
|       if (this.filters.isEmpty()) { | ||||
|          return this.valueWithoutFilters; | ||||
|       } | ||||
|  | ||||
|       if (this.filters.has('base64') && !this.filters.has('env')) { | ||||
|          return Field.base64Decode(this.valueWithoutFilters); | ||||
|       } | ||||
|  | ||||
|       if (this.environmentVariables.has(this.valueWithoutFilters) && | ||||
|           this.environmentVariables.get(this.valueWithoutFilters) === '') { | ||||
|          return undefined; | ||||
|       } | ||||
|  | ||||
|       if (!this.filters.has('base64') && this.filters.has('env')) { | ||||
|          if (this.environmentVariables.has(this.valueWithoutFilters)) { | ||||
|             return this.environmentVariables.get(this.valueWithoutFilters); | ||||
|          } | ||||
|          return undefined; | ||||
|       } | ||||
|  | ||||
|       if (this.filters.has('env') && this.filters.has('base64')) { | ||||
|          if (this.environmentVariables.has(this.valueWithoutFilters)) { | ||||
|             const encodedEnvironmentVariable = this.environmentVariables.get(this.valueWithoutFilters); | ||||
|             return Field.base64Decode(encodedEnvironmentVariable); | ||||
|          } | ||||
|          return undefined; | ||||
|       } | ||||
|  | ||||
|       return this.valueWithoutFilters; | ||||
|    } | ||||
|  | ||||
|    static base64Decode(string) { | ||||
|       return new Buffer(string, 'base64').toString('utf-8'); | ||||
|    } | ||||
| } | ||||
|  | ||||
| module.exports = Field; | ||||
							
								
								
									
										34
									
								
								api/config/filters.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								api/config/filters.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| class Filters { | ||||
|    constructor(value) { | ||||
|       this.value = value; | ||||
|       this.delimiter = '|'; | ||||
|    } | ||||
|  | ||||
|    get filters() { | ||||
|       return this.value.split(this.delimiter).slice(0, -1); | ||||
|    } | ||||
|  | ||||
|    isEmpty() { | ||||
|       return !this.hasValidType() || this.value.length === 0; | ||||
|    } | ||||
|  | ||||
|    has(filter) { | ||||
|       return this.filters.includes(filter); | ||||
|    } | ||||
|  | ||||
|    hasValidType() { | ||||
|       return (typeof this.value === 'string'); | ||||
|    } | ||||
|  | ||||
|    removeFiltersFromValue() { | ||||
|       if (this.hasValidType() === false) { | ||||
|          return this.value; | ||||
|       } | ||||
|  | ||||
|       let filtersCombined = this.filters.join(this.delimiter); | ||||
|       filtersCombined += this.filters.length >= 1 ? this.delimiter : ''; | ||||
|       return this.value.replace(filtersCombined, ''); | ||||
|    } | ||||
| } | ||||
|  | ||||
| module.exports = Filters; | ||||
							
								
								
									
										57
									
								
								api/logger.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								api/logger.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| const winston = require('winston'); | ||||
| const httpContext = require("express-http-context"); | ||||
|  | ||||
| const logLevel = 'trace'; | ||||
|  | ||||
| const customLevels = { | ||||
|   levels: { | ||||
|     fatal: 0, | ||||
|     error: 1, | ||||
|     warning: 2, | ||||
|     info: 3, | ||||
|     debug: 4, | ||||
|     trace: 5 | ||||
|   }, | ||||
|   colors: { | ||||
|     trace: 'blue', | ||||
|     debug: 'white', | ||||
|     info: 'green', | ||||
|     warning: 'yellow', | ||||
|     error: 'red', | ||||
|     fatal: 'red' | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const appendSessionId = winston.format(info => { | ||||
|   info.sessionId = httpContext.get("sessionId"); | ||||
|   return info | ||||
| }); | ||||
|  | ||||
|  | ||||
| const logger = winston.createLogger({ | ||||
|   level: logLevel, | ||||
|   levels: customLevels.levels, | ||||
|   transports: [ | ||||
|     new winston.transports.File({ | ||||
|       filename: `${__base}/logs/all-logs.log`, | ||||
|       format: winston.format.combine( | ||||
|         appendSessionId(), | ||||
|         winston.format.json() | ||||
|       ) | ||||
|     }) | ||||
|   ] | ||||
| }); | ||||
|  | ||||
| winston.addColors(customLevels.colors); | ||||
|  | ||||
| if (process.env.NODE_ENV !== 'production') { | ||||
|  | ||||
|   logger.add(new winston.transports.Console({ | ||||
|     format: winston.format.combine( | ||||
|       winston.format.colorize(), | ||||
|       winston.format.simple() | ||||
|     ) | ||||
|   })); | ||||
| }; | ||||
|  | ||||
| module.exports = logger; | ||||
							
								
								
									
										23
									
								
								api/webserver/middleware/addIdToRequest.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								api/webserver/middleware/addIdToRequest.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| const crypto = require("crypto"); | ||||
| const httpContext = require("express-http-context"); | ||||
|  | ||||
| const addIdToRequest = (req, res, next) => { | ||||
|   try { | ||||
|     crypto.randomBytes(16, (err, buf) => { | ||||
|       if (err) { | ||||
|         // log err | ||||
|         id = null; | ||||
|       } | ||||
|       id = buf.toString("hex"); | ||||
|  | ||||
|       httpContext.set("sessionId", id); | ||||
|       next(); | ||||
|     }); | ||||
|   } catch (err) { | ||||
|     // log err | ||||
|     httpContext.set("sessionId", null); | ||||
|     next(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| module.exports = addIdToRequest; | ||||
							
								
								
									
										6
									
								
								api/webserver/middleware/setupCORS.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								api/webserver/middleware/setupCORS.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| const openCORS = (req, res, next) => { | ||||
|   res.set("Access-Control-Allow-Origin", "*") | ||||
|   return next(); | ||||
| }; | ||||
|  | ||||
| module.exports = openCORS; | ||||
							
								
								
									
										37
									
								
								api/webserver/middleware/setupHeaders.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								api/webserver/middleware/setupHeaders.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| const camelToKebabCase = str => str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`); | ||||
|  | ||||
| const mapFeaturePolicyToString = (features) => { | ||||
|   return Object.entries(features).map(([key, value]) => { | ||||
|     key = camelToKebabCase(key) | ||||
|     value = value == "*" ? value : `'${ value }'` | ||||
|     return `${key} ${value}` | ||||
|   }).join("; ") | ||||
| } | ||||
|  | ||||
| const setupHeaders = (req, res, next) => { | ||||
|   res.set("Access-Control-Allow-Headers", "Content-Type") | ||||
|  | ||||
|   // Security | ||||
|   res.set("X-Content-Type-Options", "nosniff"); | ||||
|   res.set("X-XSS-Protection", "1; mode=block"); | ||||
|   res.set("X-Frame-Options", "SAMEORIGIN"); | ||||
|   res.set("X-DNS-Prefetch-Control", "off"); | ||||
|   res.set("X-Download-Options", "noopen"); | ||||
|   res.set("Strict-Transport-Security", "max-age=15552000; includeSubDomains") | ||||
|  | ||||
|   // Feature policy | ||||
|   const features = { | ||||
|     fullscreen: "*", | ||||
|     payment: "none", | ||||
|     microphone: "none", | ||||
|     camera: "self", | ||||
|     speaker: "*", | ||||
|     syncXhr: "self" | ||||
|   } | ||||
|   const featureString = mapFeaturePolicyToString(features); | ||||
|   res.set("Feature-Policy", featureString) | ||||
|  | ||||
|   return next(); | ||||
| } | ||||
|  | ||||
| module.exports = setupHeaders; | ||||
							
								
								
									
										37
									
								
								api/webserver/server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								api/webserver/server.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| const express = require("express"); | ||||
| const app = express(); | ||||
| const path = require("path"); | ||||
| global.__base = path.join(__dirname, ".."); | ||||
| global.__middleware = path.join(__dirname, "middleware"); | ||||
| global.__controllers = path.join(__dirname, "controllers"); | ||||
|  | ||||
| // logging | ||||
| const logger = require(`${__base}/logger`); | ||||
|  | ||||
| // middleware | ||||
| const httpContext = require("express-http-context"); | ||||
| const setupCORS = require(`${__middleware}/setupCORS`); | ||||
| const setupHeaders = require(`${__middleware}/setupHeaders`); | ||||
| const addIdToRequest = require(`${__middleware}/addIdToRequest`); | ||||
| app.use(httpContext.middleware); | ||||
| app.use(setupCORS); | ||||
| app.use(setupHeaders); | ||||
| app.use(addIdToRequest); | ||||
|  | ||||
| // parse application/json | ||||
| app.use(express.json()); | ||||
|  | ||||
| const router = express.Router(); | ||||
| // const TokenController = require(`${__controllers}/tokenController`); | ||||
| const PostController = require(`${__controllers}/postController`); | ||||
|  | ||||
| router.get("/api/post/:id/render", PostController.renderPost); | ||||
| router.get("/api/post/:id", PostController.getPost); | ||||
| router.put("/api/post/:id", PostController.updatePost); | ||||
| // router.post("/api/payment/callback/v2/payments/:id", PaymentController.updatePayment); | ||||
|  | ||||
| app.use(router); | ||||
|  | ||||
| logger.info("Server started, listening at :30010"); | ||||
|  | ||||
| app.listen(30010); | ||||
		Reference in New Issue
	
	Block a user