重构大量文件
This commit is contained in:
@@ -1,31 +1,38 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { verifyPassword } from "@/lib/auth";
|
||||
import { createSessionToken, getSessionCookieName, getSessionTtlSeconds } from "@/lib/session";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const { password } = await req.json();
|
||||
|
||||
// Ensure settings exist, if not, maybe we should init or fail?
|
||||
// Ideally init should happen on app startup or manual trigger, but for simplicity:
|
||||
// If no settings exist, check against "admin" (fallback) but DO NOT create DB entry implicitely here for security,
|
||||
// unless we strictly define that "admin" is the default.
|
||||
// Let's assume DB must be populated.
|
||||
const { password, rememberMe, durationDays } = await req.json();
|
||||
|
||||
const settings = await prisma.globalSettings.findUnique({
|
||||
where: { id: "default" },
|
||||
});
|
||||
|
||||
const isValid = settings
|
||||
? await verifyPassword(password, settings.password)
|
||||
: password === "admin"; // Fallback only if DB empty
|
||||
if (!settings) {
|
||||
return NextResponse.json({ error: "Settings not initialized" }, { status: 503 });
|
||||
}
|
||||
|
||||
const isValid = await verifyPassword(password, settings.password);
|
||||
|
||||
if (isValid) {
|
||||
return NextResponse.json({ success: true });
|
||||
const days = rememberMe ? Number(durationDays) || 1 : 1;
|
||||
const token = await createSessionToken(days);
|
||||
const response = NextResponse.json({ success: true });
|
||||
response.cookies.set(getSessionCookieName(), token, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
path: "/",
|
||||
maxAge: getSessionTtlSeconds(days),
|
||||
});
|
||||
return response;
|
||||
} else {
|
||||
return NextResponse.json({ error: "Invalid password" }, { status: 401 });
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Login failed" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import { getSessionCookieName } from "@/lib/session";
|
||||
|
||||
export async function POST() {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.delete("auth");
|
||||
cookieStore.delete(getSessionCookieName());
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
@@ -12,7 +13,7 @@ export async function GET(
|
||||
});
|
||||
if (!page) return NextResponse.json({ error: 'Page not found' }, { status: 404 });
|
||||
return NextResponse.json({ ...page, tags: JSON.parse(page.tags || "[]") });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Error fetching page' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -25,7 +26,7 @@ export async function PUT(
|
||||
try {
|
||||
const body = await request.json();
|
||||
// Separate update logic for flexibility (e.g. only updating title)
|
||||
const updateData: any = {};
|
||||
const updateData: Prisma.PageUncheckedUpdateInput = {};
|
||||
if (body.title !== undefined) updateData.title = body.title;
|
||||
if (body.content !== undefined) updateData.content = body.content;
|
||||
if (body.parentId !== undefined) updateData.parentId = body.parentId;
|
||||
@@ -38,7 +39,7 @@ export async function PUT(
|
||||
data: updateData,
|
||||
});
|
||||
return NextResponse.json({ ...page, tags: JSON.parse(page.tags || "[]") });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Error updating page' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -53,7 +54,7 @@ export async function DELETE(
|
||||
where: { id },
|
||||
});
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Error deleting page' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export async function PUT(request: Request) {
|
||||
);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Error reordering pages' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function GET() {
|
||||
tags: JSON.parse(p.tags || "[]")
|
||||
}));
|
||||
return NextResponse.json(parsedPages);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Error fetching pages' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export async function POST(request: Request) {
|
||||
},
|
||||
});
|
||||
return NextResponse.json({ ...page, tags: JSON.parse(page.tags || "[]") });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Error creating page' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ export async function PUT(req: Request) {
|
||||
try {
|
||||
const { currentPassword, newPassword } = await req.json();
|
||||
|
||||
// Cast to any to bypass build error until server restart allows prisma generate to run
|
||||
const settings = await (prisma as any).globalSettings.findUnique({
|
||||
const settings = await prisma.globalSettings.findUnique({
|
||||
where: { id: "default" },
|
||||
});
|
||||
|
||||
@@ -22,13 +21,13 @@ export async function PUT(req: Request) {
|
||||
|
||||
const hashedPassword = await hashPassword(newPassword);
|
||||
|
||||
await (prisma as any).globalSettings.update({
|
||||
await prisma.globalSettings.update({
|
||||
where: { id: "default" },
|
||||
data: { password: hashedPassword },
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Failed to change password" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
+19
-21
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
@@ -8,7 +8,7 @@ export default function LoginPage() {
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
const [duration, setDuration] = useState("1"); // days
|
||||
const [duration, setDuration] = useState("1");
|
||||
const router = useRouter();
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
@@ -19,23 +19,21 @@ export default function LoginPage() {
|
||||
const res = await fetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ password }),
|
||||
body: JSON.stringify({
|
||||
password,
|
||||
rememberMe,
|
||||
durationDays: parseInt(duration, 10),
|
||||
}),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
let cookieString = "auth=true; path=/";
|
||||
if (rememberMe) {
|
||||
const seconds = parseInt(duration) * 24 * 60 * 60;
|
||||
cookieString += `; max-age=${seconds}`;
|
||||
}
|
||||
document.cookie = cookieString;
|
||||
router.push("/");
|
||||
} else {
|
||||
const data = await res.json();
|
||||
setError(data.error || "登录失败");
|
||||
setError(data.error || "Login failed");
|
||||
}
|
||||
} catch (err) {
|
||||
setError("发生错误,请重试");
|
||||
} catch {
|
||||
setError("Something went wrong. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,8 +44,8 @@ export default function LoginPage() {
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-primary rounded-2xl text-primary-foreground mb-4">
|
||||
<Sparkles size={32} />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">欢迎回来</h1>
|
||||
<p className="text-muted-foreground">请输入访问密码以进入您的个人空间</p>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Welcome back</h1>
|
||||
<p className="text-muted-foreground">Enter your access password to continue.</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleLogin} className="space-y-4">
|
||||
@@ -56,7 +54,7 @@ export default function LoginPage() {
|
||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" size={18} />
|
||||
<input
|
||||
type="password"
|
||||
placeholder="访问密码"
|
||||
placeholder="Access password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-3 bg-muted/50 border rounded-xl focus:ring-2 focus:ring-primary outline-none transition-all"
|
||||
@@ -74,7 +72,7 @@ export default function LoginPage() {
|
||||
onChange={(e) => setRememberMe(e.target.checked)}
|
||||
className="w-4 h-4 rounded border-input bg-background/50 text-primary focus:ring-primary/50"
|
||||
/>
|
||||
记住我
|
||||
Remember me
|
||||
</label>
|
||||
|
||||
{rememberMe && (
|
||||
@@ -83,9 +81,9 @@ export default function LoginPage() {
|
||||
onChange={(e) => setDuration(e.target.value)}
|
||||
className="bg-transparent border-none outline-none text-muted-foreground hover:text-foreground cursor-pointer text-xs"
|
||||
>
|
||||
<option value="1">1天</option>
|
||||
<option value="7">7天</option>
|
||||
<option value="30">30天</option>
|
||||
<option value="1">1 day</option>
|
||||
<option value="7">7 days</option>
|
||||
<option value="30">30 days</option>
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
@@ -94,12 +92,12 @@ export default function LoginPage() {
|
||||
type="submit"
|
||||
className="w-full py-3 bg-primary text-primary-foreground font-semibold rounded-xl hover:opacity-90 active:scale-[0.98] transition-all shadow-lg"
|
||||
>
|
||||
开启灵感
|
||||
Sign in
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="text-center text-xs text-muted-foreground">
|
||||
NoteAI • 您的私人第二大脑
|
||||
NoteAI - Your private second brain
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-9
@@ -3,7 +3,7 @@
|
||||
import { SidebarContent, ResizableSidebar } from "@/components/sidebar";
|
||||
import { Editor } from "@/components/editor";
|
||||
import { useEditorStore } from "@/lib/store";
|
||||
import { FileDown, Hash, X, Plus, ChevronLeft, ChevronRight, Sparkles, Lock, Unlock } from "lucide-react";
|
||||
import { Hash, X, Plus, ChevronLeft, ChevronRight, Sparkles, Lock, Unlock } from "lucide-react";
|
||||
import { useSettingsStore } from "@/lib/settings-store";
|
||||
import { exportPageAsMarkdown } from "@/lib/export";
|
||||
import { useState } from "react";
|
||||
@@ -46,14 +46,6 @@ export default function Home() {
|
||||
<div className="flex-1 overflow-y-auto scroll-smooth">
|
||||
<div className="max-w-7xl mx-auto px-4 md:px-16 py-6 min-h-screen content-start">
|
||||
|
||||
{/* Mobile Back Button */}
|
||||
<div className="md:hidden mb-4 flex items-center text-muted-foreground" onClick={() => updatePage(null as any, {} as any)}>
|
||||
{/* Note: updatePage isn't the right way to clear selection. We need setPageId(null).
|
||||
But store only exposes updatePage. Let's fix store usage or use a store action if available.
|
||||
Actually, looking at store.ts, we need `setActivePageId`.
|
||||
*/}
|
||||
</div>
|
||||
|
||||
<div className="group mb-8 relative">
|
||||
{/* Mobile Back Button Integration in Header */}
|
||||
<div className="md:hidden absolute -top-12 left-0 flex items-center gap-1 py-2 text-muted-foreground hover:text-foreground cursor-pointer"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useSettingsStore, defaultPrompts, fontOptions, timezoneOptions } from "@/lib/settings-store";
|
||||
import { useSettingsStore, fontOptions, timezoneOptions } from "@/lib/settings-store";
|
||||
import { Lock, Type, Save, Sparkles } from "lucide-react";
|
||||
import { ResizableSidebar } from "@/components/sidebar";
|
||||
import { PromptManagement } from "@/components/settings/prompt-management";
|
||||
@@ -16,7 +16,6 @@ export default function SettingsPage() {
|
||||
tableLineHeight, setTableLineHeight,
|
||||
timezone, setTimezone,
|
||||
aiConfig, setAIConfig,
|
||||
prompts, resetPrompts, updatePrompt, deletePrompt, addPrompt
|
||||
} = useSettingsStore();
|
||||
|
||||
// Security
|
||||
@@ -50,7 +49,7 @@ export default function SettingsPage() {
|
||||
const data = await res.json();
|
||||
setMsg({ type: 'error', text: data.error || "修改失败" });
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
setMsg({ type: 'error', text: "系统错误,请重试" });
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user