feat(ui): make evses works in the web ui
[e-mobility-charging-stations-simulator.git] / src / charging-station / ui-server / AbstractUIServer.ts
CommitLineData
01f4001e 1import { type IncomingMessage, Server, type ServerResponse } from 'node:http';
daa6505e
JB
2
3import type { WebSocket } from 'ws';
fe94fce0 4
eb3abc4f
JB
5import {
6 AuthenticationType,
268a74bb 7 type ChargingStationData,
e0b0ee21
JB
8 type ProcedureName,
9 type ProtocolRequest,
10 type ProtocolResponse,
11 type ProtocolVersion,
12 type RequestPayload,
13 type ResponsePayload,
268a74bb
JB
14 type UIServerConfiguration,
15} from '../../types';
17bc43d7 16import { type AbstractUIService, UIServiceFactory } from '../internal';
8114d10e 17
fe94fce0 18export abstract class AbstractUIServer {
32de5a57 19 public readonly chargingStations: Map<string, ChargingStationData>;
976d11ec
JB
20 protected readonly httpServer: Server;
21 protected readonly responseHandlers: Map<string, ServerResponse | WebSocket>;
5e3cb728 22 protected readonly uiServices: Map<ProtocolVersion, AbstractUIService>;
fe94fce0 23
eb3abc4f 24 public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) {
32de5a57 25 this.chargingStations = new Map<string, ChargingStationData>();
daa6505e 26 this.httpServer = new Server();
94dc3080 27 this.responseHandlers = new Map<string, ServerResponse | WebSocket>();
fe94fce0
JB
28 this.uiServices = new Map<ProtocolVersion, AbstractUIService>();
29 }
30
852a4c5f
JB
31 public buildProtocolRequest(
32 id: string,
33 procedureName: ProcedureName,
34 requestPayload: RequestPayload
5e3cb728
JB
35 ): ProtocolRequest {
36 return [id, procedureName, requestPayload];
852a4c5f
JB
37 }
38
5e3cb728
JB
39 public buildProtocolResponse(id: string, responsePayload: ResponsePayload): ProtocolResponse {
40 return [id, responsePayload];
852a4c5f
JB
41 }
42
daa6505e
JB
43 public stop(): void {
44 this.chargingStations.clear();
45 }
46
a307349b
JB
47 protected startHttpServer(): void {
48 if (this.httpServer.listening === false) {
49 this.httpServer.listen(this.uiServerConfiguration.options);
50 }
51 }
52
143498c8
JB
53 protected registerProtocolVersionUIService(version: ProtocolVersion): void {
54 if (this.uiServices.has(version) === false) {
55 this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this));
56 }
57 }
58
976d11ec
JB
59 protected authenticate(req: IncomingMessage, next: (err?: Error) => void): void {
60 if (this.isBasicAuthEnabled() === true) {
61 if (this.isValidBasicAuth(req) === false) {
62 next(new Error('Unauthorized'));
63 }
64 next();
65 }
66 next();
67 }
68
69 private isBasicAuthEnabled(): boolean {
eb3abc4f
JB
70 return (
71 this.uiServerConfiguration.authentication?.enabled === true &&
72 this.uiServerConfiguration.authentication?.type === AuthenticationType.BASIC_AUTH
73 );
74 }
75
976d11ec 76 private isValidBasicAuth(req: IncomingMessage): boolean {
eb3abc4f
JB
77 const authorizationHeader = req.headers.authorization ?? '';
78 const authorizationToken = authorizationHeader.split(/\s+/).pop() ?? '';
79 const authentication = Buffer.from(authorizationToken, 'base64').toString();
80 const authenticationParts = authentication.split(/:/);
81 const username = authenticationParts.shift();
82 const password = authenticationParts.join(':');
83 return (
84 this.uiServerConfiguration.authentication?.username === username &&
85 this.uiServerConfiguration.authentication?.password === password
86 );
87 }
88
fe94fce0 89 public abstract start(): void;
5e3cb728
JB
90 public abstract sendRequest(request: ProtocolRequest): void;
91 public abstract sendResponse(response: ProtocolResponse): void;
92 public abstract logPrefix(
93 moduleName?: string,
94 methodName?: string,
95 prefixSuffix?: string
96 ): string;
fe94fce0 97}