diff --git a/src/app/auth/auth-screen.tsx b/src/app/auth/auth-screen.tsx
index a16ca88c..2bdd0f2a 100644
--- a/src/app/auth/auth-screen.tsx
+++ b/src/app/auth/auth-screen.tsx
@@ -17,10 +17,15 @@ import { Logger } from "@/utils";
const log = new Logger("AppAuthAuthScreen");
-export function AuthScreen() {
+export interface AuthScreenProps {
+ redirectTo?: string | null;
+}
+
+export function AuthScreen({ redirectTo = null }: AuthScreenProps) {
const state = useAuthState();
const authDispatch = useAuthDispatch();
const router = useRouter();
+ const safeRedirectTo = getSafeRedirectPath(redirectTo) ?? ROUTES.chat;
// 邮箱登录成功 → 跳转;User hydrate 由根级 监听 loginStatus 变化处理。
// 用 pendingRedirect flag(不是 useRef transition detection)——
@@ -34,13 +39,19 @@ export function AuthScreen() {
});
if (state.pendingRedirect && state.loginStatus !== "notLoggedIn") {
- log.debug(
- "[auth-screen] useEffect → router.replace(/chat) [via pendingRedirect flag]",
- );
+ log.debug("[auth-screen] useEffect → router.replace [via pendingRedirect flag]", {
+ redirectTo: safeRedirectTo,
+ });
authDispatch({ type: "AuthClearPendingRedirect" });
- router.replace(ROUTES.chat);
+ router.replace(safeRedirectTo);
}
- }, [state.loginStatus, state.pendingRedirect, authDispatch, router]);
+ }, [
+ state.loginStatus,
+ state.pendingRedirect,
+ authDispatch,
+ router,
+ safeRedirectTo,
+ ]);
return (
@@ -53,3 +64,9 @@ export function AuthScreen() {
);
}
+
+function getSafeRedirectPath(redirectTo: string | null | undefined): string | null {
+ if (!redirectTo) return null;
+ if (!redirectTo.startsWith("/") || redirectTo.startsWith("//")) return null;
+ return redirectTo;
+}
diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx
index a81af6ec..d7f86b80 100644
--- a/src/app/auth/page.tsx
+++ b/src/app/auth/page.tsx
@@ -1,7 +1,16 @@
-"use client";
-
import { AuthScreen } from "@/app/auth/auth-screen";
-export default function AuthPage() {
- return ;
+interface AuthPageProps {
+ searchParams: Promise<{
+ redirect?: string | string[];
+ }>;
+}
+
+export default async function AuthPage({ searchParams }: AuthPageProps) {
+ const params = await searchParams;
+ const redirectTo = Array.isArray(params.redirect)
+ ? params.redirect[0]
+ : params.redirect;
+
+ return ;
}
diff --git a/src/app/chat/chat-screen.tsx b/src/app/chat/chat-screen.tsx
index 03017e1d..610d798f 100644
--- a/src/app/chat/chat-screen.tsx
+++ b/src/app/chat/chat-screen.tsx
@@ -133,7 +133,12 @@ export function ChatScreen() {
}
function handleUnlockImagePaywall(): void {
- router.push(`${ROUTES.subscription}?type=vip`);
+ const subscriptionUrl = ROUTE_BUILDERS.subscription("vip");
+ if (isGuest) {
+ router.push(ROUTE_BUILDERS.authWithRedirect(subscriptionUrl));
+ return;
+ }
+ router.push(subscriptionUrl);
}
function handleMessageLimitUnlock(): void {
@@ -141,7 +146,7 @@ export function ChatScreen() {
router.push(ROUTES.auth);
return;
}
- router.push(`${ROUTES.subscription}?type=vip`);
+ router.push(ROUTE_BUILDERS.subscription("vip"));
}
return (
diff --git a/src/router/routes.ts b/src/router/routes.ts
index cf14ac3e..d5e631b7 100644
--- a/src/router/routes.ts
+++ b/src/router/routes.ts
@@ -30,6 +30,10 @@ export type StaticRoute = (typeof ROUTES)[keyof typeof ROUTES];
export const ROUTE_BUILDERS = {
chatDeviceId: (deviceId: string): `/chat/deviceid/${string}` =>
`/chat/deviceid/${encodeURIComponent(deviceId)}` as const,
+ subscription: (type: "vip" | "voice"): `/subscription?type=${string}` =>
+ `${ROUTES.subscription}?type=${encodeURIComponent(type)}` as const,
+ authWithRedirect: (redirectTo: string): `/auth?redirect=${string}` =>
+ `${ROUTES.auth}?redirect=${encodeURIComponent(redirectTo)}` as const,
} as const;
/** 仅未登录态可见的路由(命中后已登录用户应跳走) */