[ { "id": "vpn_final_v4", "type": "tab", "label": "VPN Dashboard V4", "disabled": false, "info": "" }, { "id": "node_http_in", "type": "http in", "z": "vpn_final_v4", "name": "", "url": "/vpn-status", "method": "post", "upload": false, "swaggerDoc": "", "x": 120, "y": 120, "wires": [ [ "node_logic", "node_http_res", "58624bbd43ed4aa5" ] ] }, { "id": "node_http_res", "type": "http response", "z": "vpn_final_v4", "name": "", "statusCode": "200", "headers": {}, "x": 340, "y": 80, "wires": [] }, { "id": "node_logic", "type": "function", "z": "vpn_final_v4", "name": "Status-Zentrale", "func": "// Status-Zentrale Function-Node (V7 - No-Crash Edition)\nvar vpnData = global.get('vpnData') || {};\n\n// 1. Reset-Logik (muss ganz oben stehen)\nif (msg.topic === \"reset\") {\n vpnData = {};\n global.set('vpnData', vpnData);\n msg.payload = [];\n return msg;\n}\n\n// 2. Sicherheits-Check: Ist überhaupt eine Payload da?\nif (msg.payload !== null && typeof msg.payload === 'object') {\n\n // Nur verarbeiten, wenn die Pflichtfelder existieren\n if (msg.payload.server && msg.payload.client) {\n\n var s = msg.payload.server;\n var c = msg.payload.client;\n var st = msg.payload.status;\n var hs = msg.payload.handshake || \"\";\n var time = new Date().toLocaleTimeString('de-DE');\n\n let isOnline = (st === true || st === 'true');\n\n // Key generieren (ohne Sonderzeichen)\n var normalizedClient = c.replace(/[^a-zA-Z0-9._-]/g, \"\");\n var key = s + \"_\" + normalizedClient;\n\n // Status-Text zusammenbauen\n let statusText = isOnline ? \"🟢 Online (\" + hs + \")\" : \"🔴 Offline (\" + hs + \")\";\n if (hs === \"never\") { statusText = \"🔴 Offline (Nie)\"; }\n\n // Daten in den Speicher schreiben (Keys exakt wie in deiner ui-table!)\n vpnData[key] = {\n \"Server\": s,\n \"VPN-Client\": c,\n \"Status\": statusText,\n \"Letztes_Update\": time\n };\n\n // Global speichern\n global.set('vpnData', vpnData);\n }\n}\n\n// 3. Ausgabe für die Tabelle (Immer als Array!)\nmsg.payload = Object.values(vpnData);\nreturn msg;", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 360, "y": 160, "wires": [ [ "node_ui_table", "2124bcb8672a7c64" ] ] }, { "id": "node_ui_table", "type": "ui-table", "z": "vpn_final_v4", "group": "node_ui_group", "name": "VPN Übersicht", "label": "", "order": 1, "width": "0", "height": "0", "maxrows": "", "autocols": false, "showSearch": true, "deselect": true, "selectionType": "none", "columns": [ { "title": "Server", "key": "Server", "keyType": "key", "type": "text", "width": "", "align": "start" }, { "title": "VPN-Client", "key": "VPN-Client", "keyType": "key", "type": "text", "width": "", "align": "start" }, { "title": "Status", "key": "Status", "keyType": "key", "type": "text", "width": "", "align": "start" } ], "mobileBreakpoint": "sm", "mobileBreakpointType": "defaults", "action": "replace", "className": "", "x": 600, "y": 160, "wires": [ [] ] }, { "id": "node_inject_test", "type": "inject", "z": "vpn_final_v4", "name": "Test-Daten", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "{\"server\":\"TestNode\",\"client\":\"vpn-test\",\"status\":\"true\"}", "x": 130, "y": 200, "wires": [ [ "node_logic" ] ] }, { "id": "2124bcb8672a7c64", "type": "debug", "z": "vpn_final_v4", "name": "debug 1", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 600, "y": 260, "wires": [] }, { "id": "58624bbd43ed4aa5", "type": "debug", "z": "vpn_final_v4", "name": "debug 2", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 340, "y": 40, "wires": [] }, { "id": "node_ui_group", "type": "ui-group", "name": "..:..", "page": "node_ui_page", "width": "12", "height": "1", "order": 1, "showTitle": false, "className": "", "visible": "true", "disabled": "false", "groupType": "default" }, { "id": "node_ui_page", "type": "ui-page", "name": "NETMAKER VPN Client Status", "ui": "db2_base", "path": "/vpn", "icon": "", "layout": "grid", "theme": "b752cb4ed9bda6eb", "breakpoints": [ { "name": "Default", "px": "0", "cols": "3" }, { "name": "Tablet", "px": "576", "cols": "6" }, { "name": "Small Desktop", "px": "768", "cols": "9" }, { "name": "Desktop", "px": "1024", "cols": "12" } ], "order": 1, "className": "", "visible": "true", "disabled": "false" }, { "id": "db2_base", "type": "ui-base", "name": "Dashboard 2.0", "path": "/dashboard", "includeClientData": true, "acceptsClientConfig": [ "ui-control", "ui-notification" ] }, { "id": "b752cb4ed9bda6eb", "type": "ui-theme", "name": "Standardthema", "colors": { "surface": "#737373", "primary": "#0094ce", "bgPage": "#eeeeee", "groupBg": "#ffffff", "groupOutline": "#cccccc" }, "sizes": { "density": "comfortable", "pagePadding": "12px", "groupGap": "12px", "groupBorderRadius": "4px", "widgetGap": "12px" } }, { "id": "180348698317a147", "type": "global-config", "env": [], "modules": { "@flowfuse/node-red-dashboard": "1.30.2" } } ]