diff --git a/frontend/.env.local b/frontend/.env.local index e720d351..ced57054 100644 --- a/frontend/.env.local +++ b/frontend/.env.local @@ -15,6 +15,11 @@ NEXT_PUBLIC_POLYWEATHER_API_BASE_URL= NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= +# 必填:生产环境站点 URL(OAuth 回调强制使用此域名) +# 设置后,所有登录回调将始终跳转到此域名,而非当前浏览器地址。 +# 生产环境必须设为 https://polyweather-pro.vercel.app +NEXT_PUBLIC_SITE_URL= + # 常用:前端鉴权开关 # true: 启用 Supabase 登录 # false: 关闭登录能力,访客模式 diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 02455197..40bd0dec 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,4 +1,5 @@ import type { Metadata } from "next"; +import { redirect } from "next/navigation"; import { DashboardEntry } from "@/components/dashboard/DashboardEntry"; export const metadata: Metadata = { @@ -7,6 +8,26 @@ export const metadata: Metadata = { "PolyWeather dashboard with METAR, MGM, DEB fusion forecast, multi-model comparison, and AI weather decision cards.", }; -export default function HomePage() { +export default async function HomePage({ + searchParams, +}: { + searchParams: Promise>; +}) { + const params = await searchParams; + const code = params.code; + if (typeof code === "string" && code.trim()) { + const usp = new URLSearchParams(); + for (const [key, value] of Object.entries(params)) { + if (value != null) { + if (Array.isArray(value)) { + for (const v of value) usp.append(key, v); + } else { + usp.set(key, value); + } + } + } + const qs = usp.toString(); + redirect(`/auth/callback${qs ? `?${qs}` : ""}`); + } return ; } diff --git a/frontend/components/auth/LoginClient.tsx b/frontend/components/auth/LoginClient.tsx index 1e97d26c..bcafcaf5 100644 --- a/frontend/components/auth/LoginClient.tsx +++ b/frontend/components/auth/LoginClient.tsx @@ -37,6 +37,9 @@ export function LoginClient({ nextPath }: LoginClientProps) { const [resetSent, setResetSent] = useState(false); const supabaseReady = hasSupabasePublicEnv(); + const siteOrigin = + process.env.NEXT_PUBLIC_SITE_URL?.trim() || + (typeof window !== "undefined" ? window.location.origin : ""); const isEn = locale === "en-US"; const copy = { backHome: isEn ? "Back to Home" : "返回首页", @@ -136,7 +139,7 @@ export function LoginClient({ nextPath }: LoginClientProps) { setLoading(true); try { const supabase = getSupabaseBrowserClient(); - const redirectTo = `${window.location.origin}/auth/callback?next=${encodeURIComponent( + const redirectTo = `${siteOrigin}/auth/callback?next=${encodeURIComponent( nextPath, )}`; const { error } = await supabase.auth.signInWithOAuth({ @@ -182,7 +185,7 @@ export function LoginClient({ nextPath }: LoginClientProps) { return; } - const emailRedirectTo = `${window.location.origin}/auth/callback?next=${encodeURIComponent( + const emailRedirectTo = `${siteOrigin}/auth/callback?next=${encodeURIComponent( nextPath, )}`; const { data, error } = await supabase.auth.signUp({