diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index ee5d069..f4594d7 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -36,10 +36,13 @@ services: OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET:-} OIDC_REDIRECT_URL: ${OIDC_REDIRECT_URL:-} KEY_ENCRYPTION_KEY: ${KEY_ENCRYPTION_KEY:-} + VANTAGE_WORKFLOW_LOG_DIR: ${VANTAGE_WORKFLOW_LOG_DIR:-} GUACD_ADDR: guacd:4822 depends_on: redis: condition: service_healthy + volumes: + - ./data:/data web: image: gitea.hostxtra.co.uk/mrhid6/vantage/web:latest restart: unless-stopped diff --git a/web/app/workflows/[id]/page.tsx b/web/app/workflows/[id]/page.tsx index 24302e6..32f851e 100644 --- a/web/app/workflows/[id]/page.tsx +++ b/web/app/workflows/[id]/page.tsx @@ -105,14 +105,35 @@ export default function WorkflowBuilder() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [secretGroups]); - if (!wf) { - return
Loading…
; - } - // Keep a ref to the latest workflow so an in-flight save can tell whether // the user edited again while the request was on the wire. wfRef.current = wf; + // Autosave: debounce 800ms after any change to the workflow (step added, + // removed, reordered, or edited) and persist. Diffing the serialized state + // against the last saved snapshot skips no-op saves and the initial load. + // Must stay above the early return below so hook order is stable. + useEffect(() => { + if (!wf || savedSnapshotRef.current === null) return; + if (JSON.stringify(wf) === savedSnapshotRef.current) return; + const t = setTimeout(() => { + save(); + }, 800); + return () => clearTimeout(t); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [wf]); + + // Re-render every 15s so the "Saved … ago" label stays current. + useEffect(() => { + if (!lastSaved) return; + const iv = setInterval(() => setTick((n) => n + 1), 15000); + return () => clearInterval(iv); + }, [lastSaved]); + + if (!wf) { + return
Loading…
; + } + const libById = (sid?: string) => (sid ? library?.find((l) => l.step_id === sid) : undefined); const sortedSteps = [...wf.steps].sort((a, b) => a.order - b.order); @@ -162,26 +183,6 @@ export default function WorkflowBuilder() { } }; - // Autosave: debounce 800ms after any change to the workflow (step added, - // removed, reordered, or edited) and persist. Diffing the serialized state - // against the last saved snapshot skips no-op saves and the initial load. - useEffect(() => { - if (!wf || savedSnapshotRef.current === null) return; - if (JSON.stringify(wf) === savedSnapshotRef.current) return; - const t = setTimeout(() => { - save(); - }, 800); - return () => clearTimeout(t); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [wf]); - - // Re-render every 15s so the "Saved … ago" label stays current. - useEffect(() => { - if (!lastSaved) return; - const iv = setInterval(() => setTick((n) => n + 1), 15000); - return () => clearInterval(iv); - }, [lastSaved]); - const run = async () => { setRunning(true); setError(null);