Add login gate, calculation history, and AI markdown download.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import {
|
||||
isAuthEnabled,
|
||||
verifyCredentials,
|
||||
} from "@/lib/auth/config";
|
||||
import {
|
||||
createSessionToken,
|
||||
SESSION_COOKIE,
|
||||
SESSION_MAX_AGE_SEC,
|
||||
} from "@/lib/auth/session";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
if (!isAuthEnabled()) {
|
||||
return NextResponse.json({ ok: true, authEnabled: false });
|
||||
}
|
||||
|
||||
let body: { username?: string; password?: string };
|
||||
try {
|
||||
body = await req.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: "请求格式错误" }, { status: 400 });
|
||||
}
|
||||
|
||||
const username = body.username?.trim() ?? "";
|
||||
const password = body.password ?? "";
|
||||
|
||||
if (!verifyCredentials(username, password)) {
|
||||
return NextResponse.json({ error: "用户名或密码错误" }, { status: 401 });
|
||||
}
|
||||
|
||||
const token = await createSessionToken(username);
|
||||
const res = NextResponse.json({ ok: true, username });
|
||||
res.cookies.set(SESSION_COOKIE, token, {
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
path: "/",
|
||||
maxAge: SESSION_MAX_AGE_SEC,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { SESSION_COOKIE } from "@/lib/auth/session";
|
||||
|
||||
export async function POST() {
|
||||
const res = NextResponse.json({ ok: true });
|
||||
res.cookies.set(SESSION_COOKIE, "", {
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
path: "/",
|
||||
maxAge: 0,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import { isAuthEnabled } from "@/lib/auth/config";
|
||||
import { getSessionUsername, SESSION_COOKIE } from "@/lib/auth/session";
|
||||
|
||||
export async function GET() {
|
||||
if (!isAuthEnabled()) {
|
||||
return NextResponse.json({ authEnabled: false, loggedIn: true });
|
||||
}
|
||||
|
||||
const token = (await cookies()).get(SESSION_COOKIE)?.value;
|
||||
const username = await getSessionUsername(token);
|
||||
|
||||
return NextResponse.json({
|
||||
authEnabled: true,
|
||||
loggedIn: !!username,
|
||||
username: username ?? undefined,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import HistoryPageClient from "@/components/history/history-page";
|
||||
|
||||
export default function HistoryPage() {
|
||||
return <HistoryPageClient />;
|
||||
}
|
||||
+6
-3
@@ -4,6 +4,7 @@ import React from "react";
|
||||
import Umami from "@/components/umami";
|
||||
import PwaProvider from "@/components/pwa/pwa-provider";
|
||||
import PwaDisplayMode from "@/components/pwa/pwa-display-mode";
|
||||
import { AuthProvider } from "@/components/auth/auth-provider";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -53,9 +54,11 @@ export default function RootLayout({
|
||||
defaultTheme="system"
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{children}
|
||||
<PwaDisplayMode />
|
||||
<PwaProvider />
|
||||
<AuthProvider>
|
||||
{children}
|
||||
<PwaDisplayMode />
|
||||
<PwaProvider />
|
||||
</AuthProvider>
|
||||
</ThemeProvider>
|
||||
<Umami />
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Suspense } from "react";
|
||||
import PageShell from "@/components/page-shell";
|
||||
import LoginForm from "@/components/auth/login-form";
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<PageShell width="narrow" className="py-12">
|
||||
<div className="mb-8 text-center">
|
||||
<h1 className="text-2xl font-bold tracking-wide">登录知命阁</h1>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
测算功能需登录后使用
|
||||
</p>
|
||||
</div>
|
||||
<Suspense fallback={<p className="text-center text-sm">加载中…</p>}>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
+2
-53
@@ -1,39 +1,6 @@
|
||||
import Link from "next/link";
|
||||
import PageShell from "@/components/page-shell";
|
||||
import { BookOpen, BrainCircuit, Compass, Sparkles } from "lucide-react";
|
||||
import { ZenCard } from "@/components/ui/zen-card";
|
||||
import { TaijiIcon } from "@/components/svg/taiji";
|
||||
|
||||
const MODULES = [
|
||||
{
|
||||
href: "/learn",
|
||||
title: "易经学习",
|
||||
description: "64 卦卡片择读,繁体精简与简体图文",
|
||||
icon: BookOpen,
|
||||
accent: "from-amber-500/10 to-transparent",
|
||||
},
|
||||
{
|
||||
href: "/liuyao",
|
||||
title: "六爻算卦",
|
||||
description: "三钱法起卦,卦辞 AI 智能解读",
|
||||
icon: Sparkles,
|
||||
accent: "from-stone-500/10 to-transparent",
|
||||
},
|
||||
{
|
||||
href: "/bazi",
|
||||
title: "生辰八字",
|
||||
description: "四柱排盘,十神大运,AI 命理解读",
|
||||
icon: BrainCircuit,
|
||||
accent: "from-zinc-500/10 to-transparent",
|
||||
},
|
||||
{
|
||||
href: "/combined",
|
||||
title: "综合测算",
|
||||
description: "天时地利人和,六爻可选",
|
||||
icon: Compass,
|
||||
accent: "from-neutral-500/10 to-transparent",
|
||||
},
|
||||
];
|
||||
import HomeModules from "@/components/home/home-modules";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@@ -45,25 +12,7 @@ export default function Home() {
|
||||
融合周易智慧与人工智能
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{MODULES.map(({ href, title, description, icon: Icon, accent }) => (
|
||||
<Link key={href} href={href} className="group block">
|
||||
<ZenCard
|
||||
className={`h-full bg-gradient-to-br ${accent} transition-all duration-300 group-hover:-translate-y-0.5 group-hover:shadow-md`}
|
||||
>
|
||||
<div className="mb-3 flex items-center gap-3">
|
||||
<span className="flex h-10 w-10 items-center justify-center rounded-full border border-border/60 bg-background/80">
|
||||
<Icon size={20} className="text-primary/80" />
|
||||
</span>
|
||||
<span className="text-lg font-medium tracking-wide">{title}</span>
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed text-muted-foreground">
|
||||
{description}
|
||||
</p>
|
||||
</ZenCard>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<HomeModules />
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user