fix(preview): resolve ReferenceError on isForceStarting initialization order

This commit is contained in:
2026-06-12 16:46:35 -07:00
parent de950b1fb0
commit 95253c7707
32 changed files with 542 additions and 6325 deletions

View File

@@ -158,10 +158,7 @@ function renderDevCompose(projectSlug: string, projectId: string): string {
// process is actually listening on the port — Traefik does the
// health check.
const token = projectPreviewToken(projectId);
const traefikLabels: string[] = [
'"traefik.enable=true"',
'"traefik.docker.network=coolify"',
];
const traefikLabels: string[] = ['"traefik.enable=true"'];
for (let i = 0; i < PREVIEW_PORT_COUNT; i++) {
const port = PREVIEW_BASE_PORT + i;
const router = `vibn-dev-${projectSlug}-${i}`;
@@ -186,7 +183,6 @@ 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
@@ -336,29 +332,6 @@ 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 {
@@ -388,17 +361,11 @@ export async function suspendDevContainer(projectId: string): Promise<void> {
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<void> {
const row = await getDevContainerRow(projectId);
if (!row) return;
if (!row) throw new Error(`No dev container provisioned for ${projectId}`);
if (row.state === "running") return;
await startService(row.service_uuid);
await query(
@@ -407,12 +374,6 @@ export async function resumeDevContainer(projectId: string): Promise<void> {
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<void> {
@@ -796,7 +757,7 @@ export async function probeDevServerReadiness(
`for i in $(seq 1 300); do ` +
`for path in / ''; do ` +
`code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 2 --connect-timeout 2 ` +
`"http://localhost:${port}$path" 2>/dev/null || curl -sS -o /dev/null -w '%{http_code}' --max-time 2 --connect-timeout 2 "http://0.0.0.0:${port}$path" 2>/dev/null || printf '000'); ` +
`"http://127.0.0.1:${port}$path" 2>/dev/null || printf '000'); ` +
`last_code=$code; ` +
`[ "$code" != "000" ] && [ -n "$code" ] && exit 0; ` +
`done; ` +
@@ -873,36 +834,14 @@ export async function startDevServer(
// to keep the dashboard clean and prevent memory leaks.
const existingRows = await query<{
id: string;
project_id: string;
workspace: string;
name: string;
command: string;
port: number;
pid: number | null;
preview_url: string;
state: "starting" | "running" | "stopped" | "failed";
started_at: string;
stopped_at: string | null;
port: number;
}>(
`SELECT * FROM fs_dev_servers
`SELECT id, pid, port FROM fs_dev_servers
WHERE project_id = $1 AND state IN ('starting','running','failed')`,
[opts.projectId],
);
// IDEMPOTENCY: If the exact same command is already starting or running on the same port,
// do not kill it! Just return the existing record. This prevents the AI from accidentally
// bouncing the server and dropping the cache after every file edit, which leads to 502s.
const alreadyRunning = existingRows.find(
(r) =>
r.port === opts.port &&
r.command === opts.command &&
(r.state === "starting" || r.state === "running"),
);
if (alreadyRunning) {
return alreadyRunning;
}
const killPortNodeCmd =
`node -e '` +
`const fs = require("fs"); ` +