From 36d02cab10450575eb4a52444cf51792b5589063 Mon Sep 17 00:00:00 2001 From: Jos Vooges | STH Date: Wed, 23 Sep 2026 00:36:40 +0200 Subject: [PATCH] Add Token Tray NSIS installer hosting and /agent download page. Serve the Windows setup via /install, ship deploy/agent in Master, and document volume upload for the gitignored exe. --- apps/admin-ui/src/app/agent/page.tsx | 151 ++++++++++++++++++ apps/admin-ui/src/app/settings/page.tsx | 5 +- apps/master-api/src/install/routes.ts | 67 +++++++- apps/token-tray/.gitignore | 5 + apps/token-tray/README.md | 36 ++++- apps/token-tray/package.json | 30 +++- apps/token-tray/scripts/publish-installer.cjs | 40 +++++ apps/token-tray/src/main.ts | 8 + deploy/agent/.gitignore | 20 +++ deploy/agent/.gitkeep | 0 deploy/agent/token-tray.json | 9 ++ deploy/docker/Dockerfile.master-api | 1 + deploy/docker/docker-compose.dokploy.yml | 3 + 13 files changed, 362 insertions(+), 13 deletions(-) create mode 100644 apps/admin-ui/src/app/agent/page.tsx create mode 100644 apps/token-tray/.gitignore create mode 100644 apps/token-tray/scripts/publish-installer.cjs create mode 100644 deploy/agent/.gitignore create mode 100644 deploy/agent/.gitkeep create mode 100644 deploy/agent/token-tray.json diff --git a/apps/admin-ui/src/app/agent/page.tsx b/apps/admin-ui/src/app/agent/page.tsx new file mode 100644 index 0000000..28c5d85 --- /dev/null +++ b/apps/admin-ui/src/app/agent/page.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { useCallback, useEffect, useState } from "react"; +import Link from "next/link"; +import { Nav, useAuth } from "@/components/Nav"; + +const MASTER_URL = "https://master.vonas.nl"; + +type TrayMeta = { + available?: boolean; + version?: string | null; + filename?: string; + sizeBytes?: number; + builtAt?: string | null; + productName?: string; + downloadUrl?: string; + note?: string; +}; + +function formatBytes(n: number): string { + if (!n || n <= 0) return "—"; + if (n < 1024 * 1024) return `${(n / 1024).toFixed(0)} KB`; + return `${(n / (1024 * 1024)).toFixed(1)} MB`; +} + +export default function AgentPage() { + useAuth(); + const [meta, setMeta] = useState(null); + const [err, setErr] = useState(null); + const [loading, setLoading] = useState(true); + + const load = useCallback(async () => { + setLoading(true); + setErr(null); + try { + const res = await fetch(`${MASTER_URL}/install/token-tray.json`, { + cache: "no-store", + }); + if (!res.ok) throw new Error(`Meta ophalen mislukt (${res.status})`); + const data = (await res.json()) as TrayMeta; + setMeta(data); + } catch (e) { + setErr(e instanceof Error ? e.message : String(e)); + setMeta(null); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + void load(); + }, [load]); + + const available = Boolean(meta?.available); + const downloadHref = + meta?.downloadUrl?.trim() || `${MASTER_URL}/install/token-tray-setup.exe`; + + return ( + <> +