From e13fe9d22fd9b6ccfe33455e4acc8cf0942e3c06 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Wed, 7 Jan 2026 22:29:23 -0500 Subject: [PATCH] Fix worker ID mapping - use database ID for command routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: - Workers generate random UUID on startup (runtime ID) - Database stores workers with persistent IDs (database ID) - UI sends commands using database ID - Server couldn't find worker connection (stored by runtime ID) - Result: 400 Bad Request "Worker not connected" Solution: - When worker connects, look up database ID by worker name - Store WebSocket connection in Map using BOTH IDs: * Runtime ID (from worker_connect message) * Database ID (from database lookup by name) - Commands from UI use database ID → finds correct WebSocket - Cleanup both IDs when worker disconnects Co-Authored-By: Claude Sonnet 4.5 --- server.js | 60 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/server.js b/server.js index be39cc2..61b4a19 100644 --- a/server.js +++ b/server.js @@ -179,21 +179,40 @@ wss.on('connection', (ws) => { const { worker_id, worker_name } = message; console.log(`Worker connected: ${worker_name} (${worker_id})`); - // Store worker WebSocket connection - workers.set(worker_id, ws); - - // Update worker status to online - await pool.query( - `UPDATE workers SET status='online', last_heartbeat=NOW() WHERE id=?`, - [worker_id] + // Find the database worker ID by name + const [dbWorkers] = await pool.query( + 'SELECT id FROM workers WHERE name = ?', + [worker_name] ); - // Broadcast worker status update - broadcast({ - type: 'worker_update', - worker_id: worker_id, - status: 'online' - }); + if (dbWorkers.length > 0) { + const dbWorkerId = dbWorkers[0].id; + + // Store worker WebSocket connection using BOTH IDs + workers.set(worker_id, ws); // Runtime ID + workers.set(dbWorkerId, ws); // Database ID + + // Store mapping for cleanup + ws.workerId = worker_id; + ws.dbWorkerId = dbWorkerId; + + console.log(`Mapped worker: runtime_id=${worker_id}, db_id=${dbWorkerId}, name=${worker_name}`); + + // Update worker status to online + await pool.query( + `UPDATE workers SET status='online', last_heartbeat=NOW() WHERE id=?`, + [dbWorkerId] + ); + + // Broadcast worker status update with database ID + broadcast({ + type: 'worker_update', + worker_id: dbWorkerId, + status: 'online' + }); + } else { + console.log(`Worker ${worker_name} not found in database, will be created on heartbeat`); + } } if (message.type === 'pong') { @@ -212,13 +231,14 @@ wss.on('connection', (ws) => { ws.on('close', () => { clients.delete(ws); - // Remove worker from workers map when disconnected - for (const [workerId, workerWs] of workers.entries()) { - if (workerWs === ws) { - workers.delete(workerId); - console.log(`Worker ${workerId} disconnected`); - break; - } + // Remove worker from workers map when disconnected (both runtime and db IDs) + if (ws.workerId) { + workers.delete(ws.workerId); + console.log(`Worker ${ws.workerId} (runtime ID) disconnected`); + } + if (ws.dbWorkerId) { + workers.delete(ws.dbWorkerId); + console.log(`Worker ${ws.dbWorkerId} (database ID) disconnected`); } }); });