diff --git a/app/(chat)/canvas/page.tsx b/app/(chat)/canvas/page.tsx index 511f5d2b9..539258ba8 100644 --- a/app/(chat)/canvas/page.tsx +++ b/app/(chat)/canvas/page.tsx @@ -1,66 +1,71 @@ -"use client"; +import { randomUUID } from "node:crypto"; +import { redirect } from "next/navigation"; +import { auth } from "@/app/(auth)/auth"; +import { db } from "@/lib/db"; +import { canvasProject } from "@/lib/db/schema"; +import { and, desc, eq } from "drizzle-orm"; -import { useEffect } from "react"; -import { useRouter, useSearchParams } from "next/navigation"; -import { Loader2 } from "lucide-react"; +type CanvasRedirectPageProps = { + searchParams?: Promise>; +}; -// 默认进入最近画布,避免误触直接新建 -export default function CanvasRedirectPage() { - const router = useRouter(); - const searchParams = useSearchParams(); - - useEffect(() => { - const shouldCreateNew = searchParams.get("new") === "1"; - const passthroughParams = new URLSearchParams(searchParams.toString()); - passthroughParams.delete("new"); - const passthroughQuery = passthroughParams.toString(); - const buildCanvasUrl = (canvasId: string) => - `/canvas/${canvasId}${passthroughQuery ? `?${passthroughQuery}` : ""}`; - - const openCanvas = async () => { - if (shouldCreateNew) { - const newId = crypto.randomUUID(); - router.replace(buildCanvasUrl(newId)); - return; - } - - try { - const response = await fetch("/api/canvas?limit=1&page=1", { - cache: "no-store", - }); - if (response.ok) { - const result = await response.json(); - const items = result?.data || []; - if (Array.isArray(items) && items.length > 0) { - const sorted = [...items].sort((a, b) => { - const aTime = new Date(a.updatedAt || a.createdAt || 0).getTime(); - const bTime = new Date(b.updatedAt || b.createdAt || 0).getTime(); - return bTime - aTime; - }); - const latest = sorted[0]; - if (latest?.id) { - router.replace(buildCanvasUrl(latest.id)); - return; - } - } - } - } catch (_error) { - // ignore and fallback - } - - const newId = crypto.randomUUID(); - router.replace(buildCanvasUrl(newId)); - }; - - void openCanvas(); - }, [router, searchParams]); - - return ( -
-
- - 正在打开画布... -
-
- ); +function getSearchParam( + params: Record, + key: string +) { + const value = params[key]; + return Array.isArray(value) ? value[0] : value; +} + +function buildCanvasUrl( + canvasId: string, + params: Record +) { + const query = new URLSearchParams(); + for (const [key, value] of Object.entries(params)) { + if (key === "new" || value === undefined) { + continue; + } + if (Array.isArray(value)) { + value.forEach((item) => query.append(key, item)); + continue; + } + query.set(key, value); + } + const queryString = query.toString(); + return `/canvas/${canvasId}${queryString ? `?${queryString}` : ""}`; +} + +async function getLatestCanvasId(userId: string) { + const [latestProject] = await db + .select({ id: canvasProject.id }) + .from(canvasProject) + .where( + and( + eq(canvasProject.userId, userId), + eq(canvasProject.workspaceType, "canvas") + ) + ) + .orderBy(desc(canvasProject.updatedAt)) + .limit(1); + + return latestProject?.id || null; +} + +export default async function CanvasRedirectPage({ + searchParams, +}: CanvasRedirectPageProps) { + const params = (await searchParams) || {}; + const shouldCreateNew = getSearchParam(params, "new") === "1"; + + if (shouldCreateNew) { + redirect(buildCanvasUrl(randomUUID(), params)); + } + + const session = await auth(); + const latestCanvasId = session?.user?.id + ? await getLatestCanvasId(session.user.id).catch(() => null) + : null; + + redirect(buildCanvasUrl(latestCanvasId || randomUUID(), params)); } diff --git a/app/(chat)/layout.tsx b/app/(chat)/layout.tsx index 5bfe79233..32da4cea5 100644 --- a/app/(chat)/layout.tsx +++ b/app/(chat)/layout.tsx @@ -1,5 +1,4 @@ import { cookies } from "next/headers"; -import Script from "next/script"; import { Suspense } from "react"; import { ChatShell } from "@/components/chat-shell"; import { DataStreamProvider } from "@/components/data-stream-provider"; @@ -13,17 +12,11 @@ import { export default function Layout({ children }: { children: React.ReactNode }) { return ( - <> -