274 lines
8.7 KiB
TypeScript
274 lines
8.7 KiB
TypeScript
import { Brand } from "@/components/ui/brand"
|
||
import { Input } from "@/components/ui/input"
|
||
import { Label } from "@/components/ui/label"
|
||
import { SubmitButton } from "@/components/ui/submit-button"
|
||
//import { createClient } from "@/lib/supabase/server"
|
||
import { Database } from "@/supabase/types"
|
||
//import { createServerClient } from "@supabase/ssr"
|
||
import { getSupabaseServerClient } from "@/lib/supabase/server"
|
||
import { get } from "@vercel/edge-config"
|
||
import { Metadata } from "next"
|
||
import { cookies, headers } from "next/headers"
|
||
import { redirect } from "next/navigation"
|
||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||
import { PostgrestError } from '@supabase/supabase-js';
|
||
|
||
import initTranslations from "@/lib/i18n";
|
||
|
||
export const metadata: Metadata = {
|
||
title: "Login"
|
||
}
|
||
|
||
export default async function Login({
|
||
searchParams,
|
||
params: { locale },
|
||
}: {
|
||
searchParams: { message: string; email?: string };
|
||
params: { locale: string };
|
||
}) {
|
||
const cookieStore = cookies()
|
||
|
||
const localeString = locale;
|
||
const { t, resources } = await initTranslations(localeString, ['translation']);
|
||
|
||
// const supabase = createServerClient<Database>(
|
||
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||
// process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||
// {
|
||
// cookies: {
|
||
// get(name: string) {
|
||
// return cookieStore.get(name)?.value
|
||
// }
|
||
// }
|
||
// }
|
||
// )
|
||
|
||
const supabase = getSupabaseServerClient()
|
||
|
||
const session = (await supabase.auth.getSession()).data.session
|
||
|
||
console.log("[login page]Login session:", session)
|
||
|
||
if (session) {
|
||
const { data: homeWorkspace, error } = await supabase
|
||
.from("workspaces")
|
||
.select("*")
|
||
.eq("user_id", session.user.id)
|
||
.eq("is_home", true)
|
||
.maybeSingle();
|
||
|
||
if (!homeWorkspace) {
|
||
await supabase.auth.signOut();
|
||
return redirect(`/${localeString}/login?message=sessionExpired`);
|
||
}
|
||
|
||
return redirect(`/${localeString}/${homeWorkspace.id}/chat`);
|
||
|
||
}
|
||
|
||
const signIn = async (formData: FormData) => {
|
||
"use server"
|
||
|
||
const email = formData.get("email") as string
|
||
const password = formData.get("password") as string
|
||
//const cookieStore = cookies()
|
||
//const supabase = createClient(cookieStore)
|
||
const supabase = getSupabaseServerClient()
|
||
|
||
const { data, error } = await supabase.auth.signInWithPassword({
|
||
email,
|
||
password
|
||
})
|
||
|
||
if (error) {
|
||
// console.log(`[login page]==================> ${localeString}/login?message=${error.message}`);
|
||
// return redirect(`/${localeString}/login?message=${error.message}`)
|
||
return redirect(`/${localeString}/login?message=invalidCredentials`)
|
||
}
|
||
|
||
const { data: homeWorkspace, error: homeWorkspaceError } = await supabase
|
||
.from("workspaces")
|
||
.select("*")
|
||
.eq("user_id", data.user.id)
|
||
.eq("is_home", true)
|
||
.limit(1) // Limit to 1 row
|
||
//.single()
|
||
|
||
if (!homeWorkspace || homeWorkspace.length === 0) {
|
||
// 找不到home workspace,执行登出清理cookie,重定向到登录页或引导页
|
||
await supabase.auth.signOut();
|
||
// 这里可改为跳转到登录页,带提示参数
|
||
return redirect(`/${localeString}/login?message=sessionExpired`);
|
||
}
|
||
// If more than 1 row is returned, we handle that case
|
||
if (homeWorkspace.length > 1) {
|
||
// Handle the case where multiple rows are returned
|
||
console.warn("Multiple home workspaces found for this user.");
|
||
}
|
||
const workspace = homeWorkspace[0]; // Use the first (and only) row
|
||
|
||
return redirect(`/${localeString}/${workspace.id}/chat`)
|
||
}
|
||
|
||
const getEnvVarOrEdgeConfigValue = async (name: string) => {
|
||
"use server"
|
||
if (process.env.EDGE_CONFIG) {
|
||
return await get<string>(name)
|
||
}
|
||
|
||
return process.env[name]
|
||
}
|
||
|
||
const signUp = async (formData: FormData) => {
|
||
"use server"
|
||
|
||
|
||
const email = formData.get("email") as string
|
||
const password = formData.get("password") as string
|
||
|
||
const emailDomainWhitelistPatternsString = await getEnvVarOrEdgeConfigValue(
|
||
"EMAIL_DOMAIN_WHITELIST"
|
||
)
|
||
const emailDomainWhitelist = emailDomainWhitelistPatternsString?.trim()
|
||
? emailDomainWhitelistPatternsString?.split(",")
|
||
: []
|
||
const emailWhitelistPatternsString =
|
||
await getEnvVarOrEdgeConfigValue("EMAIL_WHITELIST")
|
||
const emailWhitelist = emailWhitelistPatternsString?.trim()
|
||
? emailWhitelistPatternsString?.split(",")
|
||
: []
|
||
|
||
// If there are whitelist patterns, check if the email is allowed to sign up
|
||
if (emailDomainWhitelist.length > 0 || emailWhitelist.length > 0) {
|
||
const domainMatch = emailDomainWhitelist?.includes(email.split("@")[1])
|
||
const emailMatch = emailWhitelist?.includes(email)
|
||
if (!domainMatch && !emailMatch) {
|
||
return redirect(
|
||
// `/${localeString}/login?message=Email ${email} is not allowed to sign up.`
|
||
`/${localeString}/login?message=signupNotAllowed&email=${encodeURIComponent(email)}`
|
||
)
|
||
}
|
||
}
|
||
|
||
//const cookieStore = cookies()
|
||
//const supabase = createClient(cookieStore)
|
||
const supabase = getSupabaseServerClient()
|
||
const { error } = await supabase.auth.signUp({
|
||
email,
|
||
password,
|
||
options: {
|
||
// USE IF YOU WANT TO SEND EMAIL VERIFICATION, ALSO CHANGE TOML FILE
|
||
// emailRedirectTo: `${origin}/auth/callback`
|
||
}
|
||
})
|
||
|
||
if (error) {
|
||
console.error(error)
|
||
return redirect(`/${localeString}/login?message=${error.message}`)
|
||
}
|
||
|
||
return redirect(`/${localeString}/setup`)
|
||
|
||
// USE IF YOU WANT TO SEND EMAIL VERIFICATION, ALSO CHANGE TOML FILE
|
||
// return redirect("/login?message=Check email to continue sign in process")
|
||
}
|
||
|
||
const handleResetPassword = async (formData: FormData) => {
|
||
"use server"
|
||
|
||
const origin = headers().get("origin")
|
||
const email = formData.get("email") as string
|
||
//const cookieStore = cookies()
|
||
//const supabase = createClient(cookieStore)
|
||
const supabase = getSupabaseServerClient()
|
||
|
||
const { error } = await supabase.auth.resetPasswordForEmail(email, {
|
||
redirectTo: `${origin}/auth/callback?next=${localeString}/login/password`
|
||
})
|
||
|
||
if (error) {
|
||
return redirect(`/${localeString}/login?message=${error.message}`)
|
||
}
|
||
|
||
// const emailtoResetMessage = String(t("login.checkEmailToReset")) // ← 这是字符串
|
||
// return redirect(`/${localeString}/login?message=${emailtoResetMessage}`)
|
||
return redirect(`/${localeString}/login?message=Check email to reset password`)
|
||
}
|
||
|
||
|
||
let translatedMessage: string | null = null;
|
||
|
||
if (searchParams.message === "signupNotAllowed") {
|
||
translatedMessage = t("login.signupNotAllowed", { email: searchParams.email });
|
||
} else if (searchParams.message === "signupNotAllowed") {
|
||
|
||
} else if (searchParams.message) {
|
||
translatedMessage = t(`login.${searchParams.message}`);
|
||
}
|
||
|
||
return (
|
||
<div className="flex w-full flex-1 flex-col justify-center gap-2 px-8 sm:max-w-md">
|
||
<form
|
||
className="animate-in text-foreground flex w-full flex-1 flex-col justify-center gap-2"
|
||
action={signIn}
|
||
>
|
||
<Brand />
|
||
|
||
<Label className="text-md mt-4" htmlFor="email">
|
||
{t("login.email")}
|
||
</Label>
|
||
<Input
|
||
className="mb-3 rounded-md border bg-inherit px-4 py-2"
|
||
name="email"
|
||
placeholder={t("login.emailPlaceholder")}
|
||
required
|
||
/>
|
||
|
||
<Label className="text-md" htmlFor="password">
|
||
{t("login.password")}
|
||
</Label>
|
||
<Input
|
||
className="mb-6 rounded-md border bg-inherit px-4 py-2"
|
||
type="password"
|
||
name="password"
|
||
placeholder={t("login.passwordPlaceholder")}
|
||
/>
|
||
|
||
<SubmitButton className="mb-2 rounded-md bg-blue-700 px-4 py-2 text-white">
|
||
{t("login.loginButton")}
|
||
</SubmitButton>
|
||
|
||
<SubmitButton
|
||
formAction={signUp}
|
||
className="border-foreground/20 mb-2 rounded-md border px-4 py-2"
|
||
>
|
||
{t("login.signUpButton")}
|
||
</SubmitButton>
|
||
|
||
<div className="text-muted-foreground mt-1 flex justify-center text-sm">
|
||
<span className="mr-1">{t("login.forgotPassword")}</span>
|
||
<button
|
||
formAction={handleResetPassword}
|
||
className="text-primary ml-1 underline hover:opacity-80"
|
||
>
|
||
{t("login.reset")}
|
||
</button>
|
||
</div>
|
||
|
||
{/* {searchParams?.message && (
|
||
<p className="bg-foreground/10 text-foreground mt-4 p-4 text-center">
|
||
{searchParams.message}
|
||
</p>
|
||
)} */}
|
||
|
||
{translatedMessage && (
|
||
<p className="bg-foreground/10 text-foreground mt-4 p-4 text-center">
|
||
{translatedMessage}
|
||
</p>
|
||
)}
|
||
|
||
</form>
|
||
</div>
|
||
)
|
||
}
|