agmission/Development/server/server.js

163 lines
5.2 KiB
JavaScript

/* eslint-disable no-unused-vars */
'use strict';
// Ref: http://javascript.tutorialhorizon.com/2014/09/20/organizing-your-expressjs-routes-in-separate-files/
const express = require('express');
require('express-async-errors');
const rateLimit = require('express-rate-limit')
const debug = require('debug')('agm:server'),
compression = require('compression'),
path = require('path'),
fs = require('fs-extra'),
http = require('http'),
https = require('node:http2'), // Use Node's https for HTTP2 support instead of 3rd party spdy (for Node <=v14)
app = express(),
env = require('./helpers/env'),
{ checkUser } = require('./middlewares/app_validator.js'),
{ ErrorHandler } = require('./middlewares/error_handler.js'),
errorHandler = require('error-handler').errorHandler;
http.globalAgent.maxSockets = Infinity;
const MAX_REQ_BDY_MB = env.MAX_REQ_BDY_MB || '150mb'
// Load ENV vars specified in the .env file (within the same folder)
debug("Is in Production: ", env.PRODUCTION);
app.isProd = env.PRODUCTION;
process.setMaxListeners(0);
errorHandler && (errorHandler.registerUnCaughtProcessErrorsHandler(process, path.join(__dirname, 'agm_server.rlog')));
require('./helpers/db/connect.js')();
app.set('trust proxy', env.APP_RATE_TRUST_PROXIES /* number of proxies between user and server */);
if (!env.PRODUCTION && (env.INV_IMG_VIR_DIR && env.INV_UPLOAD_DIR)) {
// Map static resources, cached for 8 hours
app.use(env.INV_IMG_VIR_DIR, express.static(env.INV_UPLOAD_DIR, { maxAge: 31557600 }));
}
// for parsing application/x-www-form-urlencoded
app.use(express.urlencoded({ limit: MAX_REQ_BDY_MB, extended: true, parameterLimit: 100000 }));
// Rate limiting middleware. Define a rate limiter
const apiLimiter = rateLimit({
windowMs: (env.APP_RATE_MINS || 15) * 60 * 1000, // 15 minutes
max: env.APP_RATE_REQS || 100, // Limit each IP to 100 requests per windowMs
skipFailedRequests: env.APP_RATE_SKIPFAIL || true, // Skip failed requests
message: {
error: "Too many requests, please try again later."
}
});
// Apply the rate limiter to all routes in this router
app.use(apiLimiter);
// Handle webhook events from Stripe
require('./routes/subscription_webhooks')(express, app);
function shouldCompress(req, res) {
if (req.headers['x-no-compression']) {
return false; // don't compress responses with this request header
}
// fallback to standard filter function
return compression.filter(req, res);
}
// Enable the compression middleware
app.use(compression({ filter: shouldCompress }));
// Parsers for POST JSON data
app.use(express.json({ limit: MAX_REQ_BDY_MB }));
// Allow all CORS requests
// Ref at http://restlet.com/company/blog/2015/12/15/understanding-and-using-cors
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, Methods, X-Requested-With, Content-Type, Content-Disposition, Accept');
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PATCH, DELETE, OPTIONS');
next();
});
async function setupRoutes() {
// const resLogger = async (req, res, next) => {
// debug('Request:', req.url);
// next();
// };
// Handle routes with middelware functions
app.use(checkUser/*, resLogger*/);
// REGISTER OUR ROUTES
require('./routes')(app);
app.use(ErrorHandler);
app.get('/*', (req, res) => {
// console.log(req.path);
res.status(404).send({ status: 404, error: 'Not found' }).end();
});
// Avoid logging aborted requests
// Ref: https://github.com/nodejs/help/issues/2155
app.use((err, req, res, next) => {
if (err && err.code === 'ECONNABORTED') {
res.status(400).end(); // Don't process this error any further to avoid its logging
} else
next(err);
});
}
/**
* Preload some common libs, ussually written in ESM modules that require asynchronously loaded or import..
* instead of using requires in CommonJS ones.
*/
async function preloadLibs() {
let mod = await import('@mickeyjohn/geodesy/utm.js');
app.locals.UTM = mod.default;
app.locals.LatLonUTM = mod.LatLon; // More accuracy, for converting from ll to utm
app.locals.Dms = mod.Dms;
mod = await import('@mickeyjohn/geodesy/latlon-spherical.js');
app.locals.LatLonSP = mod.default;
}
async function ensureFolders() {
try {
await fs.ensureDir(env.INV_UPLOAD_DIR);
await fs.ensureDir(env.UNZIP_DIR);
await fs.ensureDir(env.REPORT_DIR);
await fs.ensureDir(env.TEMP_DIR);
await fs.ensureDir(env.AREAS_UPLOAD_DIR);
} catch (error) {
debug(error);
}
}
const port = env.AGM_PORT || '4000';
// Create a secure HTTPS - HTTP2 server
https.createSecureServer({ key: fs.readFileSync(env.SSL_KEY), cert: fs.readFileSync(env.SSL_CERT), allowHTTP1: true }, app)
.listen(port, async (error) => {
const onAppErr = (err) => {
debug(error);
process.exit(1);
}
if (error) return onAppErr(error);
try {
await ensureFolders();
await preloadLibs();
await setupRoutes();
// Start Job Importer queue consumer
const jobQueuer = require('./helpers/job_queue').getInstance();
jobQueuer.start();
console.log(`Server is running on port ${port}`)
debug(`HTTPS-v2 Agmission Server listening on port ${port}`);
} catch (error) {
onAppErr(error);
}
});