'use client'; import { useEffect, useState } from 'react'; import Link from 'next/link'; import { useSearchParams } from 'next/navigation'; type EnvStatus = { required: Record; optional: Record; }; type InstallStatus = { env: EnvStatus; db: { connected: boolean; schemaReady: boolean; usersCount: number; }; }; const requiredLabels: Record = { DATABASE_URL: 'Database connection string', AUTH_SECRET: 'Auth cookie secret', NEXT_PUBLIC_NODE_DOMAIN: 'Public node domain', NEXT_PUBLIC_NODE_NAME: 'Node display name', ADMIN_EMAILS: 'Admin emails list', }; const optionalLabels: Record = {}; const StepCard = ({ title, description, status, children, }: { title: string; description?: string; status?: { label: string; tone: 'ok' | 'warn' }; children: React.ReactNode; }) => (
{title}
{description &&
{description}
}
{status && (
{status.label}
)}
{children}
); export default function InstallPage() { const searchParams = useSearchParams(); const force = searchParams.get('force') === '1'; const [status, setStatus] = useState(null); const [loading, setLoading] = useState(true); const loadStatus = async () => { setLoading(true); try { const res = await fetch('/api/install/status'); const data = await res.json(); setStatus(data); } catch { setStatus(null); } finally { setLoading(false); } }; useEffect(() => { loadStatus(); }, []); const isInstalled = status?.db.connected && status?.db.schemaReady && status?.db.usersCount > 0; if (loading) { return (
Checking install status...
); } if (!status) { return (

Setup Wizard

We could not load the install status.

); } if (isInstalled && !force) { return (

Synapsis is already set up

Your database is connected and at least one user exists.

Go to home Re-run setup
); } const missingRequired = Object.entries(status.env.required).filter(([, ok]) => !ok); const missingOptional = Object.entries(status.env.optional).filter(([, ok]) => !ok); const envComplete = missingRequired.length === 0; const dbComplete = status.db.connected && status.db.schemaReady; const adminComplete = status.db.usersCount > 0; const completedSteps = [envComplete, dbComplete, adminComplete].filter(Boolean).length; const progressPercent = Math.round((completedSteps / 3) * 100); return (

Synapsis Setup

Follow these steps to complete your installation.

{completedSteps} / 3 steps complete {progressPercent}%
Environment
{envComplete ? 'Ready' : 'Needs values'}
Database
{dbComplete ? 'Ready' : 'Not ready'}
Admin
{adminComplete ? 'Ready' : 'Not created'}
{Object.entries(status.env.required).map(([key, ok]) => (
{key}
{requiredLabels[key]}
{ok ? 'Set' : 'Missing'}
))}
{missingRequired.length > 0 && (
Missing required values: {missingRequired.map(([key]) => key).join(', ')}
)} {missingRequired.some(([key]) => key === 'AUTH_SECRET') && (
To generate an AUTH_SECRET, run: openssl rand -base64 33
)}
Optional
{Object.entries(status.env.optional).map(([key, ok]) => (
{key}
{optionalLabels[key]}
{ok ? 'Set' : 'Not set'}
))}
{missingOptional.length > 0 && (
Optional values missing: {missingOptional.map(([key]) => key).join(', ')}
)}
Database connection {status.db.connected ? 'Connected' : 'Not connected'}
Schema {status.db.schemaReady ? 'Ready' : 'Missing tables'}
{!status.db.schemaReady && (
Run npm run db:push to create tables.
)}
Existing users {status.db.usersCount}

Register a user via the login page. Then add their email to ADMIN_EMAILS and redeploy.

Go to login / register
Go to home Open moderation dashboard
); }