From 544d9e611d454bf43fe5c2f092bbf1e00e9eb1af Mon Sep 17 00:00:00 2001 From: sysops Date: Tue, 23 Jun 2026 10:14:48 +0000 Subject: [PATCH] =?UTF-8?q?SysOps:=20homarr-dashboard-update=20=E2=80=94?= =?UTF-8?q?=202026-06-23=2010:14=20UTC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/homarr_update_foodlinkk.py | 316 +++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 scripts/homarr_update_foodlinkk.py diff --git a/scripts/homarr_update_foodlinkk.py b/scripts/homarr_update_foodlinkk.py new file mode 100644 index 0000000..bdc61ff --- /dev/null +++ b/scripts/homarr_update_foodlinkk.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python3 +"""Homarr dashboard — alle actieve Foodlinkk links + tiles op board.""" +from __future__ import annotations + +import json +import random +import sqlite3 +import string + +DB = "/opt/homarr_db/db.sqlite" +BOARD_ID = "jnyjc9756utndw62gsg78ssb" +SECTION_ID = "lvq9qc0p59sllgek92n77qi8" +LAYOUT_ID = "lft1byu8yp01glpe006l7cuq" + +ICON = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons" + +# (name, description, href, ping_url, icon_url) +APPS: list[tuple[str, str, str, str | None, str]] = [ + # —— Kern (Foodlinkk) —— + ( + "Foodlinkk Cockpit", + "Command Center — hoofd-UI", + "http://10.4.7.18:8600/", + "http://10.4.7.18:8600/", + f"{ICON}/svg/home-assistant.svg", + ), + ( + "Wereldexport", + "Export Intel · kaart · distributeurs", + "http://10.4.7.18:8600/export-intel", + "http://10.4.7.18:8600/export-intel", + f"{ICON}/svg/world.svg", + ), + ( + "Voice Live", + "Herman voice · hands-free", + "http://10.4.7.18:8600/voice", + "http://10.4.7.18:8600/voice", + f"{ICON}/svg/google-assistant.svg", + ), + ( + "Herman Chat", + "Browser chat + agy builds", + "http://10.4.7.18:8600/herman", + "http://10.4.7.18:8600/herman", + f"{ICON}/svg/open-webui.svg", + ), + ( + "Agents", + "Mesh · terminals · webbuilder", + "http://10.4.7.18:8600/agents", + "http://10.4.7.18:8600/agents", + f"{ICON}/svg/portainer.svg", + ), + ( + "Revenue Cockpit", + "Excel-live dashboard", + "http://10.4.7.18:8600/revenue-cockpit", + "http://10.4.7.18:8600/revenue-cockpit", + f"{ICON}/svg/grafana.svg", + ), + ( + "Gitea", + "Git · backups · repo", + "http://10.4.7.18:3001/aissa/foodlinkk-command-center", + "http://10.4.7.18:3001/", + f"{ICON}/svg/gitea.svg", + ), + # —— Infra —— + ( + "Proxmox", + "Hypervisor R340", + "https://10.4.7.14:8006/", + "https://10.4.7.14:8006/", + f"{ICON}/svg/proxmox.svg", + ), + ( + "Synology NAS", + "FL-nas DSM", + "http://10.4.7.11:5000/", + "http://10.4.7.11:5000/", + f"{ICON}/png/synology-dsm.png", + ), + ( + "Homarr", + "Dit dashboard", + "http://10.4.7.13:3000/", + "http://10.4.7.13:3000/", + f"{ICON}/svg/homarr.svg", + ), + ( + "Dockhand", + "Docker beheer VM106", + "http://10.4.7.18:3000/", + "http://10.4.7.18:3000/", + f"{ICON}/svg/dockge.svg", + ), + # —— AI VM105 / VM107 —— + ( + "Herman Orchestrator", + "VM105 · agent routing", + "http://10.4.7.19:8090/", + "http://10.4.7.19:8090/health", + f"{ICON}/svg/ollama-light.svg", + ), + ( + "Open WebUI", + "Ollama chat UI", + "http://10.4.7.19:8080/", + "http://10.4.7.19:8080/", + f"{ICON}/svg/open-webui.svg", + ), + ( + "Doc Ingest", + "NAS documenten · RAG", + "http://10.4.7.19:8750/", + "http://10.4.7.19:8750/health", + f"{ICON}/svg/paperless-ngx.svg", + ), + ( + "Browser Use", + "AI browser agent", + "http://10.4.7.19:7788/", + "http://10.4.7.19:7788/", + f"{ICON}/svg/chromium.svg", + ), + ( + "n8n", + "Workflow automation", + "http://10.4.7.19:5678/", + "http://10.4.7.19:5678/", + f"{ICON}/svg/n8n.svg", + ), + ( + "Web Builder (agy)", + "Antigravity · VM107 API", + "http://10.4.7.27:8798/health", + "http://10.4.7.27:8798/health", + f"{ICON}/svg/nginx.svg", + ), + ( + "Website Preview", + "agy build preview", + "http://10.4.7.27:8080/", + "http://10.4.7.27:8080/", + f"{ICON}/svg/nginx.svg", + ), + # —— Tools VM106 —— + ( + "ComfyUI", + "Image generation", + "http://10.4.7.18:8188/", + "http://10.4.7.18:8188/", + f"{ICON}/svg/comfyui.svg", + ), + ( + "Browser VNC", + "Live browser VM106", + "http://10.4.7.18:6080/", + "http://10.4.7.18:6080/", + f"{ICON}/svg/chromium.svg", + ), + ( + "MinIO", + "Object storage", + "http://10.4.7.18:9001/", + "http://10.4.7.18:9001/", + f"{ICON}/svg/minio-light.svg", + ), + ( + "Grafana", + "Monitoring dashboards", + "http://10.4.7.18:3002/", + "http://10.4.7.18:3002/", + f"{ICON}/svg/grafana.svg", + ), + ( + "Prometheus", + "Metrics", + "http://10.4.7.18:9090/", + "http://10.4.7.18:9090/", + f"{ICON}/svg/prometheus.svg", + ), + # —— Overig —— + ( + "BentoPDF", + "PDF tools", + "http://10.4.7.17:8080/", + "http://10.4.7.17:8080/", + f"{ICON}/svg/stirling-pdf.svg", + ), + ( + "Excalidraw", + "Whiteboard", + "http://10.4.7.16:3000/", + "http://10.4.7.16:3000/", + f"{ICON}/svg/excalidraw.svg", + ), +] + +# Volgorde op het board (namen) +BOARD_ORDER = [ + "Foodlinkk Cockpit", + "Wereldexport", + "Voice Live", + "Herman Chat", + "Agents", + "Revenue Cockpit", + "Gitea", + "Proxmox", + "Synology NAS", + "Homarr", + "Dockhand", + "Herman Orchestrator", + "Open WebUI", + "Web Builder (agy)", + "Website Preview", + "Doc Ingest", + "Browser Use", + "n8n", + "ComfyUI", + "Browser VNC", + "MinIO", + "Grafana", + "Prometheus", + "BentoPDF", + "Excalidraw", +] + +MOBILE_CSS = """ +@media (max-width: 900px) { + html { font-size: 18px !important; } + [data-testid="app-tile"] { min-height: 72px !important; } + button, .mantine-Button-root { min-height: 44px !important; } +} +""" + + +def gen_id() -> str: + return "".join(random.choices(string.ascii_lowercase + string.digits, k=24)) + + +def upsert_app(cur: sqlite3.Cursor, name: str, desc: str, href: str, ping: str | None, icon: str) -> str: + row = cur.execute("SELECT id FROM app WHERE name=?", (name,)).fetchone() + if row: + aid = row[0] + cur.execute( + "UPDATE app SET description=?, href=?, ping_url=?, icon_url=? WHERE id=?", + (desc, href, ping, icon, aid), + ) + else: + aid = gen_id() + cur.execute( + "INSERT INTO app (id, name, description, icon_url, href, ping_url) VALUES (?,?,?,?,?,?)", + (aid, name, desc, icon, href, ping), + ) + return aid + + +def main() -> None: + conn = sqlite3.connect(DB) + cur = conn.cursor() + + cur.execute("UPDATE board SET custom_css=?, item_radius=? WHERE id=?", (MOBILE_CSS.strip(), "md", BOARD_ID)) + cur.execute("UPDATE layout SET column_count=? WHERE id=?", (4, LAYOUT_ID)) + + name_to_id: dict[str, str] = {} + for name, desc, href, ping, icon in APPS: + name_to_id[name] = upsert_app(cur, name, desc, href, ping, icon) + + # Verwijder oude board-tiles (behoud app-records) + old_items = [r[0] for r in cur.execute("SELECT id FROM item WHERE board_id=?", (BOARD_ID,)).fetchall()] + for iid in old_items: + cur.execute("DELETE FROM item_layout WHERE item_id=?", (iid,)) + cur.execute("DELETE FROM item WHERE id=?", (iid,)) + + cols = 4 + for slot, name in enumerate(BOARD_ORDER): + aid = name_to_id.get(name) + if not aid: + continue + item_id = gen_id() + opts = json.dumps( + { + "json": { + "appId": aid, + "openInNewTab": True, + "showTitle": True, + "pingEnabled": True, + } + } + ) + cur.execute( + "INSERT INTO item (id, board_id, kind, options, advanced_options) VALUES (?,?,?,?,?)", + (item_id, BOARD_ID, "app", opts, "{}"), + ) + x = slot % cols + y = slot // cols + cur.execute( + """INSERT INTO item_layout + (item_id, section_id, layout_id, x_offset, y_offset, width, height) + VALUES (?,?,?,?,?,?,?)""", + (item_id, SECTION_ID, LAYOUT_ID, x, y, 1, 1), + ) + + conn.commit() + print(f"Board tiles: {len(BOARD_ORDER)}") + for name in BOARD_ORDER: + href = cur.execute("SELECT href FROM app WHERE name=?", (name,)).fetchone() + if href: + print(f" ✓ {name}: {href[0]}") + conn.close() + + +if __name__ == "__main__": + main()