1 import express, { Request, Response } from 'express';
2 import { join } from 'path';
3 import { isString } from 'lodash';
4 import { merge } from 'lodash';
5 import bodyParser from 'body-parser';
6 import { ExpressAdapter } from '@bull-board/express';
7 import http from 'http';
8 import { Server, Socket } from 'socket.io';
17 export type HttpHandler<I, O> = (params: I & AuthInfo, rawReq: Request, rawRes: Response) => Promise<O>;
19 export class HttpServer {
20 server: express.Application;
23 port: string | number;
24 bullAdapter: ExpressAdapter;
25 constructor(port: string | number) {
27 this.server = express();
28 this.bullAdapter = new ExpressAdapter()
29 this.configureMiddleWare();
30 this.http = http.createServer(this.server);
31 this.ws = new Server(this.http);
34 configureMiddleWare() {
35 this.server.use(express.json());
36 this.server.use(bodyParser());
37 this.server.use(express.static(join(__dirname, '..', '..', 'public')));
39 this.bullAdapter.setBasePath('/admin/queues');
40 this.server.use('/admin/queues', this.bullAdapter.getRouter());
43 authFromUrl(raw: string): {authInfo: {token: string, accountId: string}} {
44 let url = new URL('http://localhost.com?id=null&token=null');
49 token: url.searchParams.get('token'),
50 accountId: url.searchParams.get('id')
61 getSocketFromAuthenticatedUser(authInfo: {token: string, accountId: string}): Socket | null {
64 this.ws.of('/').sockets.forEach(s => {
65 const auth = this.authFromUrl(s.request.headers['referer']);
66 if (auth.authInfo.accountId === authInfo.accountId && auth.authInfo.token === authInfo.token) {
74 wrap<I, O>(handler: HttpHandler<I, O>, hxEvents: string) {
76 return async function (req: Request, res: Response) {
78 const start = Date.now();
79 console.log(`Req: ${req.method.toUpperCase()} ${req.path}`);
81 // extract hx game vars (token, id);
82 const headerData = self.authFromUrl(req.headers['hx-current-url'].toString());
83 const output: O = await handler(merge(req, headerData) as unknown as (I & AuthInfo), req, res);
84 console.log(`Runtime: ${Date.now() - start}ms`);
86 res.setHeader('hx-trigger', hxEvents);
87 if(output === undefined) {
90 else if(isString(output)) {
100 <div class="alert danger autofade">${e.message}</div>
109 get<I, O>(endpoint: string, handler: HttpHandler<I, O>, hxEvents: string = ''): void {
110 console.log(`Mapped GET ${endpoint}`);
111 this.server.get(endpoint, this.wrap(handler, hxEvents));
114 post<I, O>(endpoint: string, handler: HttpHandler<I, O>, hxEvents: string = ''): void {
115 console.log(`Mapped POST ${endpoint}`);
116 this.server.post(endpoint, this.wrap(handler, hxEvents));
119 start(fn?: any): void {
120 console.log(`Listening on port ${this.port}`);
121 this.http.listen(this.port, fn?.bind(this));