"use client"; import { ProxyResourceTargetsForm, type LocalTarget } from "@app/app/[orgId]/settings/resources/public/ProxyResourceTargetsForm"; import { SettingsContainer, SettingsFormCell, SettingsFormGrid, SettingsSection, SettingsSectionBody, SettingsSectionDescription, SettingsSectionForm, SettingsSectionHeader, SettingsSectionTitle, SettingsSubsectionDescription, SettingsSubsectionHeader, SettingsSubsectionTitle } from "@app/components/Settings"; import HeaderTitle from "@app/components/SettingsSectionTitle"; import { AiProviderTypeSelect } from "@app/components/AiProviderTypeSelect"; import { StrategySelect } from "@app/components/StrategySelect"; import { SwitchInput } from "@app/components/SwitchInput"; import { Button } from "@app/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@app/components/ui/form"; import { Input } from "@app/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@app/components/ui/select"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { aiProviderCreateFormSchema, emptyUpstreamForType, showsUpstreamUrlField, toAiProviderCreatePayload, upstreamUrlRequired, type AiProviderFormValues } from "@app/lib/aiProviderFormSchema"; import { zodResolver } from "@hookform/resolvers/zod"; import type { CreateOrEditAiProviderResponse } from "@server/routers/aiProvider/types"; import type { AxiosResponse } from "axios"; import { useTranslations } from "next-intl"; import { useParams, useRouter } from "next/navigation"; import { useRef, useState } from "react"; import { useForm } from "react-hook-form"; export default function CreateAiProviderPage() { const { env } = useEnvContext(); const api = createApiClient({ env }); const params = useParams(); const orgId = params.orgId as string; const router = useRouter(); const t = useTranslations(); const [loading, setLoading] = useState(false); const targetsRef = useRef([]); const form = useForm({ resolver: zodResolver(aiProviderCreateFormSchema), defaultValues: { name: "", type: "openai", upstreamUrl: emptyUpstreamForType("openai"), apiKey: "", authType: "bearer", routingMode: "url", skipTlsVerification: false, budgetAmount: null, budgetUnit: null, enabled: true } }); const providerType = form.watch("type"); const routingMode = form.watch("routingMode"); const showUpstream = showsUpstreamUrlField(providerType, routingMode); const requireUpstream = upstreamUrlRequired(providerType, routingMode); const showRoutingMode = providerType === "custom"; const showAuthType = providerType === "custom"; const showTargets = providerType === "custom" && routingMode === "target"; async function createTargets( providerId: number, localTargets: LocalTarget[] ) { for (const target of localTargets) { const data = { ip: target.ip, port: target.port, method: target.method, enabled: target.enabled, siteId: target.siteId, hcEnabled: target.hcEnabled, hcPath: target.hcPath || null, hcMethod: target.hcMethod || null, hcInterval: target.hcInterval || null, hcTimeout: target.hcTimeout || null, hcHeaders: target.hcHeaders || null, hcScheme: target.hcScheme || null, hcHostname: target.hcHostname || null, hcPort: target.hcPort || null, hcFollowRedirects: target.hcFollowRedirects || null, hcStatus: target.hcStatus || null, hcUnhealthyInterval: target.hcUnhealthyInterval || null, hcMode: target.hcMode || null, hcTlsServerName: target.hcTlsServerName, hcHealthyThreshold: target.hcHealthyThreshold || null, hcUnhealthyThreshold: target.hcUnhealthyThreshold || null, path: target.path, pathMatchType: target.pathMatchType, rewritePath: target.rewritePath, rewritePathType: target.rewritePathType, priority: target.priority }; await api.put(`/ai-provider/${providerId}/target`, data); } } async function onSubmit(values: AiProviderFormValues) { const targets = targetsRef.current; if (showTargets) { const invalidTargets = targets.filter( (target) => !target.ip || target.ip.trim() === "" || !target.port || target.port <= 0 || isNaN(target.port) ); if (invalidTargets.length > 0) { toast({ variant: "destructive", title: t("targetErrorInvalidIp"), description: t("targetErrorInvalidIpDescription") }); return; } } setLoading(true); try { const res = await api.put< AxiosResponse >(`/org/${orgId}/ai-provider`, toAiProviderCreatePayload(values)); const providerId = res.data.data.provider.providerId; if (showTargets && targets.length > 0) { try { await createTargets(providerId, targets); } catch (e) { toast({ variant: "destructive", title: t("aiProviderErrorCreate"), description: formatAxiosError( e, t("aiProviderErrorCreate") ) }); router.push( `/${orgId}/settings/ai-providers/${providerId}/network` ); return; } } toast({ title: t("success"), description: t("aiProviderCreated") }); router.push(`/${orgId}/settings/ai-providers/${providerId}`); } catch (e) { toast({ variant: "destructive", title: t("aiProviderErrorCreate"), description: formatAxiosError(e, t("aiProviderErrorCreate")) }); } finally { setLoading(false); } } return ( <>
{t("aiProviderGeneral")} {t("aiProviderGeneralDescription")} ( {t("name")} )} /> ( {t("aiProviderType")} { field.onChange( value ); form.setValue( "upstreamUrl", emptyUpstreamForType( value ) ); if ( value !== "custom" ) { form.setValue( "routingMode", "url" ); targetsRef.current = []; } }} /> )} /> {t("aiProviderNetworkSettings")} {t("aiProviderNetworkSettingsDescription")} {showRoutingMode && ( ( {t( "aiProviderRoutingMode" )} { field.onChange( value ); if ( value === "target" ) { form.setValue( "upstreamUrl", "" ); } else { targetsRef.current = []; } }} /> {t( "aiProviderRoutingModeDescription" )} )} /> )} {showUpstream && ( ( {t( "aiProviderUpstreamUrl" )} {requireUpstream ? "" : " (optional)"} {requireUpstream ? t( "aiProviderUpstreamUrlDescription" ) : t( "aiProviderUpstreamUrlOptionalDescription" )} )} /> )} ( )} /> {showTargets && (
{t("targets")} {t("targetsDescription")} { targetsRef.current = nextTargets; }} allowedMethods={["http", "https"]} emptyMessage={t( "aiProviderTargetNoOne" )} embedded hideSaveButton />
)}
{t("aiProviderAuthSettings")} {t("aiProviderAuthSettingsDescription")} {showAuthType && ( ( {t( "aiProviderAuthType" )} {t( "aiProviderAuthTypeDescription" )} )} /> )} ( {t("aiProviderApiKey")} {t( "aiProviderApiKeyDescription" )} )} />
); }