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