The one-time SQLite→PostgreSQL migration runs inside createTaskStoreForBackend before any HTTP server listens, so browsers saw "connection refused" and open tabs failed silently for minutes. Now: - CLI: a temporary holding server binds the dashboard port for the boot window, serving an auto-reloading "Database migration in progress" page and an /api/health payload with status "migrating" + structured progress; the port is handed off (awaited) to the real app.listen(). - Dashboard SPA: already-open tabs render the new MigrationInProgressBanner from the 15s health poll when status is "migrating". - Desktop: LocalRuntimeManager publishes migration progress on DesktopRuntimeStatus via the new core onMigrationProgress option; DesktopLaunchGate shows the live label and extends its 30s startup timeout while progress advances (2min stall cap), in both boot and first-run flows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
39 lines
1.5 KiB
TypeScript
39 lines
1.5 KiB
TypeScript
/*
|
|
FNXC:MigrationHoldingPage 2026-07-17-12:40:
|
|
When the Fusion server restarts and performs the one-time SQLite→PostgreSQL
|
|
migration, an already-open dashboard tab keeps polling /api/health (every 15s
|
|
via useDashboardHealth) and reaches the CLI's boot-window holding server, which
|
|
answers with status "migrating" plus a progress label. This banner surfaces
|
|
that state so the open tab explains the outage instead of failing silently.
|
|
It renders from health status alone (no project gate — during the boot window
|
|
no project data is fetchable) and disappears on the next poll of the real
|
|
server. Fresh navigations during migration get the holding page instead.
|
|
*/
|
|
import { useTranslation } from "react-i18next";
|
|
import { DatabaseZap } from "lucide-react";
|
|
import "./MigrationInProgressBanner.css";
|
|
|
|
interface MigrationInProgressBannerProps {
|
|
isActive: boolean;
|
|
progressLabel?: string;
|
|
}
|
|
|
|
export function MigrationInProgressBanner({ isActive, progressLabel }: MigrationInProgressBannerProps) {
|
|
const { t } = useTranslation("app");
|
|
if (!isActive) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="migration-in-progress-banner" role="status" aria-live="polite">
|
|
<DatabaseZap aria-hidden="true" />
|
|
<span>
|
|
{t("app.migrationInProgress", "Database migration in progress — the dashboard will reconnect when it completes.")}
|
|
{progressLabel ? (
|
|
<span className="migration-in-progress-banner-progress">{progressLabel}</span>
|
|
) : null}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|