'use strict'; const express = require('express'); const path = require('path'); const favicon = require('serve-favicon'); const bodyParser = require('body-parser'); const requestLogger = require('morgan'); // logger const debug = require('debug'); const error = debug('cc:server:err'); const logger = debug('cc:server'); logger.log = console.log.bind(console); const cors = require('cors'); const mongoose = require('mongoose'); const {exec} = require('child_process'); // cluster mode const cluster = require('cluster'); const envWorkerNum = process.env.NODE_WORKER_COUNT; const cpuCount = require('os').cpus().length; const numWorkers = (envWorkerNum) ? envWorkerNum : cpuCount; // own modules const config = require('./config/config'); const urls = require('./config/api-url'); const restAPIchecks = require('./middleware/request-checks.js'); const errorResponseWare = require('./middleware/error-response'); const apiAuthenticationMiddleware = require('./middleware/auth-middleware'); const checkAdmin = require('./middleware/permission-check').checkAdmin; const signatureCronJob = require('./cron-job/cron').cronJobSignature; const backupCronJob = require('./cron-job/cron').cronJobBackup; // router modules const authenticateRouter = require('./routes/authenticate'); const accountRouter = require('./routes/account'); const overviewRouter = require('./routes/overview'); const userRouter = require('./routes/users'); const squadRouter = require('./routes/squads'); const rankRouter = require('./routes/ranks'); const decorationRouter = require('./routes/decorations'); const awardingRouter = require('./routes/awardings'); const requestRouter = require('./routes/request'); const playerRouter = require('./routes/players'); const signatureRouter = require('./routes/signatures'); const commandRouter = require('./routes/command'); const campaignRouter = require('./routes/campaigns'); const warRouter = require('./routes/wars'); const logRouter = require('./routes/logs'); // Configuration *********************************** // mongoose promise setup mongoose.Promise = global.Promise; // app creation const app = express(); // Middlewares ************************************************* // setup CORS-middleware const corsOptions = { methods: ['GET'], optionsSuccessStatus: 200, }; app.use(cors(corsOptions)); app.use(favicon(path.join(__dirname + '/..', 'public', 'favicon.ico'))); app.use(express.static(path.join(__dirname + '/..', 'public'))); app.use(bodyParser.json({limit: '10mb'})); app.use(bodyParser.urlencoded({limit: '10mb', extended: true})); // API request checks for API-version and JSON etc. app.use(restAPIchecks); // Routes ****************************************************** app.use(urls.signatures, signatureRouter); // initialize logging at this point to exclude /signature requests if (process.env.NODE_ENV === config.dev.env) { // development logging app.use(requestLogger('dev')); } else if (process.env.NODE_ENV !== config.test.dredd.env && process.env.NODE_ENV !== config.test.unit.env) { // production logging, apache style app.use(requestLogger(':date[clf] :method :url :response-time ms :status')); } app.use(urls.auth, authenticateRouter); app.use(urls.overview, overviewRouter); app.use(urls.users, userRouter); app.use(urls.squads, squadRouter); app.use(urls.ranks, rankRouter); app.use(urls.decorations, decorationRouter); app.use(urls.request, requestRouter); app.use(urls.awards, awardingRouter); app.use(urls.wars, warRouter); app.use(urls.players, playerRouter); app.use(urls.campaigns, campaignRouter); app.use(urls.logs, logRouter); app.use(urls.command, apiAuthenticationMiddleware, checkAdmin, commandRouter); app.use(urls.account, apiAuthenticationMiddleware, checkAdmin, accountRouter); // send index.html on all different paths app.use((req, res) => { res.sendFile('public/index.html', {root: __dirname + '/..'}); }); // register error handlers errorResponseWare(app); // Start the server if (process.env.NODE_ENV === config.test.unit.env || process.env.NODE_ENV === config.test.dredd.env) { const MongodbMemoryServer = require('mongodb-memory-server').default; const mongoServer = new MongodbMemoryServer(); mongoose.Promise = Promise; mongoServer.getConnectionString().then((mongoUri) => { mongoose.connect(mongoUri, {useNewUrlParser: true}); mongoose.connection.on('error', (e) => { if (e.message.code === 'ETIMEDOUT') { error(e); mongoose.connect(mongoUri); } error(e); }); if (process.env.NODE_ENV === config.test.dredd.env) { const mongoPortAndDB = mongoUri.replace('mongodb://localhost:', '').split('/'); exec( __dirname + '/apib/dredd/populate-data.sh' .concat(' -p ').concat(mongoPortAndDB[0]) .concat(' -d ').concat(mongoPortAndDB[1]), (err, stdout, stderr) => { if (err) { error(err.message); } else { logger('\x1b[32m%s\x1b[0m', stderr); } }); } mongoose.connection.once('open', () => { logger(`MongoDB successfully connected to ${mongoUri}`); app.listen(config.test.port); logger('Listening on port ' + config.test.port); }); }); } else { mongoose.connect(config.database.uri + config.database.db, {useNewUrlParser: true}).then((db) => { let cronWorkerPID; if (cluster.isMaster) { // Fork workers for (let i = 0; i < numWorkers; i++) { if (i === 0) { const spawnedWorker = cluster.fork({START_CRON: true}); cronWorkerPID = spawnedWorker.process.pid; } else { cluster.fork(); } } logger(`Master ${process.pid} is running. Forking ${numWorkers} workers`); // Check if worker id is died cluster.on('exit', (worker, code, signal) => { logger(`worker ${worker.process.pid} died`); if (worker.process.pid === cronWorkerPID) { const spawnedWorker = cluster.fork({START_CRON: true}); cronWorkerPID = spawnedWorker.process.pid; } else { cluster.fork(); } }); } else { app.listen(config.port, (err) => { if (err) { error(`Error on startup ${err}`); } else { logger(`Worker ${process.pid} started. Listening on port ${config.port}`); if (process.env.START_CRON) { logger(`Attaching cronJobs to cluster worker ${process.pid}`); signatureCronJob.start(); backupCronJob.start(); } } }); } }); } module.exports = app;