Files
Job-Info-Prod/ModeSwitch/backend/orchestrator.ts
T

151 lines
4.0 KiB
TypeScript

import { Hono } from 'hono';
import { serveStatic } from 'hono/bun';
import { spawn } from 'bun';
import fs from 'fs';
// Configuration
const MODES_DIR = '/home/admin/Job-Info-Prod';
const MODE_CONFIG_FILE = `${MODES_DIR}/ModeSwitch/activeMode.txt`;
const SHARED_PORT = 3000;
const ORCHESTRATOR_PORT = 3001;
// Mode folder mappings
const MODE_FOLDERS: Record<string, string> = {
'Mode1Test': 'Mode1Test',
'Mode2Test': 'Mode2Test',
'Mode3Test': 'Mode3Test',
'Mode6Test': 'Mode6Test',
};
let currentMode: string = 'Mode1Test';
let currentProcess: any = null;
// Helper: Read active mode from config file
function readActiveMode(): string {
try {
if (fs.existsSync(MODE_CONFIG_FILE)) {
const content = fs.readFileSync(MODE_CONFIG_FILE, 'utf-8').trim();
return content || 'Test1Mode';
}
} catch (err) {
console.error('Failed to read mode config:', err);
}
return 'Mode1Test';
}
// Helper: Write active mode to config file
function writeActiveMode(mode: string): void {
try {
fs.writeFileSync(MODE_CONFIG_FILE, mode, 'utf-8');
} catch (err) {
console.error('Failed to write mode config:', err);
}
}
// Helper: Kill process on shared port
async function killProcessOnPort(port: number): Promise<void> {
try {
await Bun.spawn({
cmd: ['pkill', '-f', `bun.*:${port}`],
stdout: 'ignore',
stderr: 'ignore',
});
// Fallback: Try to kill any Bun process we spawned
if (currentProcess) {
currentProcess.kill();
currentProcess = null;
}
// Give OS time to release the port
await new Promise(resolve => setTimeout(resolve, 500));
} catch (err) {
console.error(`Failed to kill process on port ${port}:`, err);
}
}
// Helper: Start a mode
async function startMode(mode: string): Promise<boolean> {
try {
// Kill any existing process
await killProcessOnPort(SHARED_PORT);
const folderName = MODE_FOLDERS[mode];
if (!folderName) {
console.error(`Unknown mode: ${mode}`);
return false;
}
const modePath = `${MODES_DIR}/${folderName}`;
if (!fs.existsSync(modePath)) {
console.error(`Mode folder not found: ${modePath}`);
return false;
}
console.log(`[ModeSwitch] Starting ${mode} (${folderName}/) on port ${SHARED_PORT}...`);
// Determine server path based on mode
// Mode2Test uses AuthAndToken/backend/server.ts
// Mode1Test and Mode3Test use backend/server.ts
let serverPath = 'backend/server.ts';
if (mode === 'Mode2Test') {
serverPath = 'AuthAndToken/backend/server.ts';
}
// Start the mode's backend server
currentProcess = Bun.spawn({
cmd: [process.execPath, 'run', serverPath],
cwd: modePath,
env: {
// Force mode service to bind to shared port
PORT: String(SHARED_PORT),
},
stdout: 'pipe',
stderr: 'pipe',
});
currentMode = mode;
writeActiveMode(mode);
// Read and log output from the spawned process
if (currentProcess.stdout) {
(async () => {
const reader = currentProcess.stdout.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(`[${mode}]`, new TextDecoder().decode(value));
}
})();
}
console.log(`${mode} started successfully on port ${SHARED_PORT}`);
return true;
} catch (err) {
console.error(`Failed to start ${mode}:`, err);
return false;
}
}
// Initialize app
const app = new Hono();
// Startup message
console.log(`
================================================================================
[ModeSwitch Orchestrator]
================================================================================
Listening on port: ${ORCHESTRATOR_PORT}
Shared mode port: ${SHARED_PORT}
Config file: ${MODE_CONFIG_FILE}
================================================================================
`);
// Start initial mode
const initialMode = readActiveMode();
await startMode(initialMode);
// Export for Bun
export default {
port: ORCHESTRATOR_PORT,
fetch: app.fetch,
};