From 224738f2e760c5352f1d7999dc4270bf5e3695d7 Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Sun, 22 Mar 2026 02:12:13 -0400 Subject: [PATCH] Add commandId generation for idempotent requests in API calls --- web/client/src/store/inventoryStore.js | 37 ++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/web/client/src/store/inventoryStore.js b/web/client/src/store/inventoryStore.js index d88569e..95508d6 100644 --- a/web/client/src/store/inventoryStore.js +++ b/web/client/src/store/inventoryStore.js @@ -8,6 +8,11 @@ const API_URL = import.meta.env.VITE_API_URL || console.log('🔌 WebSocket URL:', WS_URL); console.log('📡 API URL:', API_URL); +// Generate a unique command ID for idempotent requests +function newCommandId() { + return crypto.randomUUID(); +} + // ========== Timers (module-level so they survive store re-creates) ========== let _httpPollTimer = null; let _wsHealthTimer = null; @@ -183,7 +188,7 @@ export const useInventoryStore = create((set, get) => ({ const response = await fetch(`${API_URL}/order`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ itemName, amount, dropperName }), + body: JSON.stringify({ itemName, amount, dropperName, commandId: newCommandId() }), }); return await response.json(); } catch (error) { @@ -194,7 +199,11 @@ export const useInventoryStore = create((set, get) => ({ requestScan: async () => { try { - const response = await fetch(`${API_URL}/scan`, { method: 'POST' }); + const response = await fetch(`${API_URL}/scan`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ commandId: newCommandId() }), + }); return await response.json(); } catch (error) { console.error('❌ Error requesting scan:', error); @@ -204,7 +213,11 @@ export const useInventoryStore = create((set, get) => ({ toggleSmelting: async () => { try { - const response = await fetch(`${API_URL}/smelting/toggle`, { method: 'POST' }); + const response = await fetch(`${API_URL}/smelting/toggle`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ commandId: newCommandId() }), + }); return await response.json(); } catch (error) { console.error('❌ Error toggling smelting:', error); @@ -217,7 +230,7 @@ export const useInventoryStore = create((set, get) => ({ const response = await fetch(`${API_URL}/recipes/toggle`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ recipe }), + body: JSON.stringify({ recipe, commandId: newCommandId() }), }); return await response.json(); } catch (error) { @@ -228,7 +241,11 @@ export const useInventoryStore = create((set, get) => ({ enableAllRecipes: async () => { try { - await fetch(`${API_URL}/recipes/enable-all`, { method: 'POST' }); + await fetch(`${API_URL}/recipes/enable-all`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ commandId: newCommandId() }), + }); } catch (error) { console.error('❌ Error enabling all recipes:', error); } @@ -236,7 +253,11 @@ export const useInventoryStore = create((set, get) => ({ disableAllRecipes: async () => { try { - await fetch(`${API_URL}/recipes/disable-all`, { method: 'POST' }); + await fetch(`${API_URL}/recipes/disable-all`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ commandId: newCommandId() }), + }); } catch (error) { console.error('❌ Error disabling all recipes:', error); } @@ -247,7 +268,7 @@ export const useInventoryStore = create((set, get) => ({ const response = await fetch(`${API_URL}/craft`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ recipeIdx }), + body: JSON.stringify({ recipeIdx, commandId: newCommandId() }), }); return await response.json(); } catch (error) { @@ -261,7 +282,7 @@ export const useInventoryStore = create((set, get) => ({ const response = await fetch(`${API_URL}/sort-barrel`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ barrelName }), + body: JSON.stringify({ barrelName, commandId: newCommandId() }), }); return await response.json(); } catch (error) {