105 lines
3.5 KiB
JavaScript
105 lines
3.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/* start-all.js — orchestrator for local dev
|
|
- Ensures the playground server (3002) and the add-in static server (3003) are running
|
|
- If healthy endpoints are reachable, does not start duplicates
|
|
- Streams logs from started processes and ensures clean shutdown
|
|
*/
|
|
|
|
const { spawn } = require('child_process');
|
|
const fetch = require('node-fetch');
|
|
const path = require('path');
|
|
|
|
const services = [
|
|
{
|
|
name: 'playground',
|
|
dir: path.join(__dirname, 'minimal-uploader-playground'),
|
|
startCmd: ['npm', ['run','server']],
|
|
healthUrl: 'http://localhost:3002/api/health'
|
|
},
|
|
{
|
|
name: 'addin',
|
|
dir: path.join(__dirname, 'officeaddin', 'minimal-playground'),
|
|
startCmd: ['npm', ['start']],
|
|
// static taskpane page used as a health endpoint
|
|
healthUrl: 'http://localhost:3003/taskpane.html'
|
|
}
|
|
];
|
|
|
|
async function checkHealth(url, timeout = 2000) {
|
|
try {
|
|
const controller = new AbortController();
|
|
const id = setTimeout(() => controller.abort(), timeout);
|
|
const res = await fetch(url, { signal: controller.signal });
|
|
clearTimeout(id);
|
|
return res.ok;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function spawnService(svc) {
|
|
const [cmd, args] = svc.startCmd;
|
|
const proc = spawn(cmd, args, { cwd: svc.dir, shell: true, env: process.env });
|
|
|
|
proc.stdout.on('data', (d) => process.stdout.write(`[${svc.name}] ${d}`));
|
|
proc.stderr.on('data', (d) => process.stderr.write(`[${svc.name}] ${d}`));
|
|
proc.on('exit', (code, sig) => console.log(`[${svc.name}] exited code=${code} signal=${sig}`));
|
|
return proc;
|
|
}
|
|
|
|
async function startAll() {
|
|
console.log('Orchestrator: checking service health...');
|
|
const started = [];
|
|
|
|
for (const svc of services) {
|
|
const healthy = await checkHealth(svc.healthUrl, 1500);
|
|
if (healthy) {
|
|
console.log(`${svc.name} is already healthy at ${svc.healthUrl}`);
|
|
started.push({ svc, proc: null, already: true });
|
|
continue;
|
|
}
|
|
|
|
console.log(`${svc.name} not healthy — starting: ${svc.startCmd.join(' ')}`);
|
|
const proc = spawnService(svc);
|
|
started.push({ svc, proc, already: false });
|
|
|
|
// wait for the service to report healthy
|
|
const maxWait = 30 * 1000; // 30s
|
|
const start = Date.now();
|
|
let ok = false;
|
|
while (Date.now() - start < maxWait) {
|
|
// wait a moment
|
|
await new Promise((r) => setTimeout(r, 500));
|
|
if (await checkHealth(svc.healthUrl, 1500)) { ok = true; break; }
|
|
}
|
|
if (!ok) {
|
|
console.error(`
|
|
Service ${svc.name} did not become healthy at ${svc.healthUrl} within ${maxWait/1000}s. Check its logs above.`);
|
|
// Continue retrying other services — do not abort so we can still start them
|
|
} else {
|
|
console.log(`${svc.name} is healthy at ${svc.healthUrl}`);
|
|
}
|
|
}
|
|
|
|
console.log('All start requests processed. Press Ctrl-C to stop (or send SIGTERM).');
|
|
|
|
// graceful cleanup
|
|
process.on('SIGINT', () => shutdown(started));
|
|
process.on('SIGTERM', () => shutdown(started));
|
|
}
|
|
|
|
function shutdown(started) {
|
|
console.log('\nStopping orchestrator and children...');
|
|
for (const { svc, proc, already } of started) {
|
|
if (proc && !proc.killed) {
|
|
try { proc.kill('SIGINT'); } catch (e) { /* ignore */ }
|
|
console.log(`Requested stop for ${svc.name}`);
|
|
} else if (already) {
|
|
console.log(`Note: ${svc.name} was already running and will not be killed by the orchestrator.`);
|
|
}
|
|
}
|
|
process.exit(0);
|
|
}
|
|
|
|
startAll().catch((err) => { console.error('Orchestrator failed:', err); process.exit(1); });
|