Harden onion boot flow and deepen site surfaces
Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation. Made-with: Cursor
This commit is contained in:
118
app/sign-up/page.tsx
Normal file
118
app/sign-up/page.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
"use client";
|
||||
|
||||
import { FormEvent, useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useAccount } from "@/contexts/AccountContext";
|
||||
|
||||
export default function SignUpPage() {
|
||||
const router = useRouter();
|
||||
const { user, hydrated, signUp } = useAccount();
|
||||
const [username, setUsername] = useState("");
|
||||
const [displayName, setDisplayName] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (hydrated && user) router.replace("/dashboard");
|
||||
}, [hydrated, user, router]);
|
||||
|
||||
const onSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setBusy(true);
|
||||
const res = await signUp(username, password, displayName || undefined);
|
||||
setBusy(false);
|
||||
if (!res.ok) {
|
||||
setError(res.error);
|
||||
return;
|
||||
}
|
||||
router.push("/dashboard");
|
||||
};
|
||||
|
||||
if (!hydrated || user) {
|
||||
return (
|
||||
<div className="flex min-h-[50vh] items-center justify-center font-mono text-sm text-zinc-500">
|
||||
Loading…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#0a0a0a] px-4 py-16 text-zinc-200">
|
||||
<div className="mx-auto max-w-md">
|
||||
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-neon-purple/80">provision</p>
|
||||
<h1 className="mt-3 font-orbitron text-3xl font-bold">Create account</h1>
|
||||
<p className="mt-3 text-sm text-zinc-500">
|
||||
Provisioning writes credentials only to this browser profile. Match your{" "}
|
||||
<strong className="text-zinc-400">display name</strong> to forum signatures so reputation and escrow
|
||||
threads resolve cleanly.
|
||||
</p>
|
||||
<p className="mt-3 text-sm text-zinc-500">
|
||||
Separate forum / exchange / wiki onions each need their own copy:{" "}
|
||||
<Link href="/account/hidden-services" className="text-neon-cyan hover:underline">
|
||||
Hidden services account guide
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
|
||||
<form onSubmit={onSubmit} className="glass mt-10 space-y-5 rounded-2xl border border-white/10 p-8">
|
||||
{error ? (
|
||||
<p className="rounded-lg border border-red-500/40 bg-red-500/10 px-4 py-2 text-sm text-red-300">{error}</p>
|
||||
) : null}
|
||||
<div>
|
||||
<label className="mb-2 block text-xs font-medium text-zinc-400">Handle (unique, login id)</label>
|
||||
<input
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
autoComplete="username"
|
||||
required
|
||||
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
|
||||
placeholder="ledger_rat"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-2 block text-xs font-medium text-zinc-400">Display name (shown on posts)</label>
|
||||
<input
|
||||
value={displayName}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
|
||||
placeholder="Defaults to handle if empty"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-2 block text-xs font-medium text-zinc-400">Passphrase (8+ characters)</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
autoComplete="new-password"
|
||||
required
|
||||
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={busy}
|
||||
className="w-full rounded-xl bg-gradient-to-r from-neon-purple to-neon-pink py-3 font-bold text-white disabled:opacity-50"
|
||||
>
|
||||
{busy ? "Provisioning…" : "Create & enter"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="mt-8 text-center text-sm text-zinc-500">
|
||||
Already have a handle?{" "}
|
||||
<Link href="/sign-in" className="text-neon-cyan hover:underline">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
<p className="mt-6 text-center">
|
||||
<Link href="/" className="text-xs text-zinc-600 hover:text-zinc-400">
|
||||
← Hub
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user