chore: switch coding style to JS standard
[e-mobility-charging-stations-simulator.git] / src / charging-station / ui-server / AbstractUIServer.ts
CommitLineData
66a7748d
JB
1import { type IncomingMessage, Server, type ServerResponse } from 'node:http'
2import { type Http2Server, createServer } from 'node:http2'
daa6505e 3
66a7748d 4import type { WebSocket } from 'ws'
fe94fce0 5
66a7748d
JB
6import type { AbstractUIService } from './ui-services/AbstractUIService.js'
7import { UIServiceFactory } from './ui-services/UIServiceFactory.js'
8import { BaseError } from '../../exception/index.js'
eb3abc4f 9import {
a6080904 10 ApplicationProtocolVersion,
eb3abc4f 11 AuthenticationType,
268a74bb 12 type ChargingStationData,
e0b0ee21
JB
13 type ProcedureName,
14 type ProtocolRequest,
15 type ProtocolResponse,
f130b8e6 16 ProtocolVersion,
e0b0ee21
JB
17 type RequestPayload,
18 type ResponsePayload,
66a7748d
JB
19 type UIServerConfiguration
20} from '../../types/index.js'
8114d10e 21
fe94fce0 22export abstract class AbstractUIServer {
66a7748d
JB
23 public readonly chargingStations: Map<string, ChargingStationData>
24 protected readonly httpServer: Server | Http2Server
25 protected readonly responseHandlers: Map<string, ServerResponse | WebSocket>
26 protected readonly uiServices: Map<ProtocolVersion, AbstractUIService>
fe94fce0 27
66a7748d
JB
28 public constructor (protected readonly uiServerConfiguration: UIServerConfiguration) {
29 this.chargingStations = new Map<string, ChargingStationData>()
a6080904
JB
30 switch (this.uiServerConfiguration.version) {
31 case ApplicationProtocolVersion.VERSION_11:
66a7748d
JB
32 this.httpServer = new Server()
33 break
a6080904 34 case ApplicationProtocolVersion.VERSION_20:
66a7748d
JB
35 this.httpServer = createServer()
36 break
a6080904
JB
37 default:
38 throw new BaseError(
66a7748d
JB
39 `Unsupported application protocol version ${this.uiServerConfiguration.version}`
40 )
a6080904 41 }
66a7748d
JB
42 this.responseHandlers = new Map<string, ServerResponse | WebSocket>()
43 this.uiServices = new Map<ProtocolVersion, AbstractUIService>()
fe94fce0
JB
44 }
45
66a7748d 46 public buildProtocolRequest (
852a4c5f
JB
47 id: string,
48 procedureName: ProcedureName,
66a7748d 49 requestPayload: RequestPayload
5e3cb728 50 ): ProtocolRequest {
66a7748d 51 return [id, procedureName, requestPayload]
852a4c5f
JB
52 }
53
66a7748d
JB
54 public buildProtocolResponse (id: string, responsePayload: ResponsePayload): ProtocolResponse {
55 return [id, responsePayload]
852a4c5f
JB
56 }
57
66a7748d
JB
58 public stop (): void {
59 this.stopHttpServer()
60 this.chargingStations.clear()
daa6505e
JB
61 }
62
66a7748d
JB
63 public async sendInternalRequest (request: ProtocolRequest): Promise<ProtocolResponse> {
64 const protocolVersion = ProtocolVersion['0.0.1']
65 this.registerProtocolVersionUIService(protocolVersion)
66 return await (this.uiServices
e1d9a0f4 67 .get(protocolVersion)
66a7748d 68 ?.requestHandler(request) as Promise<ProtocolResponse>)
6bd808fd
JB
69 }
70
66a7748d
JB
71 public hasResponseHandler (id: string): boolean {
72 return this.responseHandlers.has(id)
1ca4a038
JB
73 }
74
66a7748d
JB
75 protected startHttpServer (): void {
76 if (!this.httpServer.listening) {
77 this.httpServer.listen(this.uiServerConfiguration.options)
a307349b
JB
78 }
79 }
80
66a7748d
JB
81 protected registerProtocolVersionUIService (version: ProtocolVersion): void {
82 if (!this.uiServices.has(version)) {
83 this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this))
143498c8
JB
84 }
85 }
86
66a7748d
JB
87 protected authenticate (req: IncomingMessage, next: (err?: Error) => void): void {
88 if (this.isBasicAuthEnabled()) {
89 if (!this.isValidBasicAuth(req)) {
90 next(new BaseError('Unauthorized'))
976d11ec 91 }
66a7748d 92 next()
976d11ec 93 }
66a7748d 94 next()
976d11ec
JB
95 }
96
66a7748d
JB
97 private stopHttpServer (): void {
98 if (this.httpServer.listening) {
99 this.httpServer.close()
36adaf06
JB
100 }
101 }
102
66a7748d 103 private isBasicAuthEnabled (): boolean {
eb3abc4f
JB
104 return (
105 this.uiServerConfiguration.authentication?.enabled === true &&
106 this.uiServerConfiguration.authentication?.type === AuthenticationType.BASIC_AUTH
66a7748d 107 )
eb3abc4f
JB
108 }
109
66a7748d
JB
110 private isValidBasicAuth (req: IncomingMessage): boolean {
111 const authorizationHeader = req.headers.authorization ?? ''
112 const authorizationToken = authorizationHeader.split(/\s+/).pop() ?? ''
113 const authentication = Buffer.from(authorizationToken, 'base64').toString()
114 const authenticationParts = authentication.split(/:/)
115 const username = authenticationParts.shift()
116 const password = authenticationParts.join(':')
eb3abc4f
JB
117 return (
118 this.uiServerConfiguration.authentication?.username === username &&
119 this.uiServerConfiguration.authentication?.password === password
66a7748d 120 )
eb3abc4f
JB
121 }
122
66a7748d
JB
123 public abstract start (): void
124 public abstract sendRequest (request: ProtocolRequest): void
125 public abstract sendResponse (response: ProtocolResponse): void
126 public abstract logPrefix (moduleName?: string, methodName?: string, prefixSuffix?: string): string
fe94fce0 127}