SysOps: homarr-dashboard-update — 2026-06-23 10:14 UTC
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user