Initial commit

This commit is contained in:
2025-10-13 23:17:05 -05:00
commit c9715d22f6
17 changed files with 2354 additions and 0 deletions

103
src/Web.ts Normal file
View File

@@ -0,0 +1,103 @@
import * as http from "http";
import crypto from 'crypto';
import express from 'express';
import session from 'express-session';
import ky from 'ky';
import bodyParser from 'body-parser';
import type { Express } from 'express';
interface WebConfig {
sessionSecret?: string;
port?: number;
secure?: boolean;
}
/**
* I still hate typescript.
*/
function notStupidParseInt(v: string | undefined): number {
return v === undefined ? NaN : parseInt(v);
}
class Web {
private _webserver: http.Server | null = null;
private app: Express | null = null;
private port: number;
private options: WebConfig;
constructor(options: WebConfig = {}) {
this.options = options;
this.port = notStupidParseInt(process.env['PORT']) || options['port'] as number || 8080;
}
initialize = async () => {
const options = this.options;
const sessionSecret = process.env['SESSIONSECRET'] || options.sessionSecret;
const app: Express = this.app = express();
if(!sessionSecret) {
console.error('sessionSecret is required.');
throw new Error('sessionSecret is required.');
}
app.set('trust proxy', 1);
app.set('view engine', 'ejs');
app.set('view options', { outputFunctionName: 'echo' });
app.use('/assets', express.static('assets', { maxAge: '30 days' }));
app.use(session({
name: 'sessionId',
secret: sessionSecret,
resave: true,
saveUninitialized: false,
store: undefined,
cookie: {
maxAge: notStupidParseInt(process.env['COOKIETTL']) || 1000 * 60 * 60 * 24 * 30, // 30 days
httpOnly: true,
secure: !!options.secure
}
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use((_req, res, next) => {
crypto.randomBytes(32, (err, randomBytes) => {
if (err) {
console.error(err);
next(err);
} else {
res.locals['cspNonce'] = randomBytes.toString("hex");
next();
}
});
});
app.get('/healthcheck', (_, res) => {
res.send('Healthy');
});
app.get('/', (_, res) => {
res.render('index', {
page: {
title: 'Web',
titlesuffix: 'Home',
description: 'Homepage'
}
});
});
app.get('/ky', async (_, res) => {
res.send(await (await ky.get('https://sanin.dev')).text());
});
this._webserver = this.app.listen(this.port, () => console.log(`archery is running on port ${this.port}`));
}
close = () => {
if (this._webserver) {
this._webserver.close();
}
}
}
export default Web;
export { Web, notStupidParseInt };
export type { WebConfig };