From 86ca3921c9778c26624ea8446257584db66eaf87 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 22 Oct 2025 23:36:40 -0400 Subject: [PATCH] use platform services in startup routines --- packages/backend/src/GlobalModule.ts | 13 ++-- packages/backend/src/boot/common.ts | 6 +- packages/backend/src/boot/entry.ts | 20 ++++-- packages/backend/src/boot/master.ts | 63 +++++++------------ packages/backend/src/boot/worker.ts | 19 +++--- packages/backend/src/config.ts | 17 ++--- packages/backend/src/postgres.ts | 7 ++- packages/backend/test-server/entry.ts | 26 ++++---- packages/backend/test/e2e/2fa.ts | 6 +- packages/backend/test/e2e/move.ts | 6 +- packages/backend/test/e2e/timelines.ts | 6 +- .../test/unit/core/HttpRequestService.ts | 5 +- .../server/api/endpoints/notes/create.test.ts | 9 ++- packages/backend/test/utils.ts | 7 ++- 14 files changed, 117 insertions(+), 93 deletions(-) diff --git a/packages/backend/src/GlobalModule.ts b/packages/backend/src/GlobalModule.ts index c3431d1566..e6c84b544b 100644 --- a/packages/backend/src/GlobalModule.ts +++ b/packages/backend/src/GlobalModule.ts @@ -27,20 +27,15 @@ import type { Provider, OnApplicationShutdown } from '@nestjs/common'; const $config: Provider = { provide: DI.config, - useValue: loadConfig(), + useFactory: (loggerService: LoggerService) => loadConfig(loggerService), + inject: [LoggerService], }; const $db: Provider = { provide: DI.db, useFactory: async (config: Config, loggerService: LoggerService) => { - const dbLogger = loggerService.getLogger('db'); - try { - const db = createPostgresDataSource(config, dbLogger); - return await db.initialize(); - } catch (e) { - dbLogger.error('failed to initialize database connection', { e }); - throw e; - } + const db = createPostgresDataSource(config, loggerService); + return await db.initialize(); }, inject: [DI.config, LoggerService], }; diff --git a/packages/backend/src/boot/common.ts b/packages/backend/src/boot/common.ts index a10597c7a7..8ba25c7c0b 100644 --- a/packages/backend/src/boot/common.ts +++ b/packages/backend/src/boot/common.ts @@ -12,7 +12,7 @@ import { QueueStatsService } from '@/daemons/QueueStatsService.js'; import { ServerStatsService } from '@/daemons/ServerStatsService.js'; import { ServerService } from '@/server/ServerService.js'; import { MainModule } from '@/MainModule.js'; -import { envOption } from '@/env.js'; +import { EnvService } from '@/global/EnvService.js'; import { ApLogCleanupService } from '@/daemons/ApLogCleanupService.js'; export async function server() { @@ -27,7 +27,9 @@ export async function server() { if (process.env.NODE_ENV !== 'test') { app.get(ChartManagementService).start(); } - if (!envOption.noDaemons) { + + const envService = app.get(EnvService); + if (!envService.options.noDaemons) { app.get(QueueStatsService).start(); app.get(ServerStatsService).start(); app.get(ApLogCleanupService).start(); diff --git a/packages/backend/src/boot/entry.ts b/packages/backend/src/boot/entry.ts index 6bad70cf59..2e7aa95db3 100644 --- a/packages/backend/src/boot/entry.ts +++ b/packages/backend/src/boot/entry.ts @@ -13,8 +13,10 @@ import { inspect } from 'node:util'; import chalk from 'chalk'; import Xev from 'xev'; import Logger from '@/logger.js'; +import { EnvService } from '@/global/EnvService.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { NativeTimeService } from '@/global/TimeService.js'; import { prepEnv } from '@/boot/prepEnv.js'; -import { envOption } from '../env.js'; import { masterMain } from './master.js'; import { workerMain } from './worker.js'; import { readyRef } from './ready.js'; @@ -25,14 +27,20 @@ process.title = `Misskey (${cluster.isPrimary ? 'master' : 'worker'})`; prepEnv(); -const logger = new Logger('core', 'cyan'); -const clusterLogger = logger.createSubLogger('cluster', 'orange'); const ev = new Xev(); // We wrap this in a main function, that gets called, // because not all platforms support top level await :/ async function main() { + const envService = new EnvService(); + const envOption = envService.options; + + // eslint-disable-next-line no-restricted-globals + const loggerService = new LoggerService(console, new NativeTimeService(), envService); + const logger = loggerService.getLogger('core', 'cyan'); + const clusterLogger = logger.createSubLogger('cluster', 'orange'); + //#region Events // Listen new workers cluster.on('fork', worker => { @@ -97,18 +105,18 @@ async function main() { if (!envOption.disableClustering) { if (cluster.isPrimary) { logger.info(`Start main process... pid: ${process.pid}`); - await masterMain(); + await masterMain(loggerService, envService); ev.mount(); } else if (cluster.isWorker) { logger.info(`Start worker process... pid: ${process.pid}`); - await workerMain(); + await workerMain(loggerService, envService); } else { throw new Error('Unknown process type'); } } else { // 非clusterの場合はMasterのみが起動するため、Workerの処理は行わない(cluster.isWorker === trueの状態でこのブロックに来ることはない) logger.info(`Start main process... pid: ${process.pid}`); - await masterMain(); + await masterMain(loggerService, envService); ev.mount(); } diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index cd7a31de17..017778b3c1 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -13,11 +13,14 @@ import chalk from 'chalk'; import chalkTemplate from 'chalk-template'; import * as Sentry from '@sentry/node'; import { nodeProfilingIntegration } from '@sentry/profiling-node'; -import Logger from '@/logger.js'; +import type Logger from '@/logger.js'; import { loadConfig } from '@/config.js'; import type { Config } from '@/config.js'; +import type { LoggerService } from '@/core/LoggerService.js'; +import type { EnvService } from '@/global/EnvService.js'; +import type { EnvOption } from '@/env.js'; +import { renderInlineError } from '@/misc/render-inline-error.js'; import { showMachineInfo } from '@/misc/show-machine-info.js'; -import { envOption } from '@/env.js'; import { jobQueue, server } from './common.js'; const _filename = fileURLToPath(import.meta.url); @@ -25,12 +28,9 @@ const _dirname = dirname(_filename); const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); -const logger = new Logger('core', 'cyan'); -const bootLogger = logger.createSubLogger('boot', 'magenta'); - const themeColor = chalk.hex('#86b300'); -function greet() { +function greet(logger: Logger, bootLogger: Logger, envOption: EnvOption) { if (!envOption.quiet) { //#region Misskey logo logger.info(themeColor(' _____ _ _ ')); @@ -57,20 +57,25 @@ function greet() { /** * Init master process */ -export async function masterMain() { +export async function masterMain(loggerService: LoggerService, envService: EnvService) { let config!: Config; + const logger = loggerService.getLogger('core', 'cyan'); + const bootLogger = logger.createSubLogger('boot', 'magenta'); + + const envOption = envService.options; + // initialize app try { - greet(); - showEnvironment(); + greet(logger, bootLogger, envOption); + showEnvironment(bootLogger); await showMachineInfo(bootLogger); - showNodejsVersion(); - config = loadConfigBoot(); + showNodejsVersion(bootLogger); + config = loadConfig(loggerService); //await connectDb(); if (config.pidFile) fs.writeFileSync(config.pidFile, process.pid.toString()); } catch (e) { - bootLogger.error('Fatal error occurred during initialization', null, true); + bootLogger.error(`Fatal error occurred during initialization: ${renderInlineError(e)}`, { e }, true); process.exit(1); } @@ -125,7 +130,7 @@ export async function masterMain() { process.exit(1); } - await spawnWorkers(config.clusterLimit); + await spawnWorkers(bootLogger, config.clusterLimit); } else { // clusterモジュール無効時 @@ -147,7 +152,7 @@ export async function masterMain() { } } -function showEnvironment(): void { +function showEnvironment(bootLogger: Logger): void { const env = process.env.NODE_ENV; const logger = bootLogger.createSubLogger('env'); logger.info(typeof env === 'undefined' ? 'NODE_ENV is not set' : `NODE_ENV: ${env}`); @@ -158,34 +163,12 @@ function showEnvironment(): void { } } -function showNodejsVersion(): void { +function showNodejsVersion(bootLogger: Logger): void { const nodejsLogger = bootLogger.createSubLogger('nodejs'); nodejsLogger.info(`Version ${process.version} detected.`); } -function loadConfigBoot(): Config { - const configLogger = bootLogger.createSubLogger('config'); - let config; - - try { - config = loadConfig(); - } catch (exception) { - if (typeof exception === 'string') { - configLogger.error('Exception loading config:', exception); - process.exit(1); - } else if ((exception as any).code === 'ENOENT') { - configLogger.error('Configuration file not found', null, true); - process.exit(1); - } - throw exception; - } - - configLogger.info('Loaded'); - - return config; -} - /* async function connectDb(): Promise { const dbLogger = bootLogger.createSubLogger('db'); @@ -204,17 +187,17 @@ async function connectDb(): Promise { } */ -async function spawnWorkers(limit = 1) { +async function spawnWorkers(bootLogger: Logger, limit = 1) { const cpuCount = os.cpus().length; // in some weird environments, node can't count the CPUs; we trust the config in those cases const workers = cpuCount === 0 ? limit : Math.min(limit, cpuCount); bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`); - await Promise.all([...Array(workers)].map(spawnWorker)); + await Promise.all([...Array(workers)].map(() => spawnWorker(bootLogger))); bootLogger.info('All workers started'); } -function spawnWorker(): Promise { +function spawnWorker(bootLogger: Logger): Promise { return new Promise(res => { const worker = cluster.fork(); worker.on('message', message => { diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 8cf3cadd22..762471e88b 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -4,14 +4,16 @@ */ import cluster from 'node:cluster'; -import * as Sentry from '@sentry/node'; -import { nodeProfilingIntegration } from '@sentry/profiling-node'; -import { envOption } from '@/env.js'; -import { loadConfig } from '@/config.js'; -import { jobQueue, server } from './common.js'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import * as fs from 'node:fs'; +import * as Sentry from '@sentry/node'; +import { nodeProfilingIntegration } from '@sentry/profiling-node'; +import { loadConfig } from '@/config.js'; +import type { LoggerService } from '@/core/LoggerService.js'; +import type { EnvService } from '@/global/EnvService.js'; +import { jobQueue, server } from './common.js'; + const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); @@ -19,8 +21,9 @@ const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json /** * Init worker process */ -export async function workerMain() { - const config = loadConfig(); +export async function workerMain(loggerService: LoggerService, envService: EnvService) { + const config = loadConfig(loggerService); + const envOption = envService.options; if (config.sentryForBackend) { Sentry.init({ @@ -37,7 +40,7 @@ export async function workerMain() { maxBreadcrumbs: 0, // Set release version - release: "Sharkey@" + (meta.gitVersion ?? meta.version), + release: 'Sharkey@' + (meta.gitVersion ?? meta.version), ...config.sentryForBackend.options, }); diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 968f220b6d..5607f50eb7 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -14,6 +14,7 @@ import type * as Sentry from '@sentry/node'; import type * as SentryVue from '@sentry/vue'; import type { RedisOptions } from 'ioredis'; import type { IPv4, IPv6 } from 'ipaddr.js'; +import type { LoggerService } from '@/core/LoggerService.js'; type RedisOptionsSource = Partial & { host?: string; @@ -159,8 +160,6 @@ type Source = { } }; -const configLogger = new Logger('config'); - export type PrivateNetworkSource = string | { network?: string, ports?: number[] }; export type PrivateNetwork = { @@ -179,10 +178,10 @@ export type PrivateNetwork = { export type CIDR = [ip: IPv4 | IPv6, prefixLength: number]; -export function parsePrivateNetworks(patterns: PrivateNetworkSource[]): PrivateNetwork[]; -export function parsePrivateNetworks(patterns: undefined): undefined; -export function parsePrivateNetworks(patterns: PrivateNetworkSource[] | undefined): PrivateNetwork[] | undefined; -export function parsePrivateNetworks(patterns: PrivateNetworkSource[] | undefined): PrivateNetwork[] | undefined { +export function parsePrivateNetworks(patterns: PrivateNetworkSource[], configLogger: Logger): PrivateNetwork[]; +export function parsePrivateNetworks(patterns: undefined, configLogger: Logger): undefined; +export function parsePrivateNetworks(patterns: PrivateNetworkSource[] | undefined, configLogger: Logger): PrivateNetwork[] | undefined; +export function parsePrivateNetworks(patterns: PrivateNetworkSource[] | undefined, configLogger: Logger): PrivateNetwork[] | undefined { if (!patterns) return undefined; return patterns .map(e => { @@ -368,7 +367,9 @@ const path = process.env.MISSKEY_CONFIG_YML ? resolve(dir, 'test.yml') : resolve(dir, 'default.yml'); -export function loadConfig(): Config { +export function loadConfig(loggerService: LoggerService): Config { + const configLogger = loggerService.getLogger('config'); + const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8')); const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json'); @@ -456,7 +457,7 @@ export function loadConfig(): Config { proxy: config.proxy, proxySmtp: config.proxySmtp, proxyBypassHosts: config.proxyBypassHosts, - allowedPrivateNetworks: parsePrivateNetworks(config.allowedPrivateNetworks), + allowedPrivateNetworks: parsePrivateNetworks(config.allowedPrivateNetworks, configLogger), disallowExternalApRedirect: config.disallowExternalApRedirect ?? false, maxFileSize: config.maxFileSize ?? 262144000, maxNoteLength: config.maxNoteLength ?? 3000, diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 12c1bd1fdc..d08adb70b4 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -9,7 +9,9 @@ import { DataSource, Logger, type QueryRunner } from 'typeorm'; import * as highlight from 'cli-highlight'; import { entities as charts } from '@/core/chart/entities.js'; import { Config } from '@/config.js'; -import MisskeyLogger, { type Data } from '@/logger.js'; +import type MisskeyLogger from '@/logger.js'; +import type { Data } from '@/logger.js'; +import type { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; @@ -298,7 +300,8 @@ export const entities = [ const log = process.env.NODE_ENV !== 'production'; -export function createPostgresDataSource(config: Config, dbLogger: MisskeyLogger) { +export function createPostgresDataSource(config: Config, loggerService: LoggerService) { + const dbLogger = loggerService.getLogger('db'); return new DataSource({ type: 'postgres', host: config.db.host, diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts index 1e0de094cb..eb3e4c9b13 100644 --- a/packages/backend/test-server/entry.ts +++ b/packages/backend/test-server/entry.ts @@ -4,11 +4,11 @@ import Fastify from 'fastify'; import { NestFactory } from '@nestjs/core'; import { MainModule } from '@/MainModule.js'; import { ServerService } from '@/server/ServerService.js'; -import { loadConfig } from '@/config.js'; +import type { Config } from '@/config.js'; import { NestLogger } from '@/NestLogger.js'; +import { DI } from '@/di-symbols.js'; import { INestApplicationContext } from '@nestjs/common'; -const config = loadConfig(); const originEnv = JSON.stringify(process.env); process.env.NODE_ENV = 'test'; @@ -20,18 +20,21 @@ let serverService: ServerService; * テスト用のサーバインスタンスを起動する */ async function launch() { - await killTestServer(); - - console.log('starting application...'); - app = await NestFactory.createApplicationContext(MainModule, { logger: new NestLogger(), }); app.enableShutdownHooks(); + + const config = app.get(DI.config); + + await killTestServer(config); + + console.log('starting application...'); + serverService = app.get(ServerService); await serverService.launch(); - await startControllerEndpoints(); + await startControllerEndpoints(config); // ジョブキューは必要な時にテストコード側で起動する // ジョブキューが動くとテスト結果の確認に支障が出ることがあるので意図的に動かさないでいる @@ -42,7 +45,7 @@ async function launch() { /** * 既に重複したポートで待ち受けしているサーバがある場合はkillする */ -async function killTestServer() { +async function killTestServer(config: Config) { // try { const pid = await portToPid(config.port); @@ -56,9 +59,10 @@ async function killTestServer() { /** * 別プロセスに切り離してしまったが故に出来なくなった環境変数の書き換え等を実現するためのエンドポイントを作る - * @param port + * @param config */ -async function startControllerEndpoints(port = config.port + 1000) { +async function startControllerEndpoints(config: Config) { + const port = config.port + 1000; const fastify = Fastify(); fastify.post<{ Body: { key?: string, value?: string } }>('/env', async (req, res) => { @@ -80,7 +84,7 @@ async function startControllerEndpoints(port = config.port + 1000) { await serverService.dispose(); await app.close(); - await killTestServer(); + await killTestServer(config); console.log('starting application...'); diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts index 289359a2ce..1cce37c2fb 100644 --- a/packages/backend/test/e2e/2fa.ts +++ b/packages/backend/test/e2e/2fa.ts @@ -20,11 +20,15 @@ import type { RegistrationResponseJSON, } from '@simplewebauthn/types'; import type * as misskey from 'misskey-js'; +import { NativeTimeService } from '@/global/TimeService.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { EnvService } from '@/global/EnvService.js'; describe('2要素認証', () => { let alice: misskey.entities.SignupResponse; - const config = loadConfig(); + const loggerService = new LoggerService(console, new NativeTimeService(), new EnvService()); + const config = loadConfig(loggerService); const password = 'test'; const username = 'alice'; diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts index fd798bdb25..e1252cf9fe 100644 --- a/packages/backend/test/e2e/move.ts +++ b/packages/backend/test/e2e/move.ts @@ -15,6 +15,9 @@ import { secureRndstr } from '@/misc/secure-rndstr.js'; import { jobQueue } from '@/boot/common.js'; import { api, castAsError, initTestDb, signup, successfulApiCall, uploadFile } from '../utils.js'; import type * as misskey from 'misskey-js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { NativeTimeService } from '@/global/TimeService.js'; +import { EnvService } from '@/global/EnvService.js'; describe('Account Move', () => { let jq: INestApplicationContext; @@ -33,7 +36,8 @@ describe('Account Move', () => { beforeAll(async () => { jq = await jobQueue(); - const config = loadConfig(); + const loggerService = new LoggerService(console, new NativeTimeService(), new EnvService()); + const config = loadConfig(loggerService); url = new URL(config.url); const connection = await initTestDb(false); root = await signup({ username: 'root' }); diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 46f549aec7..8462bd65db 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -12,6 +12,9 @@ import { Redis } from 'ioredis'; import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl, withNotesCount, initTestDb } from '../utils.js'; import { loadConfig } from '@/config.js'; import { MiInstance } from '@/models/Instance.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { NativeTimeService } from '@/global/TimeService.js'; +import { EnvService } from '@/global/EnvService.js'; async function genHost() { const hostname = randomString() + '.example.com'; @@ -33,7 +36,8 @@ let redisForTimelines: Redis; describe('Timelines', () => { beforeAll(async () => { - redisForTimelines = new Redis(loadConfig().redisForTimelines); + const loggerService = new LoggerService(console, new NativeTimeService(), new EnvService()); + redisForTimelines = new Redis(loadConfig(loggerService).redisForTimelines); }); afterAll(() => { diff --git a/packages/backend/test/unit/core/HttpRequestService.ts b/packages/backend/test/unit/core/HttpRequestService.ts index ccce32ffee..fecb4ca14f 100644 --- a/packages/backend/test/unit/core/HttpRequestService.ts +++ b/packages/backend/test/unit/core/HttpRequestService.ts @@ -4,21 +4,24 @@ */ import { describe, jest } from '@jest/globals'; +import { MockConsole } from '../../misc/MockConsole.js'; import type { Mock } from 'jest-mock'; import type { PrivateNetwork } from '@/config.js'; import type { Socket } from 'net'; import { HttpRequestService, isAllowedPrivateIp, isPrivateUrl, resolveIp, validateSocketConnect } from '@/core/HttpRequestService.js'; import { parsePrivateNetworks } from '@/config.js'; +import Logger from '@/logger.js'; describe(HttpRequestService, () => { let allowedPrivateNetworks: PrivateNetwork[] | undefined; beforeEach(() => { + const logger = new Logger('mock', undefined, undefined, undefined, new MockConsole()); allowedPrivateNetworks = parsePrivateNetworks([ '10.0.0.1/32', { network: '127.0.0.1/32', ports: [1] }, { network: '127.0.0.1/32', ports: [3, 4, 5] }, - ]); + ], logger); }); describe(isAllowedPrivateIp, () => { diff --git a/packages/backend/test/unit/server/api/endpoints/notes/create.test.ts b/packages/backend/test/unit/server/api/endpoints/notes/create.test.ts index b83c652172..5d333a1dab 100644 --- a/packages/backend/test/unit/server/api/endpoints/notes/create.test.ts +++ b/packages/backend/test/unit/server/api/endpoints/notes/create.test.ts @@ -6,11 +6,16 @@ process.env.NODE_ENV = 'test'; import { describe, test, expect } from '@jest/globals'; -import { loadConfig } from '@/config.js'; +import { MockConsole } from '../../../../../misc/MockConsole.js'; import { getValidator } from '../../../../../../test/prelude/get-api-validator.js'; +import { loadConfig } from '@/config.js'; import { paramDef } from '@/server/api/endpoints/notes/create.js'; +import { NativeTimeService } from '@/global/TimeService.js'; +import { EnvService } from '@/global/EnvService.js'; +import { LoggerService } from '@/core/LoggerService.js'; -const config = loadConfig(); +const loggerService = new LoggerService(new MockConsole, new NativeTimeService(), new EnvService()); +const config = loadConfig(loggerService); const VALID = true; const INVALID = false; diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 25a3e07f9f..4a0d73a8b9 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -21,6 +21,9 @@ import type * as misskey from 'misskey-js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js'; import { ApiError } from '@/server/api/error.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { EnvService } from '@/global/EnvService.js'; +import { NativeTimeService } from '@/global/TimeService.js'; export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js'; @@ -38,7 +41,9 @@ export type SystemWebhookPayload = { body: any; }; -const config = loadConfig(); +// eslint-disable-next-line no-restricted-globals +const loggerService = new LoggerService(console, new NativeTimeService(), new EnvService()); +const config = loadConfig(loggerService); export const port = config.port; export const origin = config.url; export const host = new URL(config.url).host;