From c3fdc170d1e3c9777479b610a13318c7b12c0068 Mon Sep 17 00:00:00 2001 From: mawkone Date: Fri, 12 Jun 2026 16:08:03 -0700 Subject: [PATCH] fix(preview): sync fs_dev_servers state with container suspension to properly handle idle wakeup --- vibn-frontend/lib/dev-container.ts | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/vibn-frontend/lib/dev-container.ts b/vibn-frontend/lib/dev-container.ts index 8808a9c5..f8fc67a0 100644 --- a/vibn-frontend/lib/dev-container.ts +++ b/vibn-frontend/lib/dev-container.ts @@ -186,6 +186,7 @@ function renderDevCompose(projectSlug: string, projectId: string): string { image: ${VIBN_DEV_IMAGE} pull_policy: never restart: unless-stopped + command: ["bash", "-c", "echo 'Booting Vibn Container...'; if [ -f /workspace/package.json ]; then echo 'Found package.json, checking deps...'; if [ ! -d /workspace/node_modules ]; then npm install; fi; echo 'Starting dev server...'; npx next dev -H 0.0.0.0 --webpack; else echo 'No package.json found. Standing by...'; sleep infinity; fi"] working_dir: /workspace volumes: - workspace:/workspace @@ -335,6 +336,29 @@ export async function ensureDevContainer( ], ); + // In Path 2, the dev container natively runs the Next.js server on port 3000. + // We automatically inject the static preview tracking row so the UI sees it instantly. + const previewUrl = buildPreviewUrl(opts.projectId, opts.projectSlug, 3000); + if (previewUrl) { + await query( + `INSERT INTO fs_dev_servers + (id, project_id, workspace, name, command, port, preview_url, state) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + ON CONFLICT (id) DO UPDATE + SET state = EXCLUDED.state`, + [ + `ds_primary_${opts.projectId.replace(/-/g, "").slice(0, 10)}`, + opts.projectId, + opts.workspace.slug, + "Primary App", + "npx next dev -H 0.0.0.0 --webpack", + 3000, + previewUrl, + "running", + ], + ); + } + // Bookkeeping link so apps_list / projects_get see the dev container // under the right Vibn project. try { @@ -364,11 +388,17 @@ export async function suspendDevContainer(projectId: string): Promise { WHERE project_id = $1`, [projectId], ); + + // Also mark the fixed port-3000 app as stopped so the UI knows + await query( + `UPDATE fs_dev_servers SET state = 'stopped' WHERE project_id = $1 AND port = 3000`, + [projectId], + ).catch(() => {}); } export async function resumeDevContainer(projectId: string): Promise { const row = await getDevContainerRow(projectId); - if (!row) throw new Error(`No dev container provisioned for ${projectId}`); + if (!row) return; if (row.state === "running") return; await startService(row.service_uuid); await query( @@ -377,6 +407,12 @@ export async function resumeDevContainer(projectId: string): Promise { WHERE project_id = $1`, [projectId], ); + + // Mark the fixed port-3000 app as running again since the container boots it + await query( + `UPDATE fs_dev_servers SET state = 'running' WHERE project_id = $1 AND port = 3000`, + [projectId], + ).catch(() => {}); } async function touchActivity(projectId: string): Promise {