diff --git a/lib/compose-full-target-url.js b/lib/compose-full-target-url.js new file mode 100644 index 0000000..99710cf --- /dev/null +++ b/lib/compose-full-target-url.js @@ -0,0 +1,14 @@ +const urlJoin = require('url-join'); + +const PROTOCOL = process.env.HOSTNAME_PROTOCOL || 'https'; +const PORT = process.env.HOSTNAME_PORT || ''; + +module.exports = (hostname, path) => urlJoin( + [ + PROTOCOL, + '://', + hostname, + PORT || '', + ].join(''), + path, +); diff --git a/lib/webserver/access-logger.js b/lib/webserver/access-logger.js new file mode 100644 index 0000000..b5e47ba --- /dev/null +++ b/lib/webserver/access-logger.js @@ -0,0 +1,38 @@ +const morgan = require('morgan'); +const rfs = require('rotating-file-stream'); +const composeFullTargetUrl = require('../compose-full-target-url'); + +/** + * + * @param {import('express').Application} app + * @param {string} logDir + */ +module.exports = (app, logDir) => { + const stream = rfs.createStream('access.log', { + // size: '10M', // rotate every 10 MegaBytes written + interval: '1d', // rotate daily + // compress: 'gzip' // compress rotated files + maxFiles: Number(process.env.WEB_KEEP_ACESSS_FILES) || 45, + path: logDir, + }); + + morgan.token('xid', (req, res) => res.locals.xid || '-'); + morgan.token('email', (req) => req.session?.email || '-'); + // morgan.token('hostname', (req) => req.hostname || '-'); + morgan.token( + 'url', + /** + * + * @param {import('express').Request} req + * @returns + */ + (req) => composeFullTargetUrl(req.hostname, req.originalUrl), + ); + + app.use(morgan( + ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" :email :xid', + { + stream, + }, + )); +}; diff --git a/lib/webserver/index.js b/lib/webserver/index.js index 17ced39..b03b2e4 100644 --- a/lib/webserver/index.js +++ b/lib/webserver/index.js @@ -11,6 +11,7 @@ const redis = require('redis'); const dayjs = require('dayjs'); const logger = require('../logger'); +const accessLogger = require('./access-logger'); const targetLookup = require('./target-lookup'); const validateSession = require('./validate-session'); const composeTrustProxy = require('./compose-trust-proxies'); @@ -45,7 +46,14 @@ module.exports = async () => { logger.verbose(`${MODULE_NAME} 1A4F8EE0: Will use cf-connecting-ip as requester ip`); } - logger.verbose(`${MODULE_NAME} A3F66C95: Creating redis client for sesssion store`); + const trustedProxies = await composeTrustProxy(); + logger.verbose(`${MODULE_NAME} 1DFD2897: Trusting proxies`, { + trustedProxies, + }); + app.set('trust proxy', trustedProxies); + + // sessions + logger.verbose(`${MODULE_NAME} A3F66C95: Initializing session store`); const redisClient = redis.createClient(); await redisClient.connect(); @@ -61,25 +69,7 @@ module.exports = async () => { saveUninitialized: false, secret: process.env.WEB_SESSION_SECRET || uniqid(), })); - - app.use(express.static('./node_modules/bootstrap/dist')); - app.use(express.static('./public/')); - app.use(express.static('./public.local/')); - - const nunjucksEnv = nunjucks.configure('views', { - autoescape: true, - noCache: !!process.env.WEB_TEMPLATE_NO_CACHE, - express: app, - }); - - nunjucksEnv.addFilter('simpleDate', (ts) => dayjs(ts).format('YYYY-MM-DD')); - nunjucksEnv.addFilter('simpleDateTime', (ts) => dayjs(ts).format('YYYY-MM-DD Hh:mm:ssZ[Z]')); - - const trustedProxies = await composeTrustProxy(); - logger.verbose(`${MODULE_NAME} 1DFD2897: Trusting proxies`, { - trustedProxies, - }); - app.set('trust proxy', trustedProxies); + // end of sessions app.use((req, res, next) => { const xid = uniqid(); @@ -111,6 +101,21 @@ module.exports = async () => { next(); }); + app.use(express.static('./node_modules/bootstrap/dist')); + app.use(express.static('./public/')); + app.use(express.static('./public.local/')); + + const nunjucksEnv = nunjucks.configure('views', { + autoescape: true, + noCache: !!process.env.WEB_TEMPLATE_NO_CACHE, + express: app, + }); + + nunjucksEnv.addFilter('simpleDate', (ts) => dayjs(ts).format('YYYY-MM-DD')); + nunjucksEnv.addFilter('simpleDateTime', (ts) => dayjs(ts).format('YYYY-MM-DD Hh:mm:ssZ[Z]')); + + accessLogger(app, 'logs'); + app.get('/', (req, res) => res.redirect(res.locals.mainPage)); app.use('/dashboard', validateSession, routerDashboard); diff --git a/package-lock.json b/package-lock.json index 7a34902..ac1446f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,10 +19,12 @@ "express-session": "^1.17.3", "geoip-lite": "^1.4.8", "ip-toolkit": "^1.0.10", + "morgan": "^1.10.0", "mysql2": "^3.6.5", "nunjucks": "^3.2.4", "prompt": "^1.3.0", "redis": "^4.6.11", + "rotating-file-stream": "^3.1.1", "sd-notify": "^2.8.0", "simple-git": "^3.21.0", "uniqid": "^5.4.0", @@ -626,6 +628,22 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -3448,6 +3466,45 @@ "node": ">= 0.6.0" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4342,6 +4399,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rotating-file-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/rotating-file-stream/-/rotating-file-stream-3.1.1.tgz", + "integrity": "sha512-PNF1iDkxcZG+T87uUzLlcO4aquTCyY8yl+Q/OTK4dMwhwWDYWU4ZATYeIXHmYVGIzqZ2MrpY4WIkYc9Bsc3Nzw==", + "engines": { + "node": ">=14.0" + }, + "funding": { + "url": "https://www.blockchain.com/btc/address/12p1p5q7sK75tPyuesZmssiMYr4TKzpSCN" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", diff --git a/package.json b/package.json index 52123f3..16a1b21 100644 --- a/package.json +++ b/package.json @@ -46,10 +46,12 @@ "express-session": "^1.17.3", "geoip-lite": "^1.4.8", "ip-toolkit": "^1.0.10", + "morgan": "^1.10.0", "mysql2": "^3.6.5", "nunjucks": "^3.2.4", "prompt": "^1.3.0", "redis": "^4.6.11", + "rotating-file-stream": "^3.1.1", "sd-notify": "^2.8.0", "simple-git": "^3.21.0", "uniqid": "^5.4.0",