feat: allow to provision number of stations by template
[e-mobility-charging-stations-simulator.git] / src / utils / Configuration.ts
CommitLineData
66a7748d
JB
1import { type FSWatcher, readFileSync, watch } from 'node:fs'
2import { dirname, join } from 'node:path'
3import { env } from 'node:process'
4import { fileURLToPath } from 'node:url'
8114d10e 5
66a7748d 6import chalk from 'chalk'
38ae4ce2 7import { mergeDeepRight, once } from 'rambda'
8114d10e 8
83e00df1 9import {
268a74bb 10 ApplicationProtocol,
329eab0e 11 ApplicationProtocolVersion,
83e00df1 12 type ConfigurationData,
5d049829 13 ConfigurationSection,
268a74bb 14 FileType,
3d48c1c1 15 type LogConfiguration,
83e00df1
JB
16 type StationTemplateUrl,
17 type StorageConfiguration,
268a74bb 18 StorageType,
e7aeea18 19 SupervisionUrlDistribution,
83e00df1 20 type UIServerConfiguration,
66a7748d
JB
21 type WorkerConfiguration
22} from '../types/index.js'
769d3b10 23import {
da47bc29 24 DEFAULT_ELEMENT_ADD_DELAY,
769d3b10
JB
25 DEFAULT_POOL_MAX_SIZE,
26 DEFAULT_POOL_MIN_SIZE,
27 DEFAULT_WORKER_START_DELAY,
66a7748d
JB
28 WorkerProcessType
29} from '../worker/index.js'
4c3f6c20
JB
30import {
31 buildPerformanceUriFilePath,
32 checkWorkerElementsPerWorker,
33 checkWorkerProcessType,
34 getDefaultPerformanceStorageUri,
35 handleFileException,
36 logPrefix
37} from './ConfigurationUtils.js'
38import { Constants } from './Constants.js'
38ae4ce2 39import { hasOwnProp, isCFEnvironment } from './Utils.js'
7dde0b73 40
e7c0fce0
JB
41type ConfigurationSectionType =
42 | LogConfiguration
43 | StorageConfiguration
44 | WorkerConfiguration
66a7748d 45 | UIServerConfiguration
e7c0fce0 46
66a7748d 47// eslint-disable-next-line @typescript-eslint/no-extraneous-class
268a74bb 48export class Configuration {
5199f9fd 49 public static configurationChangeCallback?: () => Promise<void>
6501eda9 50
66a7748d 51 private static readonly configurationFile = join(
d972af76 52 dirname(fileURLToPath(import.meta.url)),
e7aeea18 53 'assets',
66a7748d
JB
54 'config.json'
55 )
10068088 56
66a7748d
JB
57 private static configurationFileReloading = false
58 private static configurationData?: ConfigurationData
59 private static configurationFileWatcher?: FSWatcher
60 private static readonly configurationSectionCache = new Map<
61 ConfigurationSection,
62 ConfigurationSectionType
5d049829
JB
63 >([
64 [ConfigurationSection.log, Configuration.buildLogSection()],
65 [ConfigurationSection.performanceStorage, Configuration.buildPerformanceStorageSection()],
66 [ConfigurationSection.worker, Configuration.buildWorkerSection()],
66a7748d
JB
67 [ConfigurationSection.uiServer, Configuration.buildUIServerSection()]
68 ])
974efe6c 69
66a7748d 70 private constructor () {
d5bd1c00
JB
71 // This is intentional
72 }
73
e7c0fce0 74 public static getConfigurationSection<T extends ConfigurationSectionType>(
66a7748d 75 sectionName: ConfigurationSection
e7c0fce0 76 ): T {
81b9a105 77 if (!Configuration.isConfigurationSectionCached(sectionName)) {
66a7748d 78 Configuration.cacheConfigurationSection(sectionName)
c1c97db8 79 }
66a7748d 80 return Configuration.configurationSectionCache.get(sectionName) as T
5d049829
JB
81 }
82
66a7748d 83 public static getStationTemplateUrls (): StationTemplateUrl[] | undefined {
5f742aac 84 const checkDeprecatedConfigurationKeysOnce = once(
38ae4ce2 85 Configuration.checkDeprecatedConfigurationKeys.bind(Configuration)
66a7748d
JB
86 )
87 checkDeprecatedConfigurationKeysOnce()
88 return Configuration.getConfigurationData()?.stationTemplateUrls
5d049829
JB
89 }
90
66a7748d 91 public static getSupervisionUrls (): string | string[] | undefined {
a37fc6dc 92 if (
d760a0a6 93 Configuration.getConfigurationData()?.['supervisionURLs' as keyof ConfigurationData] != null
a37fc6dc 94 ) {
66a7748d 95 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
5d049829 96 Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
a37fc6dc 97 'supervisionURLs' as keyof ConfigurationData
66a7748d 98 ] as string | string[]
5d049829 99 }
66a7748d 100 return Configuration.getConfigurationData()?.supervisionUrls
5d049829
JB
101 }
102
66a7748d 103 public static getSupervisionUrlDistribution (): SupervisionUrlDistribution | undefined {
5d049829
JB
104 return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
105 ? Configuration.getConfigurationData()?.supervisionUrlDistribution
66a7748d 106 : SupervisionUrlDistribution.ROUND_ROBIN
5d049829
JB
107 }
108
66a7748d 109 public static workerPoolInUse (): boolean {
1d8f226b 110 return [WorkerProcessType.dynamicPool, WorkerProcessType.fixedPool].includes(
66a7748d 111 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
eb979012 112 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
66a7748d
JB
113 .processType!
114 )
c831d2bc
JB
115 }
116
66a7748d 117 public static workerDynamicPoolInUse (): boolean {
eb979012
JB
118 return (
119 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
120 .processType === WorkerProcessType.dynamicPool
66a7748d 121 )
c831d2bc
JB
122 }
123
66a7748d
JB
124 private static isConfigurationSectionCached (sectionName: ConfigurationSection): boolean {
125 return Configuration.configurationSectionCache.has(sectionName)
81b9a105
JB
126 }
127
66a7748d 128 private static cacheConfigurationSection (sectionName: ConfigurationSection): void {
81b9a105
JB
129 switch (sectionName) {
130 case ConfigurationSection.log:
66a7748d
JB
131 Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection())
132 break
81b9a105
JB
133 case ConfigurationSection.performanceStorage:
134 Configuration.configurationSectionCache.set(
135 sectionName,
66a7748d
JB
136 Configuration.buildPerformanceStorageSection()
137 )
138 break
81b9a105 139 case ConfigurationSection.worker:
66a7748d
JB
140 Configuration.configurationSectionCache.set(sectionName, Configuration.buildWorkerSection())
141 break
81b9a105
JB
142 case ConfigurationSection.uiServer:
143 Configuration.configurationSectionCache.set(
144 sectionName,
66a7748d
JB
145 Configuration.buildUIServerSection()
146 )
147 break
81b9a105
JB
148 default:
149 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
66a7748d 150 throw new Error(`Unknown configuration section '${sectionName}'`)
81b9a105
JB
151 }
152 }
153
66a7748d 154 private static buildUIServerSection (): UIServerConfiguration {
675fa8e3 155 let uiServerConfiguration: UIServerConfiguration = {
b803eefa 156 enabled: false,
1f7fa4de 157 type: ApplicationProtocol.WS,
329eab0e 158 version: ApplicationProtocolVersion.VERSION_11,
c127bd64 159 options: {
adbddcb4 160 host: Constants.DEFAULT_UI_SERVER_HOST,
66a7748d
JB
161 port: Constants.DEFAULT_UI_SERVER_PORT
162 }
163 }
f74e97ac 164 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.uiServer)) {
b4c82e73 165 uiServerConfiguration = mergeDeepRight(
598c886d 166 uiServerConfiguration,
66a7748d
JB
167 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
168 Configuration.getConfigurationData()!.uiServer!
169 )
6a49ad23 170 }
66a7748d
JB
171 if (isCFEnvironment()) {
172 delete uiServerConfiguration.options?.host
173 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
174 uiServerConfiguration.options!.port = parseInt(env.PORT!)
b803eefa 175 }
66a7748d 176 return uiServerConfiguration
6a49ad23
JB
177 }
178
66a7748d 179 private static buildPerformanceStorageSection (): StorageConfiguration {
789007bd
JB
180 let storageConfiguration: StorageConfiguration
181 switch (Configuration.getConfigurationData()?.performanceStorage?.type) {
182 case StorageType.SQLITE:
183 storageConfiguration = {
184 enabled: false,
185 type: StorageType.SQLITE,
186 uri: getDefaultPerformanceStorageUri(StorageType.SQLITE)
187 }
188 break
189 case StorageType.JSON_FILE:
789007bd
JB
190 storageConfiguration = {
191 enabled: false,
192 type: StorageType.JSON_FILE,
193 uri: getDefaultPerformanceStorageUri(StorageType.JSON_FILE)
194 }
195 break
a66bbcfe
JB
196 case StorageType.NONE:
197 default:
198 storageConfiguration = {
199 enabled: true,
200 type: StorageType.NONE
201 }
202 break
66a7748d 203 }
f74e97ac 204 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
e7aeea18 205 storageConfiguration = {
1ba1e8fb 206 ...storageConfiguration,
f74e97ac 207 ...Configuration.getConfigurationData()?.performanceStorage,
789007bd
JB
208 ...((Configuration.getConfigurationData()?.performanceStorage?.type ===
209 StorageType.JSON_FILE ||
210 Configuration.getConfigurationData()?.performanceStorage?.type === StorageType.SQLITE) &&
66a7748d
JB
211 Configuration.getConfigurationData()?.performanceStorage?.uri != null && {
212 uri: buildPerformanceUriFilePath(
213 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
214 new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname
215 )
216 })
217 }
72f041bd 218 }
66a7748d 219 return storageConfiguration
7dde0b73
JB
220 }
221
66a7748d 222 private static buildLogSection (): LogConfiguration {
3d48c1c1
JB
223 const defaultLogConfiguration: LogConfiguration = {
224 enabled: true,
225 file: 'logs/combined.log',
226 errorFile: 'logs/error.log',
227 statisticsInterval: Constants.DEFAULT_LOG_STATISTICS_INTERVAL,
228 level: 'info',
229 format: 'simple',
66a7748d
JB
230 rotate: true
231 }
3d48c1c1 232 const deprecatedLogConfiguration: LogConfiguration = {
f74e97ac 233 ...(hasOwnProp(Configuration.getConfigurationData(), 'logEnabled') && {
66a7748d 234 enabled: Configuration.getConfigurationData()?.logEnabled
3d48c1c1 235 }),
f74e97ac 236 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFile') && {
66a7748d 237 file: Configuration.getConfigurationData()?.logFile
3d48c1c1 238 }),
f74e97ac 239 ...(hasOwnProp(Configuration.getConfigurationData(), 'logErrorFile') && {
66a7748d 240 errorFile: Configuration.getConfigurationData()?.logErrorFile
3d48c1c1 241 }),
f74e97ac 242 ...(hasOwnProp(Configuration.getConfigurationData(), 'logStatisticsInterval') && {
66a7748d 243 statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval
3d48c1c1 244 }),
f74e97ac 245 ...(hasOwnProp(Configuration.getConfigurationData(), 'logLevel') && {
66a7748d 246 level: Configuration.getConfigurationData()?.logLevel
3d48c1c1 247 }),
f74e97ac 248 ...(hasOwnProp(Configuration.getConfigurationData(), 'logConsole') && {
66a7748d 249 console: Configuration.getConfigurationData()?.logConsole
3d48c1c1 250 }),
f74e97ac 251 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFormat') && {
66a7748d 252 format: Configuration.getConfigurationData()?.logFormat
3d48c1c1 253 }),
f74e97ac 254 ...(hasOwnProp(Configuration.getConfigurationData(), 'logRotate') && {
66a7748d 255 rotate: Configuration.getConfigurationData()?.logRotate
3d48c1c1 256 }),
f74e97ac 257 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxFiles') && {
66a7748d 258 maxFiles: Configuration.getConfigurationData()?.logMaxFiles
3d48c1c1 259 }),
f74e97ac 260 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxSize') && {
66a7748d
JB
261 maxSize: Configuration.getConfigurationData()?.logMaxSize
262 })
263 }
3d48c1c1
JB
264 const logConfiguration: LogConfiguration = {
265 ...defaultLogConfiguration,
266 ...deprecatedLogConfiguration,
f74e97ac 267 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) &&
66a7748d
JB
268 Configuration.getConfigurationData()?.log)
269 }
270 return logConfiguration
3d48c1c1
JB
271 }
272
66a7748d 273 private static buildWorkerSection (): WorkerConfiguration {
3602e107
JB
274 const defaultWorkerConfiguration: WorkerConfiguration = {
275 processType: WorkerProcessType.workerSet,
276 startDelay: DEFAULT_WORKER_START_DELAY,
277 elementsPerWorker: 'auto',
da47bc29 278 elementAddDelay: DEFAULT_ELEMENT_ADD_DELAY,
3602e107 279 poolMinSize: DEFAULT_POOL_MIN_SIZE,
66a7748d
JB
280 poolMaxSize: DEFAULT_POOL_MAX_SIZE
281 }
da47bc29 282
3602e107
JB
283 const deprecatedWorkerConfiguration: WorkerConfiguration = {
284 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
66a7748d 285 processType: Configuration.getConfigurationData()?.workerProcess
3602e107
JB
286 }),
287 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && {
66a7748d 288 startDelay: Configuration.getConfigurationData()?.workerStartDelay
3602e107
JB
289 }),
290 ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
66a7748d 291 elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker
3602e107 292 }),
da47bc29
JB
293 ...(hasOwnProp(Configuration.getConfigurationData(), 'elementAddDelay') && {
294 elementAddDelay: Configuration.getConfigurationData()?.elementAddDelay
295 }),
296 ...(hasOwnProp(Configuration.getConfigurationData()?.worker, 'elementStartDelay') && {
297 elementAddDelay: Configuration.getConfigurationData()?.worker?.elementStartDelay
3602e107
JB
298 }),
299 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
66a7748d 300 poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize
3602e107
JB
301 }),
302 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && {
66a7748d
JB
303 poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize
304 })
305 }
3602e107 306 hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
66a7748d 307 delete Configuration.getConfigurationData()?.workerPoolStrategy
3602e107
JB
308 const workerConfiguration: WorkerConfiguration = {
309 ...defaultWorkerConfiguration,
310 ...deprecatedWorkerConfiguration,
311 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
66a7748d
JB
312 Configuration.getConfigurationData()?.worker)
313 }
314 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
315 checkWorkerProcessType(workerConfiguration.processType!)
316 checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker)
317 return workerConfiguration
3602e107
JB
318 }
319
66a7748d 320 private static checkDeprecatedConfigurationKeys (): void {
3602e107
JB
321 // connection timeout
322 Configuration.warnDeprecatedConfigurationKey(
323 'autoReconnectTimeout',
324 undefined,
66a7748d
JB
325 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
326 )
3602e107
JB
327 Configuration.warnDeprecatedConfigurationKey(
328 'connectionTimeout',
329 undefined,
66a7748d
JB
330 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
331 )
3602e107
JB
332 // connection retries
333 Configuration.warnDeprecatedConfigurationKey(
334 'autoReconnectMaxRetries',
335 undefined,
66a7748d
JB
336 'Use it in charging station template instead'
337 )
3602e107
JB
338 // station template url(s)
339 Configuration.warnDeprecatedConfigurationKey(
340 'stationTemplateURLs',
341 undefined,
66a7748d
JB
342 "Use 'stationTemplateUrls' instead"
343 )
d760a0a6
JB
344 Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData] !=
345 null &&
66a7748d 346 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3602e107 347 (Configuration.getConfigurationData()!.stationTemplateUrls =
66a7748d 348 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3602e107
JB
349 Configuration.getConfigurationData()![
350 'stationTemplateURLs' as keyof ConfigurationData
66a7748d 351 ] as StationTemplateUrl[])
1c9de2b9 352 Configuration.getConfigurationData()?.stationTemplateUrls.forEach(
3602e107 353 (stationTemplateUrl: StationTemplateUrl) => {
5199f9fd 354 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
d760a0a6 355 if (stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl] != null) {
3602e107 356 console.error(
4354af5a 357 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
358 `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`
359 )}`
360 )
3602e107 361 }
66a7748d
JB
362 }
363 )
3602e107
JB
364 // supervision url(s)
365 Configuration.warnDeprecatedConfigurationKey(
366 'supervisionURLs',
367 undefined,
66a7748d
JB
368 "Use 'supervisionUrls' instead"
369 )
3602e107
JB
370 // supervision urls distribution
371 Configuration.warnDeprecatedConfigurationKey(
372 'distributeStationToTenantEqually',
373 undefined,
66a7748d
JB
374 "Use 'supervisionUrlDistribution' instead"
375 )
3602e107
JB
376 Configuration.warnDeprecatedConfigurationKey(
377 'distributeStationsToTenantsEqually',
378 undefined,
66a7748d
JB
379 "Use 'supervisionUrlDistribution' instead"
380 )
3602e107 381 // worker section
e7aeea18 382 Configuration.warnDeprecatedConfigurationKey(
e80bc579 383 'useWorkerPool',
1895299d 384 undefined,
66a7748d
JB
385 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`
386 )
cf2a5d9b
JB
387 Configuration.warnDeprecatedConfigurationKey(
388 'workerProcess',
1895299d 389 undefined,
66a7748d
JB
390 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`
391 )
cf2a5d9b
JB
392 Configuration.warnDeprecatedConfigurationKey(
393 'workerStartDelay',
1895299d 394 undefined,
66a7748d
JB
395 `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`
396 )
cf2a5d9b
JB
397 Configuration.warnDeprecatedConfigurationKey(
398 'chargingStationsPerWorker',
1895299d 399 undefined,
66a7748d
JB
400 `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`
401 )
cf2a5d9b 402 Configuration.warnDeprecatedConfigurationKey(
da47bc29 403 'elementAddDelay',
1895299d 404 undefined,
da47bc29 405 `Use '${ConfigurationSection.worker}' section to define the worker's element add delay instead`
66a7748d 406 )
cf2a5d9b
JB
407 Configuration.warnDeprecatedConfigurationKey(
408 'workerPoolMinSize',
1895299d 409 undefined,
66a7748d
JB
410 `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`
411 )
e7aeea18 412 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 413 'workerPoolSize',
1895299d 414 undefined,
66a7748d
JB
415 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`
416 )
cf2a5d9b 417 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 418 'workerPoolMaxSize',
1895299d 419 undefined,
66a7748d
JB
420 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`
421 )
cf2a5d9b 422 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 423 'workerPoolStrategy',
1895299d 424 undefined,
66a7748d
JB
425 `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`
426 )
eda9c451
JB
427 Configuration.warnDeprecatedConfigurationKey(
428 'poolStrategy',
974efe6c 429 ConfigurationSection.worker,
66a7748d
JB
430 'Not publicly exposed to end users'
431 )
da47bc29
JB
432 Configuration.warnDeprecatedConfigurationKey(
433 'elementStartDelay',
434 ConfigurationSection.worker,
435 "Use 'elementAddDelay' instead"
436 )
1d8f226b
JB
437 if (
438 Configuration.getConfigurationData()?.worker?.processType ===
439 ('staticPool' as WorkerProcessType)
440 ) {
441 console.error(
4354af5a 442 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
443 `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`
444 )}`
445 )
1d8f226b 446 }
3602e107
JB
447 // log section
448 Configuration.warnDeprecatedConfigurationKey(
449 'logEnabled',
450 undefined,
66a7748d
JB
451 `Use '${ConfigurationSection.log}' section to define the logging enablement instead`
452 )
3602e107
JB
453 Configuration.warnDeprecatedConfigurationKey(
454 'logFile',
455 undefined,
66a7748d
JB
456 `Use '${ConfigurationSection.log}' section to define the log file instead`
457 )
3602e107
JB
458 Configuration.warnDeprecatedConfigurationKey(
459 'logErrorFile',
460 undefined,
66a7748d
JB
461 `Use '${ConfigurationSection.log}' section to define the log error file instead`
462 )
3602e107
JB
463 Configuration.warnDeprecatedConfigurationKey(
464 'logConsole',
465 undefined,
66a7748d
JB
466 `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`
467 )
3602e107
JB
468 Configuration.warnDeprecatedConfigurationKey(
469 'logStatisticsInterval',
470 undefined,
66a7748d
JB
471 `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`
472 )
3602e107
JB
473 Configuration.warnDeprecatedConfigurationKey(
474 'logLevel',
475 undefined,
66a7748d
JB
476 `Use '${ConfigurationSection.log}' section to define the log level instead`
477 )
3602e107
JB
478 Configuration.warnDeprecatedConfigurationKey(
479 'logFormat',
480 undefined,
66a7748d
JB
481 `Use '${ConfigurationSection.log}' section to define the log format instead`
482 )
3602e107
JB
483 Configuration.warnDeprecatedConfigurationKey(
484 'logRotate',
485 undefined,
66a7748d
JB
486 `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`
487 )
3602e107
JB
488 Configuration.warnDeprecatedConfigurationKey(
489 'logMaxFiles',
490 undefined,
66a7748d
JB
491 `Use '${ConfigurationSection.log}' section to define the log maximum files instead`
492 )
3602e107
JB
493 Configuration.warnDeprecatedConfigurationKey(
494 'logMaxSize',
495 undefined,
66a7748d
JB
496 `Use '${ConfigurationSection.log}' section to define the log maximum size instead`
497 )
3602e107
JB
498 // performanceStorage section
499 Configuration.warnDeprecatedConfigurationKey(
500 'URI',
501 ConfigurationSection.performanceStorage,
66a7748d
JB
502 "Use 'uri' instead"
503 )
3602e107
JB
504 // uiServer section
505 if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
506 console.error(
4354af5a 507 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
508 `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`
509 )}`
510 )
b5b2c3e8 511 }
7dde0b73 512 }
eb3937cb 513
66a7748d 514 private static warnDeprecatedConfigurationKey (
e7aeea18 515 key: string,
23c97c47 516 configurationSection?: ConfigurationSection,
66a7748d
JB
517 logMsgToAppend = ''
518 ): void {
e7aeea18 519 if (
23c97c47
JB
520 configurationSection != null &&
521 Configuration.getConfigurationData()?.[configurationSection as keyof ConfigurationData] !=
522 null &&
300418e9 523 (
23c97c47
JB
524 Configuration.getConfigurationData()?.[
525 configurationSection as keyof ConfigurationData
526 ] as Record<string, unknown>
d760a0a6 527 )[key] != null
e7aeea18
JB
528 ) {
529 console.error(
4354af5a 530 `${chalk.green(logPrefix())} ${chalk.red(
23c97c47 531 `Deprecated configuration key '${key}' usage in section '${configurationSection}'${
c5e52a07 532 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
66a7748d
JB
533 }`
534 )}`
535 )
d760a0a6 536 } else if (Configuration.getConfigurationData()?.[key as keyof ConfigurationData] != null) {
e7aeea18 537 console.error(
4354af5a 538 `${chalk.green(logPrefix())} ${chalk.red(
c5e52a07
JB
539 `Deprecated configuration key '${key}' usage${
540 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
66a7748d
JB
541 }`
542 )}`
543 )
eb3937cb
JB
544 }
545 }
546
8f8f87c4 547 public static getConfigurationData (): ConfigurationData | undefined {
66a7748d 548 if (Configuration.configurationData == null) {
23132a44 549 try {
f74e97ac 550 Configuration.configurationData = JSON.parse(
66a7748d
JB
551 readFileSync(Configuration.configurationFile, 'utf8')
552 ) as ConfigurationData
553 if (Configuration.configurationFileWatcher == null) {
554 Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher()
1f8f6332 555 }
23132a44 556 } catch (error) {
4354af5a 557 handleFileException(
a95873d8 558 Configuration.configurationFile,
7164966d
JB
559 FileType.Configuration,
560 error as NodeJS.ErrnoException,
66a7748d
JB
561 logPrefix()
562 )
23132a44 563 }
eb3937cb 564 }
66a7748d 565 return Configuration.configurationData
eb3937cb 566 }
963ee397 567
66a7748d 568 private static getConfigurationFileWatcher (): FSWatcher | undefined {
23132a44 569 try {
d972af76 570 return watch(Configuration.configurationFile, (event, filename): void => {
1b4ccee3
JB
571 if (
572 !Configuration.configurationFileReloading &&
66a7748d 573 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
aafba9d8 574 filename!.trim().length > 0 &&
1b4ccee3
JB
575 event === 'change'
576 ) {
66a7748d 577 Configuration.configurationFileReloading = true
38ae4ce2 578 const consoleWarnOnce = once(console.warn)
1b4ccee3 579 consoleWarnOnce(
4354af5a 580 `${chalk.green(logPrefix())} ${chalk.yellow(
66a7748d
JB
581 `${FileType.Configuration} ${this.configurationFile} file have changed, reload`
582 )}`
583 )
584 delete Configuration.configurationData
585 Configuration.configurationSectionCache.clear()
d760a0a6 586 if (Configuration.configurationChangeCallback != null) {
1b4ccee3 587 Configuration.configurationChangeCallback()
ea32ea05 588 .catch((error: unknown) => {
66a7748d 589 throw typeof error === 'string' ? new Error(error) : error
1b4ccee3
JB
590 })
591 .finally(() => {
66a7748d
JB
592 Configuration.configurationFileReloading = false
593 })
41d95b76 594 } else {
66a7748d 595 Configuration.configurationFileReloading = false
3ec10737 596 }
23132a44 597 }
66a7748d 598 })
23132a44 599 } catch (error) {
4354af5a 600 handleFileException(
a95873d8 601 Configuration.configurationFile,
7164966d
JB
602 FileType.Configuration,
603 error as NodeJS.ErrnoException,
66a7748d
JB
604 logPrefix()
605 )
23132a44 606 }
ded13d97 607 }
7dde0b73 608}