build(deps-dev): apply updates
[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
JB
23import {
24 DEFAULT_ELEMENT_START_DELAY,
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',
278 elementStartDelay: DEFAULT_ELEMENT_START_DELAY,
279 poolMinSize: DEFAULT_POOL_MIN_SIZE,
66a7748d
JB
280 poolMaxSize: DEFAULT_POOL_MAX_SIZE
281 }
3602e107
JB
282 const deprecatedWorkerConfiguration: WorkerConfiguration = {
283 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
66a7748d 284 processType: Configuration.getConfigurationData()?.workerProcess
3602e107
JB
285 }),
286 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && {
66a7748d 287 startDelay: Configuration.getConfigurationData()?.workerStartDelay
3602e107
JB
288 }),
289 ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
66a7748d 290 elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker
3602e107
JB
291 }),
292 ...(hasOwnProp(Configuration.getConfigurationData(), 'elementStartDelay') && {
66a7748d 293 elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay
3602e107
JB
294 }),
295 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
66a7748d 296 poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize
3602e107
JB
297 }),
298 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && {
66a7748d
JB
299 poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize
300 })
301 }
3602e107 302 hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
66a7748d 303 delete Configuration.getConfigurationData()?.workerPoolStrategy
3602e107
JB
304 const workerConfiguration: WorkerConfiguration = {
305 ...defaultWorkerConfiguration,
306 ...deprecatedWorkerConfiguration,
307 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
66a7748d
JB
308 Configuration.getConfigurationData()?.worker)
309 }
310 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
311 checkWorkerProcessType(workerConfiguration.processType!)
312 checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker)
313 return workerConfiguration
3602e107
JB
314 }
315
66a7748d 316 private static checkDeprecatedConfigurationKeys (): void {
3602e107
JB
317 // connection timeout
318 Configuration.warnDeprecatedConfigurationKey(
319 'autoReconnectTimeout',
320 undefined,
66a7748d
JB
321 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
322 )
3602e107
JB
323 Configuration.warnDeprecatedConfigurationKey(
324 'connectionTimeout',
325 undefined,
66a7748d
JB
326 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
327 )
3602e107
JB
328 // connection retries
329 Configuration.warnDeprecatedConfigurationKey(
330 'autoReconnectMaxRetries',
331 undefined,
66a7748d
JB
332 'Use it in charging station template instead'
333 )
3602e107
JB
334 // station template url(s)
335 Configuration.warnDeprecatedConfigurationKey(
336 'stationTemplateURLs',
337 undefined,
66a7748d
JB
338 "Use 'stationTemplateUrls' instead"
339 )
d760a0a6
JB
340 Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData] !=
341 null &&
66a7748d 342 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3602e107 343 (Configuration.getConfigurationData()!.stationTemplateUrls =
66a7748d 344 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
3602e107
JB
345 Configuration.getConfigurationData()![
346 'stationTemplateURLs' as keyof ConfigurationData
66a7748d 347 ] as StationTemplateUrl[])
1c9de2b9 348 Configuration.getConfigurationData()?.stationTemplateUrls.forEach(
3602e107 349 (stationTemplateUrl: StationTemplateUrl) => {
5199f9fd 350 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
d760a0a6 351 if (stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl] != null) {
3602e107 352 console.error(
4354af5a 353 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
354 `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`
355 )}`
356 )
3602e107 357 }
66a7748d
JB
358 }
359 )
3602e107
JB
360 // supervision url(s)
361 Configuration.warnDeprecatedConfigurationKey(
362 'supervisionURLs',
363 undefined,
66a7748d
JB
364 "Use 'supervisionUrls' instead"
365 )
3602e107
JB
366 // supervision urls distribution
367 Configuration.warnDeprecatedConfigurationKey(
368 'distributeStationToTenantEqually',
369 undefined,
66a7748d
JB
370 "Use 'supervisionUrlDistribution' instead"
371 )
3602e107
JB
372 Configuration.warnDeprecatedConfigurationKey(
373 'distributeStationsToTenantsEqually',
374 undefined,
66a7748d
JB
375 "Use 'supervisionUrlDistribution' instead"
376 )
3602e107 377 // worker section
e7aeea18 378 Configuration.warnDeprecatedConfigurationKey(
e80bc579 379 'useWorkerPool',
1895299d 380 undefined,
66a7748d
JB
381 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`
382 )
cf2a5d9b
JB
383 Configuration.warnDeprecatedConfigurationKey(
384 'workerProcess',
1895299d 385 undefined,
66a7748d
JB
386 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`
387 )
cf2a5d9b
JB
388 Configuration.warnDeprecatedConfigurationKey(
389 'workerStartDelay',
1895299d 390 undefined,
66a7748d
JB
391 `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`
392 )
cf2a5d9b
JB
393 Configuration.warnDeprecatedConfigurationKey(
394 'chargingStationsPerWorker',
1895299d 395 undefined,
66a7748d
JB
396 `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`
397 )
cf2a5d9b
JB
398 Configuration.warnDeprecatedConfigurationKey(
399 'elementStartDelay',
1895299d 400 undefined,
66a7748d
JB
401 `Use '${ConfigurationSection.worker}' section to define the worker's element start delay instead`
402 )
cf2a5d9b
JB
403 Configuration.warnDeprecatedConfigurationKey(
404 'workerPoolMinSize',
1895299d 405 undefined,
66a7748d
JB
406 `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`
407 )
e7aeea18 408 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 409 'workerPoolSize',
1895299d 410 undefined,
66a7748d
JB
411 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`
412 )
cf2a5d9b 413 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 414 'workerPoolMaxSize',
1895299d 415 undefined,
66a7748d
JB
416 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`
417 )
cf2a5d9b 418 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 419 'workerPoolStrategy',
1895299d 420 undefined,
66a7748d
JB
421 `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`
422 )
eda9c451
JB
423 Configuration.warnDeprecatedConfigurationKey(
424 'poolStrategy',
974efe6c 425 ConfigurationSection.worker,
66a7748d
JB
426 'Not publicly exposed to end users'
427 )
1d8f226b
JB
428 if (
429 Configuration.getConfigurationData()?.worker?.processType ===
430 ('staticPool' as WorkerProcessType)
431 ) {
432 console.error(
4354af5a 433 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
434 `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`
435 )}`
436 )
1d8f226b 437 }
3602e107
JB
438 // log section
439 Configuration.warnDeprecatedConfigurationKey(
440 'logEnabled',
441 undefined,
66a7748d
JB
442 `Use '${ConfigurationSection.log}' section to define the logging enablement instead`
443 )
3602e107
JB
444 Configuration.warnDeprecatedConfigurationKey(
445 'logFile',
446 undefined,
66a7748d
JB
447 `Use '${ConfigurationSection.log}' section to define the log file instead`
448 )
3602e107
JB
449 Configuration.warnDeprecatedConfigurationKey(
450 'logErrorFile',
451 undefined,
66a7748d
JB
452 `Use '${ConfigurationSection.log}' section to define the log error file instead`
453 )
3602e107
JB
454 Configuration.warnDeprecatedConfigurationKey(
455 'logConsole',
456 undefined,
66a7748d
JB
457 `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`
458 )
3602e107
JB
459 Configuration.warnDeprecatedConfigurationKey(
460 'logStatisticsInterval',
461 undefined,
66a7748d
JB
462 `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`
463 )
3602e107
JB
464 Configuration.warnDeprecatedConfigurationKey(
465 'logLevel',
466 undefined,
66a7748d
JB
467 `Use '${ConfigurationSection.log}' section to define the log level instead`
468 )
3602e107
JB
469 Configuration.warnDeprecatedConfigurationKey(
470 'logFormat',
471 undefined,
66a7748d
JB
472 `Use '${ConfigurationSection.log}' section to define the log format instead`
473 )
3602e107
JB
474 Configuration.warnDeprecatedConfigurationKey(
475 'logRotate',
476 undefined,
66a7748d
JB
477 `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`
478 )
3602e107
JB
479 Configuration.warnDeprecatedConfigurationKey(
480 'logMaxFiles',
481 undefined,
66a7748d
JB
482 `Use '${ConfigurationSection.log}' section to define the log maximum files instead`
483 )
3602e107
JB
484 Configuration.warnDeprecatedConfigurationKey(
485 'logMaxSize',
486 undefined,
66a7748d
JB
487 `Use '${ConfigurationSection.log}' section to define the log maximum size instead`
488 )
3602e107
JB
489 // performanceStorage section
490 Configuration.warnDeprecatedConfigurationKey(
491 'URI',
492 ConfigurationSection.performanceStorage,
66a7748d
JB
493 "Use 'uri' instead"
494 )
3602e107
JB
495 // uiServer section
496 if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
497 console.error(
4354af5a 498 `${chalk.green(logPrefix())} ${chalk.red(
66a7748d
JB
499 `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`
500 )}`
501 )
b5b2c3e8 502 }
7dde0b73 503 }
eb3937cb 504
66a7748d 505 private static warnDeprecatedConfigurationKey (
e7aeea18
JB
506 key: string,
507 sectionName?: string,
66a7748d
JB
508 logMsgToAppend = ''
509 ): void {
e7aeea18 510 if (
66a7748d 511 sectionName != null &&
d760a0a6 512 Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] != null &&
300418e9
JB
513 (
514 Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] as Record<
515 string,
516 unknown
517 >
d760a0a6 518 )[key] != null
e7aeea18
JB
519 ) {
520 console.error(
4354af5a 521 `${chalk.green(logPrefix())} ${chalk.red(
c5e52a07
JB
522 `Deprecated configuration key '${key}' usage in section '${sectionName}'${
523 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
66a7748d
JB
524 }`
525 )}`
526 )
d760a0a6 527 } else if (Configuration.getConfigurationData()?.[key as keyof ConfigurationData] != null) {
e7aeea18 528 console.error(
4354af5a 529 `${chalk.green(logPrefix())} ${chalk.red(
c5e52a07
JB
530 `Deprecated configuration key '${key}' usage${
531 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
66a7748d
JB
532 }`
533 )}`
534 )
eb3937cb
JB
535 }
536 }
537
66a7748d
JB
538 private static getConfigurationData (): ConfigurationData | undefined {
539 if (Configuration.configurationData == null) {
23132a44 540 try {
f74e97ac 541 Configuration.configurationData = JSON.parse(
66a7748d
JB
542 readFileSync(Configuration.configurationFile, 'utf8')
543 ) as ConfigurationData
544 if (Configuration.configurationFileWatcher == null) {
545 Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher()
1f8f6332 546 }
23132a44 547 } catch (error) {
4354af5a 548 handleFileException(
a95873d8 549 Configuration.configurationFile,
7164966d
JB
550 FileType.Configuration,
551 error as NodeJS.ErrnoException,
66a7748d
JB
552 logPrefix()
553 )
23132a44 554 }
eb3937cb 555 }
66a7748d 556 return Configuration.configurationData
eb3937cb 557 }
963ee397 558
66a7748d 559 private static getConfigurationFileWatcher (): FSWatcher | undefined {
23132a44 560 try {
d972af76 561 return watch(Configuration.configurationFile, (event, filename): void => {
1b4ccee3
JB
562 if (
563 !Configuration.configurationFileReloading &&
66a7748d 564 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
aafba9d8 565 filename!.trim().length > 0 &&
1b4ccee3
JB
566 event === 'change'
567 ) {
66a7748d 568 Configuration.configurationFileReloading = true
38ae4ce2 569 const consoleWarnOnce = once(console.warn)
1b4ccee3 570 consoleWarnOnce(
4354af5a 571 `${chalk.green(logPrefix())} ${chalk.yellow(
66a7748d
JB
572 `${FileType.Configuration} ${this.configurationFile} file have changed, reload`
573 )}`
574 )
575 delete Configuration.configurationData
576 Configuration.configurationSectionCache.clear()
d760a0a6 577 if (Configuration.configurationChangeCallback != null) {
1b4ccee3 578 Configuration.configurationChangeCallback()
ea32ea05 579 .catch((error: unknown) => {
66a7748d 580 throw typeof error === 'string' ? new Error(error) : error
1b4ccee3
JB
581 })
582 .finally(() => {
66a7748d
JB
583 Configuration.configurationFileReloading = false
584 })
41d95b76 585 } else {
66a7748d 586 Configuration.configurationFileReloading = false
3ec10737 587 }
23132a44 588 }
66a7748d 589 })
23132a44 590 } catch (error) {
4354af5a 591 handleFileException(
a95873d8 592 Configuration.configurationFile,
7164966d
JB
593 FileType.Configuration,
594 error as NodeJS.ErrnoException,
66a7748d
JB
595 logPrefix()
596 )
23132a44 597 }
ded13d97 598 }
7dde0b73 599}