/* ---------------------------------------------------------------------------- Question Server GitLab: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ------------------------------------------------------------------------- */ console.log('Node version: ' + process.version) console.log('Current working directory: ' + process.cwd()) const startHTTPS = true const isRoot = process.getuid && process.getuid() === 0 const port = 8080 const httpsport = 5001 // import os from 'os' // os.setPriority(10) // console.log(`Process priority set to ${os.getPriority()}`) import express from 'express' import vhost from 'vhost' import http from 'http' import https from 'https' import cors from 'cors' import cookieParser from 'cookie-parser' import { v4 as uuidv4 } from 'uuid' import logger from './utils/logger' import utils from './utils/utils' import dbtools from './utils/dbtools' import reqlogger from './middlewares/reqlogger.middleware' import idStats from './utils/ids' const extraModulesFile = './data/extraModules.json' const statExcludeFile = './data/statExclude.json' const modulesFile = './src/modules.json' const usersDBPath = './data/dbs/users.db' const logFile = logger.logDir + logger.logFileName const vlogFile = logger.vlogDir + logger.logFileName function moveLogIfNotFromToday(path, to) { if (utils.FileExists(path)) { const today = new Date() const stat = utils.statFile(path) if ( today.getFullYear() !== stat.mtime.getFullYear() || today.getMonth() !== stat.mtime.getMonth() || today.getDate() !== stat.mtime.getDate() ) { utils.renameFile(path, to + utils.GetDateString(stat.mtime)) } } } moveLogIfNotFromToday(logFile, logger.logDir) moveLogIfNotFromToday(vlogFile, logger.vlogDir) idStats.Load() logger.Load() interface Modules { [name: string]: Module } interface Module { path: string publicdirs: Array name: string urls: Array nextdir?: string isNextJs?: boolean app: express.Application dailyAction: Function cleanup: Function } export interface SetupData { url: string publicdirs: Array userDB?: any nextdir?: string httpServer: any httpsServer: any } if (!utils.FileExists(usersDBPath)) { throw new Error('No user DB exists yet! please run utils/dbSetup.js first!') } const userDB = dbtools.GetDB(usersDBPath) let modules: Modules = utils.ReadJSON(modulesFile) const debugLevel = parseInt(process.env.NS_LOGLEVEL) || 0 logger.Log('Loglevel is: ' + debugLevel) logger.Log(`Log path: ${logFile}`) logger.Log(`vLog path: ${vlogFile}`) try { if (utils.FileExists(extraModulesFile)) { const extraModules = JSON.parse(utils.ReadFile(extraModulesFile)) modules = { ...extraModules, ...modules, } } } catch (err) { logger.Log('Failed to read extra modules file') console.error(err) } process.on('SIGINT', () => exit('SIGINT')) process.on('SIGTERM', () => exit('SIGTERM')) function exit(reason) { console.log() logger.Log(`Exiting, reason: ${reason}`) Object.keys(modules).forEach((key) => { const module = modules[key] if (module.cleanup) { try { module.cleanup() } catch (err) { logger.Log( `Error in ${key} cleanup! Details in STDERR`, logger.GetColor('redbg') ) console.error(err) } } }) logger.Log('Closing Auth DB') userDB.close() process.exit() } // https://certbot.eff.org/ const privkeyFile = '/etc/letsencrypt/live/frylabs.net/privkey.pem' const fullchainFile = '/etc/letsencrypt/live/frylabs.net/fullchain.pem' const chainFile = '/etc/letsencrypt/live/frylabs.net/chain.pem' let certsLoaded = false let certs if ( startHTTPS && utils.FileExists(privkeyFile) && utils.FileExists(fullchainFile) && utils.FileExists(chainFile) ) { try { const key = utils.ReadFile(privkeyFile) const cert = utils.ReadFile(fullchainFile) const ca = utils.ReadFile(chainFile) certs = { key: key, cert: cert, ca: ca, } certsLoaded = true } catch (err) { logger.Log('Error loading cert files!', logger.GetColor('redbg')) console.error(err) } } const app = express() const httpServer = http.createServer(app) let httpsServer if (certsLoaded) { httpsServer = https.createServer(certs, app) logger.Log('Listening on port: ' + httpsport + ' (https)') } else { logger.Log('Https not avaible') } if (!process.env.NS_DEVEL) { app.use(function (req, res, next) { if (req.secure) { next() } else { logger.DebugLog( `HTTPS ${req.method} redirect to: ${ 'https://' + req.headers.host + req.url }`, 'https', 1 ) if (req.method === 'POST') { res.redirect(307, 'https://' + req.headers.host + req.url) } else { res.redirect('https://' + req.headers.host + req.url) } } }) } // https://github.com/expressjs/cors#configuration-options app.use( cors({ credentials: true, origin: true, // origin: [ /\.frylabs\.net$/ ] }) ) const cookieSecret = uuidv4() app.use(cookieParser(cookieSecret)) if (!utils.FileExists(statExcludeFile)) { utils.WriteFile('[]', statExcludeFile) } const excludeFromStats = utils.ReadJSON(statExcludeFile) app.use( reqlogger({ loggableKeywords: ['news.json'], loggableModules: [], exceptions: ['_next/static'], excludeFromStats: excludeFromStats, }) ) Object.keys(modules).forEach(function (key) { const module = modules[key] try { const mod = require(module.path).default // eslint-disable-line // const mod = require(module.path) logger.Log(`Loading ${mod.name} module`, logger.GetColor('yellow')) module.publicdirs.forEach((pdir) => { utils.CreatePath(pdir) }) if (mod.setup) { mod.setup({ url: 'https://' + module.urls[0], userDB: userDB, publicdirs: module.publicdirs, nextdir: module.nextdir, httpServer: httpServer, httpsServer: httpsServer, }) } const modApp = mod.getApp() module.app = modApp.app module.dailyAction = modApp.dailyAction module.cleanup = modApp.cleanup module.urls.forEach((url) => { app.use(vhost(url, module.app)) }) } catch (err) { console.error(err) } }) setLogTimer() function setLogTimer() { const now = new Date() const night = new Date( now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 1 ) logger.DebugLog(`Next daily action: ${night}`, 'daily', 1) const msToMidnight = night.getTime() - now.getTime() + 10000 logger.DebugLog(`msToMidnight: ${msToMidnight}`, 'daily', 1) logger.DebugLog(`Seconds To Midnight: ${msToMidnight / 1000}`, 'daily', 1) if (msToMidnight < 0) { logger.Log( `Error setting up Log Timer, msToMidnight is negative! (${msToMidnight})`, logger.GetColor('redbg') ) return } setTimeout(function () { LogTimerAction() rotateLog() setLogTimer() }, msToMidnight) } function rotateLog() { const date = new Date() date.setDate(date.getDate() - 1) const fname = date.getFullYear() + '-' + ('0' + (date.getMonth() + 1)).slice(-2) + '-' + ('0' + date.getDate()).slice(-2) if (utils.FileExists(logFile)) { utils.CopyFile(logFile, logger.logDir + fname) } if (utils.FileExists(vlogFile)) { utils.CopyFile(vlogFile, logger.vlogDir + fname) } utils.WriteFile(fname, logFile) utils.WriteFile(fname, vlogFile) } function LogTimerAction() { logger.DebugLog(`Running Log Timer Action`, 'daily', 1) Object.keys(modules).forEach((key) => { const module = modules[key] logger.DebugLog(`Ckecking ${key}`, 'daily', 1) if (module.dailyAction) { try { logger.Log(`Running daily action of ${key}`) module.dailyAction() } catch (err) { logger.Log( `Error in ${key} daily action! Details in STDERR`, logger.GetColor('redbg') ) console.error(err) } } }) const line = '===================================================================================================================================================' logger.Log(line) } logger.Log('Node version: ' + process.version) logger.Log('Current working directory: ' + process.cwd()) logger.Log('Listening on port: ' + port) if (isRoot) { logger.Log('Running as root', logger.GetColor('red')) } httpServer.listen(port) if (httpsServer) { httpsServer.listen(httpsport) }