From 17d4d21620ba3b73437496e20fec2362d924068e Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 1 Dec 2025 10:06:06 +0100 Subject: [PATCH] Add new daemon start script and update localization for director salary - Introduced a new script `start-daemon` in `package.json` for running the daemon server. - Added translations for "director payed out" in both English and German localization files to enhance user notifications. --- backend/daemonServer.js | 98 ++++++++++++++++++++++ backend/models/trigger.js | 13 +-- backend/package.json | 1 + frontend/src/i18n/locales/de/falukant.json | 1 + frontend/src/i18n/locales/en/falukant.json | 1 + 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 backend/daemonServer.js diff --git a/backend/daemonServer.js b/backend/daemonServer.js new file mode 100644 index 0000000..36f4691 --- /dev/null +++ b/backend/daemonServer.js @@ -0,0 +1,98 @@ +import WebSocket, { WebSocketServer } from 'ws'; + +const PORT = 4551; + +// Einfache In-Memory-Struktur für Verbindungen (für spätere Erweiterungen) +const connections = new Set(); + +function createServer() { + const wss = new WebSocketServer({ port: PORT }); + + console.log(`[Daemon] WebSocket-Server startet auf Port ${PORT} ...`); + + wss.on('connection', (ws, req) => { + const peer = req.socket.remoteAddress + ':' + req.socket.remotePort; + ws.isAlive = true; + ws.userId = null; + connections.add(ws); + + console.log(`[Daemon] Neue Verbindung von ${peer}`); + + ws.on('message', (message) => { + try { + if (message.toString() === 'pong') { + // Client-Pong für unser Ping + ws.isAlive = true; + return; + } + + const data = JSON.parse(message.toString()); + + // Vom Frontend gesendet nach Verbindungsaufbau + if (data.event === 'setUserId' && data.data?.userId) { + ws.userId = data.data.userId; + console.log(`[Daemon] setUserId erhalten: ${ws.userId}`); + return; + } + + // Admin-Dialog: WebSocket-Log anfordern + if (data.event === 'getWebsocketLog') { + const response = { + event: 'getWebsocketLogResponse', + entries: [] // aktuell keine Log-Historie implementiert + }; + ws.send(JSON.stringify(response)); + return; + } + + // Platzhalter für spätere Events + // console.log('[Daemon] Unbekanntes Event:', data); + } catch (err) { + console.error('[Daemon] Fehler beim Verarbeiten einer Nachricht:', err); + } + }); + + ws.on('close', () => { + connections.delete(ws); + console.log('[Daemon] Verbindung geschlossen'); + }); + + ws.on('error', (err) => { + console.error('[Daemon] WebSocket-Fehler (Verbindung):', err); + }); + }); + + // Einfache Ping/Pong-Mechanik, damit Verbindungen sauber erkannt werden + const interval = setInterval(() => { + for (const ws of connections) { + if (ws.isAlive === false) { + console.log('[Daemon] Verbindung wegen fehlendem Pong beendet'); + ws.terminate(); + connections.delete(ws); + continue; + } + ws.isAlive = false; + try { + ws.send('ping'); + } catch (err) { + console.error('[Daemon] Fehler beim Senden von Ping:', err); + } + } + }, 30000); + + wss.on('close', () => { + clearInterval(interval); + connections.clear(); + console.log('[Daemon] Server gestoppt'); + }); + + wss.on('error', (err) => { + console.error('[Daemon] Server-Fehler:', err); + }); + + return wss; +} + +createServer(); + + diff --git a/backend/models/trigger.js b/backend/models/trigger.js index 6500188..295ec73 100644 --- a/backend/models/trigger.js +++ b/backend/models/trigger.js @@ -378,16 +378,19 @@ export async function createTriggers() { tp.election_id, tp.tp_office_type_id, tp.tp_election_date, - ( - SELECT json_agg(vr) - FROM votes vr - WHERE vr.election_id = tp.election_id + COALESCE( + ( + SELECT json_agg(vr) + FROM votes vr + WHERE vr.election_id = tp.election_id + ), + '[]'::json -- oder '{}'::json, wenn dir ein Objekt lieber ist ), NOW() AS created_at, NOW() AS updated_at FROM to_process tp ), - + -- 10) Cleanup: Stimmen, Kandidaten und Wahlen löschen _del_votes AS ( DELETE FROM falukant_data.vote diff --git a/backend/package.json b/backend/package.json index 31a49a2..eea600a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -7,6 +7,7 @@ "scripts": { "start": "node server.js", "dev": "NODE_ENV=development node server.js", + "start-daemon": "node daemonServer.js", "sync-db": "node sync-database.js", "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index 4c5e655..2bf348f 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -409,6 +409,7 @@ "Sell all products": "Alle Produkte verkauft", "sell products": "Produkte verkauft", "director starts production": "Direktor beginnt Produktion", + "director payed out": "Direktorgehalt ausgezahlt", "Buy storage (type: field)": "Lagerplatz gekauft (Typ: Feld)", "Buy storage (type: iron)": "Lagerplatz gekauft (Typ: Eisen)", "Buy storage (type: stone)": "Lagerplatz gekauft (Typ: Stein)", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index b25dda4..62b952b 100644 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -42,6 +42,7 @@ "Sell all products": "Sell all products", "sell products": "Sell products", "director starts production": "Director starts production", + "director payed out": "Director salary paid out", "Buy storage (type: field)": "Bought storage (type: field)", "Buy storage (type: iron)": "Bought storage (type: iron)", "Buy storage (type: stone)": "Bought storage (type: stone)",