@spfn/core 0.1.0-alpha.82 → 0.1.0-alpha.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/index.js +6 -136
- package/dist/cache/index.js.map +1 -1
- package/dist/codegen/generators/index.js +6 -134
- package/dist/codegen/generators/index.js.map +1 -1
- package/dist/codegen/index.js +6 -134
- package/dist/codegen/index.js.map +1 -1
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.js +41 -135
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.js +6 -134
- package/dist/env/index.js.map +1 -1
- package/dist/index.js +160 -178
- package/dist/index.js.map +1 -1
- package/dist/logger/index.d.ts +90 -34
- package/dist/logger/index.js +7 -137
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.js +22 -138
- package/dist/middleware/index.js.map +1 -1
- package/dist/route/index.js +12 -152
- package/dist/route/index.js.map +1 -1
- package/dist/server/index.d.ts +110 -4
- package/dist/server/index.js +160 -178
- package/dist/server/index.js.map +1 -1
- package/package.json +2 -4
package/dist/server/index.d.ts
CHANGED
|
@@ -229,13 +229,119 @@ interface ServerConfig {
|
|
|
229
229
|
detailed?: boolean;
|
|
230
230
|
};
|
|
231
231
|
/**
|
|
232
|
-
*
|
|
232
|
+
* Infrastructure initialization control
|
|
233
|
+
* Controls automatic initialization of database and Redis
|
|
234
|
+
* @default Both enabled if credentials exist
|
|
233
235
|
*/
|
|
234
|
-
|
|
236
|
+
infrastructure?: {
|
|
237
|
+
/**
|
|
238
|
+
* Enable/disable automatic database initialization
|
|
239
|
+
* @default true if DATABASE_URL exists
|
|
240
|
+
*/
|
|
241
|
+
database?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Enable/disable automatic Redis initialization
|
|
244
|
+
* @default true if REDIS_URL exists
|
|
245
|
+
*/
|
|
246
|
+
redis?: boolean;
|
|
247
|
+
};
|
|
235
248
|
/**
|
|
236
|
-
*
|
|
249
|
+
* Server lifecycle hooks for custom infrastructure setup and management
|
|
250
|
+
* Allows initialization of custom services and resources at different stages
|
|
237
251
|
*/
|
|
238
|
-
|
|
252
|
+
lifecycle?: {
|
|
253
|
+
/**
|
|
254
|
+
* Hook: Run before infrastructure initialization
|
|
255
|
+
* Execute before database and Redis are initialized
|
|
256
|
+
* Use this for pre-initialization setup (logging, monitoring, etc.)
|
|
257
|
+
*
|
|
258
|
+
* @param config - The final server configuration
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* beforeInfrastructure: async (config) => {
|
|
262
|
+
* await initMonitoring();
|
|
263
|
+
* await setupCustomLogger();
|
|
264
|
+
* }
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
beforeInfrastructure?: (config: ServerConfig) => Promise<void>;
|
|
268
|
+
/**
|
|
269
|
+
* Hook: Run after infrastructure (DB/Redis) is initialized
|
|
270
|
+
* Database and Redis instances are available via getDatabase() and getRedis()
|
|
271
|
+
* Use this for:
|
|
272
|
+
* - Running migrations
|
|
273
|
+
* - Seeding initial data
|
|
274
|
+
* - Initializing services that depend on DB/Redis
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* import { getDatabase } from '@spfn/core/db';
|
|
279
|
+
* import { migrate } from 'drizzle-orm/postgres-js/migrator';
|
|
280
|
+
*
|
|
281
|
+
* afterInfrastructure: async () => {
|
|
282
|
+
* const db = getDatabase();
|
|
283
|
+
* await migrate(db, { migrationsFolder: './drizzle' });
|
|
284
|
+
* await seedInitialData(db);
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
afterInfrastructure?: () => Promise<void>;
|
|
289
|
+
/**
|
|
290
|
+
* Hook: Run before routes are loaded
|
|
291
|
+
* Use this to add global middleware or prepare the app before routes
|
|
292
|
+
*
|
|
293
|
+
* @param app - The Hono app instance
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* beforeRoutes: async (app) => {
|
|
297
|
+
* app.use('/*', globalMiddleware());
|
|
298
|
+
* }
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
beforeRoutes?: (app: Hono) => void | Promise<void>;
|
|
302
|
+
/**
|
|
303
|
+
* Hook: Run after routes are loaded
|
|
304
|
+
* Use this to add fallback handlers or final middleware
|
|
305
|
+
*
|
|
306
|
+
* @param app - The Hono app instance
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* afterRoutes: async (app) => {
|
|
310
|
+
* app.notFound((c) => c.json({ error: 'Not Found' }, 404));
|
|
311
|
+
* }
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
afterRoutes?: (app: Hono) => void | Promise<void>;
|
|
315
|
+
/**
|
|
316
|
+
* Hook: Run after server starts successfully
|
|
317
|
+
* Server is listening and ready to accept requests
|
|
318
|
+
* Receives the server instance for runtime access
|
|
319
|
+
*
|
|
320
|
+
* @param instance - The server instance with server, app, config, and close()
|
|
321
|
+
* @example
|
|
322
|
+
* ```typescript
|
|
323
|
+
* afterStart: async (instance) => {
|
|
324
|
+
* console.log(`Server ready at http://${instance.config.host}:${instance.config.port}`);
|
|
325
|
+
* await notifyHealthCheckService();
|
|
326
|
+
* }
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
afterStart?: (instance: ServerInstance) => Promise<void>;
|
|
330
|
+
/**
|
|
331
|
+
* Hook: Run before graceful shutdown
|
|
332
|
+
* Infrastructure (DB/Redis) is still available
|
|
333
|
+
* Use this to cleanup custom resources and services
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* beforeShutdown: async () => {
|
|
338
|
+
* await closeMessageQueue();
|
|
339
|
+
* await closeSearchService();
|
|
340
|
+
* }
|
|
341
|
+
* ```
|
|
342
|
+
*/
|
|
343
|
+
beforeShutdown?: () => Promise<void>;
|
|
344
|
+
};
|
|
239
345
|
}
|
|
240
346
|
/**
|
|
241
347
|
* App Factory Function
|
package/dist/server/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import pino from 'pino';
|
|
2
1
|
import { readFileSync, existsSync, readdirSync, statSync, mkdirSync, accessSync, constants, writeFileSync, unlinkSync, createWriteStream, renameSync } from 'fs';
|
|
3
2
|
import { join, dirname, relative, basename } from 'path';
|
|
4
3
|
import { config } from 'dotenv';
|
|
@@ -24,76 +23,6 @@ var __export = (target, all) => {
|
|
|
24
23
|
for (var name in all)
|
|
25
24
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
26
25
|
};
|
|
27
|
-
var PinoAdapter;
|
|
28
|
-
var init_pino = __esm({
|
|
29
|
-
"src/logger/adapters/pino.ts"() {
|
|
30
|
-
PinoAdapter = class _PinoAdapter {
|
|
31
|
-
logger;
|
|
32
|
-
constructor(config) {
|
|
33
|
-
const isDevelopment = process.env.NODE_ENV === "development";
|
|
34
|
-
const transport = isDevelopment ? {
|
|
35
|
-
target: "pino-pretty",
|
|
36
|
-
options: {
|
|
37
|
-
colorize: true,
|
|
38
|
-
translateTime: "HH:MM:ss.l",
|
|
39
|
-
ignore: "pid,hostname",
|
|
40
|
-
singleLine: false,
|
|
41
|
-
messageFormat: "{module} {msg}",
|
|
42
|
-
errorLikeObjectKeys: ["err", "error"]
|
|
43
|
-
}
|
|
44
|
-
} : void 0;
|
|
45
|
-
try {
|
|
46
|
-
this.logger = pino({
|
|
47
|
-
level: config.level,
|
|
48
|
-
// 기본 필드
|
|
49
|
-
base: config.module ? { module: config.module } : void 0,
|
|
50
|
-
// Transport (pretty print in development if available)
|
|
51
|
-
transport
|
|
52
|
-
});
|
|
53
|
-
} catch (error) {
|
|
54
|
-
this.logger = pino({
|
|
55
|
-
level: config.level,
|
|
56
|
-
base: config.module ? { module: config.module } : void 0
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
child(module) {
|
|
61
|
-
const childLogger = new _PinoAdapter({ level: this.logger.level, module });
|
|
62
|
-
childLogger.logger = this.logger.child({ module });
|
|
63
|
-
return childLogger;
|
|
64
|
-
}
|
|
65
|
-
debug(message, context) {
|
|
66
|
-
this.logger.debug(context || {}, message);
|
|
67
|
-
}
|
|
68
|
-
info(message, context) {
|
|
69
|
-
this.logger.info(context || {}, message);
|
|
70
|
-
}
|
|
71
|
-
warn(message, errorOrContext, context) {
|
|
72
|
-
if (errorOrContext instanceof Error) {
|
|
73
|
-
this.logger.warn({ err: errorOrContext, ...context }, message);
|
|
74
|
-
} else {
|
|
75
|
-
this.logger.warn(errorOrContext || {}, message);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
error(message, errorOrContext, context) {
|
|
79
|
-
if (errorOrContext instanceof Error) {
|
|
80
|
-
this.logger.error({ err: errorOrContext, ...context }, message);
|
|
81
|
-
} else {
|
|
82
|
-
this.logger.error(errorOrContext || {}, message);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
fatal(message, errorOrContext, context) {
|
|
86
|
-
if (errorOrContext instanceof Error) {
|
|
87
|
-
this.logger.fatal({ err: errorOrContext, ...context }, message);
|
|
88
|
-
} else {
|
|
89
|
-
this.logger.fatal(errorOrContext || {}, message);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async close() {
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
26
|
|
|
98
27
|
// src/logger/types.ts
|
|
99
28
|
var LOG_LEVEL_PRIORITY;
|
|
@@ -769,7 +698,7 @@ var init_config = __esm({
|
|
|
769
698
|
}
|
|
770
699
|
});
|
|
771
700
|
|
|
772
|
-
// src/logger/
|
|
701
|
+
// src/logger/factory.ts
|
|
773
702
|
function initializeTransports() {
|
|
774
703
|
const transports = [];
|
|
775
704
|
const consoleConfig = getConsoleConfig();
|
|
@@ -780,89 +709,19 @@ function initializeTransports() {
|
|
|
780
709
|
}
|
|
781
710
|
return transports;
|
|
782
711
|
}
|
|
783
|
-
var CustomAdapter;
|
|
784
|
-
var init_custom = __esm({
|
|
785
|
-
"src/logger/adapters/custom.ts"() {
|
|
786
|
-
init_logger();
|
|
787
|
-
init_console();
|
|
788
|
-
init_file();
|
|
789
|
-
init_config();
|
|
790
|
-
CustomAdapter = class _CustomAdapter {
|
|
791
|
-
logger;
|
|
792
|
-
constructor(config) {
|
|
793
|
-
this.logger = new Logger({
|
|
794
|
-
level: config.level,
|
|
795
|
-
module: config.module,
|
|
796
|
-
transports: initializeTransports()
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
child(module) {
|
|
800
|
-
const adapter = new _CustomAdapter({ level: this.logger.level, module });
|
|
801
|
-
adapter.logger = this.logger.child(module);
|
|
802
|
-
return adapter;
|
|
803
|
-
}
|
|
804
|
-
debug(message, context) {
|
|
805
|
-
this.logger.debug(message, context);
|
|
806
|
-
}
|
|
807
|
-
info(message, context) {
|
|
808
|
-
this.logger.info(message, context);
|
|
809
|
-
}
|
|
810
|
-
warn(message, errorOrContext, context) {
|
|
811
|
-
if (errorOrContext instanceof Error) {
|
|
812
|
-
this.logger.warn(message, errorOrContext, context);
|
|
813
|
-
} else {
|
|
814
|
-
this.logger.warn(message, errorOrContext);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
error(message, errorOrContext, context) {
|
|
818
|
-
if (errorOrContext instanceof Error) {
|
|
819
|
-
this.logger.error(message, errorOrContext, context);
|
|
820
|
-
} else {
|
|
821
|
-
this.logger.error(message, errorOrContext);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
fatal(message, errorOrContext, context) {
|
|
825
|
-
if (errorOrContext instanceof Error) {
|
|
826
|
-
this.logger.fatal(message, errorOrContext, context);
|
|
827
|
-
} else {
|
|
828
|
-
this.logger.fatal(message, errorOrContext);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
async close() {
|
|
832
|
-
await this.logger.close();
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
|
-
}
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
// src/logger/adapter-factory.ts
|
|
839
|
-
function createAdapter(type) {
|
|
840
|
-
const level = getDefaultLogLevel();
|
|
841
|
-
switch (type) {
|
|
842
|
-
case "pino":
|
|
843
|
-
return new PinoAdapter({ level });
|
|
844
|
-
case "custom":
|
|
845
|
-
return new CustomAdapter({ level });
|
|
846
|
-
default:
|
|
847
|
-
return new PinoAdapter({ level });
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
function getAdapterType() {
|
|
851
|
-
const adapterEnv = process.env.LOGGER_ADAPTER;
|
|
852
|
-
if (adapterEnv === "custom" || adapterEnv === "pino") {
|
|
853
|
-
return adapterEnv;
|
|
854
|
-
}
|
|
855
|
-
return "pino";
|
|
856
|
-
}
|
|
857
712
|
function initializeLogger() {
|
|
858
713
|
validateConfig();
|
|
859
|
-
return
|
|
714
|
+
return new Logger({
|
|
715
|
+
level: getDefaultLogLevel(),
|
|
716
|
+
transports: initializeTransports()
|
|
717
|
+
});
|
|
860
718
|
}
|
|
861
719
|
var logger;
|
|
862
|
-
var
|
|
863
|
-
"src/logger/
|
|
864
|
-
|
|
865
|
-
|
|
720
|
+
var init_factory = __esm({
|
|
721
|
+
"src/logger/factory.ts"() {
|
|
722
|
+
init_logger();
|
|
723
|
+
init_console();
|
|
724
|
+
init_file();
|
|
866
725
|
init_config();
|
|
867
726
|
logger = initializeLogger();
|
|
868
727
|
}
|
|
@@ -871,7 +730,8 @@ var init_adapter_factory = __esm({
|
|
|
871
730
|
// src/logger/index.ts
|
|
872
731
|
var init_logger2 = __esm({
|
|
873
732
|
"src/logger/index.ts"() {
|
|
874
|
-
|
|
733
|
+
init_factory();
|
|
734
|
+
init_logger();
|
|
875
735
|
}
|
|
876
736
|
});
|
|
877
737
|
|
|
@@ -1592,7 +1452,7 @@ async function createDatabaseFromEnv(options) {
|
|
|
1592
1452
|
}
|
|
1593
1453
|
}
|
|
1594
1454
|
var dbLogger2;
|
|
1595
|
-
var
|
|
1455
|
+
var init_factory2 = __esm({
|
|
1596
1456
|
"src/db/manager/factory.ts"() {
|
|
1597
1457
|
init_logger2();
|
|
1598
1458
|
init_env();
|
|
@@ -1708,17 +1568,51 @@ var dbLogger3;
|
|
|
1708
1568
|
var init_health_check = __esm({
|
|
1709
1569
|
"src/db/manager/health-check.ts"() {
|
|
1710
1570
|
init_logger2();
|
|
1711
|
-
|
|
1571
|
+
init_factory2();
|
|
1712
1572
|
init_global_state();
|
|
1713
1573
|
dbLogger3 = logger.child("database");
|
|
1714
1574
|
}
|
|
1715
1575
|
});
|
|
1716
1576
|
|
|
1717
1577
|
// src/db/manager/manager.ts
|
|
1578
|
+
function getCallerInfo() {
|
|
1579
|
+
try {
|
|
1580
|
+
const stack = new Error().stack;
|
|
1581
|
+
if (!stack) return void 0;
|
|
1582
|
+
const lines = stack.split("\n");
|
|
1583
|
+
for (let i = 3; i < lines.length; i++) {
|
|
1584
|
+
const line = lines[i];
|
|
1585
|
+
if (!line.includes("node_modules") && !line.includes("/db/manager/")) {
|
|
1586
|
+
const match = line.match(/\((.+):(\d+):(\d+)\)/) || line.match(/at (.+):(\d+):(\d+)/);
|
|
1587
|
+
if (match) {
|
|
1588
|
+
const fullPath = match[1];
|
|
1589
|
+
const parts = fullPath.split("/");
|
|
1590
|
+
const srcIndex = parts.lastIndexOf("src");
|
|
1591
|
+
if (srcIndex !== -1) {
|
|
1592
|
+
const relativePath = parts.slice(srcIndex).join("/");
|
|
1593
|
+
return `${relativePath}:${match[2]}`;
|
|
1594
|
+
}
|
|
1595
|
+
return `${fullPath}:${match[2]}`;
|
|
1596
|
+
}
|
|
1597
|
+
break;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
} catch {
|
|
1601
|
+
}
|
|
1602
|
+
return void 0;
|
|
1603
|
+
}
|
|
1718
1604
|
function getDatabase(type) {
|
|
1719
1605
|
const writeInst = getWriteInstance();
|
|
1720
1606
|
const readInst = getReadInstance();
|
|
1721
|
-
|
|
1607
|
+
if (process.env.DB_DEBUG_TRACE === "true") {
|
|
1608
|
+
const caller = getCallerInfo();
|
|
1609
|
+
dbLogger4.debug("getDatabase() called", {
|
|
1610
|
+
type: type || "write",
|
|
1611
|
+
hasWrite: !!writeInst,
|
|
1612
|
+
hasRead: !!readInst,
|
|
1613
|
+
caller
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1722
1616
|
if (type === "read") {
|
|
1723
1617
|
return readInst ?? writeInst;
|
|
1724
1618
|
}
|
|
@@ -1823,7 +1717,7 @@ var dbLogger4;
|
|
|
1823
1717
|
var init_manager = __esm({
|
|
1824
1718
|
"src/db/manager/manager.ts"() {
|
|
1825
1719
|
init_logger2();
|
|
1826
|
-
|
|
1720
|
+
init_factory2();
|
|
1827
1721
|
init_config3();
|
|
1828
1722
|
init_global_state();
|
|
1829
1723
|
init_health_check();
|
|
@@ -1834,7 +1728,7 @@ var init_manager = __esm({
|
|
|
1834
1728
|
// src/db/manager/index.ts
|
|
1835
1729
|
var init_manager2 = __esm({
|
|
1836
1730
|
"src/db/manager/index.ts"() {
|
|
1837
|
-
|
|
1731
|
+
init_factory2();
|
|
1838
1732
|
init_manager();
|
|
1839
1733
|
init_connection();
|
|
1840
1734
|
}
|
|
@@ -3017,6 +2911,22 @@ function generateRequestId() {
|
|
|
3017
2911
|
const randomPart = randomBytes(6).toString("hex");
|
|
3018
2912
|
return `req_${timestamp2}_${randomPart}`;
|
|
3019
2913
|
}
|
|
2914
|
+
function maskSensitiveData2(obj, sensitiveFields, seen = /* @__PURE__ */ new WeakSet()) {
|
|
2915
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
2916
|
+
if (seen.has(obj)) return "[Circular]";
|
|
2917
|
+
seen.add(obj);
|
|
2918
|
+
const lowerFields = sensitiveFields.map((f) => f.toLowerCase());
|
|
2919
|
+
const masked = Array.isArray(obj) ? [...obj] : { ...obj };
|
|
2920
|
+
for (const key in masked) {
|
|
2921
|
+
const lowerKey = key.toLowerCase();
|
|
2922
|
+
if (lowerFields.some((field) => lowerKey.includes(field))) {
|
|
2923
|
+
masked[key] = "***MASKED***";
|
|
2924
|
+
} else if (typeof masked[key] === "object" && masked[key] !== null) {
|
|
2925
|
+
masked[key] = maskSensitiveData2(masked[key], sensitiveFields, seen);
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
return masked;
|
|
2929
|
+
}
|
|
3020
2930
|
function RequestLogger(config) {
|
|
3021
2931
|
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
3022
2932
|
const apiLogger = logger.child("api");
|
|
@@ -3042,8 +2952,6 @@ function RequestLogger(config) {
|
|
|
3042
2952
|
await next();
|
|
3043
2953
|
const duration = Date.now() - startTime;
|
|
3044
2954
|
const status = c.res.status;
|
|
3045
|
-
const logLevel = status >= 400 ? "warn" : "info";
|
|
3046
|
-
const isSlowRequest = duration >= cfg.slowRequestThreshold;
|
|
3047
2955
|
const logData = {
|
|
3048
2956
|
requestId,
|
|
3049
2957
|
method,
|
|
@@ -3051,9 +2959,25 @@ function RequestLogger(config) {
|
|
|
3051
2959
|
status,
|
|
3052
2960
|
duration
|
|
3053
2961
|
};
|
|
2962
|
+
const isSlowRequest = duration >= cfg.slowRequestThreshold;
|
|
3054
2963
|
if (isSlowRequest) {
|
|
3055
2964
|
logData.slow = true;
|
|
3056
2965
|
}
|
|
2966
|
+
if (status >= 400) {
|
|
2967
|
+
try {
|
|
2968
|
+
const responseBody = await c.res.clone().json();
|
|
2969
|
+
logData.response = responseBody;
|
|
2970
|
+
} catch {
|
|
2971
|
+
}
|
|
2972
|
+
if (["POST", "PUT", "PATCH"].includes(method)) {
|
|
2973
|
+
try {
|
|
2974
|
+
const requestBody = await c.req.json();
|
|
2975
|
+
logData.request = maskSensitiveData2(requestBody, cfg.sensitiveFields);
|
|
2976
|
+
} catch {
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
const logLevel = status >= 500 ? "error" : status >= 400 ? "warn" : "info";
|
|
3057
2981
|
apiLogger[logLevel]("Request completed", logData);
|
|
3058
2982
|
} catch (error) {
|
|
3059
2983
|
const duration = Date.now() - startTime;
|
|
@@ -3251,11 +3175,11 @@ function registerHealthCheckEndpoint(app, config) {
|
|
|
3251
3175
|
}
|
|
3252
3176
|
}
|
|
3253
3177
|
async function executeBeforeRoutesHook(app, config) {
|
|
3254
|
-
if (!config?.beforeRoutes) {
|
|
3178
|
+
if (!config?.lifecycle?.beforeRoutes) {
|
|
3255
3179
|
return;
|
|
3256
3180
|
}
|
|
3257
3181
|
try {
|
|
3258
|
-
await config.beforeRoutes(app);
|
|
3182
|
+
await config.lifecycle.beforeRoutes(app);
|
|
3259
3183
|
} catch (error) {
|
|
3260
3184
|
serverLogger.error("beforeRoutes hook failed", error);
|
|
3261
3185
|
throw new Error("Server initialization failed in beforeRoutes hook");
|
|
@@ -3270,11 +3194,11 @@ async function loadAppRoutes(app, config) {
|
|
|
3270
3194
|
});
|
|
3271
3195
|
}
|
|
3272
3196
|
async function executeAfterRoutesHook(app, config) {
|
|
3273
|
-
if (!config?.afterRoutes) {
|
|
3197
|
+
if (!config?.lifecycle?.afterRoutes) {
|
|
3274
3198
|
return;
|
|
3275
3199
|
}
|
|
3276
3200
|
try {
|
|
3277
|
-
await config.afterRoutes(app);
|
|
3201
|
+
await config.lifecycle.afterRoutes(app);
|
|
3278
3202
|
} catch (error) {
|
|
3279
3203
|
serverLogger.error("afterRoutes hook failed", error);
|
|
3280
3204
|
throw new Error("Server initialization failed in afterRoutes hook");
|
|
@@ -3384,10 +3308,10 @@ async function startServer(config) {
|
|
|
3384
3308
|
port
|
|
3385
3309
|
});
|
|
3386
3310
|
logServerStarted(debug, host, port, finalConfig, timeouts);
|
|
3387
|
-
const shutdownServer = createShutdownHandler(server);
|
|
3311
|
+
const shutdownServer = createShutdownHandler(server, finalConfig);
|
|
3388
3312
|
const shutdown = createGracefulShutdown(shutdownServer, finalConfig);
|
|
3389
3313
|
registerShutdownHandlers(shutdown);
|
|
3390
|
-
|
|
3314
|
+
const serverInstance = {
|
|
3391
3315
|
server,
|
|
3392
3316
|
app,
|
|
3393
3317
|
config: finalConfig,
|
|
@@ -3396,10 +3320,19 @@ async function startServer(config) {
|
|
|
3396
3320
|
await shutdownServer();
|
|
3397
3321
|
}
|
|
3398
3322
|
};
|
|
3323
|
+
if (finalConfig.lifecycle?.afterStart) {
|
|
3324
|
+
serverLogger2.debug("Executing afterStart hook...");
|
|
3325
|
+
try {
|
|
3326
|
+
await finalConfig.lifecycle.afterStart(serverInstance);
|
|
3327
|
+
} catch (error) {
|
|
3328
|
+
serverLogger2.error("afterStart hook failed", error);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
return serverInstance;
|
|
3399
3332
|
} catch (error) {
|
|
3400
3333
|
const err = error;
|
|
3401
3334
|
serverLogger2.error("Server initialization failed", err);
|
|
3402
|
-
await cleanupOnFailure();
|
|
3335
|
+
await cleanupOnFailure(finalConfig);
|
|
3403
3336
|
throw error;
|
|
3404
3337
|
}
|
|
3405
3338
|
}
|
|
@@ -3437,10 +3370,38 @@ function logMiddlewareOrder(config) {
|
|
|
3437
3370
|
});
|
|
3438
3371
|
}
|
|
3439
3372
|
async function initializeInfrastructure(config) {
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3373
|
+
if (config.lifecycle?.beforeInfrastructure) {
|
|
3374
|
+
serverLogger2.debug("Executing beforeInfrastructure hook...");
|
|
3375
|
+
try {
|
|
3376
|
+
await config.lifecycle.beforeInfrastructure(config);
|
|
3377
|
+
} catch (error) {
|
|
3378
|
+
serverLogger2.error("beforeInfrastructure hook failed", error);
|
|
3379
|
+
throw new Error("Server initialization failed in beforeInfrastructure hook");
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
const shouldInitDatabase = config.infrastructure?.database !== false;
|
|
3383
|
+
if (shouldInitDatabase) {
|
|
3384
|
+
serverLogger2.debug("Initializing database...");
|
|
3385
|
+
await initDatabase(config.database);
|
|
3386
|
+
} else {
|
|
3387
|
+
serverLogger2.debug("Database initialization disabled");
|
|
3388
|
+
}
|
|
3389
|
+
const shouldInitRedis = config.infrastructure?.redis !== false;
|
|
3390
|
+
if (shouldInitRedis) {
|
|
3391
|
+
serverLogger2.debug("Initializing Redis...");
|
|
3392
|
+
await initRedis();
|
|
3393
|
+
} else {
|
|
3394
|
+
serverLogger2.debug("Redis initialization disabled");
|
|
3395
|
+
}
|
|
3396
|
+
if (config.lifecycle?.afterInfrastructure) {
|
|
3397
|
+
serverLogger2.debug("Executing afterInfrastructure hook...");
|
|
3398
|
+
try {
|
|
3399
|
+
await config.lifecycle.afterInfrastructure();
|
|
3400
|
+
} catch (error) {
|
|
3401
|
+
serverLogger2.error("afterInfrastructure hook failed", error);
|
|
3402
|
+
throw new Error("Server initialization failed in afterInfrastructure hook");
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3444
3405
|
}
|
|
3445
3406
|
function startHttpServer(app, host, port) {
|
|
3446
3407
|
serverLogger2.debug(`Starting server on ${host}:${port}...`);
|
|
@@ -3466,7 +3427,7 @@ function logServerStarted(debug, host, port, config, timeouts) {
|
|
|
3466
3427
|
config: startupConfig
|
|
3467
3428
|
});
|
|
3468
3429
|
}
|
|
3469
|
-
function createShutdownHandler(server) {
|
|
3430
|
+
function createShutdownHandler(server, config) {
|
|
3470
3431
|
return async () => {
|
|
3471
3432
|
serverLogger2.debug("Closing HTTP server...");
|
|
3472
3433
|
await new Promise((resolve) => {
|
|
@@ -3475,10 +3436,24 @@ function createShutdownHandler(server) {
|
|
|
3475
3436
|
resolve();
|
|
3476
3437
|
});
|
|
3477
3438
|
});
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3439
|
+
if (config.lifecycle?.beforeShutdown) {
|
|
3440
|
+
serverLogger2.debug("Executing beforeShutdown hook...");
|
|
3441
|
+
try {
|
|
3442
|
+
await config.lifecycle.beforeShutdown();
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
serverLogger2.error("beforeShutdown hook failed", error);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
const shouldCloseDatabase = config.infrastructure?.database !== false;
|
|
3448
|
+
const shouldCloseRedis = config.infrastructure?.redis !== false;
|
|
3449
|
+
if (shouldCloseDatabase) {
|
|
3450
|
+
serverLogger2.debug("Closing database connections...");
|
|
3451
|
+
await closeDatabase();
|
|
3452
|
+
}
|
|
3453
|
+
if (shouldCloseRedis) {
|
|
3454
|
+
serverLogger2.debug("Closing Redis connections...");
|
|
3455
|
+
await closeRedis();
|
|
3456
|
+
}
|
|
3482
3457
|
serverLogger2.info("Server shutdown completed");
|
|
3483
3458
|
};
|
|
3484
3459
|
}
|
|
@@ -3510,6 +3485,7 @@ function createGracefulShutdown(shutdownServer, config) {
|
|
|
3510
3485
|
};
|
|
3511
3486
|
}
|
|
3512
3487
|
function registerShutdownHandlers(shutdown) {
|
|
3488
|
+
process.setMaxListeners(15);
|
|
3513
3489
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
3514
3490
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
3515
3491
|
process.on("uncaughtException", (error) => {
|
|
@@ -3524,11 +3500,17 @@ function registerShutdownHandlers(shutdown) {
|
|
|
3524
3500
|
shutdown("UNHANDLED_REJECTION");
|
|
3525
3501
|
});
|
|
3526
3502
|
}
|
|
3527
|
-
async function cleanupOnFailure() {
|
|
3503
|
+
async function cleanupOnFailure(config) {
|
|
3528
3504
|
try {
|
|
3529
3505
|
serverLogger2.debug("Cleaning up after initialization failure...");
|
|
3530
|
-
|
|
3531
|
-
|
|
3506
|
+
const shouldCleanupDatabase = config.infrastructure?.database !== false;
|
|
3507
|
+
const shouldCleanupRedis = config.infrastructure?.redis !== false;
|
|
3508
|
+
if (shouldCleanupDatabase) {
|
|
3509
|
+
await closeDatabase();
|
|
3510
|
+
}
|
|
3511
|
+
if (shouldCleanupRedis) {
|
|
3512
|
+
await closeRedis();
|
|
3513
|
+
}
|
|
3532
3514
|
serverLogger2.debug("Cleanup completed");
|
|
3533
3515
|
} catch (cleanupError) {
|
|
3534
3516
|
serverLogger2.error("Cleanup failed", cleanupError);
|