From a5e6be7f4ba03c1fc3f99258a3f82df09408709b Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Sun, 22 Mar 2026 01:29:16 -0400 Subject: [PATCH] Enhance command handling: implement monotonic ID for deduplication and improve acknowledgment process --- web/server/server.js | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/web/server/server.js b/web/server/server.js index 02302c2..20a1dd5 100644 --- a/web/server/server.js +++ b/web/server/server.js @@ -49,6 +49,7 @@ if (lastUpdate > 0) { // Pending commands for bridge HTTP polling (fallback) let pendingCommands = []; +let nextCommandId = 1; // Monotonic ID for deduplication // ========== Helpers ========== @@ -70,9 +71,10 @@ function pushCommandToBridge(command) { } } if (!sent) { - // Fallback: queue for HTTP polling - pendingCommands.push({ ...command, timestamp: Date.now() }); - console.log(`[Bridge] Queued command via HTTP poll (no WS bridge)`); + // Fallback: queue for HTTP polling with monotonic ID + const id = nextCommandId++; + pendingCommands.push({ ...command, id, timestamp: Date.now() }); + console.log(`[Bridge] Queued command #${id} via HTTP poll (no WS bridge)`); } } @@ -413,12 +415,24 @@ app.get('/api/bridge/commands', (req, res) => { } }); -// Bridge acknowledges commands +// Bridge acknowledges commands by last processed ID +// Only removes commands with id <= lastProcessedId, preventing +// race conditions where new commands arrive between GET and ACK. app.post('/api/bridge/commands/ack', (req, res) => { try { - const cleared = pendingCommands.length; - pendingCommands = []; - res.json({ success: true, cleared }); + const { lastProcessedId } = req.body || {}; + if (lastProcessedId !== undefined && lastProcessedId !== null) { + // Remove only commands that have been processed + const before = pendingCommands.length; + pendingCommands = pendingCommands.filter(cmd => (cmd.id || 0) > lastProcessedId); + const cleared = before - pendingCommands.length; + res.json({ success: true, cleared }); + } else { + // Legacy: clear all (backwards-compatible) + const cleared = pendingCommands.length; + pendingCommands = []; + res.json({ success: true, cleared }); + } } catch (error) { res.status(500).json({ error: error.message }); }