212 lines
5.4 KiB
TypeScript
212 lines
5.4 KiB
TypeScript
import { Hono } from 'hono';
|
|
import { cors } from 'hono/cors';
|
|
import * as fs from 'node:fs';
|
|
|
|
// Configuration
|
|
const MODES_DIR = '/home/admin/Job-Info-Test';
|
|
const MODE_CONFIG_FILE = `${MODES_DIR}/ModeSwitch/activeMode.txt`;
|
|
const SHARED_PORT = 3005; // Mode services run here - NEVER use 3000 or 3001
|
|
const ORCHESTRATOR_PORT = 3006; // Orchestrator API runs here
|
|
|
|
// Mode folder mappings - only Mode6Test exists currently
|
|
const MODE_FOLDERS: Record<string, string> = {
|
|
'Mode6Test': 'Mode6Test',
|
|
};
|
|
|
|
let currentMode: string = 'Mode6Test';
|
|
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();
|
|
if (content && MODE_FOLDERS[content]) {
|
|
return content;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to read mode config:', err);
|
|
}
|
|
return 'Mode6Test';
|
|
}
|
|
|
|
// 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 - all modes use backend/server.ts
|
|
const serverPath = 'backend/server.ts';
|
|
|
|
// Start the mode's backend server
|
|
currentProcess = Bun.spawn({
|
|
cmd: [process.execPath, 'run', serverPath],
|
|
cwd: modePath,
|
|
env: {
|
|
...process.env, // Inherit all env vars (secrets, etc.)
|
|
PORT: String(SHARED_PORT), // Force mode service to bind to 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();
|
|
|
|
// Enable CORS
|
|
app.use('*', cors());
|
|
|
|
// API: Get current mode status
|
|
app.get('/status', (c) => {
|
|
return c.json({
|
|
currentMode,
|
|
availableModes: Object.keys(MODE_FOLDERS),
|
|
sharedPort: SHARED_PORT,
|
|
orchestratorPort: ORCHESTRATOR_PORT,
|
|
});
|
|
});
|
|
|
|
// API: Switch to a different mode
|
|
app.post('/switch/:mode', async (c) => {
|
|
const mode = c.req.param('mode');
|
|
|
|
if (!MODE_FOLDERS[mode]) {
|
|
return c.json({
|
|
success: false,
|
|
error: `Unknown mode: ${mode}. Available: ${Object.keys(MODE_FOLDERS).join(', ')}`
|
|
}, 400);
|
|
}
|
|
|
|
if (mode === currentMode) {
|
|
return c.json({
|
|
success: true,
|
|
message: `Already running ${mode}`,
|
|
currentMode
|
|
});
|
|
}
|
|
|
|
const success = await startMode(mode);
|
|
|
|
if (success) {
|
|
return c.json({
|
|
success: true,
|
|
message: `Switched to ${mode}`,
|
|
currentMode: mode
|
|
});
|
|
} else {
|
|
return c.json({
|
|
success: false,
|
|
error: `Failed to start ${mode}`
|
|
}, 500);
|
|
}
|
|
});
|
|
|
|
// API: Restart current mode
|
|
app.post('/restart', async (c) => {
|
|
console.log(`[ModeSwitch] Restarting ${currentMode}...`);
|
|
const success = await startMode(currentMode);
|
|
|
|
return c.json({
|
|
success,
|
|
message: success ? `Restarted ${currentMode}` : `Failed to restart ${currentMode}`,
|
|
currentMode
|
|
});
|
|
});
|
|
|
|
// API: List available modes
|
|
app.get('/modes', (c) => {
|
|
return c.json({
|
|
modes: Object.keys(MODE_FOLDERS),
|
|
currentMode
|
|
});
|
|
});
|
|
|
|
// 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,
|
|
};
|