diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index a210abb..76e7875 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -99,7 +99,11 @@ export default function Page() { if (session && session.user) { console.log("[DingTalk] Session validated, redirecting to home..."); setIsSuccessful(true); - window.location.replace("/"); + // 使用 replace 而不是 href,避免在历史记录中留下登录页 + // 增加短暂延迟确保 cookie 完全同步 + setTimeout(() => { + window.location.replace("/"); + }, 200); } else { console.error("[DingTalk] Session not created after login"); setIsHandlingToken(false); @@ -124,7 +128,11 @@ export default function Page() { if (session && session.user) { console.log("[DingTalk] Session validated, redirecting..."); setIsSuccessful(true); - window.location.replace(result.url || "/"); + // 使用 replace 而不是 href,避免在历史记录中留下登录页 + // 增加短暂延迟确保 cookie 完全同步 + setTimeout(() => { + window.location.replace(result.url || "/"); + }, 200); } else { console.error("[DingTalk] Session not created"); setIsHandlingToken(false); @@ -176,8 +184,8 @@ export default function Page() { console.log("🔔 登录页面代码版本: 2024-12-10 v3.0"); console.log("=".repeat(50)); - // 获取网站设置 - fetch("/api/site-settings") + // 获取网站设置(添加时间戳防止缓存) + fetch(`/api/site-settings?t=${Date.now()}`) .then((res) => res.json()) .then((data) => { console.log("[Login] Site settings data:", data); diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx index 8ea6e23..672c37b 100644 --- a/app/(auth)/register/page.tsx +++ b/app/(auth)/register/page.tsx @@ -29,8 +29,8 @@ export default function Page() { }) .catch(console.error); - // 获取网站设置 - fetch("/api/site-settings") + // 获取网站设置(添加时间戳防止缓存) + fetch(`/api/site-settings?t=${Date.now()}`) .then((res) => res.json()) .then((data) => { if (data.siteName) setSiteName(data.siteName); diff --git a/app/(chat)/chat/[id]/page.tsx b/app/(chat)/chat/[id]/page.tsx index f0e4d72..718403a 100644 --- a/app/(chat)/chat/[id]/page.tsx +++ b/app/(chat)/chat/[id]/page.tsx @@ -27,11 +27,12 @@ async function ChatPage({ params }: { params: Promise<{ id: string }> }) { const session = await auth(); - if (chat.visibility === "private") { - if (!session?.user) { - return notFound(); - } + // 如果用户未登录,重定向到登录页面 + if (!session?.user) { + redirect("/login"); + } + if (chat.visibility === "private") { if (session.user.id !== chat.userId) { return notFound(); } diff --git a/app/(chat)/page.tsx b/app/(chat)/page.tsx index fa47a01..50b3d68 100644 --- a/app/(chat)/page.tsx +++ b/app/(chat)/page.tsx @@ -1,4 +1,5 @@ import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; import { Suspense } from "react"; import { Chat } from "@/components/chat"; import { DataStreamHandler } from "@/components/data-stream-handler"; @@ -18,6 +19,12 @@ async function NewChatPage() { const id = generateUUID(); const [session, cookieStore] = await Promise.all([auth(), cookies()]); + + // 如果用户未登录,重定向到登录页面 + if (!session?.user) { + redirect("/login"); + } + const modelIdFromCookie = cookieStore.get("chat-model"); // 如果没有保存的模型 ID,从数据库获取默认模型 diff --git a/app/api/site-settings/route.ts b/app/api/site-settings/route.ts index 0eb58db..5dac60b 100644 --- a/app/api/site-settings/route.ts +++ b/app/api/site-settings/route.ts @@ -32,10 +32,17 @@ export async function GET() { usageTimeMessage: "由壹点累了也是需要休息的", }; - return NextResponse.json({ + const response = NextResponse.json({ ...baseSettings, notification, }); + + // 禁用缓存,确保总是获取最新设置 + response.headers.set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); + response.headers.set("Pragma", "no-cache"); + response.headers.set("Expires", "0"); + + return response; } catch (error) { console.error("[Site Settings API] Get error:", error); return NextResponse.json( diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..61c482c --- /dev/null +++ b/middleware.ts @@ -0,0 +1,65 @@ +import { type NextRequest, NextResponse } from "next/server"; +import { getToken } from "next-auth/jwt"; + +export async function middleware(request: NextRequest) { + const { pathname } = request.nextUrl; + + // 允许访问 ping 端点 + if (pathname.startsWith("/ping")) { + return NextResponse.next(); + } + + // 允许访问认证相关 API + if (pathname.startsWith("/api/auth")) { + return NextResponse.next(); + } + + // 允许访问钉钉回调页面(public 文件夹中的文件) + if (pathname === "/dingtalk-callback.html") { + return NextResponse.next(); + } + + // 检测是否为 HTTPS(通过 AUTH_URL 或请求协议判断) + const isSecure = process.env.AUTH_URL?.startsWith("https://") || + request.nextUrl.protocol === "https:"; + + const token = await getToken({ + req: request, + secret: process.env.AUTH_SECRET, + secureCookie: isSecure, + }); + + const externalBase = + process.env.AUTH_URL ?? + `${request.nextUrl.protocol}//${request.headers.get("host") ?? request.nextUrl.host}`; + + // 未登录用户只能访问 /login 和 /register + if (!token && !["/login", "/register"].includes(pathname)) { + const loginUrl = new URL("/login", externalBase); + // 保存当前路径作为回调 URL + loginUrl.searchParams.set("callbackUrl", pathname); + return NextResponse.redirect(loginUrl); + } + + // 已登录用户访问 /login 或 /register 时重定向到首页 + if (token && ["/login", "/register"].includes(pathname)) { + return NextResponse.redirect(new URL("/", externalBase)); + } + + return NextResponse.next(); +} + +export const config = { + matcher: [ + /* + * 匹配所有请求路径,除了: + * - api/auth (NextAuth.js) + * - _next/static (静态文件) + * - _next/image (图片优化文件) + * - favicon.ico (favicon文件) + * - public 文件夹中的文件 + */ + "/((?!api/auth|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", + ], +}; + diff --git a/public/dingtalk-callback.html b/public/dingtalk-callback.html index d867b9a..5c0c524 100644 --- a/public/dingtalk-callback.html +++ b/public/dingtalk-callback.html @@ -168,20 +168,43 @@ statusText.textContent = '登录成功,正在跳转...'; console.log('[DingTalk Callback] Login successful, redirecting to home...'); - // 等待一小段时间确保 cookie 被设置 - await new Promise(resolve => setTimeout(resolve, 300)); + // 等待更长时间确保 cookie 被设置并同步 + await new Promise(resolve => setTimeout(resolve, 800)); - // 使用 window.top.location 确保跳转整个页面(即使在 iframe 中) - try { - if (window.top && window.top.location) { - window.top.location.href = '/'; - } else { - window.location.href = '/'; + // 验证 session 是否已创建 + const sessionRes = await fetch('/api/auth/session', { + credentials: 'include' + }); + const session = await sessionRes.json(); + console.log('[DingTalk Callback] Session after login:', session); + + if (session && session.user) { + console.log('[DingTalk Callback] Session validated, redirecting...'); + // 使用 window.top.location 确保跳转整个页面(即使在 iframe 中) + try { + if (window.top && window.top.location) { + window.top.location.replace('/'); + } else { + window.location.replace('/'); + } + } catch (e) { + // 如果跨域阻止,使用当前窗口跳转 + console.warn('[DingTalk Callback] Cross-origin blocked, using current window'); + window.location.replace('/'); + } + } else { + console.error('[DingTalk Callback] Session not created, retrying...'); + // 如果 session 还未创建,再等待一段时间后重试 + await new Promise(resolve => setTimeout(resolve, 500)); + const retrySessionRes = await fetch('/api/auth/session', { + credentials: 'include' + }); + const retrySession = await retrySessionRes.json(); + if (retrySession && retrySession.user) { + window.location.replace('/'); + } else { + throw new Error('登录失败:无法创建会话,请刷新页面重试'); } - } catch (e) { - // 如果跨域阻止,使用当前窗口跳转 - console.warn('[DingTalk Callback] Cross-origin blocked, using current window'); - window.location.href = '/'; } } else { const result = await loginRes.json(); diff --git a/图片标注功能-部署完成总结.md b/图片标注功能-部署完成总结.md index 881a8c8..694b2f8 100644 --- a/图片标注功能-部署完成总结.md +++ b/图片标注功能-部署完成总结.md @@ -524,3 +524,4 @@ curl http://localhost:8002/health +