const express = require("express"); const http = require("http"); const WebSocket = require("ws"); const pool = require("./db"); const app = express(); const PORT = process.env.PORT || 3100; app.use(express.json()); app.use(express.urlencoded({ extended: true })); async function upsertItemsForShop(shopId, items) { if (!items.length) return; const placeholders = items.map(() => "(?, ?, ?)").join(", "); const values = items.flatMap((i) => [ shopId, i.item_name, Number(i.stock) || 0, ]); const sql = ` INSERT INTO Item (shopId, item_name, stock) VALUES ${placeholders} ON DUPLICATE KEY UPDATE stock = VALUES(stock) `; await pool.query(sql, values); } async function zeroMissingItems(shopId, items) { const names = items.map((i) => i.item_name); items; if (names.length === 0) { await pool.query(`UPDATE Item SET stock = 0 WHERE shopId = ?`, [shopId]); return; } const sql = ` UPDATE Item SET stock = 0 WHERE shopId = ? AND item_name NOT IN (?) `; await pool.query(sql, [shopId, names]); } async function zeroAllItemsForShop(shopId) { await pool.query(`UPDATE Item SET stock = 0 WHERE shopId = ?`, [shopId]); } // Create HTTP server & WebSocket server const server = http.createServer(app); const wss = new WebSocket.Server({ server }); // Helper to send JSON to a ws client function sendJSON(ws, obj) { try { ws.send(JSON.stringify(obj)); } catch (err) { // ignore send errors for now } } wss.on("connection", (ws) => { const clientId = Date.now().toString(36) + Math.random().toString(36).slice(2, 8); ws._clientId = clientId; ws._shopId = null; console.log("Client connected:", clientId); sendJSON(ws, { type: "welcome", id: clientId }); ws.on("message", async (raw) => { let parsed; try { parsed = JSON.parse(raw.toString()); } catch { return; } parsed.shopId = Number(parsed.id); if (parsed && Array.isArray(parsed.items) && parsed.shopId != null) { const shopId = Number(parsed.shopId); ws._shopId = shopId; const items = parsed.items.map((i) => ({ item_name: String(i.name), stock: Number(i.count) || 0, })); try { // 1. Upsert received items await upsertItemsForShop(shopId, items); // 2. Set missing items to 0 (ONLY this shop) await zeroMissingItems(shopId, items); console.log(`Updated shop ${shopId} (${items.length} items)`); } catch (err) { console.error("DB error:", err.message); } } // ---- broadcast logic unchanged ---- if (parsed?.type && parsed.type !== "items") { wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(parsed)); } }); } }); ws.on("close", async () => { console.log("Client disconnected:", clientId); if (ws._shopId != null) { try { await zeroAllItemsForShop(ws._shopId); console.log(`Shop ${ws._shopId} set to 0 stock`); } catch (err) { console.error("DB error on close:", err.message); } } }); ws.on("error", (err) => { console.log("WebSocket error:", err.message); }); }); app.post("/api/send", (req, res) => { const { shopId, address, items } = req.body; // Basic validation if ( typeof shopId !== "number" || typeof address !== "string" || !Array.isArray(items) ) { return res.status(400).json({ ok: false, error: "Invalid parameters", }); } for (const item of items) { if (typeof item.id !== "string" || typeof item.quantity !== "number") { return res.status(400).json({ ok: false, error: "Invalid item format", }); } } // Build request payload const request = { type: "request", to: shopId, address, items: items.map((item) => ({ name: item.id, _requestCount: item.quantity, })), }; // Send via WebSocket wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(request)); console.log("Sent request to client:", client._clientId); } }); return res.status(200).json({ ok: true, message: "Items sent successfully", data: request, }); }); server.listen(PORT, () => { console.log(`🚀 Server listening on http://localhost:${PORT}`); });