refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)

* refactor(auth): replace dashboard sessions with stateless tokens

* feat(auth): harden session issuance and distributed enforcement

* fix(proxy): preserve trusted proxy compatibility defaults

* refactor: address dashboard auth review feedback

* refactor: remove classic frontend and flatten web app
This commit is contained in:
Calcium-Ion
2026-07-20 16:48:43 +08:00
committed by GitHub
parent 5a6c53d496
commit 31d70fca39
1605 changed files with 17511 additions and 147913 deletions
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { ForgotPassword } from '@/features/auth/forgot-password'
export const Route = createFileRoute('/(auth)/forgot-password')({
component: ForgotPassword,
})
+72
View File
@@ -0,0 +1,72 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, useNavigate, useSearch } from '@tanstack/react-router'
import i18next from 'i18next'
import { useEffect } from 'react'
import { toast } from 'sonner'
import { wechatLoginByCode } from '@/features/auth/api'
import { sanitizeAuthRedirect } from '@/features/auth/lib/auth-redirect'
import { applyAuthBundle, isAuthBundle } from '@/lib/api'
import { getServerErrorMessageKey } from '@/lib/server-error-message'
function OAuthComponent() {
const navigate = useNavigate()
const search = useSearch({ from: '/(auth)/oauth' }) as {
redirect?: string
provider?: 'github' | 'discord' | 'oidc' | 'linuxdo' | 'telegram' | 'wechat'
code?: string
state?: string
}
useEffect(() => {
;(async () => {
try {
if (search?.provider === 'wechat' && search.code) {
const res = await wechatLoginByCode(search.code)
if (res?.success && isAuthBundle(res.data)) {
applyAuthBundle(res.data)
const target =
sanitizeAuthRedirect(search?.redirect, window.location.origin) ??
'/dashboard'
navigate({ href: target, replace: true })
return
}
if (getServerErrorMessageKey(res)) {
navigate({ to: '/sign-in', replace: true })
return
}
}
} catch (error: unknown) {
if (getServerErrorMessageKey(error)) {
navigate({ to: '/sign-in', replace: true })
return
}
}
toast.error(i18next.t('OAuth failed'))
navigate({ to: '/sign-in', replace: true })
})()
}, [navigate, search])
return null
}
export const Route = createFileRoute('/(auth)/oauth')({
component: OAuthComponent,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { Otp } from '@/features/auth/otp'
export const Route = createFileRoute('/(auth)/otp')({
component: Otp,
})
+29
View File
@@ -0,0 +1,29 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
export const Route = createFileRoute('/(auth)/register')({
beforeLoad: ({ location }) => {
throw redirect({
to: '/sign-up',
search: location.search,
replace: true,
})
},
})
+35
View File
@@ -0,0 +1,35 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, useSearch } from '@tanstack/react-router'
import {
ResetPasswordConfirm,
type ResetPasswordSearchParams,
} from '@/features/auth/reset-password-confirm'
export const Route = createFileRoute('/(auth)/reset')({
component: ResetPassword,
})
function ResetPassword() {
const search = useSearch({
from: '/(auth)/reset',
}) as ResetPasswordSearchParams
return <ResetPasswordConfirm email={search?.email} token={search?.token} />
}
+21
View File
@@ -0,0 +1,21 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/(auth)')({})
+44
View File
@@ -0,0 +1,44 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { z } from 'zod'
import { sanitizeAuthRedirect } from '@/features/auth/lib/auth-redirect'
import { SignIn } from '@/features/auth/sign-in'
import { useAuthStore } from '@/stores/auth-store'
const searchSchema = z.object({
redirect: z.string().optional(),
})
export const Route = createFileRoute('/(auth)/sign-in')({
component: SignIn,
validateSearch: searchSchema,
beforeLoad: async ({ search }) => {
const { auth } = useAuthStore.getState()
// 如果已经有用户信息,说明已登录
if (auth.user) {
const target =
sanitizeAuthRedirect(search?.redirect, window.location.origin) ??
'/dashboard'
throw redirect({ href: target, replace: true })
}
},
})
+34
View File
@@ -0,0 +1,34 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SignUp } from '@/features/auth/sign-up'
import { useAuthStore } from '@/stores/auth-store'
export const Route = createFileRoute('/(auth)/sign-up')({
component: SignUp,
beforeLoad: async () => {
const { auth } = useAuthStore.getState()
// 如果已经有用户信息,说明已登录,注册页对其无意义,跳转到 dashboard
if (auth.user) {
throw redirect({ to: '/dashboard' })
}
},
})
+36
View File
@@ -0,0 +1,36 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, useSearch } from '@tanstack/react-router'
import {
ResetPasswordConfirm,
type ResetPasswordSearchParams,
} from '@/features/auth/reset-password-confirm'
export const Route = createFileRoute('/(auth)/user/reset')({
component: UserResetPassword,
})
function UserResetPassword() {
const search = useSearch({
from: '/(auth)/user/reset',
}) as ResetPasswordSearchParams
return <ResetPasswordConfirm email={search?.email} token={search?.token} />
}
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { UnauthorisedError } from '@/features/errors/unauthorized-error'
export const Route = createFileRoute('/(errors)/401')({
component: UnauthorisedError,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { ForbiddenError } from '@/features/errors/forbidden'
export const Route = createFileRoute('/(errors)/403')({
component: ForbiddenError,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { NotFoundError } from '@/features/errors/not-found-error'
export const Route = createFileRoute('/(errors)/404')({
component: NotFoundError,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { GeneralError } from '@/features/errors/general-error'
export const Route = createFileRoute('/(errors)/500')({
component: GeneralError,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { MaintenanceError } from '@/features/errors/maintenance-error'
export const Route = createFileRoute('/(errors)/503')({
component: MaintenanceError,
})
+182
View File
@@ -0,0 +1,182 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useQueryClient, type QueryClient } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import {
createRootRouteWithContext,
Outlet,
redirect,
useNavigate,
} from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import { useEffect } from 'react'
import { NavigationProgress } from '@/components/navigation-progress'
import { Toaster } from '@/components/ui/sonner'
import { ThemeCustomizationProvider } from '@/context/theme-customization-provider'
import { saveAffiliateCode } from '@/features/auth/lib/storage'
import { GeneralError } from '@/features/errors/general-error'
import { NotFoundError } from '@/features/errors/not-found-error'
import { getSetupStatus } from '@/features/setup/api'
import { useSystemConfig } from '@/hooks/use-system-config'
import {
bootstrapAuthentication,
clearAuthenticatedClientState,
clearAuthentication,
} from '@/lib/auth-session'
import { subscribeAuthSessionEvents } from '@/lib/auth-session-sync'
import { resolveLegacyRoute } from '@/lib/legacy-route'
import { useAuthStore } from '@/stores/auth-store'
function RootComponent() {
const navigate = useNavigate()
const queryClient = useQueryClient()
// Load system configuration (logo, system name, etc.) from backend
useSystemConfig({ autoLoad: true })
useEffect(() => {
const aff = new URLSearchParams(window.location.search).get('aff')?.trim()
if (aff) {
saveAffiliateCode(aff)
}
}, [])
useEffect(
() =>
useAuthStore.subscribe((state, previousState) => {
const sid = state.auth.session?.sid
const previousSID = previousState.auth.session?.sid
if (sid !== previousSID) {
queryClient.clear()
}
}),
[queryClient]
)
useEffect(
() =>
subscribeAuthSessionEvents((event) => {
const currentSID = useAuthStore.getState().auth.session?.sid
if (event.kind === 'authenticated') {
if (event.sid === currentSID) return
if (currentSID) {
clearAuthentication(false)
}
window.location.reload()
return
}
if (currentSID && event.sid === currentSID) {
clearAuthenticatedClientState(queryClient, false)
void navigate({ to: '/sign-in', replace: true })
}
}),
[navigate, queryClient]
)
return (
<ThemeCustomizationProvider>
<NavigationProgress />
<Outlet />
<Toaster closeButton duration={5000} position='top-center' richColors />
{import.meta.env.MODE === 'development' && (
<>
<ReactQueryDevtools buttonPosition='bottom-left' />
<TanStackRouterDevtools position='bottom-right' />
</>
)}
</ThemeCustomizationProvider>
)
}
// 缓存 setup 状态检查结果,避免每次导航都重复调用 API
// 使用 localStorage 持久化,避免页面刷新后重复检查
const SETUP_CHECKED_KEY = 'setup_status_checked'
function getSetupStatusFromCache(): boolean {
try {
if (typeof window !== 'undefined') {
return window.localStorage.getItem(SETUP_CHECKED_KEY) === 'true'
}
} catch {
/* empty */
}
return false
}
function setSetupStatusCache(value: boolean): void {
try {
if (typeof window !== 'undefined') {
if (value) {
window.localStorage.setItem(SETUP_CHECKED_KEY, 'true')
} else {
window.localStorage.removeItem(SETUP_CHECKED_KEY)
}
}
} catch {
/* empty */
}
}
// 内存中的标记,避免同一会话中重复检查
let setupStatusChecked = getSetupStatusFromCache()
export const Route = createRootRouteWithContext<{
queryClient: QueryClient
}>()({
// 应用初始化与路由解析前统一校验会话
beforeLoad: async ({ location }) => {
const legacyTarget = resolveLegacyRoute(location.href)
if (legacyTarget) {
throw redirect({ href: legacyTarget, replace: true })
}
const pathname = location?.pathname || ''
const needsSetupCheck =
!setupStatusChecked && !pathname.startsWith('/setup')
const authBootstrap = bootstrapAuthentication()
// 只检查 setup 状态(如果需要)
if (needsSetupCheck) {
const [status] = await Promise.all([
getSetupStatus().catch((error) => {
if (import.meta.env.DEV) {
// eslint-disable-next-line no-console
console.warn('[root.beforeLoad] setup status check failed', error)
}
return null
}),
authBootstrap,
])
if (status?.success && status.data && !status.data.status) {
throw redirect({ to: '/setup' })
}
setupStatusChecked = true
setSetupStatusCache(true)
} else {
await authBootstrap
}
},
component: RootComponent,
notFoundComponent: NotFoundError,
errorComponent: GeneralError,
})
+48
View File
@@ -0,0 +1,48 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { Channels } from '@/features/channels'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
const channelsSearchSchema = z.object({
page: z.number().optional().catch(1),
pageSize: z.number().optional().catch(undefined),
filter: z.string().optional().catch(''),
status: z.array(z.string()).optional().catch([]),
type: z.array(z.string()).optional().catch([]),
group: z.array(z.string()).optional().catch([]),
model: z.string().optional().catch(''),
})
export const Route = createFileRoute('/_authenticated/channels/')({
beforeLoad: () => {
const { auth } = useAuthStore.getState()
if (!auth.user || auth.user.role < ROLE.ADMIN) {
throw redirect({
to: '/403',
})
}
},
validateSearch: channelsSearchSchema,
component: Channels,
})
+165
View File
@@ -0,0 +1,165 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { Link, createFileRoute, redirect } from '@tanstack/react-router'
import { Loader2, MessageCircleWarning } from 'lucide-react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { useActiveChatKey } from '@/features/chat/hooks/use-active-chat-key'
import { useChatPresets } from '@/features/chat/hooks/use-chat-presets'
import {
chatLinkRequiresApiKey,
resolveChatUrl,
} from '@/features/chat/lib/chat-links'
export const Route = createFileRoute('/_authenticated/chat/$chatId')({
loader: async ({ params }) => {
if (!Number.isInteger(Number(params.chatId))) {
throw redirect({ to: '/dashboard' })
}
},
component: ChatRouteComponent,
})
function ChatRouteComponent() {
const { t } = useTranslation()
const { chatId } = Route.useParams()
const { chatPresets, serverAddress } = useChatPresets()
const preset = useMemo(() => {
const index = Number(chatId)
if (!Number.isInteger(index)) return undefined
return chatPresets[index]
}, [chatId, chatPresets])
const isWebLink = preset?.type === 'web'
const requiresActiveKey = useMemo(() => {
if (!preset || !isWebLink) return false
return chatLinkRequiresApiKey(preset.url ?? '')
}, [isWebLink, preset])
const {
data: activeKey,
isPending,
isError,
error,
} = useActiveChatKey(Boolean(preset && requiresActiveKey))
const iframeSrc = useMemo(() => {
if (!preset || !isWebLink) return ''
if (requiresActiveKey && !activeKey) return ''
return resolveChatUrl({
template: preset.url,
apiKey: requiresActiveKey ? activeKey : undefined,
serverAddress,
})
}, [activeKey, isWebLink, preset, requiresActiveKey, serverAddress])
if (!preset) {
return (
<div className='flex h-full flex-col items-center justify-center gap-4 p-6 text-center'>
<MessageCircleWarning className='text-muted-foreground h-12 w-12' />
<div className='space-y-1'>
<h2 className='text-lg font-semibold'>
{t('Chat preset not found')}
</h2>
<p className='text-muted-foreground'>
{t('The requested chat preset does not exist or has been removed.')}
</p>
</div>
<Button variant='outline' render={<Link to='/dashboard' />}>
{t('Return to dashboard')}
</Button>
</div>
)
}
if (!isWebLink) {
return (
<div className='flex h-full flex-col items-center justify-center gap-4 p-6 text-center'>
<MessageCircleWarning className='text-muted-foreground h-12 w-12' />
<div className='space-y-1'>
<h2 className='text-lg font-semibold'>{t('Use sidebar shortcut')}</h2>
<p className='text-muted-foreground'>
{preset.name}{' '}
{t(
'opens in an external client. Trigger it from the sidebar or API key actions to launch the configured application.'
)}
</p>
</div>
<Button variant='outline' render={<Link to='/dashboard' />}>
{t('Return to dashboard')}
</Button>
</div>
)
}
if (requiresActiveKey && isPending) {
return (
<div className='flex h-full flex-col items-center justify-center gap-4'>
<Loader2 className='text-muted-foreground h-8 w-8 animate-spin' />
<p className='text-muted-foreground text-sm'>
{t('Preparing your chat link…')}
</p>
</div>
)
}
if (requiresActiveKey && (isError || !activeKey || !iframeSrc)) {
const message =
error instanceof Error
? error.message
: 'Unable to generate chat link. Please check your API keys.'
return (
<div className='flex h-full flex-col items-center justify-center p-6'>
<Alert variant='destructive' className='max-w-xl'>
<AlertTitle>{t('Unable to open chat')}</AlertTitle>
<AlertDescription>{message}</AlertDescription>
</Alert>
</div>
)
}
if (!requiresActiveKey && !iframeSrc) {
return (
<div className='flex h-full flex-col items-center justify-center p-6'>
<Alert variant='destructive' className='max-w-xl'>
<AlertTitle>{t('Unable to open chat')}</AlertTitle>
<AlertDescription>
{t(
'Unable to generate chat link. Please contact your administrator.'
)}
</AlertDescription>
</Alert>
</div>
)
}
return (
<iframe
src={iframeSrc}
key={iframeSrc}
className='h-full w-full border-0'
allow='camera; microphone'
title={`Chat preset: ${preset.name}`}
/>
)
}
+94
View File
@@ -0,0 +1,94 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { Loader2 } from 'lucide-react'
import { useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { useActiveChatKey } from '@/features/chat/hooks/use-active-chat-key'
import { useChatPresets } from '@/features/chat/hooks/use-chat-presets'
import { resolveChatUrl } from '@/features/chat/lib/chat-links'
export const Route = createFileRoute('/_authenticated/chat2link')({
component: Chat2LinkPage,
})
function Chat2LinkPage() {
const { t } = useTranslation()
const navigate = useNavigate()
const { chatPresets, serverAddress } = useChatPresets()
const firstWebPreset = useMemo(
() => chatPresets.find((p) => p.type === 'web'),
[chatPresets]
)
const { data: activeKey, error: keyError } = useActiveChatKey(
Boolean(firstWebPreset)
)
useEffect(() => {
if (!firstWebPreset) {
if (chatPresets.length > 0) {
toast.error(t('No available Web chat links'))
}
return
}
if (activeKey === undefined && !keyError) return
if (keyError || !activeKey) {
const message =
keyError instanceof Error
? keyError.message
: t('No enabled tokens available')
toast.error(message)
navigate({ to: '/keys' })
return
}
const url = resolveChatUrl({
template: firstWebPreset.url,
apiKey: activeKey,
serverAddress,
})
if (url) {
window.location.href = url
}
}, [
firstWebPreset,
activeKey,
keyError,
serverAddress,
chatPresets.length,
navigate,
t,
])
return (
<div className='flex h-full flex-col items-center justify-center gap-3'>
<Loader2 className='text-muted-foreground h-8 w-8 animate-spin' />
<p className='text-muted-foreground text-sm'>
{t('Redirecting to chat page...')}
</p>
</div>
)
}
+38
View File
@@ -0,0 +1,38 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { Dashboard } from '@/features/dashboard'
import {
DASHBOARD_SECTION_IDS,
DASHBOARD_DEFAULT_SECTION,
} from '@/features/dashboard/section-registry'
export const Route = createFileRoute('/_authenticated/dashboard/$section')({
beforeLoad: ({ params }) => {
const validSections = DASHBOARD_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/dashboard/$section',
params: { section: DASHBOARD_DEFAULT_SECTION },
})
}
},
component: Dashboard,
})
+30
View File
@@ -0,0 +1,30 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { DASHBOARD_DEFAULT_SECTION } from '@/features/dashboard/section-registry'
export const Route = createFileRoute('/_authenticated/dashboard/')({
beforeLoad: () => {
throw redirect({
to: '/dashboard/$section',
params: { section: DASHBOARD_DEFAULT_SECTION },
})
},
})
+63
View File
@@ -0,0 +1,63 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { ConfigDrawer } from '@/components/config-drawer'
import { Header } from '@/components/layout'
import { ProfileDropdown } from '@/components/profile-dropdown'
import { Search } from '@/components/search'
import { ThemeSwitch } from '@/components/theme-switch'
import { ForbiddenError } from '@/features/errors/forbidden'
import { GeneralError } from '@/features/errors/general-error'
import { MaintenanceError } from '@/features/errors/maintenance-error'
import { NotFoundError } from '@/features/errors/not-found-error'
import { UnauthorisedError } from '@/features/errors/unauthorized-error'
export const Route = createFileRoute('/_authenticated/errors/$error')({
component: RouteComponent,
})
function RouteComponent() {
const { error } = Route.useParams()
const errorMap: Record<string, React.ComponentType> = {
unauthorized: UnauthorisedError,
forbidden: ForbiddenError,
'not-found': NotFoundError,
'internal-server-error': GeneralError,
'maintenance-error': MaintenanceError,
}
const ErrorComponent = errorMap[error] || NotFoundError
return (
<>
<Header>
<Search />
<div className='ms-auto flex items-center md:space-x-4'>
<ThemeSwitch />
<ConfigDrawer />
<ProfileDropdown />
</div>
</Header>
<div className='flex-1 [&>div]:h-full'>
<ErrorComponent />
</div>
</>
)
}
+39
View File
@@ -0,0 +1,39 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import z from 'zod'
import { ApiKeys } from '@/features/keys'
import { API_KEY_STATUS_OPTIONS } from '@/features/keys/constants'
const apiKeySearchSchema = z.object({
page: z.number().optional().catch(1),
pageSize: z.number().optional().catch(undefined),
status: z
.array(z.enum(API_KEY_STATUS_OPTIONS.map((s) => s.value as `${number}`)))
.optional()
.catch([]),
filter: z.string().optional().catch(''),
token: z.string().optional().catch(''),
})
export const Route = createFileRoute('/_authenticated/keys/')({
validateSearch: apiKeySearchSchema,
component: ApiKeys,
})
+63
View File
@@ -0,0 +1,63 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { Models } from '@/features/models'
import {
MODELS_SECTION_IDS,
MODELS_DEFAULT_SECTION,
} from '@/features/models/section-registry'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
const modelsSearchSchema = z.object({
page: z.number().optional().catch(1),
pageSize: z.number().optional().catch(10),
filter: z.string().optional().catch(''),
vendor: z.array(z.string()).optional().catch([]),
status: z.array(z.string()).optional().catch([]),
sync: z.array(z.string()).optional().catch([]),
dPage: z.number().optional().catch(1),
dPageSize: z.number().optional().catch(10),
dFilter: z.string().optional().catch(''),
dStatus: z.array(z.string()).optional().catch([]),
})
export const Route = createFileRoute('/_authenticated/models/$section')({
beforeLoad: ({ params }) => {
const { auth } = useAuthStore.getState()
if (!auth.user || auth.user.role < ROLE.ADMIN) {
throw redirect({
to: '/403',
})
}
const validSections = MODELS_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/models/$section',
params: { section: MODELS_DEFAULT_SECTION },
})
}
},
validateSearch: modelsSearchSchema,
component: Models,
})
+40
View File
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { MODELS_DEFAULT_SECTION } from '@/features/models/section-registry'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
export const Route = createFileRoute('/_authenticated/models/')({
beforeLoad: () => {
const { auth } = useAuthStore.getState()
if (!auth.user || auth.user.role < ROLE.ADMIN) {
throw redirect({
to: '/403',
})
}
throw redirect({
to: '/models/$section',
params: { section: MODELS_DEFAULT_SECTION },
})
},
})
+40
View File
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { Main } from '@/components/layout'
import { Playground } from '@/features/playground'
import { isSidebarModuleEnabled } from '@/lib/nav-modules'
export const Route = createFileRoute('/_authenticated/playground/')({
beforeLoad: () => {
if (!isSidebarModuleEnabled('chat', 'playground')) {
throw redirect({ to: '/dashboard' })
}
},
component: PlaygroundPage,
})
function PlaygroundPage() {
return (
<Main className='p-0'>
<Playground />
</Main>
)
}
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { Profile } from '@/features/profile'
export const Route = createFileRoute('/_authenticated/profile/')({
component: Profile,
})
@@ -0,0 +1,46 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { Redemptions } from '@/features/redemption-codes'
import { REDEMPTION_FILTER_VALUES } from '@/features/redemption-codes/constants'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
const redemptionsSearchSchema = z.object({
page: z.number().optional().catch(1),
pageSize: z.number().optional().catch(10),
filter: z.string().optional().catch(''),
status: z.array(z.enum(REDEMPTION_FILTER_VALUES)).optional().catch([]),
})
export const Route = createFileRoute('/_authenticated/redemption-codes/')({
beforeLoad: () => {
const { auth } = useAuthStore.getState()
if (!auth.user || auth.user.role < ROLE.ADMIN) {
throw redirect({
to: '/403',
})
}
},
validateSearch: redemptionsSearchSchema,
component: Redemptions,
})
+36
View File
@@ -0,0 +1,36 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { AuthenticatedLayout } from '@/components/layout'
import { useAuthStore } from '@/stores/auth-store'
export const Route = createFileRoute('/_authenticated')({
beforeLoad: ({ location }) => {
const { auth } = useAuthStore.getState()
if (!auth.user || !auth.accessToken) {
throw redirect({
to: '/sign-in',
search: { redirect: location.href },
})
}
},
component: AuthenticatedLayout,
})
+33
View File
@@ -0,0 +1,33 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { Subscriptions } from '@/features/subscriptions'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
export const Route = createFileRoute('/_authenticated/subscriptions/')({
beforeLoad: () => {
const { auth } = useAuthStore.getState()
if (!auth.user || auth.user.role < ROLE.ADMIN) {
throw redirect({ to: '/403' })
}
},
component: Subscriptions,
})
+36
View File
@@ -0,0 +1,36 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SystemInfo } from '@/features/system-info'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
export const Route = createFileRoute('/_authenticated/system-info/')({
beforeLoad: () => {
const { auth } = useAuthStore.getState()
if (auth.user?.role !== ROLE.SUPER_ADMIN) {
throw redirect({
to: '/403',
})
}
},
component: SystemInfo,
})
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { AuthSettings } from '@/features/system-settings/auth'
import {
AUTH_DEFAULT_SECTION,
AUTH_SECTION_IDS,
} from '@/features/system-settings/auth/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/auth/$section'
)({
beforeLoad: ({ params }) => {
const validSections = AUTH_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/system-settings/auth/$section',
params: { section: AUTH_DEFAULT_SECTION },
})
}
},
component: AuthSettings,
})
@@ -0,0 +1,30 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { AUTH_DEFAULT_SECTION } from '@/features/system-settings/auth/section-registry.tsx'
export const Route = createFileRoute('/_authenticated/system-settings/auth/')({
beforeLoad: () => {
throw redirect({
to: '/system-settings/auth/$section',
params: { section: AUTH_DEFAULT_SECTION },
})
},
})
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { BillingSettings } from '@/features/system-settings/billing'
import {
BILLING_DEFAULT_SECTION,
BILLING_SECTION_IDS,
} from '@/features/system-settings/billing/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/billing/$section'
)({
beforeLoad: ({ params }) => {
const validSections = BILLING_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/system-settings/billing/$section',
params: { section: BILLING_DEFAULT_SECTION },
})
}
},
component: BillingSettings,
})
@@ -0,0 +1,32 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { BILLING_DEFAULT_SECTION } from '@/features/system-settings/billing/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/billing/'
)({
beforeLoad: () => {
throw redirect({
to: '/system-settings/billing/$section',
params: { section: BILLING_DEFAULT_SECTION },
})
},
})
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { ContentSettings } from '@/features/system-settings/content'
import {
CONTENT_DEFAULT_SECTION,
CONTENT_SECTION_IDS,
} from '@/features/system-settings/content/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/content/$section'
)({
beforeLoad: ({ params }) => {
const validSections = CONTENT_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/system-settings/content/$section',
params: { section: CONTENT_DEFAULT_SECTION },
})
}
},
component: ContentSettings,
})
@@ -0,0 +1,32 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { CONTENT_DEFAULT_SECTION } from '@/features/system-settings/content/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/content/'
)({
beforeLoad: () => {
throw redirect({
to: '/system-settings/content/$section',
params: { section: CONTENT_DEFAULT_SECTION },
})
},
})
+27
View File
@@ -0,0 +1,27 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated/system-settings/')({
beforeLoad: () => {
throw redirect({
to: '/system-settings/site',
})
},
})
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { ModelSettings } from '@/features/system-settings/models'
import {
MODELS_DEFAULT_SECTION,
MODELS_SECTION_IDS,
} from '@/features/system-settings/models/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/models/$section'
)({
beforeLoad: ({ params }) => {
const validSections = MODELS_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/system-settings/models/$section',
params: { section: MODELS_DEFAULT_SECTION },
})
}
},
component: ModelSettings,
})
@@ -0,0 +1,32 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { MODELS_DEFAULT_SECTION } from '@/features/system-settings/models/section-registry.tsx'
export const Route = createFileRoute('/_authenticated/system-settings/models/')(
{
beforeLoad: () => {
throw redirect({
to: '/system-settings/models/$section',
params: { section: MODELS_DEFAULT_SECTION },
})
},
}
)
@@ -0,0 +1,47 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { OperationsSettings } from '@/features/system-settings/operations'
import {
OPERATIONS_DEFAULT_SECTION,
OPERATIONS_SECTION_IDS,
} from '@/features/system-settings/operations/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/operations/$section'
)({
beforeLoad: ({ params }) => {
if (params.section === 'monitoring') {
throw redirect({
to: '/system-settings/models/$section',
params: { section: 'routing-reliability' },
})
}
const validSections = OPERATIONS_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/system-settings/operations/$section',
params: { section: OPERATIONS_DEFAULT_SECTION },
})
}
},
component: OperationsSettings,
})
@@ -0,0 +1,32 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { OPERATIONS_DEFAULT_SECTION } from '@/features/system-settings/operations/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/operations/'
)({
beforeLoad: () => {
throw redirect({
to: '/system-settings/operations/$section',
params: { section: OPERATIONS_DEFAULT_SECTION },
})
},
})
+36
View File
@@ -0,0 +1,36 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SystemSettings } from '@/features/system-settings'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
export const Route = createFileRoute('/_authenticated/system-settings')({
beforeLoad: () => {
const { auth } = useAuthStore.getState()
if (auth.user?.role !== ROLE.SUPER_ADMIN) {
throw redirect({
to: '/403',
})
}
},
component: SystemSettings,
})
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SecuritySettings } from '@/features/system-settings/security'
import {
SECURITY_DEFAULT_SECTION,
SECURITY_SECTION_IDS,
} from '@/features/system-settings/security/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/security/$section'
)({
beforeLoad: ({ params }) => {
const validSections = SECURITY_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/system-settings/security/$section',
params: { section: SECURITY_DEFAULT_SECTION },
})
}
},
component: SecuritySettings,
})
@@ -0,0 +1,32 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SECURITY_DEFAULT_SECTION } from '@/features/system-settings/security/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/security/'
)({
beforeLoad: () => {
throw redirect({
to: '/system-settings/security/$section',
params: { section: SECURITY_DEFAULT_SECTION },
})
},
})
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SiteSettings } from '@/features/system-settings/site'
import {
SITE_DEFAULT_SECTION,
SITE_SECTION_IDS,
} from '@/features/system-settings/site/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/site/$section'
)({
beforeLoad: ({ params }) => {
const validSections = SITE_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
to: '/system-settings/site/$section',
params: { section: SITE_DEFAULT_SECTION },
})
}
},
component: SiteSettings,
})
@@ -0,0 +1,30 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SITE_DEFAULT_SECTION } from '@/features/system-settings/site/section-registry.tsx'
export const Route = createFileRoute('/_authenticated/system-settings/site/')({
beforeLoad: () => {
throw redirect({
to: '/system-settings/site/$section',
params: { section: SITE_DEFAULT_SECTION },
})
},
})
+75
View File
@@ -0,0 +1,75 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { UsageLogs } from '@/features/usage-logs'
import {
isUsageLogsSectionId,
USAGE_LOGS_DEFAULT_SECTION,
} from '@/features/usage-logs/section-registry'
const logTypeValues = ['0', '1', '2', '3', '4', '5', '6', '7'] as const
const logTypeSearchSchema = z
.preprocess((value) => {
if (value == null || value === '') return undefined
return Array.isArray(value) ? value : [value]
}, z.array(z.enum(logTypeValues)).optional())
.catch([])
const usageLogsSearchSchema = z.object({
page: z.number().optional().catch(1),
pageSize: z.number().optional().catch(undefined),
type: logTypeSearchSchema.optional(),
filter: z.string().optional().catch(''),
model: z.string().optional().catch(''),
token: z.string().optional().catch(''),
channel: z.string().optional().catch(''),
group: z.string().optional().catch(''),
username: z.string().optional().catch(''),
requestId: z.string().optional().catch(''),
upstreamRequestId: z.string().optional().catch(''),
startTime: z.number().optional(),
endTime: z.number().optional(),
})
export const Route = createFileRoute('/_authenticated/usage-logs/$section')({
beforeLoad: ({ params, search }) => {
if (!isUsageLogsSectionId(params.section)) {
throw redirect({
to: '/usage-logs/$section',
params: { section: USAGE_LOGS_DEFAULT_SECTION },
})
}
// type 仅 common 使用,非 common 时清掉 URL 里的 type
const hasTypeSearch = Array.isArray(search?.type)
? search.type.length > 0
: search?.type != null && search.type !== ''
if (params.section !== 'common' && hasTypeSearch) {
throw redirect({
to: '/usage-logs/$section',
params: { section: params.section },
search: { ...search, type: undefined },
replace: true,
})
}
},
validateSearch: usageLogsSearchSchema,
component: UsageLogs,
})
+30
View File
@@ -0,0 +1,30 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { USAGE_LOGS_DEFAULT_SECTION } from '@/features/usage-logs/section-registry'
export const Route = createFileRoute('/_authenticated/usage-logs/')({
beforeLoad: () => {
throw redirect({
to: '/usage-logs/$section',
params: { section: USAGE_LOGS_DEFAULT_SECTION },
})
},
})
+53
View File
@@ -0,0 +1,53 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { Users } from '@/features/users'
import { ROLE } from '@/lib/roles'
import { useAuthStore } from '@/stores/auth-store'
const usersSearchSchema = z.object({
page: z.number().optional().catch(1),
pageSize: z.number().optional().catch(undefined),
filter: z.string().optional().catch(''),
status: z
.array(z.enum(['-1', '1', '2']))
.optional()
.catch([]),
role: z
.array(z.enum(['1', '10', '100']))
.optional()
.catch([]),
group: z.string().optional().catch(''),
})
export const Route = createFileRoute('/_authenticated/users/')({
beforeLoad: () => {
const { auth } = useAuthStore.getState()
if (!auth.user || auth.user.role < ROLE.ADMIN) {
throw redirect({
to: '/403',
})
}
},
validateSearch: usersSearchSchema,
component: Users,
})
+36
View File
@@ -0,0 +1,36 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
import { Wallet } from '@/features/wallet'
const walletSearchSchema = z.object({
show_history: z.boolean().optional(),
})
export const Route = createFileRoute('/_authenticated/wallet/')({
component: RouteComponent,
validateSearch: walletSearchSchema,
})
function RouteComponent() {
const { show_history } = Route.useSearch()
return <Wallet initialShowHistory={show_history} />
}
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { About } from '@/features/about'
export const Route = createFileRoute('/about/')({
component: About,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { Home } from '@/features/home'
export const Route = createFileRoute('/')({
component: Home,
})
+233
View File
@@ -0,0 +1,233 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import {
createFileRoute,
useNavigate,
useParams,
useSearch,
} from '@tanstack/react-router'
import type { AxiosRequestConfig } from 'axios'
import i18next from 'i18next'
import { useEffect } from 'react'
import { toast } from 'sonner'
import { OAuthCallbackScreen } from '@/features/auth/components/oauth-callback-screen'
import {
OAUTH_BIND_CALLBACK_MESSAGE,
OAUTH_BIND_RESULT_MESSAGE,
} from '@/features/auth/constants'
import { sanitizeAuthRedirect } from '@/features/auth/lib/auth-redirect'
import {
parseTelegramBindCallback,
postTelegramBindResult,
startOAuthBindResponseDeadline,
} from '@/features/auth/lib/oauth-bind-window'
import { api, applyAuthBundle, isAuthBundle } from '@/lib/api'
import { getServerErrorMessageKey } from '@/lib/server-error-message'
type OAuthRequestConfig = AxiosRequestConfig & {
skipBusinessError?: boolean
}
interface OAuthBindingResult {
type: typeof OAUTH_BIND_RESULT_MESSAGE
provider: string
state: string
success: boolean
message?: string
}
function OAuthCallback() {
const navigate = useNavigate()
const { provider } = useParams({ from: '/oauth/$provider' }) as {
provider: string
}
const search = useSearch({ from: '/oauth/$provider' }) as {
code?: string
state?: string
error?: string
error_description?: string
redirect?: string
telegram_bind?: string
flow_token?: string
error_code?: string
}
const mode: 'login' | 'bind' =
typeof window !== 'undefined' && window.opener ? 'bind' : 'login'
useEffect(() => {
if (typeof window === 'undefined') return
const code = search.code ?? ''
const state = search.state ?? ''
const telegramCallback =
provider === 'telegram'
? parseTelegramBindCallback({
telegram_bind: search.telegram_bind,
flow_token: search.flow_token,
error_code: search.error_code,
})
: null
if (telegramCallback) {
const opener = window.opener
if (
!postTelegramBindResult(
telegramCallback,
opener,
window.location.origin
)
) {
toast.error(i18next.t('Telegram binding failed. Please try again.'))
const closeTimeout = window.setTimeout(() => window.close(), 1500)
return () => window.clearTimeout(closeTimeout)
}
window.close()
return
}
if (mode === 'bind') {
const opener = window.opener
if (!opener || opener.closed) {
toast.error(i18next.t('OAuth binding window is no longer available'))
return
}
let cancelResultTimeout: () => void = () => undefined
let delayedClose: number | undefined
const handleBindingResult = (event: MessageEvent<unknown>) => {
if (
event.origin !== window.location.origin ||
event.source !== opener
) {
return
}
const result = event.data as Partial<OAuthBindingResult> | null
if (
!result ||
result.type !== OAUTH_BIND_RESULT_MESSAGE ||
result.provider !== provider ||
result.state !== state
) {
return
}
cancelResultTimeout()
if (result.success) {
toast.success(i18next.t('Binding successful!'))
window.close()
return
}
toast.error(result.message || i18next.t('OAuth failed'))
delayedClose = window.setTimeout(() => window.close(), 1500)
}
window.addEventListener('message', handleBindingResult)
cancelResultTimeout = startOAuthBindResponseDeadline(() => {
toast.error(i18next.t('OAuth binding timed out. Please try again.'))
delayedClose = window.setTimeout(() => window.close(), 1500)
})
opener.postMessage(
{
type: OAUTH_BIND_CALLBACK_MESSAGE,
provider,
code,
state,
error: search.error,
errorDescription: search.error_description,
},
window.location.origin
)
return () => {
window.removeEventListener('message', handleBindingResult)
cancelResultTimeout()
if (delayedClose !== undefined) window.clearTimeout(delayedClose)
}
}
const safeNavigate = (target: unknown, fallback = '/dashboard') => {
const href =
sanitizeAuthRedirect(target, window.location.origin) ?? fallback
void navigate({ href, replace: true })
}
if (!code && !search.error) {
toast.error(i18next.t('Missing code'))
safeNavigate('/sign-in', '/sign-in')
return
}
void (async () => {
try {
const config: OAuthRequestConfig = {
params: {
code: code || undefined,
state,
error: search.error,
error_description: search.error_description,
},
skipBusinessError: true,
}
const response = await api.get(`/api/oauth/${provider}`, config)
if (response.data?.success && isAuthBundle(response.data?.data)) {
applyAuthBundle(response.data.data)
safeNavigate(search.redirect)
toast.success(i18next.t('Signed in successfully!'))
return
}
const messageKey = getServerErrorMessageKey(response.data)
toast.error(
messageKey
? i18next.t(messageKey)
: response.data?.message || i18next.t('OAuth failed')
)
} catch (error: unknown) {
const messageKey = getServerErrorMessageKey(error)
const responseMessage = (
error as { response?: { data?: { message?: string } } }
).response?.data?.message
if (!messageKey) {
toast.error(
responseMessage ||
(error instanceof Error
? error.message
: i18next.t('OAuth failed'))
)
}
}
safeNavigate('/sign-in', '/sign-in')
})()
}, [
mode,
navigate,
provider,
search.code,
search.error,
search.error_code,
search.error_description,
search.flow_token,
search.redirect,
search.state,
search.telegram_bind,
])
return <OAuthCallbackScreen provider={provider} mode={mode} />
}
export const Route = createFileRoute('/oauth/$provider')({
component: OAuthCallback,
})
+57
View File
@@ -0,0 +1,57 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { ModelDetails } from '@/features/pricing/components/model-details'
import { getFreshModuleAccess } from '@/lib/nav-modules'
import { useAuthStore } from '@/stores/auth-store'
const modelDetailsSearchSchema = z.object({
search: z.string().optional(),
sort: z.string().optional(),
vendor: z.string().optional(),
group: z.string().optional(),
quotaType: z.string().optional(),
endpointType: z.string().optional(),
tag: z.string().optional(),
tokenUnit: z.enum(['M', 'K']).optional(),
view: z.enum(['card', 'table']).optional().catch(undefined),
rechargePrice: z.boolean().optional(),
})
export const Route = createFileRoute('/pricing/$modelId/')({
validateSearch: modelDetailsSearchSchema,
beforeLoad: async ({ location }) => {
const access = await getFreshModuleAccess('pricing')
if (!access.enabled) {
throw redirect({ to: '/' })
}
if (access.requireAuth) {
const { auth } = useAuthStore.getState()
if (!auth.user) {
throw redirect({
to: '/sign-in',
search: { redirect: location.href },
})
}
}
},
component: ModelDetails,
})
+57
View File
@@ -0,0 +1,57 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { Pricing } from '@/features/pricing'
import { getFreshModuleAccess } from '@/lib/nav-modules'
import { useAuthStore } from '@/stores/auth-store'
const pricingSearchSchema = z.object({
search: z.string().optional(),
sort: z.string().optional(),
vendor: z.string().optional(),
group: z.string().optional(),
quotaType: z.string().optional(),
endpointType: z.string().optional(),
tag: z.string().optional(),
tokenUnit: z.enum(['M', 'K']).optional(),
view: z.enum(['card', 'table']).optional().catch(undefined),
rechargePrice: z.boolean().optional(),
})
export const Route = createFileRoute('/pricing/')({
validateSearch: pricingSearchSchema,
beforeLoad: async ({ location }) => {
const access = await getFreshModuleAccess('pricing')
if (!access.enabled) {
throw redirect({ to: '/' })
}
if (access.requireAuth) {
const { auth } = useAuthStore.getState()
if (!auth.user) {
throw redirect({
to: '/sign-in',
search: { redirect: location.href },
})
}
}
},
component: Pricing,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { PrivacyPolicy } from '@/features/legal'
export const Route = createFileRoute('/privacy-policy')({
component: PrivacyPolicy,
})
+51
View File
@@ -0,0 +1,51 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { Rankings } from '@/features/rankings'
import { getFreshModuleAccess } from '@/lib/nav-modules'
import { useAuthStore } from '@/stores/auth-store'
const rankingsSearchSchema = z.object({
period: z
.enum(['today', 'week', 'month', 'year'])
.optional()
.catch(undefined),
})
export const Route = createFileRoute('/rankings/')({
validateSearch: rankingsSearchSchema,
beforeLoad: async ({ location }) => {
const access = await getFreshModuleAccess('rankings')
if (!access.enabled) {
throw redirect({ to: '/' })
}
if (access.requireAuth) {
const { auth } = useAuthStore.getState()
if (!auth.user) {
throw redirect({
to: '/sign-in',
search: { redirect: location.href },
})
}
}
},
component: Rankings,
})
+39
View File
@@ -0,0 +1,39 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SetupWizard } from '@/features/setup'
import { getSetupStatus } from '@/features/setup/api'
export const Route = createFileRoute('/setup/')({
beforeLoad: async () => {
const status = await getSetupStatus().catch((error) => {
if (import.meta.env.DEV) {
// eslint-disable-next-line no-console
console.warn('[setup.beforeLoad] failed to fetch setup status', error)
}
return null
})
if (status?.success && status.data?.status) {
throw redirect({ to: '/' })
}
},
component: SetupWizard,
})
+25
View File
@@ -0,0 +1,25 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute } from '@tanstack/react-router'
import { UserAgreement } from '@/features/legal'
export const Route = createFileRoute('/user-agreement')({
component: UserAgreement,
})