Files
evanpage/frontend/app/init/page.tsx
evan b0b85f4d3a Initial fullstack project setup with Next.js 15, Gin, PostgreSQL and Docker Compose
- Frontend: Next.js 15 (App Router), Auth.js v5, shadcn/ui, MagicUI
- Backend: Go + Gin + GORM with layered architecture
- Auth: Local credentials login with optional Keycloak OAuth binding
- Admin: RBAC user management for admin role
- Dev: Docker Compose with hot reload for both frontend and backend
- Docker: 3-service orchestration (frontend, backend, postgres)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 15:11:20 +00:00

152 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
export default function InitPage() {
const router = useRouter();
const [loading, setLoading] = useState(true);
const [initialized, setInitialized] = useState(true);
const [form, setForm] = useState({
username: "",
email: "",
password: "",
confirmPassword: "",
});
const [error, setError] = useState("");
useEffect(() => {
fetch("/api/proxy/admin/users", {
headers: { "X-User-Role": "admin" },
})
.then((res) => {
if (res.ok) {
setInitialized(true);
} else {
setInitialized(false);
}
})
.catch(() => setInitialized(false))
.finally(() => setLoading(false));
}, []);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError("");
if (form.password !== form.confirmPassword) {
setError("两次输入的密码不一致");
return;
}
const res = await fetch("/api/proxy/auth/init", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: form.username,
email: form.email,
password: form.password,
}),
});
if (!res.ok) {
const data = await res.json();
setError(data.error || "初始化失败");
return;
}
router.push("/login");
}
if (loading) {
return (
<div className="flex min-h-screen items-center justify-center">
...
</div>
);
}
if (initialized) {
return (
<div className="flex min-h-screen items-center justify-center p-4">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-center"></CardTitle>
</CardHeader>
<CardContent>
<p className="text-center text-gray-500">
</p>
</CardContent>
</Card>
</div>
);
}
return (
<div className="flex min-h-screen items-center justify-center bg-gray-50 p-4">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-center"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-gray-500">
</p>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<Label htmlFor="username"></Label>
<Input
id="username"
value={form.username}
onChange={(e) => setForm({ ...form, username: e.target.value })}
required
/>
</div>
<div>
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
value={form.email}
onChange={(e) => setForm({ ...form, email: e.target.value })}
required
/>
</div>
<div>
<Label htmlFor="password"></Label>
<Input
id="password"
type="password"
value={form.password}
onChange={(e) => setForm({ ...form, password: e.target.value })}
required
/>
</div>
<div>
<Label htmlFor="confirmPassword"></Label>
<Input
id="confirmPassword"
type="password"
value={form.confirmPassword}
onChange={(e) =>
setForm({ ...form, confirmPassword: e.target.value })
}
required
/>
</div>
{error && <p className="text-sm text-red-500">{error}</p>}
<Button type="submit" className="w-full">
</Button>
</form>
</CardContent>
</Card>
</div>
);
}