diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx index cebec62..a360d0d 100644 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx +++ b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx @@ -126,6 +126,7 @@ export default function PreviewTab() { if (primaryRunning || primaryStarting) return; // already up or already starting ensureCalledRef.current = true; + setEnsureStatus("calling"); fetch(`/api/projects/${projectId}/dev-server/ensure`, { method: "POST", @@ -144,7 +145,14 @@ export default function PreviewTab() { } }) .catch(() => setEnsureStatus("error")); - }, [loading, anatomy, primaryRunning, primaryStarting, projectId]); + }, [ + loading, + anatomy, + primaryRunning, + primaryStarting, + projectId, + refreshKey, + ]); const [iframeSrc, setIframeSrc] = useState(null); const iframeDomRef = useRef(null); diff --git a/vibn-frontend/app/api/projects/[projectId]/anatomy/route.ts b/vibn-frontend/app/api/projects/[projectId]/anatomy/route.ts index a906ca6..4cf4b4e 100644 --- a/vibn-frontend/app/api/projects/[projectId]/anatomy/route.ts +++ b/vibn-frontend/app/api/projects/[projectId]/anatomy/route.ts @@ -825,6 +825,14 @@ async function loadPreviews(projectId: string): Promise { ping.status === 504 || ping.status === 404 ) { + // Give freshly booted servers a 60-second grace period before murdering them. + // Traefik sometimes returns 502 for a few seconds right after the internal probe succeeds. + const ageMs = Date.now() - new Date(r.started_at).getTime(); + if (ageMs < 60000) { + activePreviews.push(r); + return; + } + console.warn( `[anatomy] Preview zombie detected for ${r.preview_url} (HTTP ${ping.status}). Marking stopped.`, );