diff --git a/config/services.yaml b/config/services.yaml index c83d424f..7719c2c7 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -8,6 +8,10 @@ server: local siteMonitor: https://optical-dev.oliver.solutions/ppt-tool showStats: true + widget: + type: deploy + service: ppt-tool + label: DeckForge - GMAL Scope Builder: icon: mdi-briefcase-outline @@ -17,6 +21,10 @@ server: local siteMonitor: https://optical-dev.oliver.solutions/gsb showStats: true + widget: + type: deploy + service: gmal-scope-builder + label: Scope Builder - Semblance: icon: mdi-account-group-outline @@ -26,6 +34,10 @@ server: local siteMonitor: https://optical-dev.oliver.solutions/semblance showStats: true + widget: + type: deploy + service: semblance + label: Semblance - CC Dashboard: icon: mdi-view-dashboard-outline @@ -35,6 +47,10 @@ server: local siteMonitor: https://optical-dev.oliver.solutions/cc-dashboard showStats: true + widget: + type: deploy + service: cc-dashboard + label: CC Dashboard - OliVAS: icon: mdi-robot-outline @@ -44,6 +60,10 @@ server: local siteMonitor: https://optical-dev.oliver.solutions/api/health showStats: true + widget: + type: deploy + service: olivas + label: OliVAS - Infrastructure: - Homepage: @@ -54,12 +74,16 @@ server: local siteMonitor: https://optical-dev.oliver.solutions/homepage showStats: true + widget: + type: deploy + service: homepage + label: Homepage - Deploy API: icon: mdi-rocket-launch-outline href: https://optical-dev.oliver.solutions/deploy-api/docs description: One-click deploy service - siteMonitor: http://127.0.0.1:9000/services + siteMonitor: https://optical-dev.oliver.solutions/deploy-api/services - PostgreSQL × 4: icon: mdi-database-outline diff --git a/config/settings.yaml b/config/settings.yaml index 1a8ceeda..a3c6a8df 100644 --- a/config/settings.yaml +++ b/config/settings.yaml @@ -18,7 +18,7 @@ target: _blank layout: Widgets: style: row - columns: 9 + columns: 2 AI Tools: style: row columns: 5 diff --git a/config/widgets.yaml b/config/widgets.yaml index 0ea6e479..ac0781ce 100644 --- a/config/widgets.yaml +++ b/config/widgets.yaml @@ -13,27 +13,3 @@ timeStyle: short dateStyle: short hourCycle: h23 - -- deploy: - service: ppt-tool - label: DeckForge - -- deploy: - service: semblance - label: Semblance - -- deploy: - service: olivas - label: OliVAS - -- deploy: - service: gmal-scope-builder - label: Scope Builder - -- deploy: - service: cc-dashboard - label: CC Dashboard - -- deploy: - service: homepage - label: Homepage diff --git a/src/widgets/components.js b/src/widgets/components.js index c5f144e3..c972d209 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -26,6 +26,7 @@ const components = { iframe: dynamic(() => import("./iframe/component")), customapi: dynamic(() => import("./customapi/component")), deluge: dynamic(() => import("./deluge/component")), + deploy: dynamic(() => import("./deploy/component")), develancacheui: dynamic(() => import("./develancacheui/component")), diskstation: dynamic(() => import("./diskstation/component")), dispatcharr: dynamic(() => import("./dispatcharr/component")), diff --git a/src/widgets/deploy/component.jsx b/src/widgets/deploy/component.jsx new file mode 100644 index 00000000..cecd0425 --- /dev/null +++ b/src/widgets/deploy/component.jsx @@ -0,0 +1,78 @@ +import { useState } from "react"; +import useSWR from "swr"; + +const STATUS_COLORS = { + idle: "text-theme-500 dark:text-theme-400", + running: "text-blue-500 dark:text-blue-400", + success: "text-emerald-500 dark:text-emerald-400", + failed: "text-red-500 dark:text-red-400", +}; + +const STATUS_LABELS = { + idle: "Never deployed", + running: "Deploying...", + success: "Deployed", + failed: "Failed", +}; + +function formatTime(iso) { + if (!iso) return null; + const d = new Date(iso); + return d.toLocaleString("en-GB", { dateStyle: "short", timeStyle: "short", hourCycle: "h23" }); +} + +export default function DeployComponent({ service }) { + const { service: svcName, label, apiBase = "/deploy-api" } = service?.widget ?? {}; + const bp = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; + // SWR middleware already prepends bp, so statusUrl must NOT include it + const statusUrl = svcName ? `${apiBase}/status/${svcName}` : null; + + const { data, mutate } = useSWR(statusUrl, { + refreshInterval: (d) => (d?.status === "running" ? 2000 : 10000), + }); + + const [triggering, setTriggering] = useState(false); + + const status = data?.status ?? "idle"; + const lastRun = data?.last_run ? formatTime(data.last_run) : null; + const isRunning = status === "running"; + + const handleDeploy = async () => { + if (isRunning || triggering) return; + setTriggering(true); + try { + await fetch(`${bp}${apiBase}/deploy/${svcName}`, { method: "POST" }); + await mutate(); + } finally { + setTriggering(false); + } + }; + + if (!svcName) return null; + + return ( +
+
+ + {isRunning ? {STATUS_LABELS.running} : (STATUS_LABELS[status] ?? status)} + + {lastRun && ( + {lastRun} + )} +
+ +
+ ); +}