chatbot-ui/app/[locale]/setup/page.tsx

293 lines
9.8 KiB
TypeScript

'use client'
import { ChatbotUIContext } from "@/context/context"
import { getProfileByUserId, updateProfile } from "@/db/profile"
import {
getHomeWorkspaceByUserId,
getWorkspacesByUserId
} from "@/db/workspaces"
import {
fetchHostedModels,
fetchOpenRouterModels
} from "@/lib/models/fetch-models"
import { supabase } from "@/lib/supabase/browser-client"
import { TablesUpdate } from "@/supabase/types"
import { useRouter } from "next/navigation"
import { useContext, useEffect, useState } from "react"
import { APIStep } from "../../../components/setup/api-step"
import { FinishStep } from "../../../components/setup/finish-step"
import { ProfileStep } from "../../../components/setup/profile-step"
import {
SETUP_STEP_COUNT,
StepContainer
} from "../../../components/setup/step-container"
import { useTranslation } from 'react-i18next'
import { usePathname } from "next/navigation"
import i18nConfig from "@/i18nConfig"
export default function SetupPage() {
const {
profile,
setProfile,
setWorkspaces,
setSelectedWorkspace,
setEnvKeyMap,
setAvailableHostedModels,
setAvailableOpenRouterModels
} = useContext(ChatbotUIContext)
const router = useRouter()
const pathname = usePathname() // 获取当前路径
const pathSegments = pathname.split("/").filter(Boolean)
const locales = i18nConfig.locales
const defaultLocale = i18nConfig.defaultLocale
let locale: (typeof locales)[number] = defaultLocale
const segment = pathSegments[0] as (typeof locales)[number]
if (locales.includes(segment)) {
locale = segment
}
//const homePath = locale === defaultLocale ? "/" : `/${locale}`
const homePath = locale === defaultLocale ? "" : `/${locale}`
// // 提取当前路径中的 locale 部分
// const locale = pathname.split("/")[1] || "en" // 获取路径中的 locale 部分,如果没有则默认为 "en"
const { t } = useTranslation()
const [loading, setLoading] = useState(true)
const [currentStep, setCurrentStep] = useState(1)
// Profile Step
const [displayName, setDisplayName] = useState("")
const [username, setUsername] = useState(profile?.username || "")
const [usernameAvailable, setUsernameAvailable] = useState(true)
// API Step
const [useAzureOpenai, setUseAzureOpenai] = useState(false)
const [openaiAPIKey, setOpenaiAPIKey] = useState("")
const [openaiOrgID, setOpenaiOrgID] = useState("")
const [azureOpenaiAPIKey, setAzureOpenaiAPIKey] = useState("")
const [azureOpenaiEndpoint, setAzureOpenaiEndpoint] = useState("")
const [azureOpenai35TurboID, setAzureOpenai35TurboID] = useState("")
const [azureOpenai45TurboID, setAzureOpenai45TurboID] = useState("")
const [azureOpenai45VisionID, setAzureOpenai45VisionID] = useState("")
const [azureOpenaiEmbeddingsID, setAzureOpenaiEmbeddingsID] = useState("")
const [anthropicAPIKey, setAnthropicAPIKey] = useState("")
const [googleGeminiAPIKey, setGoogleGeminiAPIKey] = useState("")
const [mistralAPIKey, setMistralAPIKey] = useState("")
const [groqAPIKey, setGroqAPIKey] = useState("")
const [perplexityAPIKey, setPerplexityAPIKey] = useState("")
const [openrouterAPIKey, setOpenrouterAPIKey] = useState("")
useEffect(() => {
;(async () => {
const session = (await supabase.auth.getSession()).data.session
if (!session) {
// 强制跳转到带有 locale 的 login 页面
console.log("...........[setup/page.tsx]")
return router.push(`${homePath}/login`)
// return router.push(`/${locale}/login`)
} else {
const user = session.user
const profile = await getProfileByUserId(user.id)
setProfile(profile)
setUsername(profile.username)
if (!profile.has_onboarded) {
setLoading(false)
} else {
const data = await fetchHostedModels(profile)
if (!data) return
setEnvKeyMap(data.envKeyMap)
setAvailableHostedModels(data.hostedModels)
if (profile["openrouter_api_key"] || data.envKeyMap["openrouter"]) {
const openRouterModels = await fetchOpenRouterModels()
if (!openRouterModels) return
setAvailableOpenRouterModels(openRouterModels)
}
const homeWorkspaceId = await getHomeWorkspaceByUserId(
session.user.id
)
return router.push(`${homePath}/${homeWorkspaceId}/chat`)
// return router.push(`/${locale}/${homeWorkspaceId}/chat`)
}
}
})()
}, [])
const handleShouldProceed = (proceed: boolean) => {
if (proceed) {
if (currentStep === SETUP_STEP_COUNT) {
handleSaveSetupSetting()
} else {
setCurrentStep(currentStep + 1)
}
} else {
setCurrentStep(currentStep - 1)
}
}
const handleSaveSetupSetting = async () => {
const session = (await supabase.auth.getSession()).data.session
if (!session) {
// return router.push(`/${locale}/login`)
return (`${homePath}/login`)
}
const user = session.user
const profile = await getProfileByUserId(user.id)
const updateProfilePayload: TablesUpdate<"profiles"> = {
...profile,
has_onboarded: true,
display_name: displayName,
username,
openai_api_key: openaiAPIKey,
openai_organization_id: openaiOrgID,
anthropic_api_key: anthropicAPIKey,
google_gemini_api_key: googleGeminiAPIKey,
mistral_api_key: mistralAPIKey,
groq_api_key: groqAPIKey,
perplexity_api_key: perplexityAPIKey,
openrouter_api_key: openrouterAPIKey,
use_azure_openai: useAzureOpenai,
azure_openai_api_key: azureOpenaiAPIKey,
azure_openai_endpoint: azureOpenaiEndpoint,
azure_openai_35_turbo_id: azureOpenai35TurboID,
azure_openai_45_turbo_id: azureOpenai45TurboID,
azure_openai_45_vision_id: azureOpenai45VisionID,
azure_openai_embeddings_id: azureOpenaiEmbeddingsID
}
const updatedProfile = await updateProfile(profile.id, updateProfilePayload)
setProfile(updatedProfile)
const workspaces = await getWorkspacesByUserId(profile.user_id)
const homeWorkspace = workspaces.find(w => w.is_home)
// There will always be a home workspace
setSelectedWorkspace(homeWorkspace!)
setWorkspaces(workspaces)
return router.push(`${homePath}/${homeWorkspace?.id}/chat`)
// return router.push(`/${locale}/${homeWorkspace?.id}/chat`)
}
const renderStep = (stepNum: number) => {
switch (stepNum) {
// Profile Step
case 1:
return (
<StepContainer
stepDescription={t("setup.LetsCreateYourProfile")}
stepNum={currentStep}
stepTitle={t("setup.WelcomeToChatbotUI")}
onShouldProceed={handleShouldProceed}
showNextButton={!!(username && usernameAvailable)}
showBackButton={false}
>
<ProfileStep
username={username}
usernameAvailable={usernameAvailable}
displayName={displayName}
onUsernameAvailableChange={setUsernameAvailable}
onUsernameChange={setUsername}
onDisplayNameChange={setDisplayName}
/>
</StepContainer>
)
// API Step
case 2:
return (
<StepContainer
stepDescription={t("setup.EnterAPIKeysForEachServiceYoudLikeToUse")}
stepNum={currentStep}
stepTitle={t("setup.SetAPIKeysOptional")}
onShouldProceed={handleShouldProceed}
showNextButton={true}
showBackButton={true}
>
<APIStep
openaiAPIKey={openaiAPIKey}
openaiOrgID={openaiOrgID}
azureOpenaiAPIKey={azureOpenaiAPIKey}
azureOpenaiEndpoint={azureOpenaiEndpoint}
azureOpenai35TurboID={azureOpenai35TurboID}
azureOpenai45TurboID={azureOpenai45TurboID}
azureOpenai45VisionID={azureOpenai45VisionID}
azureOpenaiEmbeddingsID={azureOpenaiEmbeddingsID}
anthropicAPIKey={anthropicAPIKey}
googleGeminiAPIKey={googleGeminiAPIKey}
mistralAPIKey={mistralAPIKey}
groqAPIKey={groqAPIKey}
perplexityAPIKey={perplexityAPIKey}
useAzureOpenai={useAzureOpenai}
onOpenaiAPIKeyChange={setOpenaiAPIKey}
onOpenaiOrgIDChange={setOpenaiOrgID}
onAzureOpenaiAPIKeyChange={setAzureOpenaiAPIKey}
onAzureOpenaiEndpointChange={setAzureOpenaiEndpoint}
onAzureOpenai35TurboIDChange={setAzureOpenai35TurboID}
onAzureOpenai45TurboIDChange={setAzureOpenai45TurboID}
onAzureOpenai45VisionIDChange={setAzureOpenai45VisionID}
onAzureOpenaiEmbeddingsIDChange={setAzureOpenaiEmbeddingsID}
onAnthropicAPIKeyChange={setAnthropicAPIKey}
onGoogleGeminiAPIKeyChange={setGoogleGeminiAPIKey}
onMistralAPIKeyChange={setMistralAPIKey}
onGroqAPIKeyChange={setGroqAPIKey}
onPerplexityAPIKeyChange={setPerplexityAPIKey}
onUseAzureOpenaiChange={setUseAzureOpenai}
openrouterAPIKey={openrouterAPIKey}
onOpenrouterAPIKeyChange={setOpenrouterAPIKey}
/>
</StepContainer>
)
// Finish Step
case 3:
return (
<StepContainer
stepDescription={t("setup.YouAreAllSetUp")}
stepNum={currentStep}
stepTitle={t("setup.SetupComplete")}
onShouldProceed={handleShouldProceed}
showNextButton={true}
showBackButton={true}
>
<FinishStep displayName={displayName} />
</StepContainer>
)
default:
return null
}
}
if (loading) {
return null
}
return (
<div className="flex h-full items-center justify-center">
{renderStep(currentStep)}
</div>
)
}