Initial commit
This commit is contained in:
103
src/Web.ts
Normal file
103
src/Web.ts
Normal 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 };
|
28
src/index.ts
Normal file
28
src/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import JSON5 from 'json5';
|
||||
import { Web, type WebConfig } from './Web.js';
|
||||
|
||||
interface compositeConfig {
|
||||
web?: WebConfig
|
||||
}
|
||||
|
||||
async function readConfig(): Promise<compositeConfig> {
|
||||
try {
|
||||
return JSON5.parse(await fs.promises.readFile(process.env['config'] || process.env['CONFIG'] || path.join(process.cwd(), 'config', 'config.jsonc'), 'utf-8'));
|
||||
}
|
||||
catch (err) {
|
||||
console.error('No config file found, using default config');
|
||||
console.error(err);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const config: compositeConfig = await readConfig();
|
||||
|
||||
const web = new Web(config.web);
|
||||
await web.initialize();
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
web.close();
|
||||
});
|
Reference in New Issue
Block a user