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 storageCheck = readFileSync(resolve(import.meta.dirname, 'check-data-storage.sh'), 'utf8'); for (const [name, script] of [['deploy', deploy], ['bootstrap', bootstrap]]) { const guard = script.indexOf('/usr/local/sbin/cmpp-data-storage-check all'); const initialization = script.indexOf('APP_DIR='); if (guard < 0 || initialization < 0 || guard > initialization) { throw new Error(`${name} must verify migrated storage before initialization or release writes`); } for (const marker of ['data-disk.uuid', '50-cmpp-data-disk.conf', '[[ ! -x /usr/local/sbin/cmpp-data-storage-check ]]', 'refusing to continue.']) { if (!script.includes(marker)) throw new Error(`${name} is missing the data-disk fail-closed guard: ${marker}`); } } for (const marker of [ 'mountpoint -q /data', 'findmnt -rn -M /data -o UUID', '/data/.cmpp-data-disk.uuid', 'mountpoint -q "$target"', 'findmnt -rn -M "$target" -o UUID', "stat -Lc '%d:%i'", 'findmnt -rn -M "$target" -o OPTIONS', '/data/postgresql', '/data/redis', '/data/minio', 'PG_VERSION', 'dump.rdb', '.minio.sys/format.json', ]) { if (!storageCheck.includes(marker)) throw new Error(`data storage guard is missing: ${marker}`); } 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: migrated storage fail-closed guards, Nginx/API guards and the split durable send worker contract are present.');