zeroant-factory
Version:
Factory modules for zeroant
135 lines (123 loc) • 4.65 kB
text/typescript
import { ErrorCode, ErrorDescription } from 'zeroant-constant/response.enum'
import { InternalServerError } from 'zeroant-response/serverErrors/internalServerError.serverError'
import { resolve } from 'path'
import { type AddonConfig, type AddonConfigConstructor } from './addon.config.js'
export const ABS_PATH = resolve('.')
export const APP_PATH = new URL('../../../', import.meta.url).pathname.replace(/\/$/, '')
export type SERVER_MODE = 'standalone' | 'combine'
export type LOG_LEVEL = '*' | 'debug' | 'info' | 'error' | 'off'
export interface CustomConfig {
SERVER_MODE?: SERVER_MODE
SERVER_APP?: string
LOG_LEVEL?: LOG_LEVEL
ENABLE_HTTP?: 'true' | 'false'
ENABLE_SOCKET?: 'true' | 'false'
USE_PUB_SOCKET?: 'true' | 'false'
}
export type IDebugger = (level: LOG_LEVEL | undefined, message: string, ...args: any[]) => void
export abstract class ConfigFactory {
serverPort: number
serverHostname: string
environment: string
appName: string
isProd: boolean
readonly productionEnvironments = ['prod', 'production']
addons = new ConfigAddons<this>(this)
readonly absPath = ABS_PATH
serverMode: SERVER_MODE = 'combine'
serverApp: string
serverMountAsRoot: boolean
appKeys: string[] = []
LOG_LEVEL = '*'
enableHTTP = true
enableSocket = false
readonly appPath = APP_PATH
constructor(protected readonly _config: Record<Uppercase<string>, string | undefined>) {
this._init()
}
_init() {
this.environment = this.get('NODE_ENV', 'development')
this.isProd = this.productionEnvironments.includes(this.environment)
this.serverPort = parseInt(this.get('PORT', '8080'), 10)
this.serverHostname = this.get('HOSTNAME', '127.0.0.1')
this.appName = this.get('APP_NAME', 'ZeroAnt')
this.appKeys = this.get('APP_KEYS', 'shouldbereplace').split(',')
this.serverMode = this.get<SERVER_MODE>('SERVER_MODE', 'combine')
this.serverApp = this.get<string>('SERVER_APP', '*')
this.LOG_LEVEL = this.get<string>('LOG_LEVEL', '*')
this.enableHTTP = this.get<string>('ENABLE_HTTP', 'true').toLowerCase() === 'true'
this.enableSocket = this.get<string>('ENABLE_SOCKET', 'false').toLowerCase() === 'true'
this.serverMountAsRoot = this.get<string>('SERVER_MOUNT_AS_ROOT', 'on') === 'on'
}
logging(level: LOG_LEVEL, callback?: () => void): boolean {
if (this.LOG_LEVEL === '*' || this.LOG_LEVEL.split(',').includes(level)) {
if (callback !== undefined) {
callback()
}
return true
}
return false
}
createDebugger(name: string): IDebugger {
return (level: LOG_LEVEL = 'info', message: string, ...args: any[]) => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
this.logging(level, () => {
const logger: (...arg: any[]) => void = (console as any)[level] ?? console.log
logger(new Date(), `[${name}]:`, message, ...args)
})
}
}
get<T = any>(key: Uppercase<string>, defaultValue?: T): T {
const value = this._config[key]
if (value === null || value === undefined) {
if (defaultValue === undefined) {
throw new InternalServerError(
ErrorCode.CONFIG_EXCEPTION,
ErrorDescription.CONFIG_EXCEPTION,
`Please provide a value for ${key} or a default fallback value in your config source`
)
}
return defaultValue
}
return value as T
}
append(config: Record<Uppercase<string>, string | undefined>): this {
Object.assign(this._config, config)
this._init()
return this
}
getAddon<T extends AddonConfig<ConfigFactory>>(Type: AddonConfigConstructor<T>): T {
return this.addons.get<T>(Type)
}
setAddon(Addon: AddonConfigConstructor<AddonConfig<ConfigFactory>>): this {
this.addons.set(Addon)
return this
}
}
class ConfigAddons<T extends ConfigFactory> {
_addons = new Set()
constructor(protected config: T) {}
get<T extends AddonConfig<ConfigFactory>>(Type: AddonConfigConstructor<T>): T {
for (const addon of this._addons.values()) {
if (addon instanceof Type) {
return addon
}
}
throw new InternalServerError(
ErrorCode.UNIMPLEMENTED_EXCEPTION,
ErrorDescription.UNIMPLEMENTED_EXCEPTION,
`${Type.name} you trying to get is not implemented`
)
}
lazyGet<T extends AddonConfig<ConfigFactory>>(Type: AddonConfigConstructor<T>): T {
try {
return this.get<T>(Type)
} catch (error) {
return this.set(Type).get<T>(Type)
}
}
set(Addon: AddonConfigConstructor<AddonConfig<ConfigFactory>>): this {
this._addons.add(new Addon(this.config))
return this
}
}