69 lines
3.3 KiB
JavaScript
69 lines
3.3 KiB
JavaScript
import { readFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
|
|
const deploy = readFileSync(resolve(import.meta.dirname, 'production-deploy.sh'), 'utf8');
|
|
const bootstrap = readFileSync(resolve(import.meta.dirname, 'production-bootstrap.sh'), 'utf8');
|
|
const apiMain = readFileSync(resolve(import.meta.dirname, '../../api/src/main.ts'), 'utf8');
|
|
const apiTimeouts = readFileSync(resolve(import.meta.dirname, '../../api/src/http-server-timeouts.ts'), 'utf8');
|
|
const workerMain = readFileSync(resolve(import.meta.dirname, '../../api/src/send-worker.ts'), 'utf8');
|
|
const required = [
|
|
'compression_config=/etc/nginx/conf.d/cmpp-compression.conf',
|
|
': >"$compression_config"',
|
|
"--exclude='cmpp-compression.conf'",
|
|
"'^[[:space:]]*gzip[[:space:]]+on;'",
|
|
'Reusing existing Nginx gzip configuration',
|
|
'cat >"$compression_config"',
|
|
];
|
|
for (const marker of required) {
|
|
if (!deploy.includes(marker)) throw new Error(`production deploy is missing the idempotent Nginx compression guard: ${marker}`);
|
|
}
|
|
if (deploy.includes("cat >/etc/nginx/conf.d/cmpp-compression.conf <<'EOF'")) {
|
|
throw new Error('production deploy still writes a duplicate global gzip directive unconditionally');
|
|
}
|
|
|
|
for (const marker of ['API_HOST="${API_HOST:-127.0.0.1}"', 'API_HOST=${API_HOST}']) {
|
|
if (!bootstrap.includes(marker)) throw new Error(`production bootstrap is missing the loopback API binding: ${marker}`);
|
|
}
|
|
if (!apiMain.includes("process.env.API_HOST?.trim() || '127.0.0.1'") || !apiMain.includes('app.listen(port, host)')) {
|
|
throw new Error('NestJS API must bind to API_HOST and default to loopback');
|
|
}
|
|
for (const marker of ['API_HTTP_KEEP_ALIVE_TIMEOUT_MS', 'API_HTTP_HEADERS_TIMEOUT_MS']) {
|
|
if (!`${deploy}\n${bootstrap}\n${apiTimeouts}`.includes(marker)) {
|
|
throw new Error(`production deployment is missing the API keep-alive contract: ${marker}`);
|
|
}
|
|
}
|
|
if (!apiMain.includes('configureApiHttpServerTimeouts(apiServer, process.env)')) {
|
|
throw new Error('NestJS API must apply the explicit keep-alive contract to its HTTP server');
|
|
}
|
|
|
|
for (const marker of [
|
|
'CMPP_INBOUND_FAST_PATH_ENABLED=true',
|
|
'CMPP_INBOUND_WORKFLOW_WORKER_ENABLED=true',
|
|
'API_INBOUND_WORKFLOW_CONCURRENCY',
|
|
'API_INBOUND_WORKFLOW_BATCH_ENABLED=true',
|
|
'API_INBOUND_WORKFLOW_BATCH_SIZE',
|
|
'API_INBOUND_WORKFLOW_BATCH_WAIT_MS',
|
|
'API_INBOUND_WORKFLOW_TARGET_BATCH_SIZE',
|
|
'cmpp-send-worker.service',
|
|
'Environment=CMPP_PROCESS_ROLE=api',
|
|
'Environment=CMPP_PROCESS_ROLE=worker',
|
|
'dist/send-worker.js',
|
|
'API_WORKER_METRICS_PORT',
|
|
]) {
|
|
if (!`${deploy}\n${bootstrap}`.includes(marker)) {
|
|
throw new Error(`production deployment is missing the durable inbound worker contract: ${marker}`);
|
|
}
|
|
}
|
|
if (!workerMain.includes("process.env.API_WORKER_METRICS_HOST?.trim() || '127.0.0.1'")) {
|
|
throw new Error('send worker metrics must default to loopback');
|
|
}
|
|
|
|
const gatewayRestart = deploy.indexOf('systemctl restart cmpp-gateway');
|
|
const apiRestart = deploy.indexOf('systemctl restart cmpp-api');
|
|
const workerRestart = deploy.indexOf('systemctl restart cmpp-send-worker');
|
|
if (gatewayRestart < 0 || apiRestart < gatewayRestart || workerRestart < apiRestart) {
|
|
throw new Error('production deploy must restart Gateway before API and the send worker so API startup restores active channels');
|
|
}
|
|
|
|
console.log('Production deployment verified: Nginx/API guards and the split durable send worker contract are present.');
|