-
Notifications
You must be signed in to change notification settings - Fork 106
Expand file tree
/
Copy pathserver.ts
More file actions
120 lines (100 loc) · 3.95 KB
/
Copy pathserver.ts
File metadata and controls
120 lines (100 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/// <reference types="node" />
import app from './app';
import { envConfig } from './config';
import { logger } from './utils/logger.utils';
import { prisma } from './utils/prisma.utils';
import { verifyMigrationChecksums } from './utils/migration-checksum.utils';
import {
IndexerFlagsConfigError,
runIndexerFeatureFlagsStartupCheck,
} from './utils/indexer-flags-startup-check.utils';
import { checkOptionalDependencies } from './utils/startup.utils';
import { describeDatabasePoolConfig } from './utils/db-pool-config.utils';
import { stopOwnershipSnapshotCleanupJob } from './jobs/ownership-snapshot-cleanup.job';
import { buildStartupConfigSummary } from './utils/config-summary.utils';
async function startServer() {
try {
// Validate indexer feature flags before any code paths read them. We
// fail fast here so operators see every misconfiguration at once
// instead of cryptic runtime errors later in the boot sequence.
try {
runIndexerFeatureFlagsStartupCheck();
} catch (err) {
if (err instanceof IndexerFlagsConfigError) {
logger.error(
{ issues: err.issues },
'Refusing to start: indexer feature flags are misconfigured'
);
process.exit(1);
}
throw err;
}
await prisma.$connect();
logger.info('Connected to database');
// Surface connection-pool settings (no credentials) so connection
// exhaustion is diagnosable. Logged before the server accepts requests.
logger.info(
describeDatabasePoolConfig(),
'Database connection pool configured'
);
// Emit a structured summary of the loaded runtime config: environment
// context and key feature flags. Values flow through the masking helper,
// so no secrets or credentials are logged. See
// utils/config-summary.utils.ts for the curated field selection.
logger.info(
buildStartupConfigSummary(),
'Loaded runtime configuration summary'
);
// Verify migrations on startup
await verifyMigrationChecksums();
// Check and warn about disabled optional dependencies (non-blocking)
checkOptionalDependencies();
const server = app.listen(envConfig.PORT, () => {
logger.info(`Server running on port ${envConfig.PORT}`);
});
return server;
} catch (error) {
console.error('Failed to start server:', error);
await prisma.$disconnect();
process.exit(1);
}
}
// Handle uncaught exceptions
process.on('uncaughtException', error => {
console.error('Uncaught Exception:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
function createGracefulShutdownHandler(server: ReturnType<typeof app.listen>) {
return async () => {
stopOwnershipSnapshotCleanupJob();
await prisma.$disconnect();
console.log('💾 Database connection closed');
const DRAIN_WINDOW_MS = 5000;
const SHUTDOWN_TIMEOUT_MS = 30000;
app.use((_req, res, _next) => {
res.status(503).json({ error: 'Server is shutting down' });
});
const shutdownTimer = setTimeout(() => {
console.error('❌ Shutdown timeout reached, forcing exit');
process.exit(1);
}, SHUTDOWN_TIMEOUT_MS);
server.close(async () => {
clearTimeout(shutdownTimer);
console.log('✅ HTTP server closed, draining requests');
await new Promise(resolve => setTimeout(resolve, DRAIN_WINDOW_MS));
await prisma.$disconnect();
console.log('💾 Database connection closed');
console.log('👋 Shutdown complete');
process.exit(0);
});
};
}
startServer().then(server => {
const shutdownHandler = createGracefulShutdownHandler(server);
process.on('SIGINT', shutdownHandler);
process.on('SIGTERM', shutdownHandler);
});