chore: remove outdated install wizard

This commit is contained in:
Christomatt
2026-02-01 03:26:03 +01:00
parent 9b22b2a944
commit 4323ae90f8
3 changed files with 1 additions and 329 deletions
-54
View File
@@ -1,54 +0,0 @@
import { NextResponse } from 'next/server';
import { db, users } from '@/db';
import { count, sql } from 'drizzle-orm';
const requiredEnv = [
'DATABASE_URL',
'AUTH_SECRET',
'NEXT_PUBLIC_NODE_DOMAIN',
'NEXT_PUBLIC_NODE_NAME',
'ADMIN_EMAILS',
];
const optionalEnv: string[] = [];
export async function GET() {
try {
const envStatus = {
required: requiredEnv.reduce<Record<string, boolean>>((acc, key) => {
acc[key] = Boolean(process.env[key]);
return acc;
}, {}),
optional: optionalEnv.reduce<Record<string, boolean>>((acc, key) => {
acc[key] = Boolean(process.env[key]);
return acc;
}, {}),
};
if (!db) {
return NextResponse.json({
env: envStatus,
db: { connected: false, schemaReady: false, usersCount: 0 },
});
}
let schemaReady = true;
let usersCount = 0;
try {
await db.execute(sql`select 1 from users limit 1`);
const [result] = await db.select({ count: count() }).from(users);
usersCount = Number(result?.count || 0);
} catch {
schemaReady = false;
}
return NextResponse.json({
env: envStatus,
db: { connected: true, schemaReady, usersCount },
});
} catch (error) {
console.error('Install status error:', error);
return NextResponse.json({ error: 'Failed to check status' }, { status: 500 });
}
}
-273
View File
@@ -1,273 +0,0 @@
'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
export const dynamic = 'force-dynamic';
type EnvStatus = {
required: Record<string, boolean>;
optional: Record<string, boolean>;
};
type InstallStatus = {
env: EnvStatus;
db: {
connected: boolean;
schemaReady: boolean;
usersCount: number;
};
};
const requiredLabels: Record<string, string> = {
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<string, string> = {};
const StepCard = ({
title,
description,
status,
children,
}: {
title: string;
description?: string;
status?: { label: string; tone: 'ok' | 'warn' };
children: React.ReactNode;
}) => (
<div className="install-card">
<div className="install-card-header">
<div>
<div className="install-card-title">{title}</div>
{description && <div className="install-card-desc">{description}</div>}
</div>
{status && (
<div className={`install-step-status ${status.tone}`}>
{status.label}
</div>
)}
</div>
{children}
</div>
);
export default function InstallPage() {
const searchParams = useSearchParams();
const force = searchParams.get('force') === '1';
const [status, setStatus] = useState<InstallStatus | null>(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 (
<div className="install-shell">
<div className="install-card">Checking install status...</div>
</div>
);
}
if (!status) {
return (
<div className="install-shell">
<div className="install-card">
<h1>Setup Wizard</h1>
<p>We could not load the install status.</p>
<button className="btn btn-primary" onClick={loadStatus}>
Retry
</button>
</div>
</div>
);
}
if (isInstalled && !force) {
return (
<div className="install-shell">
<div className="install-card">
<h1>Synapsis is already set up</h1>
<p>Your database is connected and at least one user exists.</p>
<Link href="/" className="btn btn-primary" style={{ marginTop: '12px' }}>
Go to home
</Link>
<Link href="/install?force=1" className="btn btn-ghost" style={{ marginTop: '12px' }}>
Re-run setup
</Link>
</div>
</div>
);
}
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 (
<div className="install-shell">
<div className="install-hero">
<div className="install-header">
<div>
<h1>Synapsis Setup</h1>
<p>Follow these steps to complete your installation.</p>
</div>
<button className="btn btn-ghost" onClick={loadStatus}>
Recheck
</button>
</div>
<div className="install-progress">
<div className="install-progress-bar">
<div className="install-progress-fill" style={{ width: `${progressPercent}%` }} />
</div>
<div className="install-progress-meta">
<span>{completedSteps} / 3 steps complete</span>
<span>{progressPercent}%</span>
</div>
</div>
<div className="install-summary">
<div>
<div className="install-summary-title">Environment</div>
<div className="install-summary-value">{envComplete ? 'Ready' : 'Needs values'}</div>
</div>
<div>
<div className="install-summary-title">Database</div>
<div className="install-summary-value">{dbComplete ? 'Ready' : 'Not ready'}</div>
</div>
<div>
<div className="install-summary-title">Admin</div>
<div className="install-summary-value">{adminComplete ? 'Ready' : 'Not created'}</div>
</div>
</div>
</div>
<StepCard
title="1. Environment variables"
description="Set required values in your deployment environment (.env or platform settings)."
status={{
label: envComplete ? 'Complete' : 'Needs attention',
tone: envComplete ? 'ok' : 'warn',
}}
>
<div className="install-grid">
{Object.entries(status.env.required).map(([key, ok]) => (
<div key={key} className={`install-item ${ok ? 'ok' : 'missing'}`}>
<div className="install-item-title">{key}</div>
<div className="install-item-desc">{requiredLabels[key]}</div>
<div className="install-item-status">{ok ? 'Set' : 'Missing'}</div>
</div>
))}
</div>
{missingRequired.length > 0 && (
<div className="install-hint">
Missing required values: {missingRequired.map(([key]) => key).join(', ')}
</div>
)}
{missingRequired.some(([key]) => key === 'AUTH_SECRET') && (
<div className="install-hint" style={{ marginTop: '8px' }}>
To generate an AUTH_SECRET, run:
<code style={{ display: 'block', padding: '8px', background: 'rgba(0,0,0,0.05)', marginTop: '4px', borderRadius: '4px', fontFamily: 'monospace' }}>
openssl rand -base64 33
</code>
</div>
)}
<div className="install-subtitle">Optional</div>
<div className="install-grid">
{Object.entries(status.env.optional).map(([key, ok]) => (
<div key={key} className={`install-item ${ok ? 'ok' : 'missing'}`}>
<div className="install-item-title">{key}</div>
<div className="install-item-desc">{optionalLabels[key]}</div>
<div className="install-item-status">{ok ? 'Set' : 'Not set'}</div>
</div>
))}
</div>
{missingOptional.length > 0 && (
<div className="install-hint">
Optional values missing: {missingOptional.map(([key]) => key).join(', ')}
</div>
)}
</StepCard>
<StepCard
title="2. Database"
description="Ensure the database is reachable and the schema is pushed."
status={{
label: dbComplete ? 'Complete' : 'Needs attention',
tone: dbComplete ? 'ok' : 'warn',
}}
>
<div className="install-status-row">
<span>Database connection</span>
<strong>{status.db.connected ? 'Connected' : 'Not connected'}</strong>
</div>
<div className="install-status-row">
<span>Schema</span>
<strong>{status.db.schemaReady ? 'Ready' : 'Missing tables'}</strong>
</div>
{!status.db.schemaReady && (
<div className="install-hint">
Run <code>npm run db:push</code> to create tables.
</div>
)}
</StepCard>
<StepCard
title="3. Create admin account"
description="Register your first account, then grant admin access."
status={{
label: adminComplete ? 'Complete' : 'Needs attention',
tone: adminComplete ? 'ok' : 'warn',
}}
>
<div className="install-status-row">
<span>Existing users</span>
<strong>{status.db.usersCount}</strong>
</div>
<p className="install-body">
Register a user via the login page. Then add their email to
<code>ADMIN_EMAILS</code> and redeploy.
</p>
<Link href="/login" className="btn btn-primary">
Go to login / register
</Link>
</StepCard>
<StepCard
title="4. Launch"
description="Once you have at least one user and admin access, you are ready."
>
<Link href="/" className="btn btn-primary">
Go to home
</Link>
<Link href="/admin" className="btn btn-ghost">
Open moderation dashboard
</Link>
</StepCard>
</div>
);
}
+1 -2
View File
@@ -12,8 +12,7 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
// Paths that should NOT have the app layout // Paths that should NOT have the app layout
const isStandalone = const isStandalone =
pathname === '/login' || pathname === '/login' ||
pathname === '/register' || pathname === '/register';
pathname?.startsWith('/install');
// Hide right sidebar on chat page for more space // Hide right sidebar on chat page for more space
const hideRightSidebar = false; const hideRightSidebar = false;