From f4f49cd244d2f840cbb79a9286973b5c98d4f534 Mon Sep 17 00:00:00 2001 From: Mhykol Date: Thu, 8 Jan 2026 07:23:16 -0500 Subject: Initial Commit --- app.js | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 app.js (limited to 'app.js') diff --git a/app.js b/app.js new file mode 100644 index 0000000..f6a6122 --- /dev/null +++ b/app.js @@ -0,0 +1,234 @@ +const express = require('express') +const app = express() +const rateLimit = require('express-rate-limit') +const helmet = require('helmet') +const bp = require('body-parser') +const cookieParser = require('cookie-parser') +const fs = require('fs') +const path = require('path') +const { readdirSync } = require('fs') +const session = require('./src/session')() +const routesConfig = require('./config/routes') +const mime = { + html: 'text/html', + txt: 'text/plain', + css: 'text/css', + xml: 'text/xml', + gif: 'image/gif', + jpg: 'image/jpeg', + png: 'image/png', + webp: 'image/webp', + avif: 'image/avif', + ico: 'image/ico', + svg: 'image/svg+xml', + js: 'application/javascript', + mp4: 'video/mp4', + webm: 'video/webm', + ttf: 'application/x-font-truetype', + ttc: 'application/x-font-truetype', + otf: 'application/x-font-otf', + ots: 'application/x-font-otf', + woff: 'application/font-woff', + woff2: 'application/font-woff', + pfb: 'application/vnd.ms-fontbook', + pfm: 'application/vnd.ms-fontbook', + gsf: 'application/vnd.ms-fontbook', + swf: 'application/vnd.ms-fontbook' +} +const assetDir = () => { + return readdirSync(`${__dirname}/assets`, { withFileTypes: true }) + .filter(dir => dir.isDirectory()) + .map(dir => dir.name) +} + +/* +const limiter = rateLimit({ + windowMS: 30 * 1000, + max: 100, + message: '

Rate Limit Exceeded

You have exceeded the allowed number of requests. Please try again later.

' +}) +*/ + +require('dotenv').config() +const sitemap = require('./src/sitemap') +const logger = require('./src/logger')({ + baseURL: process.env.baseURL +}) +const controller = require('./src/controller')({ + baseURL: process.env.baseURL +}) + +const createLog = (req, res, next) => { + const { cookies } = req + const object = { + queryString: '', + exists: false + } + + Object.entries(req.query).forEach(([key, value]) => { + if (object.exists === false) { + object.queryString += '?' + } + object.queryString += `${key}=${value}&` + object.exists = true + }) + + if (object.exists) object.queryString = object.queryString.slice(0, -1) + + logger.Info(`${req.method} ${req.path}${object.queryString} from ${req.ip}:${cookies.session_id ? cookies.session_id.replace('session_id', '') : 'n/a'}`) + next() +} +const validateCookie = (req, res, next) => { + const { cookies } = req + if (cookies === undefined) { + res.cookie('session_id', session.Create()) + } else { + if ('session_id' in cookies) { + if (session.Exists(cookies.session_id.replace('session_id', ''))) { + session.Expired() + } else { + res.clearCookie('session_id') + res.cookie('session_id', session.Create()) + } + } else res.cookie('session_id', session.Create()) + } + next() +} + +const openFile = x => { + const data = { + path: x.filePath.split('/'), + exists: false, + data: {} + } + + if (assetDir().includes(data.path[1])) { + this.path = `${__dirname}/assets/${data.path[1]}/${data.path[2]}` + data.exists = fs.existsSync(this.path) + data.data.mime = mime[path.extname(this.path).slice(1)] || 'text/plain' + data.data.file = fs.createReadStream(this.path) + .on('error', (err) => { + return + }) + } else if (data.path[1] === 'favicon.ico' && (data.path.length === 2 || (data.path.length === 3 && data.path[2] === ''))) { + data.data.file = fs.createReadStream(`${__dirname}/config/favicon.ico`) + .on('error', (err) => { + logger.Error(`'favicon.ico' does not exist. Please add it to the config.`) + }) + } + + if (data.exists) { + x.res.set('Content-Type', data.data.mime) + data.data.file + .on('open', () => { + data.data.file.pipe(x.res) + }) + .on('error', err => { + logger.Error(err) + }) + } else { + x.res.set('Content-Type', mime.html) + x.res.status(404).send('File not found.') + } +} + +app.use(bp.json()) +app.use(cookieParser()) + +app.use((req, res, next) => { + let err = null + try { + decodeURIComponent(req.path) + } catch (e) { + err = e + } + + if (err) { + return res.redirect(['http://', req.get('Host'), '/404'].join('')) + } + next() +}) + +const handleRoutes = (req, res, next) => { + this.path = { + split: req.path.split('/'), + string: req.path + } + if(routes.has(this.path.string)) { + const route = routes.get(this.path.string) + if (route.req === req.method) { + res.send(route.method()) + } else { + next() + } + } else { + next() + } +} + +const routes = routesConfig({ + app: app, + validateCookie: validateCookie, + log: createLog, + mime: mime, + session: session +}) +app.route(/(.*)/) + .get(validateCookie, createLog, (req, res) => { + this.path = { + split: req.path.split('/'), + string: req.path + } + + if (assetDir().includes(this.path.split[1])) { + openFile({ + filePath: this.path.string, + res: res + }) + } else { + switch (this.path.split[1]) { + case 'favicon.ico': + openFile({ + filePath: this.path.string, + res: res + }) + break + case 'sitemap.xml': + const data = sitemap({baseURL: process.env.baseURL}) + data.Sitemap().then(output => { + res.send(output) + }) + break + case 'rss': + rss({ + baseURL: process.env.baseURL + }) + break + case 'deverror': + break + default: + const response = controller.Init({ + req: req, + res: res, + path: this.path.string, + baseURL: process.env.baseURL + }) + + res.send(response) + break + } + } + }) + .post(createLog, (req, res) => {}) + .put(createLog, (req, res) => {}) + .delete(createLog, (req, res) => {}) + .patch(createLog, (req, res) => {}) + .all(createLog, (req, res) => { + res.set('Content-Type', mime.html) + res.status(404).send('You are not allowed to access this resource') + }) + +app.listen(process.env.PORT, () => { + logger.Info(`Listening on: ${process.env.baseURL}`) +}) + -- cgit v1.3.1