Files
fusion/packages/dashboard/app/components/MigrationInProgressBanner.tsx
gsxdsm 0b6c4cd4ca feat(dashboard,desktop): show live database-migration progress during boot
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>
2026-07-17 20:09:56 -07:00

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>
);
}