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