chatdesk-ui/chatdesk-ui/app/[locale]/login/page.tsx

274 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
)
}