291 lines
9.7 KiB
TypeScript
291 lines
9.7 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}`
|
|
|
|
|
|
|
|
|
|
// // 提取当前路径中的 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 页面
|
|
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>
|
|
)
|
|
}
|