UI updates and bug fixes
[browser-rts.git] / src / lib / server.ts
1 import express, { Request, Response } from 'express';
2 import { join } from 'path';
3 import { isString } from 'lodash';
4 import { HttpError } from '../errors';
5 import { merge } from 'lodash';
6 import bodyParser from 'body-parser';
7 import { ExpressAdapter } from '@bull-board/express';
8
9 type AuthInfo = {
10         authInfo: { 
11                 accountId: string;
12                 token: string;
13         }
14 };
15
16 export type HttpHandler<I, O> = (params: I & AuthInfo, rawReq: Request, rawRes: Response) => Promise<O>;
17
18 export class HttpServer {
19         server: express.Application;
20         port: string | number;
21         bullAdapter: ExpressAdapter;
22         constructor(port: string | number) {
23                 this.port = port;
24                 this.bullAdapter = new ExpressAdapter()
25                 this.server = express();
26                 this.configureMiddleWare();
27         }
28
29         configureMiddleWare() {
30                 this.server.use(express.json());
31                 this.server.use(bodyParser());
32                 this.server.use(express.static(join(__dirname, '..', '..', 'public')));
33                 
34                 this.bullAdapter.setBasePath('/admin/queues');
35                 this.server.use('/admin/queues', this.bullAdapter.getRouter());
36         }
37
38         wrap<I, O>(handler: HttpHandler<I, O>, hxEvents: string) {
39                 return async function (req: Request, res: Response) {
40                         try {
41                                 const start = Date.now();
42                                 console.log(`Req: ${req.method.toUpperCase()} ${req.path}`);
43
44                                 // extract hx game vars (token, id);
45                                 let url = new URL('http://localhost.com?id=null&token=null');
46                                 try {
47                                         url = new URL(req.headers['hx-current-url'].toString());
48                                 }
49                                 catch(e) {
50                                         console.log(e);
51                                 }
52                                 const headerData = {
53                                         authInfo: {
54                                                 token: url.searchParams.get('token'),
55                                                 accountId: url.searchParams.get('id')
56                                         }
57                                 };
58
59                                 const output: O = await handler(merge(req, headerData) as unknown as (I & AuthInfo), req, res);
60                                 console.log(`Runtime: ${Date.now() - start}ms`);
61
62                                 res.setHeader('hx-trigger', hxEvents);
63                                 if(output === undefined) {
64                                         res.statusCode = 204;
65                                 }
66                                 else if(isString(output)) {
67                                         res.send(output);
68                                 }
69                                 else {
70                                         res.json(output);
71                                 }
72                         }
73                         catch(e) {
74                                 console.log(e);
75                                 //res.statusCode = (e as HttpError).statusCode || 500;
76                                 res.send(`
77                                 <div class="alert danger">${e.message}</div>
78                                 `);
79                         }
80                         finally {
81                                 res.end();
82                         }
83                 }
84         }
85
86         get<I, O>(endpoint: string, handler: HttpHandler<I, O>, hxEvents: string = ''): void {
87                 console.log(`Mapped GET ${endpoint}`);
88                 this.server.get(endpoint, this.wrap(handler, hxEvents));
89         }
90
91         post<I, O>(endpoint: string, handler: HttpHandler<I, O>, hxEvents: string = ''): void {
92                 console.log(`Mapped POST ${endpoint}`);
93                 this.server.post(endpoint, this.wrap(handler, hxEvents));
94         }
95
96         start(fn?: any): void {
97                 console.log(`Listening on port ${this.port}`);
98                 this.server.listen(this.port, fn?.bind(this));
99         }
100 }