feat: multi-feature update
This commit is contained in:
+93
-51
@@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import * as React from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Check, ChevronsUpDown } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/lib/utils'
|
||||
@@ -150,10 +151,101 @@ export function ComboboxInput({
|
||||
item?.scrollIntoView({ block: 'nearest' })
|
||||
}, [highlightedIndex])
|
||||
|
||||
const [dropdownPos, setDropdownPos] = React.useState<{
|
||||
top: number
|
||||
left: number
|
||||
width: number
|
||||
} | null>(null)
|
||||
|
||||
const updateDropdownPos = React.useCallback(() => {
|
||||
if (!containerRef.current) return
|
||||
const rect = containerRef.current.getBoundingClientRect()
|
||||
setDropdownPos({
|
||||
top: rect.bottom + 4,
|
||||
left: rect.left,
|
||||
width: rect.width,
|
||||
})
|
||||
}, [])
|
||||
|
||||
// Update dropdown position when open
|
||||
React.useEffect(() => {
|
||||
if (!open) {
|
||||
setDropdownPos(null)
|
||||
return
|
||||
}
|
||||
updateDropdownPos()
|
||||
const handleScroll = () => updateDropdownPos()
|
||||
window.addEventListener('scroll', handleScroll, true)
|
||||
window.addEventListener('resize', handleScroll)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll, true)
|
||||
window.removeEventListener('resize', handleScroll)
|
||||
}
|
||||
}, [open, updateDropdownPos])
|
||||
|
||||
const showDropdown =
|
||||
open &&
|
||||
(filteredOptions.length > 0 || (allowCustomValue && searchValue.trim()))
|
||||
|
||||
const dropdownContent = showDropdown && dropdownPos ? (
|
||||
<div
|
||||
className='bg-popover text-popover-foreground fixed z-[100] rounded-md border shadow-md'
|
||||
style={{
|
||||
top: dropdownPos.top,
|
||||
left: dropdownPos.left,
|
||||
width: dropdownPos.width,
|
||||
}}
|
||||
>
|
||||
{filteredOptions.length > 0 ? (
|
||||
<ul
|
||||
ref={listRef}
|
||||
role='listbox'
|
||||
className='max-h-[200px] overflow-y-auto p-1'
|
||||
>
|
||||
{filteredOptions.map((option, index) => (
|
||||
<li
|
||||
key={option.value}
|
||||
role='option'
|
||||
aria-selected={value === option.value}
|
||||
data-highlighted={index === highlightedIndex}
|
||||
className={cn(
|
||||
'relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm select-none',
|
||||
index === highlightedIndex &&
|
||||
'bg-accent text-accent-foreground',
|
||||
value === option.value && 'font-medium'
|
||||
)}
|
||||
onMouseEnter={() => setHighlightedIndex(index)}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault() // Prevent blur
|
||||
handleSelect(option.value)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
'size-4 shrink-0',
|
||||
value === option.value ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
{option.icon && <span>{option.icon}</span>}
|
||||
<span className='truncate'>{option.label}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<div className='px-2 py-6 text-center text-sm'>
|
||||
{t(emptyText)}
|
||||
{allowCustomValue && searchValue.trim() && (
|
||||
<div className='text-muted-foreground mt-1 text-xs'>
|
||||
{t('Press Enter to use "{{value}}"', {
|
||||
value: searchValue.trim(),
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className='relative'>
|
||||
<Input
|
||||
@@ -184,57 +276,7 @@ export function ComboboxInput({
|
||||
/>
|
||||
<ChevronsUpDown className='pointer-events-none absolute top-1/2 right-3 size-4 shrink-0 -translate-y-1/2 opacity-50' />
|
||||
|
||||
{showDropdown && (
|
||||
<div className='bg-popover text-popover-foreground absolute top-full z-100 mt-1 w-full rounded-md border shadow-md'>
|
||||
{filteredOptions.length > 0 ? (
|
||||
<ul
|
||||
ref={listRef}
|
||||
role='listbox'
|
||||
className='max-h-[200px] overflow-y-auto p-1'
|
||||
>
|
||||
{filteredOptions.map((option, index) => (
|
||||
<li
|
||||
key={option.value}
|
||||
role='option'
|
||||
aria-selected={value === option.value}
|
||||
data-highlighted={index === highlightedIndex}
|
||||
className={cn(
|
||||
'relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm select-none',
|
||||
index === highlightedIndex &&
|
||||
'bg-accent text-accent-foreground',
|
||||
value === option.value && 'font-medium'
|
||||
)}
|
||||
onMouseEnter={() => setHighlightedIndex(index)}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault() // Prevent blur
|
||||
handleSelect(option.value)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
'size-4 shrink-0',
|
||||
value === option.value ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
{option.icon && <span>{option.icon}</span>}
|
||||
<span className='truncate'>{option.label}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<div className='px-2 py-6 text-center text-sm'>
|
||||
{t(emptyText)}
|
||||
{allowCustomValue && searchValue.trim() && (
|
||||
<div className='text-muted-foreground mt-1 text-xs'>
|
||||
{t('Press Enter to use "{{value}}"', {
|
||||
value: searchValue.trim(),
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{dropdownContent && createPortal(dropdownContent, document.body)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,31 @@ import {
|
||||
} from '../lib/oauth'
|
||||
import type { SystemStatus, CustomOAuthProviderInfo } from '../types'
|
||||
|
||||
/**
|
||||
* Generate a random code verifier for PKCE
|
||||
*/
|
||||
function generateCodeVerifier(): string {
|
||||
const array = new Uint8Array(32)
|
||||
crypto.getRandomValues(array)
|
||||
return btoa(String.fromCharCode(...array))
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=+$/, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate code challenge from code verifier using SHA-256
|
||||
*/
|
||||
async function generateCodeChallenge(verifier: string): Promise<string> {
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(verifier)
|
||||
const digest = await crypto.subtle.digest('SHA-256', data)
|
||||
return btoa(String.fromCharCode(...new Uint8Array(digest)))
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=+$/, '')
|
||||
}
|
||||
|
||||
type LogoutRequestConfig = AxiosRequestConfig & {
|
||||
skipErrorHandler?: boolean
|
||||
}
|
||||
@@ -211,6 +236,16 @@ export function useOAuthLogin(status: SystemStatus | null) {
|
||||
url.searchParams.set('scope', provider.scopes)
|
||||
}
|
||||
|
||||
// Add PKCE support if enabled
|
||||
if (provider.pkce_enabled) {
|
||||
const codeVerifier = generateCodeVerifier()
|
||||
const codeChallenge = await generateCodeChallenge(codeVerifier)
|
||||
// Store code_verifier in sessionStorage keyed by state
|
||||
sessionStorage.setItem(`pkce_verifier_${state}`, codeVerifier)
|
||||
url.searchParams.set('code_challenge', codeChallenge)
|
||||
url.searchParams.set('code_challenge_method', 'S256')
|
||||
}
|
||||
|
||||
window.open(url.toString(), '_self')
|
||||
} catch (_error) {
|
||||
toast.error(
|
||||
|
||||
+1
@@ -195,6 +195,7 @@ export interface CustomOAuthProviderInfo {
|
||||
client_id: string
|
||||
authorization_endpoint: string
|
||||
scopes: string
|
||||
pkce_enabled: boolean
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
Vendored
+9
-1
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import { api } from '@/lib/api'
|
||||
import type { HomePageContentResponse } from './types'
|
||||
import type { HomePageContentResponse, HomeStatsResponse } from './types'
|
||||
|
||||
// ============================================================================
|
||||
// Home Page APIs
|
||||
@@ -31,3 +31,11 @@ export async function getHomePageContent(): Promise<HomePageContentResponse> {
|
||||
const res = await api.get('/api/home_page_content')
|
||||
return res.data
|
||||
}
|
||||
|
||||
export async function getHomeStats(): Promise<HomeStatsResponse> {
|
||||
const res = await api.get('/api/home_stats', {
|
||||
skipBusinessError: true,
|
||||
skipErrorHandler: true,
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
|
||||
+150
-202
@@ -16,226 +16,174 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { CherryStudio } from '@lobehub/icons'
|
||||
import { ArrowRight, BookOpen } from 'lucide-react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStatus } from '@/hooks/use-status'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { HeroTerminalDemo } from '../hero-terminal-demo'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { getHomeStats } from '../../api'
|
||||
import type { HomeStats } from '../../types'
|
||||
import { HeroButtons } from '../hero-buttons'
|
||||
|
||||
interface HeroProps {
|
||||
className?: string
|
||||
isAuthenticated?: boolean
|
||||
}
|
||||
|
||||
// Stylized three-dots indicator representing "More"
|
||||
const MoreIcon = () => (
|
||||
<svg
|
||||
className='text-muted-foreground/60 group-hover:text-foreground size-6 shrink-0 transition-colors'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<circle cx='6' cy='12' r='2' fill='currentColor' />
|
||||
<circle cx='12' cy='12' r='2' fill='currentColor' />
|
||||
<circle cx='18' cy='12' r='2' fill='currentColor' />
|
||||
</svg>
|
||||
)
|
||||
interface UsageMetricProps {
|
||||
label: string
|
||||
value: string
|
||||
percent?: number
|
||||
}
|
||||
|
||||
function clampPercent(value: number | undefined): number {
|
||||
if (typeof value !== 'number' || Number.isNaN(value)) return 0
|
||||
return Math.max(0, Math.min(100, value))
|
||||
}
|
||||
|
||||
function formatPercent(value: number | undefined): string {
|
||||
return `${clampPercent(value).toFixed(2)}%`
|
||||
}
|
||||
|
||||
function formatBytes(value: number | undefined): string {
|
||||
if (!value) return '--'
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let size = value
|
||||
let unitIndex = 0
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024
|
||||
unitIndex += 1
|
||||
}
|
||||
|
||||
return `${size.toFixed(size >= 10 ? 0 : 1)}${units[unitIndex]}`
|
||||
}
|
||||
|
||||
function formatCompactNumber(value: number | undefined): string {
|
||||
if (typeof value !== 'number') return '--'
|
||||
return new Intl.NumberFormat(undefined, {
|
||||
notation: 'compact',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value)
|
||||
}
|
||||
|
||||
function getUsageColor(percent: number): string {
|
||||
if (percent >= 85) return 'bg-red-500'
|
||||
if (percent >= 65) return 'bg-orange-400'
|
||||
return 'bg-green-500'
|
||||
}
|
||||
|
||||
function UsageMetric(props: UsageMetricProps) {
|
||||
const percent = clampPercent(props.percent)
|
||||
|
||||
return (
|
||||
<div className='flex flex-col whitespace-nowrap sm:w-20 lg:w-16'>
|
||||
<p className='text-muted-foreground text-xs'>{props.label}</p>
|
||||
<div className='flex items-center gap-1 text-xs font-semibold'>{props.value}</div>
|
||||
{typeof props.percent === 'number' ? (
|
||||
<div
|
||||
aria-label={`${props.label} Server Usage Bar`}
|
||||
aria-valuemax={100}
|
||||
aria-valuemin={0}
|
||||
aria-valuenow={percent}
|
||||
className='bg-secondary relative mt-1 h-[3px] w-full overflow-hidden rounded-sm'
|
||||
role='progressbar'
|
||||
>
|
||||
<div
|
||||
className={cn('h-full w-full flex-1 transition-all', getUsageColor(percent))}
|
||||
style={{ transform: `translateX(-${100 - percent}%)` }}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ServerCard(props: {
|
||||
name: string
|
||||
region: string
|
||||
stats?: HomeStats
|
||||
tokenValue: string
|
||||
memoryValue: string
|
||||
}) {
|
||||
const cpuUsage = clampPercent(props.stats?.cpu_usage)
|
||||
const memoryUsage = clampPercent(props.stats?.memory_usage)
|
||||
|
||||
return (
|
||||
<div
|
||||
className='bg-card text-card-foreground flex cursor-pointer flex-col items-center justify-start gap-3 rounded-lg p-3 shadow-md shadow-stone-200/50 ring ring-stone-200 transition-all hover:shadow-sm hover:ring-stone-300 md:px-5 dark:shadow-none dark:ring-stone-800 dark:hover:ring-stone-700 lg:flex-row'
|
||||
>
|
||||
<section className='grid items-center gap-2 lg:w-44 [grid-template-columns:auto_auto_1fr]'>
|
||||
<span className='h-2 w-2 shrink-0 self-center rounded-full bg-green-500' />
|
||||
<div className='flex min-w-[24px] items-center justify-center rounded-sm bg-muted px-1 text-[10px] font-semibold text-muted-foreground'>
|
||||
{props.region}
|
||||
</div>
|
||||
<div className='relative flex flex-col'>
|
||||
<p className='break-normal text-xs font-bold tracking-tight'>{props.name}</p>
|
||||
<p className='hidden text-[11px] text-muted-foreground lg:block'>Online</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className='-mt-2 flex items-center gap-2 lg:hidden' />
|
||||
|
||||
<div className='flex flex-col items-center gap-2 lg:items-start'>
|
||||
<section className='flex flex-wrap items-center gap-x-4 gap-y-2 sm:gap-x-5'>
|
||||
<UsageMetric label='CPU' value={formatPercent(cpuUsage)} percent={cpuUsage} />
|
||||
<UsageMetric label='MEM' value={formatPercent(memoryUsage)} percent={memoryUsage} />
|
||||
<UsageMetric label='RAM' value={props.memoryValue} />
|
||||
<UsageMetric label='Tokens' value={props.tokenValue} />
|
||||
<UsageMetric label='Mode' value='Proxy' />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Hero(props: HeroProps) {
|
||||
const { t } = useTranslation()
|
||||
const { status } = useStatus()
|
||||
const docsUrl =
|
||||
(status?.docs_link as string | undefined) || 'https://docs.newapi.pro'
|
||||
|
||||
const renderDocsButton = () => {
|
||||
const isExternal = docsUrl.startsWith('http')
|
||||
if (isExternal) {
|
||||
return (
|
||||
<Button
|
||||
variant='outline'
|
||||
className='group border-border/50 hover:border-border hover:bg-muted/50 inline-flex h-11 items-center gap-1.5 rounded-lg px-5 text-sm font-medium'
|
||||
render={
|
||||
<a href={docsUrl} target='_blank' rel='noopener noreferrer' />
|
||||
}
|
||||
>
|
||||
<BookOpen className='text-muted-foreground/80 group-hover:text-foreground size-4 transition-colors duration-200' />
|
||||
<span>{t('Docs')}</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
variant='outline'
|
||||
className='group border-border/50 hover:border-border hover:bg-muted/50 inline-flex h-11 items-center gap-1.5 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to={docsUrl} />}
|
||||
>
|
||||
<BookOpen className='text-muted-foreground/80 group-hover:text-foreground size-4 transition-colors duration-200' />
|
||||
<span>{t('Docs')}</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
const statsQuery = useQuery({
|
||||
queryKey: ['home-stats'],
|
||||
queryFn: async () => {
|
||||
const response = await getHomeStats()
|
||||
return response.data
|
||||
},
|
||||
refetchInterval: 3 * 1000,
|
||||
staleTime: 2 * 1000,
|
||||
})
|
||||
const memoryValue = `${formatBytes(statsQuery.data?.memory_used)} / ${formatBytes(statsQuery.data?.memory_total)}`
|
||||
const tokenValue = formatCompactNumber(statsQuery.data?.total_tokens)
|
||||
|
||||
return (
|
||||
<section className='relative z-10 overflow-hidden px-6 pt-24 pb-16 md:pt-32 md:pb-24 lg:pt-36 lg:pb-28'>
|
||||
{/* Radial gradient background */}
|
||||
<div
|
||||
aria-hidden
|
||||
className='pointer-events-none absolute inset-0 -z-10 opacity-25 dark:opacity-[0.12]'
|
||||
style={{
|
||||
background: [
|
||||
'radial-gradient(ellipse 60% 50% at 20% 20%, oklch(0.72 0.18 250 / 80%) 0%, transparent 70%)',
|
||||
'radial-gradient(ellipse 50% 40% at 80% 15%, oklch(0.65 0.15 200 / 60%) 0%, transparent 70%)',
|
||||
'radial-gradient(ellipse 40% 35% at 40% 80%, oklch(0.70 0.12 280 / 40%) 0%, transparent 70%)',
|
||||
].join(', '),
|
||||
}}
|
||||
/>
|
||||
{/* Grid pattern */}
|
||||
<div
|
||||
aria-hidden
|
||||
className='absolute inset-0 -z-10 bg-[linear-gradient(to_right,var(--border)_1px,transparent_1px),linear-gradient(to_bottom,var(--border)_1px,transparent_1px)] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_30%,black_20%,transparent_100%)] bg-[size:4rem_4rem] opacity-[0.08]'
|
||||
/>
|
||||
|
||||
<div className='mx-auto grid max-w-6xl grid-cols-1 items-start gap-12 lg:grid-cols-12 lg:gap-8'>
|
||||
{/* Left Column: Title, description, action buttons and application support */}
|
||||
<div className='flex flex-col items-start text-left lg:col-span-6'>
|
||||
{/* Top Pill Badge */}
|
||||
<div
|
||||
className='landing-animate-fade-up mb-5 inline-flex items-center gap-1.5 rounded-full border border-blue-500/20 bg-blue-500/5 px-3 py-1.5 text-[11px] font-medium text-blue-600 opacity-0 shadow-xs dark:border-blue-400/20 dark:bg-blue-400/5 dark:text-blue-400'
|
||||
style={{ animationDelay: '0ms' }}
|
||||
>
|
||||
<span className='relative flex size-1.5'>
|
||||
<span className='absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-75' />
|
||||
<span className='relative inline-flex size-1.5 rounded-full bg-blue-500 dark:bg-blue-400' />
|
||||
</span>
|
||||
<span>{t('AI Application Infrastructure Foundation')}</span>
|
||||
<section
|
||||
className={cn(
|
||||
'relative min-h-screen overflow-hidden bg-stone-50 text-foreground dark:bg-stone-950',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<div className='absolute inset-0 bg-[radial-gradient(circle_at_top,rgba(120,113,108,0.16),transparent_34%)] dark:bg-[radial-gradient(circle_at_top,rgba(120,113,108,0.12),transparent_34%)]' />
|
||||
<div className='container relative mx-auto flex min-h-screen flex-col items-center justify-center px-6 py-24'>
|
||||
<div className='mx-auto max-w-3xl text-center'>
|
||||
<div className='mx-auto mb-5 inline-flex items-center gap-2 rounded-full bg-card px-3 py-1 text-xs text-muted-foreground shadow-sm ring ring-stone-200 dark:ring-stone-800'>
|
||||
<span className='h-2 w-2 rounded-full bg-green-500' />
|
||||
{statsQuery.isLoading ? t('Syncing') : t('Server Status')}
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className='landing-animate-fade-up text-[clamp(2.25rem,4.5vw,3.25rem)] leading-[1.15] font-bold tracking-tight'
|
||||
style={{ animationDelay: '60ms' }}
|
||||
>
|
||||
{t('Unified API Gateway for')}
|
||||
<br />
|
||||
<span className='bg-gradient-to-r from-blue-400 via-violet-400 to-purple-500 bg-clip-text text-transparent'>
|
||||
{t('Vast Range of AI Models')}
|
||||
</span>
|
||||
<h1 className='text-5xl font-black tracking-tight text-stone-950 md:text-7xl dark:text-stone-50'>
|
||||
Universe Federation
|
||||
</h1>
|
||||
<p
|
||||
className='landing-animate-fade-up text-muted-foreground/80 mt-5 max-w-xl text-base leading-relaxed opacity-0 md:text-[15px]'
|
||||
style={{ animationDelay: '120ms' }}
|
||||
>
|
||||
{t(
|
||||
'Access a vast selection of models via a standard, unified API protocol. Power AI applications, manage digital assets, and connect the Future.'
|
||||
)}
|
||||
<p className='mt-4 text-xl font-semibold text-stone-700 md:text-2xl dark:text-stone-300'>
|
||||
{t('伟大无需多言')}
|
||||
</p>
|
||||
|
||||
<div
|
||||
className='landing-animate-fade-up mt-8 flex flex-wrap items-center gap-3 opacity-0'
|
||||
style={{ animationDelay: '180ms' }}
|
||||
>
|
||||
{props.isAuthenticated ? (
|
||||
<>
|
||||
<Button
|
||||
className='group h-11 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to='/dashboard' />}
|
||||
>
|
||||
{t('Go to Dashboard')}
|
||||
<ArrowRight className='ml-1.5 size-4 transition-transform duration-200 group-hover:translate-x-0.5' />
|
||||
</Button>
|
||||
{renderDocsButton()}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
className='group h-11 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to='/sign-up' />}
|
||||
>
|
||||
{t('Get Started')}
|
||||
<ArrowRight className='ml-1.5 size-4 transition-transform duration-200 group-hover:translate-x-0.5' />
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='border-border/50 hover:border-border hover:bg-muted/50 h-11 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to='/pricing' />}
|
||||
>
|
||||
{t('View Pricing')}
|
||||
</Button>
|
||||
{renderDocsButton()}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Supported Apps (参考图二样式,进行卡片化和信息扩充设计,增加视觉高度) */}
|
||||
<div
|
||||
className='landing-animate-fade-up mt-10 w-full max-w-xl opacity-0'
|
||||
style={{ animationDelay: '240ms' }}
|
||||
>
|
||||
<div className='mb-4 flex flex-col gap-1'>
|
||||
<span className='text-muted-foreground/50 text-[10px] font-bold tracking-[0.15em] uppercase'>
|
||||
{t('Supported Applications')}
|
||||
</span>
|
||||
<p className='text-muted-foreground/60 text-xs leading-relaxed'>
|
||||
{t(
|
||||
'Supports one-click configuration and perfectly adapts to NewAPI multi-protocol configuration.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex flex-wrap items-center gap-3'>
|
||||
{/* Cherry Studio */}
|
||||
<a
|
||||
href='https://cherry-ai.com'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='group border-border/40 bg-muted/15 text-foreground/80 hover:border-border hover:bg-muted/30 hover:text-foreground flex items-center gap-3 rounded-full border px-5 py-2.5 text-sm font-medium shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:scale-[1.02]'
|
||||
>
|
||||
<CherryStudio.Color size={24} className='shrink-0' />
|
||||
<span>Cherry Studio</span>
|
||||
</a>
|
||||
|
||||
{/* CC Switch */}
|
||||
<a
|
||||
href='https://ccswitch.io'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='group border-border/40 bg-muted/15 text-foreground/80 hover:border-border hover:bg-muted/30 hover:text-foreground flex items-center gap-3 rounded-full border px-5 py-2.5 text-sm font-medium shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:scale-[1.02]'
|
||||
>
|
||||
<img
|
||||
src='https://ccswitch.io/favicon.png'
|
||||
alt='CC Switch'
|
||||
className='size-6 shrink-0 rounded-md object-contain'
|
||||
onError={(e) => {
|
||||
// Fallback to a styled text avatar if the remote favicon fails to load in sandbox or local environments
|
||||
e.currentTarget.style.display = 'none'
|
||||
const fallback = e.currentTarget.nextSibling as HTMLElement
|
||||
if (fallback) fallback.style.display = 'flex'
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{ display: 'none' }}
|
||||
className='size-6 shrink-0 items-center justify-center rounded-md bg-blue-500/10 text-[10px] font-bold text-blue-600 dark:bg-blue-400/10 dark:text-blue-400'
|
||||
>
|
||||
CC
|
||||
</span>
|
||||
<span>CC Switch</span>
|
||||
</a>
|
||||
|
||||
{/* "更多" */}
|
||||
<div className='group border-border/40 bg-muted/15 text-foreground/55 hover:border-border hover:bg-muted/30 hover:text-foreground flex cursor-default items-center gap-2.5 rounded-full border px-5 py-2.5 text-sm font-medium shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:scale-[1.02]'>
|
||||
<MoreIcon />
|
||||
<span>{t('More Apps')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-7 flex flex-col justify-center gap-3 sm:flex-row'>
|
||||
<HeroButtons isAuthenticated={!!props.isAuthenticated} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Hero Terminal API Demo */}
|
||||
<div
|
||||
className='landing-animate-fade-up flex w-full justify-center opacity-0 lg:col-span-6'
|
||||
style={{ animationDelay: '320ms' }}
|
||||
>
|
||||
<HeroTerminalDemo className='mt-8 lg:mt-0' />
|
||||
<div className='mt-12 grid w-full max-w-4xl gap-3'>
|
||||
<ServerCard
|
||||
name='BBLBB'
|
||||
region='CN'
|
||||
stats={statsQuery.data}
|
||||
tokenValue={tokenValue}
|
||||
memoryValue={memoryValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
+1
-7
@@ -20,8 +20,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useAuthStore } from '@/stores/auth-store'
|
||||
import { Markdown } from '@/components/ui/markdown'
|
||||
import { PublicLayout } from '@/components/layout'
|
||||
import { Footer } from '@/components/layout/components/footer'
|
||||
import { CTA, Features, Hero, HowItWorks, Stats } from './components'
|
||||
import { Hero } from './components'
|
||||
import { useHomePageContent } from './hooks'
|
||||
|
||||
export function Home() {
|
||||
@@ -63,11 +62,6 @@ export function Home() {
|
||||
return (
|
||||
<PublicLayout showMainContainer={false}>
|
||||
<Hero isAuthenticated={isAuthenticated} />
|
||||
<Stats />
|
||||
<Features />
|
||||
<HowItWorks />
|
||||
<CTA isAuthenticated={isAuthenticated} />
|
||||
<Footer />
|
||||
</PublicLayout>
|
||||
)
|
||||
}
|
||||
|
||||
+14
@@ -37,3 +37,17 @@ export interface HomePageContentResult {
|
||||
isLoaded: boolean
|
||||
isUrl: boolean
|
||||
}
|
||||
|
||||
export interface HomeStats {
|
||||
cpu_usage: number
|
||||
memory_usage: number
|
||||
memory_total: number
|
||||
memory_used: number
|
||||
total_tokens: number
|
||||
}
|
||||
|
||||
export interface HomeStatsResponse {
|
||||
success: boolean
|
||||
message?: string
|
||||
data?: HomeStats
|
||||
}
|
||||
|
||||
Vendored
+24
@@ -96,6 +96,7 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
|
||||
email_field: '',
|
||||
well_known: '',
|
||||
auth_style: 0,
|
||||
pkce_enabled: false,
|
||||
access_policy: '',
|
||||
access_denied_message: '',
|
||||
},
|
||||
@@ -120,6 +121,7 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
|
||||
email_field: props.provider.email_field || '',
|
||||
well_known: props.provider.well_known || '',
|
||||
auth_style: props.provider.auth_style ?? 0,
|
||||
pkce_enabled: props.provider.pkce_enabled ?? false,
|
||||
access_policy: props.provider.access_policy || '',
|
||||
access_denied_message: props.provider.access_denied_message || '',
|
||||
})
|
||||
@@ -141,6 +143,7 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
|
||||
email_field: '',
|
||||
well_known: '',
|
||||
auth_style: 0,
|
||||
pkce_enabled: false,
|
||||
access_policy: '',
|
||||
access_denied_message: '',
|
||||
})
|
||||
@@ -373,6 +376,27 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='pkce_enabled'
|
||||
render={({ field }) => (
|
||||
<SettingsSwitchItem>
|
||||
<SettingsSwitchContent>
|
||||
<FormLabel>{t('Enable PKCE')}</FormLabel>
|
||||
<FormDescription>
|
||||
{t('Use PKCE (Proof Key for Code Exchange) for enhanced security. Required for some providers like Mastodon/Akkoma.')}
|
||||
</FormDescription>
|
||||
</SettingsSwitchContent>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</SettingsSwitchItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface CustomOAuthProvider {
|
||||
email_field: string
|
||||
well_known: string
|
||||
auth_style: number // 0=auto, 1=params, 2=header
|
||||
pkce_enabled: boolean
|
||||
access_policy: string
|
||||
access_denied_message: string
|
||||
}
|
||||
@@ -73,6 +74,7 @@ export const customOAuthFormSchema = z.object({
|
||||
email_field: z.string().optional().default(''),
|
||||
well_known: z.string().optional().default(''),
|
||||
auth_style: z.number().int().min(0).max(2).default(0),
|
||||
pkce_enabled: z.boolean().default(false),
|
||||
access_policy: z.string().optional().default(''),
|
||||
access_denied_message: z.string().optional().default(''),
|
||||
})
|
||||
|
||||
Vendored
+20
-3
@@ -93,6 +93,7 @@
|
||||
"30 Days": "30 Days",
|
||||
"30 days ago": "30 days ago",
|
||||
"30d change": "30d change",
|
||||
"30s refresh": "30s refresh",
|
||||
"5 minutes": "5 minutes",
|
||||
"5-Hour Window": "5-Hour Window",
|
||||
"50 / page": "50 / page",
|
||||
@@ -101,6 +102,7 @@
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "A billing multiplier. Lower ratios mean lower API call costs.",
|
||||
"A focused home for keys, balance, routing, and service health.": "A focused home for keys, balance, routing, and service health.",
|
||||
"A high-throughput AI API gateway with real-time capacity, resilient routing, and transparent token consumption at the very first glance.": "A high-throughput AI API gateway with real-time capacity, resilient routing, and transparent token consumption at the very first glance.",
|
||||
"About": "About",
|
||||
"About {{days}} days left": "About {{days}} days left",
|
||||
"Accept Unpriced Models": "Accept Unpriced Models",
|
||||
@@ -462,6 +464,7 @@
|
||||
"Availability (last 24h)": "Availability (last 24h)",
|
||||
"Available": "Available",
|
||||
"Available disk space": "Available disk space",
|
||||
"Available headroom": "Available headroom",
|
||||
"Available Models": "Available Models",
|
||||
"Available Rewards": "Available Rewards",
|
||||
"Average latency": "Average latency",
|
||||
@@ -815,6 +818,7 @@
|
||||
"Compliance confirmation required": "Compliance confirmation required",
|
||||
"Compliance confirmed": "Compliance confirmed",
|
||||
"Compliance confirmed successfully": "Compliance confirmed successfully",
|
||||
"Compute usage": "Compute usage",
|
||||
"Concatenate channel system prompt with user's prompt": "Concatenate channel system prompt with user's prompt",
|
||||
"Condition Path": "Condition Path",
|
||||
"Condition Settings": "Condition Settings",
|
||||
@@ -1348,6 +1352,7 @@
|
||||
"edit_this": "edit_this",
|
||||
"Editor mode": "Editor mode",
|
||||
"Education": "Education",
|
||||
"Elastic compute headroom": "Elastic compute headroom",
|
||||
"Email": "Email",
|
||||
"Email (required for verification)": "Email (required for verification)",
|
||||
"Email Address": "Email Address",
|
||||
@@ -1528,8 +1533,8 @@
|
||||
"Exists": "Exists",
|
||||
"Expand": "Expand",
|
||||
"Expand All": "Expand All",
|
||||
"Expected a JSON array.": "Expected a JSON array.",
|
||||
"Expected a JSON array of group identifiers": "Expected a JSON array of group identifiers",
|
||||
"Expected a JSON array.": "Expected a JSON array.",
|
||||
"Experiment with prompts and models in real time.": "Experiment with prompts and models in real time.",
|
||||
"Expiration Time": "Expiration Time",
|
||||
"expired": "expired",
|
||||
@@ -1816,6 +1821,7 @@
|
||||
"Full width": "Full width",
|
||||
"Function calling": "Function calling",
|
||||
"Functions": "Functions",
|
||||
"Gateway Load": "Gateway Load",
|
||||
"GC Count": "GC Count",
|
||||
"GC executed": "GC executed",
|
||||
"GC execution failed": "GC execution failed",
|
||||
@@ -1925,6 +1931,7 @@
|
||||
"header. Anthropic-formatted endpoints accept the": "header. Anthropic-formatted endpoints accept the",
|
||||
"Health": "Health",
|
||||
"Healthy": "Healthy",
|
||||
"HHHL AI Gateway": "Universe Federation",
|
||||
"Hidden — verify to reveal": "Hidden — verify to reveal",
|
||||
"Hide": "Hide",
|
||||
"Hide API key": "Hide API key",
|
||||
@@ -2198,6 +2205,9 @@
|
||||
"List of models supported by this channel. Use comma to separate multiple models.": "List of models supported by this channel. Use comma to separate multiple models.",
|
||||
"List of origins (one per line) allowed for Passkey registration and authentication.": "List of origins (one per line) allowed for Passkey registration and authentication.",
|
||||
"List view": "List view",
|
||||
"Live capacity telemetry": "Live capacity telemetry",
|
||||
"Live resource telemetry": "Live resource telemetry",
|
||||
"Live Status": "Live Status",
|
||||
"LLM Leaderboard": "LLM Leaderboard",
|
||||
"LLM prompt helper": "LLM prompt helper",
|
||||
"Load Balancing": "Load Balancing",
|
||||
@@ -2295,6 +2305,7 @@
|
||||
"Media pricing": "Media pricing",
|
||||
"Median time-to-first-token (TTFT) sampled hourly per group": "Median time-to-first-token (TTFT) sampled hourly per group",
|
||||
"Medical Q&A, mental health support": "Medical Q&A, mental health support",
|
||||
"Memory Capacity": "Memory Capacity",
|
||||
"Memory Hits": "Memory Hits",
|
||||
"Memory Threshold (%)": "Memory Threshold (%)",
|
||||
"Merchant ID": "Merchant ID",
|
||||
@@ -2646,6 +2657,7 @@
|
||||
"No Users Found": "No Users Found",
|
||||
"No vendor data available": "No vendor data available",
|
||||
"No X Found": "No X Found",
|
||||
"Node": "Node",
|
||||
"Node Name": "Node Name",
|
||||
"Non-stream": "Non-stream",
|
||||
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.",
|
||||
@@ -3193,6 +3205,7 @@
|
||||
"Reasoning Effort": "Reasoning Effort",
|
||||
"Receive Upstream Model Update Notifications": "Receive Upstream Model Update Notifications",
|
||||
"Received": "Received",
|
||||
"Received amount": "Received amount",
|
||||
"Recently launched models": "Recently launched models",
|
||||
"Recently launched models gaining traction": "Recently launched models gaining traction",
|
||||
"Recharge": "Recharge",
|
||||
@@ -3209,7 +3222,6 @@
|
||||
"Redeem codes": "Redeem codes",
|
||||
"Redeemed By": "Redeemed By",
|
||||
"Redeemed:": "Redeemed:",
|
||||
"Received amount": "Received amount",
|
||||
"redemption code": "redemption code",
|
||||
"Redemption Code": "Redemption Code",
|
||||
"Redemption code deleted successfully": "Redemption code deleted successfully",
|
||||
@@ -3611,6 +3623,8 @@
|
||||
"Server IP": "Server IP",
|
||||
"Server Log Management": "Server Log Management",
|
||||
"Server logging is not enabled (log directory not configured)": "Server logging is not enabled (log directory not configured)",
|
||||
"Server Power Core": "Server Power Core",
|
||||
"Server Status": "Server Status",
|
||||
"Server Token": "Server Token",
|
||||
"Service account JSON file(s)": "Service account JSON file(s)",
|
||||
"Session expired!": "Session expired!",
|
||||
@@ -3828,6 +3842,7 @@
|
||||
"Sync Upstream": "Sync Upstream",
|
||||
"Sync Upstream Models": "Sync Upstream Models",
|
||||
"Synchronize models and vendors from an upstream source": "Synchronize models and vendors from an upstream source",
|
||||
"Syncing": "Syncing",
|
||||
"Syncing prices, please wait...": "Syncing prices, please wait...",
|
||||
"Syncing...": "Syncing...",
|
||||
"System": "System",
|
||||
@@ -4108,6 +4123,7 @@
|
||||
"Total requests made": "Total requests made",
|
||||
"Total tokens": "Total tokens",
|
||||
"Total Tokens": "Total Tokens",
|
||||
"Total Tokens Burned": "Total Tokens Burned",
|
||||
"Total Usage": "Total Usage",
|
||||
"Total:": "Total:",
|
||||
"TPM": "TPM",
|
||||
@@ -4538,6 +4554,7 @@
|
||||
"Zero retention": "Zero retention",
|
||||
"Zhipu": "Zhipu",
|
||||
"Zhipu V4": "Zhipu V4",
|
||||
"Zoom": "Zoom"
|
||||
"Zoom": "Zoom",
|
||||
"伟大无需多言": "Greatness Needs No Words"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+22
-5
@@ -93,6 +93,7 @@
|
||||
"30 Days": "30 jours",
|
||||
"30 days ago": "Il y a 30 jours",
|
||||
"30d change": "Variation 30 j",
|
||||
"30s refresh": "Actualisation 30 s",
|
||||
"5 minutes": "5 minutes",
|
||||
"5-Hour Window": "Fenêtre de 5 heures",
|
||||
"50 / page": "50 / page",
|
||||
@@ -101,6 +102,7 @@
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "Un multiplicateur de facturation. Plus le ratio est faible, plus le coût des appels API est bas.",
|
||||
"A focused home for keys, balance, routing, and service health.": "Un accueil dédié aux clés, au solde, au routage et à l'état du service.",
|
||||
"A high-throughput AI API gateway with real-time capacity, resilient routing, and transparent token consumption at the very first glance.": "Une passerelle API IA à haut débit avec capacité en temps réel, routage résilient et consommation de tokens transparente dès le premier regard.",
|
||||
"About": "À propos",
|
||||
"About {{days}} days left": "Environ {{days}} jours restants",
|
||||
"Accept Unpriced Models": "Accepter les modèles non tarifés",
|
||||
@@ -462,6 +464,7 @@
|
||||
"Availability (last 24h)": "Disponibilité (dernières 24 h)",
|
||||
"Available": "Disponible",
|
||||
"Available disk space": "Espace disque disponible",
|
||||
"Available headroom": "Marge disponible",
|
||||
"Available Models": "Modèles disponibles",
|
||||
"Available Rewards": "Récompenses disponibles",
|
||||
"Average latency": "Latence moyenne",
|
||||
@@ -815,6 +818,7 @@
|
||||
"Compliance confirmation required": "Confirmation de conformité requise",
|
||||
"Compliance confirmed": "Conformité confirmée",
|
||||
"Compliance confirmed successfully": "Conformité confirmée avec succès",
|
||||
"Compute usage": "Utilisation du calcul",
|
||||
"Concatenate channel system prompt with user's prompt": "Concaténer l'invite système du canal avec l'invite de l'utilisateur",
|
||||
"Condition Path": "Chemin de condition",
|
||||
"Condition Settings": "Paramètres de condition",
|
||||
@@ -1348,6 +1352,7 @@
|
||||
"edit_this": "modifier_ceci",
|
||||
"Editor mode": "Mode éditeur",
|
||||
"Education": "Éducation",
|
||||
"Elastic compute headroom": "Marge de calcul élastique",
|
||||
"Email": "E-mail",
|
||||
"Email (required for verification)": "E-mail (requis pour la vérification)",
|
||||
"Email Address": "Adresse e-mail",
|
||||
@@ -1528,8 +1533,8 @@
|
||||
"Exists": "Existe",
|
||||
"Expand": "Développer",
|
||||
"Expand All": "Tout développer",
|
||||
"Expected a JSON array.": "Un tableau JSON est attendu.",
|
||||
"Expected a JSON array of group identifiers": "Un tableau JSON d'identifiants de groupe est attendu",
|
||||
"Expected a JSON array.": "Un tableau JSON est attendu.",
|
||||
"Experiment with prompts and models in real time.": "Expérimentez avec des prompts et des modèles en temps réel.",
|
||||
"Expiration Time": "Heure d'expiration",
|
||||
"expired": "expiré",
|
||||
@@ -1816,6 +1821,7 @@
|
||||
"Full width": "Pleine largeur",
|
||||
"Function calling": "Appel de fonction",
|
||||
"Functions": "Fonctions",
|
||||
"Gateway Load": "Charge de la passerelle",
|
||||
"GC Count": "Nombre de GC",
|
||||
"GC executed": "GC exécuté",
|
||||
"GC execution failed": "Échec de l'exécution du GC",
|
||||
@@ -1924,7 +1930,8 @@
|
||||
"Header Value (supports string or JSON mapping)": "Valeur de l'en-tête (chaîne ou mappage JSON)",
|
||||
"header. Anthropic-formatted endpoints accept the": ". Les points de terminaison au format Anthropic acceptent à la place",
|
||||
"Health": "Santé",
|
||||
"Healthy": "Normal",
|
||||
"Healthy": "Sain",
|
||||
"HHHL AI Gateway": "Fédération de l'Univers",
|
||||
"Hidden — verify to reveal": "Masqué — vérifiez pour révéler",
|
||||
"Hide": "Masquer",
|
||||
"Hide API key": "Masquer la clé API",
|
||||
@@ -2198,6 +2205,9 @@
|
||||
"List of models supported by this channel. Use comma to separate multiple models.": "Liste des modèles pris en charge par ce canal. Utilisez une virgule pour séparer plusieurs modèles.",
|
||||
"List of origins (one per line) allowed for Passkey registration and authentication.": "Liste des origines (une par ligne) autorisées pour l'enregistrement et l'authentification des clés d'accès (Passkey).",
|
||||
"List view": "Vue en liste",
|
||||
"Live capacity telemetry": "Télémétrie de capacité en direct",
|
||||
"Live resource telemetry": "Télémétrie des ressources en direct",
|
||||
"Live Status": "État en direct",
|
||||
"LLM Leaderboard": "Classement des LLM",
|
||||
"LLM prompt helper": "Assistant prompt LLM",
|
||||
"Load Balancing": "Équilibrage de charge",
|
||||
@@ -2295,6 +2305,7 @@
|
||||
"Media pricing": "Tarification multimédia",
|
||||
"Median time-to-first-token (TTFT) sampled hourly per group": "Latence médiane jusqu'au premier jeton (TTFT) échantillonnée par heure et par groupe",
|
||||
"Medical Q&A, mental health support": "Q&R médicales, soutien en santé mentale",
|
||||
"Memory Capacity": "Capacité mémoire",
|
||||
"Memory Hits": "Hits mémoire",
|
||||
"Memory Threshold (%)": "Seuil mémoire (%)",
|
||||
"Merchant ID": "ID du commerçant",
|
||||
@@ -2646,6 +2657,7 @@
|
||||
"No Users Found": "Aucun utilisateur trouvé",
|
||||
"No vendor data available": "Aucune donnée de fournisseur disponible",
|
||||
"No X Found": "Aucun X trouvé",
|
||||
"Node": "Nœud",
|
||||
"Node Name": "Nom du nœud",
|
||||
"Non-stream": "Non-streaming",
|
||||
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Les récompenses d’invitation non nulles nécessitent une confirmation de conformité dans les paramètres de la passerelle de paiement.",
|
||||
@@ -3193,6 +3205,7 @@
|
||||
"Reasoning Effort": "Effort de raisonnement",
|
||||
"Receive Upstream Model Update Notifications": "Recevoir les notifications de mise à jour des modèles en amont",
|
||||
"Received": "Reçu",
|
||||
"Received amount": "Montant reçu",
|
||||
"Recently launched models": "Modèles récemment lancés",
|
||||
"Recently launched models gaining traction": "Modèles récemment publiés et en forte progression",
|
||||
"Recharge": "Recharger",
|
||||
@@ -3209,7 +3222,6 @@
|
||||
"Redeem codes": "Échanger des codes",
|
||||
"Redeemed By": "Utilisé par",
|
||||
"Redeemed:": "Utilisé :",
|
||||
"Received amount": "Montant reçu",
|
||||
"redemption code": "code d'échange",
|
||||
"Redemption Code": "Code d'échange",
|
||||
"Redemption code deleted successfully": "Code d'échange supprimé avec succès",
|
||||
@@ -3232,7 +3244,7 @@
|
||||
"Referral Program": "Programme de parrainage",
|
||||
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "Le transfert des récompenses de parrainage est désactivé jusqu’à ce que l’administrateur confirme les conditions de conformité.",
|
||||
"Refine models by provider, group, type, and tags.": "Affinez les modèles par fournisseur, groupe, type et tags.",
|
||||
"Refresh": "Actualiser",
|
||||
"Refresh": "Actualisation",
|
||||
"Refresh Cache": "Actualiser le cache",
|
||||
"Refresh credential": "Actualiser l'identifiant",
|
||||
"Refresh failed": "Échec de l'actualisation",
|
||||
@@ -3611,6 +3623,8 @@
|
||||
"Server IP": "IP du serveur",
|
||||
"Server Log Management": "Gestion des journaux serveur",
|
||||
"Server logging is not enabled (log directory not configured)": "La journalisation serveur n'est pas activée (répertoire non configuré)",
|
||||
"Server Power Core": "Cœur de puissance serveur",
|
||||
"Server Status": "État du serveur",
|
||||
"Server Token": "Jeton de serveur",
|
||||
"Service account JSON file(s)": "Fichier(s) JSON de compte de service",
|
||||
"Session expired!": "Session expirée !",
|
||||
@@ -3828,6 +3842,7 @@
|
||||
"Sync Upstream": "Synchroniser l'amont",
|
||||
"Sync Upstream Models": "Synchroniser les modèles amont",
|
||||
"Synchronize models and vendors from an upstream source": "Synchroniser les modèles et les fournisseurs à partir d'une source amont",
|
||||
"Syncing": "Synchronisation",
|
||||
"Syncing prices, please wait...": "Synchronisation des prix, veuillez patienter...",
|
||||
"Syncing...": "Synchronisation...",
|
||||
"System": "Système",
|
||||
@@ -4108,6 +4123,7 @@
|
||||
"Total requests made": "Requêtes totales effectuées",
|
||||
"Total tokens": "Jetons totaux",
|
||||
"Total Tokens": "Jetons totaux",
|
||||
"Total Tokens Burned": "Total des tokens consommés",
|
||||
"Total Usage": "Utilisation totale",
|
||||
"Total:": "Total :",
|
||||
"TPM": "TPM",
|
||||
@@ -4538,6 +4554,7 @@
|
||||
"Zero retention": "Aucune rétention",
|
||||
"Zhipu": "Zhipu",
|
||||
"Zhipu V4": "Zhipu V4",
|
||||
"Zoom": "Zoom"
|
||||
"Zoom": "Zoom",
|
||||
"伟大无需多言": "La grandeur se passe de mots"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+20
-3
@@ -93,6 +93,7 @@
|
||||
"30 Days": "30日",
|
||||
"30 days ago": "30日前",
|
||||
"30d change": "30日変化",
|
||||
"30s refresh": "30秒更新",
|
||||
"5 minutes": "5 分",
|
||||
"5-Hour Window": "5時間ウィンドウ",
|
||||
"50 / page": "50 / ページ",
|
||||
@@ -101,6 +102,7 @@
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "課金倍率です。倍率が低いほど API 呼び出しコストは低くなります。",
|
||||
"A focused home for keys, balance, routing, and service health.": "キー、残高、ルーティング、サービス状態を集約したホームです。",
|
||||
"A high-throughput AI API gateway with real-time capacity, resilient routing, and transparent token consumption at the very first glance.": "リアルタイム容量、耐障害ルーティング、明瞭なトークン消費をファーストビューで示す高スループット AI API ゲートウェイです。",
|
||||
"About": "このサービスについて",
|
||||
"About {{days}} days left": "約 {{days}} 日分",
|
||||
"Accept Unpriced Models": "価格設定されていないモデルを許可",
|
||||
@@ -462,6 +464,7 @@
|
||||
"Availability (last 24h)": "可用性(過去 24 時間)",
|
||||
"Available": "空き",
|
||||
"Available disk space": "利用可能なディスク容量",
|
||||
"Available headroom": "利用可能な余力",
|
||||
"Available Models": "利用可能なモデル",
|
||||
"Available Rewards": "利用可能な報酬",
|
||||
"Average latency": "平均レイテンシ",
|
||||
@@ -815,6 +818,7 @@
|
||||
"Compliance confirmation required": "コンプライアンス確認が必要です",
|
||||
"Compliance confirmed": "コンプライアンス確認済み",
|
||||
"Compliance confirmed successfully": "コンプライアンス確認が完了しました",
|
||||
"Compute usage": "計算使用率",
|
||||
"Concatenate channel system prompt with user's prompt": "チャネルのシステムプロンプトをユーザーのプロンプトと連結する",
|
||||
"Condition Path": "条件パス",
|
||||
"Condition Settings": "条件設定",
|
||||
@@ -1348,6 +1352,7 @@
|
||||
"edit_this": "edit_this",
|
||||
"Editor mode": "エディターモード",
|
||||
"Education": "教育",
|
||||
"Elastic compute headroom": "弾力的な計算余力",
|
||||
"Email": "メールアドレス",
|
||||
"Email (required for verification)": "メールアドレス(認証に必須)",
|
||||
"Email Address": "メールアドレス",
|
||||
@@ -1528,8 +1533,8 @@
|
||||
"Exists": "存在",
|
||||
"Expand": "展開",
|
||||
"Expand All": "すべて展開",
|
||||
"Expected a JSON array.": "JSON 配列が必要です。",
|
||||
"Expected a JSON array of group identifiers": "グループ識別子の JSON 配列が必要です",
|
||||
"Expected a JSON array.": "JSON 配列が必要です。",
|
||||
"Experiment with prompts and models in real time.": "プロンプトとモデルをリアルタイムで実験する。",
|
||||
"Expiration Time": "有効期限",
|
||||
"expired": "期限切れ",
|
||||
@@ -1816,6 +1821,7 @@
|
||||
"Full width": "全幅",
|
||||
"Function calling": "関数呼び出し",
|
||||
"Functions": "関数",
|
||||
"Gateway Load": "ゲートウェイ負荷",
|
||||
"GC Count": "GC 回数",
|
||||
"GC executed": "GC 実行完了",
|
||||
"GC execution failed": "GC 実行失敗",
|
||||
@@ -1925,6 +1931,7 @@
|
||||
"header. Anthropic-formatted endpoints accept the": " ヘッダーが必要です。Anthropic 形式のエンドポイントでは",
|
||||
"Health": "ヘルスケア",
|
||||
"Healthy": "正常",
|
||||
"HHHL AI Gateway": "ユニバースフェデレーション",
|
||||
"Hidden — verify to reveal": "非表示 — 確認して表示",
|
||||
"Hide": "非表示にする",
|
||||
"Hide API key": "APIキーを非表示",
|
||||
@@ -2198,6 +2205,9 @@
|
||||
"List of models supported by this channel. Use comma to separate multiple models.": "このチャネルがサポートするモデルのリストです。複数のモデルはカンマで区切ってください。",
|
||||
"List of origins (one per line) allowed for Passkey registration and authentication.": "Passkeyの登録と認証が許可されているオリジン(1行に1つ)のリスト。",
|
||||
"List view": "リスト表示",
|
||||
"Live capacity telemetry": "ライブ容量テレメトリ",
|
||||
"Live resource telemetry": "ライブリソーステレメトリ",
|
||||
"Live Status": "ライブ状態",
|
||||
"LLM Leaderboard": "LLM リーダーボード",
|
||||
"LLM prompt helper": "LLMプロンプトヘルパー",
|
||||
"Load Balancing": "ロードバランシング",
|
||||
@@ -2295,6 +2305,7 @@
|
||||
"Media pricing": "メディア料金",
|
||||
"Median time-to-first-token (TTFT) sampled hourly per group": "グループ別に毎時サンプリングした最初のトークンまでの中央値レイテンシ (TTFT)",
|
||||
"Medical Q&A, mental health support": "医療Q&A・メンタルヘルスサポート",
|
||||
"Memory Capacity": "メモリ容量",
|
||||
"Memory Hits": "メモリヒット",
|
||||
"Memory Threshold (%)": "メモリ閾値 (%)",
|
||||
"Merchant ID": "マーチャントID",
|
||||
@@ -2646,6 +2657,7 @@
|
||||
"No Users Found": "ユーザーが見つかりません",
|
||||
"No vendor data available": "ベンダーデータがありません",
|
||||
"No X Found": "X が見つかりません",
|
||||
"Node": "ノード",
|
||||
"Node Name": "ノード名",
|
||||
"Non-stream": "非ストリーミング",
|
||||
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "0 以外の招待報酬には、支払いゲートウェイ設定でのコンプライアンス確認が必要です。",
|
||||
@@ -3193,6 +3205,7 @@
|
||||
"Reasoning Effort": "推論強度",
|
||||
"Receive Upstream Model Update Notifications": "アップストリームモデル更新通知を受け取る",
|
||||
"Received": "受信済み",
|
||||
"Received amount": "受け取り額",
|
||||
"Recently launched models": "最近リリースされたモデル",
|
||||
"Recently launched models gaining traction": "最近リリースされ勢いのあるモデル",
|
||||
"Recharge": "チャージ",
|
||||
@@ -3209,7 +3222,6 @@
|
||||
"Redeem codes": "コードを交換",
|
||||
"Redeemed By": "引き換え元",
|
||||
"Redeemed:": "引き換え済み:",
|
||||
"Received amount": "受け取り額",
|
||||
"redemption code": "引き換えコード",
|
||||
"Redemption Code": "引き換えコード",
|
||||
"Redemption code deleted successfully": "引き換えコードを正常に削除しました",
|
||||
@@ -3611,6 +3623,8 @@
|
||||
"Server IP": "サーバー IP",
|
||||
"Server Log Management": "サーバーログ管理",
|
||||
"Server logging is not enabled (log directory not configured)": "サーバーログが有効になっていません(ログディレクトリが未設定)",
|
||||
"Server Power Core": "サーバーパワーコア",
|
||||
"Server Status": "サーバー状態",
|
||||
"Server Token": "サーバートークン",
|
||||
"Service account JSON file(s)": "サービスアカウントJSONファイル",
|
||||
"Session expired!": "セッションが期限切れです!",
|
||||
@@ -3828,6 +3842,7 @@
|
||||
"Sync Upstream": "アップストリームを同期",
|
||||
"Sync Upstream Models": "アップストリームモデルを同期",
|
||||
"Synchronize models and vendors from an upstream source": "アップストリームソースからモデルとベンダーを同期",
|
||||
"Syncing": "同期中",
|
||||
"Syncing prices, please wait...": "価格を同期中、しばらくお待ちください...",
|
||||
"Syncing...": "同期中...",
|
||||
"System": "システム",
|
||||
@@ -4108,6 +4123,7 @@
|
||||
"Total requests made": "合計リクエスト数",
|
||||
"Total tokens": "合計トークン",
|
||||
"Total Tokens": "合計トークン",
|
||||
"Total Tokens Burned": "総消費トークン",
|
||||
"Total Usage": "総使用量",
|
||||
"Total:": "合計:",
|
||||
"TPM": "TPM",
|
||||
@@ -4538,6 +4554,7 @@
|
||||
"Zero retention": "データ保持なし",
|
||||
"Zhipu": "Zhipu",
|
||||
"Zhipu V4": "Zhipu V 4",
|
||||
"Zoom": "ズーム"
|
||||
"Zoom": "ズーム",
|
||||
"伟大无需多言": "偉大さに言葉はいらない"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+21
-4
@@ -93,6 +93,7 @@
|
||||
"30 Days": "30 дней",
|
||||
"30 days ago": "30 дней назад",
|
||||
"30d change": "Изменение за 30 дней",
|
||||
"30s refresh": "Обновление 30 с",
|
||||
"5 minutes": "5 минут",
|
||||
"5-Hour Window": "5-часовое окно",
|
||||
"50 / page": "50 / страница",
|
||||
@@ -101,6 +102,7 @@
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "Множитель тарификации. Чем ниже коэффициент, тем ниже стоимость вызовов API.",
|
||||
"A focused home for keys, balance, routing, and service health.": "Единый экран для ключей, баланса, маршрутов и состояния сервиса.",
|
||||
"A high-throughput AI API gateway with real-time capacity, resilient routing, and transparent token consumption at the very first glance.": "Высокопроизводительный AI API-шлюз с емкостью в реальном времени, отказоустойчивой маршрутизацией и прозрачным расходом токенов уже на первом экране.",
|
||||
"About": "О проекте",
|
||||
"About {{days}} days left": "Примерно {{days}} дней",
|
||||
"Accept Unpriced Models": "Принимать модели без цены",
|
||||
@@ -462,6 +464,7 @@
|
||||
"Availability (last 24h)": "Доступность (последние 24 ч)",
|
||||
"Available": "Доступно",
|
||||
"Available disk space": "Доступное дисковое пространство",
|
||||
"Available headroom": "Доступный резерв",
|
||||
"Available Models": "Доступные модели",
|
||||
"Available Rewards": "Доступные награды",
|
||||
"Average latency": "Средняя задержка",
|
||||
@@ -815,6 +818,7 @@
|
||||
"Compliance confirmation required": "Требуется подтверждение соответствия",
|
||||
"Compliance confirmed": "Соответствие подтверждено",
|
||||
"Compliance confirmed successfully": "Соответствие успешно подтверждено",
|
||||
"Compute usage": "Использование вычислений",
|
||||
"Concatenate channel system prompt with user's prompt": "Объединить системный промпт канала с промптом пользователя",
|
||||
"Condition Path": "Путь условия",
|
||||
"Condition Settings": "Настройки условия",
|
||||
@@ -1348,6 +1352,7 @@
|
||||
"edit_this": "изменить_это",
|
||||
"Editor mode": "Режим редактора",
|
||||
"Education": "Образование",
|
||||
"Elastic compute headroom": "Эластичный запас вычислений",
|
||||
"Email": "Электронная почта",
|
||||
"Email (required for verification)": "Email (требуется для верификации)",
|
||||
"Email Address": "Адрес электронной почты",
|
||||
@@ -1528,8 +1533,8 @@
|
||||
"Exists": "Существует",
|
||||
"Expand": "Развернуть",
|
||||
"Expand All": "Развернуть все",
|
||||
"Expected a JSON array.": "Ожидается JSON-массив.",
|
||||
"Expected a JSON array of group identifiers": "Ожидается JSON-массив идентификаторов групп",
|
||||
"Expected a JSON array.": "Ожидается JSON-массив.",
|
||||
"Experiment with prompts and models in real time.": "Экспериментируйте с промптами и моделями в реальном времени.",
|
||||
"Expiration Time": "Время истечения срока действия",
|
||||
"expired": "истек",
|
||||
@@ -1816,6 +1821,7 @@
|
||||
"Full width": "Полная ширина",
|
||||
"Function calling": "Вызов функций",
|
||||
"Functions": "Функции",
|
||||
"Gateway Load": "Нагрузка шлюза",
|
||||
"GC Count": "Кол-во GC",
|
||||
"GC executed": "GC выполнен",
|
||||
"GC execution failed": "Ошибка выполнения GC",
|
||||
@@ -1925,6 +1931,7 @@
|
||||
"header. Anthropic-formatted endpoints accept the": ". Эндпоинты формата Anthropic вместо этого принимают",
|
||||
"Health": "Здоровье",
|
||||
"Healthy": "В норме",
|
||||
"HHHL AI Gateway": "Вселенская Федерация",
|
||||
"Hidden — verify to reveal": "Скрыто — подтвердите, чтобы показать",
|
||||
"Hide": "Скрыть",
|
||||
"Hide API key": "Скрыть API ключ",
|
||||
@@ -2198,6 +2205,9 @@
|
||||
"List of models supported by this channel. Use comma to separate multiple models.": "Список моделей, поддерживаемых этим каналом. Используйте запятую для разделения нескольких моделей.",
|
||||
"List of origins (one per line) allowed for Passkey registration and authentication.": "Список источников (один на строку), разрешенных для регистрации и аутентификации Passkey.",
|
||||
"List view": "Вид списка",
|
||||
"Live capacity telemetry": "Телеметрия емкости в реальном времени",
|
||||
"Live resource telemetry": "Телеметрия ресурсов в реальном времени",
|
||||
"Live Status": "Статус в реальном времени",
|
||||
"LLM Leaderboard": "Рейтинг LLM",
|
||||
"LLM prompt helper": "Помощник с промптом для LLM",
|
||||
"Load Balancing": "Балансировка нагрузки",
|
||||
@@ -2295,6 +2305,7 @@
|
||||
"Media pricing": "Цены для медиа",
|
||||
"Median time-to-first-token (TTFT) sampled hourly per group": "Медианная задержка первого токена (TTFT), измеряемая ежечасно по группам",
|
||||
"Medical Q&A, mental health support": "Медицинские Q&A, поддержка ментального здоровья",
|
||||
"Memory Capacity": "Объем памяти",
|
||||
"Memory Hits": "Попаданий памяти",
|
||||
"Memory Threshold (%)": "Порог памяти (%)",
|
||||
"Merchant ID": "ID мерчанта",
|
||||
@@ -2646,6 +2657,7 @@
|
||||
"No Users Found": "Пользователи не найдены",
|
||||
"No vendor data available": "Данных по поставщикам нет",
|
||||
"No X Found": "X не найдено",
|
||||
"Node": "Узел",
|
||||
"Node Name": "Имя узла",
|
||||
"Non-stream": "Не потоковый",
|
||||
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Ненулевые награды за приглашения требуют подтверждения соответствия в настройках платежного шлюза.",
|
||||
@@ -3193,6 +3205,7 @@
|
||||
"Reasoning Effort": "Интенсивность рассуждения",
|
||||
"Receive Upstream Model Update Notifications": "Получать уведомления об обновлениях вышестоящих моделей",
|
||||
"Received": "Получено",
|
||||
"Received amount": "Полученная сумма",
|
||||
"Recently launched models": "Недавно запущенные модели",
|
||||
"Recently launched models gaining traction": "Недавно вышедшие модели, набирающие популярность",
|
||||
"Recharge": "Пополнение",
|
||||
@@ -3209,7 +3222,6 @@
|
||||
"Redeem codes": "Активировать коды",
|
||||
"Redeemed By": "Активировано",
|
||||
"Redeemed:": "Активировано:",
|
||||
"Received amount": "Полученная сумма",
|
||||
"redemption code": "код активации",
|
||||
"Redemption Code": "Код активации",
|
||||
"Redemption code deleted successfully": "Код активации успешно удален",
|
||||
@@ -3232,7 +3244,7 @@
|
||||
"Referral Program": "Реферальная программа",
|
||||
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "Перевод реферальных наград отключен, пока администратор не подтвердит условия соответствия.",
|
||||
"Refine models by provider, group, type, and tags.": "Уточняйте список моделей по поставщику, группе, типу и тегам.",
|
||||
"Refresh": "Обновить",
|
||||
"Refresh": "Обновление",
|
||||
"Refresh Cache": "Обновить кэш",
|
||||
"Refresh credential": "Обновить учётные данные",
|
||||
"Refresh failed": "Ошибка обновления",
|
||||
@@ -3611,6 +3623,8 @@
|
||||
"Server IP": "IP сервера",
|
||||
"Server Log Management": "Управление журналами сервера",
|
||||
"Server logging is not enabled (log directory not configured)": "Журналирование сервера не включено (каталог журналов не настроен)",
|
||||
"Server Power Core": "Силовое ядро сервера",
|
||||
"Server Status": "Статус сервера",
|
||||
"Server Token": "Токен сервера",
|
||||
"Service account JSON file(s)": "JSON-файл сервисного аккаунта",
|
||||
"Session expired!": "Сессия истекла!",
|
||||
@@ -3828,6 +3842,7 @@
|
||||
"Sync Upstream": "Синхронизировать Upstream",
|
||||
"Sync Upstream Models": "Синхронизировать модели Upstream",
|
||||
"Synchronize models and vendors from an upstream source": "Синхронизировать модели и поставщиков из upstream источника",
|
||||
"Syncing": "Синхронизация",
|
||||
"Syncing prices, please wait...": "Синхронизация цен, подождите...",
|
||||
"Syncing...": "Синхронизация...",
|
||||
"System": "Система",
|
||||
@@ -4108,6 +4123,7 @@
|
||||
"Total requests made": "Всего сделанных запросов",
|
||||
"Total tokens": "Всего токенов",
|
||||
"Total Tokens": "Всего токенов",
|
||||
"Total Tokens Burned": "Всего израсходовано токенов",
|
||||
"Total Usage": "Общее использование",
|
||||
"Total:": "Всего:",
|
||||
"TPM": "TPM",
|
||||
@@ -4538,6 +4554,7 @@
|
||||
"Zero retention": "Без хранения данных",
|
||||
"Zhipu": "Zhipu",
|
||||
"Zhipu V4": "Zhipu V4",
|
||||
"Zoom": "Zoom"
|
||||
"Zoom": "Zoom",
|
||||
"伟大无需多言": "Величие не требует слов"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+21
-4
@@ -93,6 +93,7 @@
|
||||
"30 Days": "30 ngày",
|
||||
"30 days ago": "30 ngày trước",
|
||||
"30d change": "Thay đổi 30 ngày",
|
||||
"30s refresh": "Làm mới 30 giây",
|
||||
"5 minutes": "5 phút",
|
||||
"5-Hour Window": "Cửa sổ 5 giờ",
|
||||
"50 / page": "50 / trang",
|
||||
@@ -101,6 +102,7 @@
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "Hệ số tính phí. Tỷ lệ càng thấp thì chi phí gọi API càng thấp.",
|
||||
"A focused home for keys, balance, routing, and service health.": "Trang tổng quan tập trung cho khóa, số dư, định tuyến và trạng thái dịch vụ.",
|
||||
"A high-throughput AI API gateway with real-time capacity, resilient routing, and transparent token consumption at the very first glance.": "Cổng AI API thông lượng cao hiển thị ngay từ màn hình đầu tiên năng lực thời gian thực, định tuyến bền bỉ và mức tiêu thụ token minh bạch.",
|
||||
"About": "Giới thiệu",
|
||||
"About {{days}} days left": "Còn khoảng {{days}} ngày",
|
||||
"Accept Unpriced Models": "Chấp nhận các Mô hình chưa định giá",
|
||||
@@ -462,6 +464,7 @@
|
||||
"Availability (last 24h)": "Khả dụng (24 giờ qua)",
|
||||
"Available": "Khả dụng",
|
||||
"Available disk space": "Dung lượng đĩa khả dụng",
|
||||
"Available headroom": "Dư địa khả dụng",
|
||||
"Available Models": "Mô hình khả dụng",
|
||||
"Available Rewards": "Phần thưởng hiện có",
|
||||
"Average latency": "Độ trễ trung bình",
|
||||
@@ -815,6 +818,7 @@
|
||||
"Compliance confirmation required": "Cần xác nhận tuân thủ",
|
||||
"Compliance confirmed": "Đã xác nhận tuân thủ",
|
||||
"Compliance confirmed successfully": "Xác nhận tuân thủ thành công",
|
||||
"Compute usage": "Mức dùng tính toán",
|
||||
"Concatenate channel system prompt with user's prompt": "Nối lời nhắc hệ thống kênh với lời nhắc của người dùng",
|
||||
"Condition Path": "Đường dẫn điều kiện",
|
||||
"Condition Settings": "Cài đặt điều kiện",
|
||||
@@ -1348,6 +1352,7 @@
|
||||
"edit_this": "edit_this",
|
||||
"Editor mode": "Chế độ trình sửa",
|
||||
"Education": "Giáo dục",
|
||||
"Elastic compute headroom": "Dư địa tính toán đàn hồi",
|
||||
"Email": "Email",
|
||||
"Email (required for verification)": "Email (bắt buộc để xác minh)",
|
||||
"Email Address": "Địa chỉ email",
|
||||
@@ -1528,8 +1533,8 @@
|
||||
"Exists": "Tồn tại",
|
||||
"Expand": "Mở rộng",
|
||||
"Expand All": "Mở rộng tất cả",
|
||||
"Expected a JSON array.": "Cần là một mảng JSON.",
|
||||
"Expected a JSON array of group identifiers": "Cần là một mảng JSON gồm các định danh nhóm",
|
||||
"Expected a JSON array.": "Cần là một mảng JSON.",
|
||||
"Experiment with prompts and models in real time.": "Thử nghiệm với prompt và mô hình theo thời gian thực.",
|
||||
"Expiration Time": "Thời gian hết hạn",
|
||||
"expired": "Đã hết hạn",
|
||||
@@ -1816,6 +1821,7 @@
|
||||
"Full width": "Toàn chiều rộng",
|
||||
"Function calling": "Gọi hàm",
|
||||
"Functions": "Hàm",
|
||||
"Gateway Load": "Tải cổng",
|
||||
"GC Count": "Số lần GC",
|
||||
"GC executed": "GC đã thực thi",
|
||||
"GC execution failed": "Thực thi GC thất bại",
|
||||
@@ -1924,7 +1930,8 @@
|
||||
"Header Value (supports string or JSON mapping)": "Giá trị header (hỗ trợ chuỗi hoặc ánh xạ JSON)",
|
||||
"header. Anthropic-formatted endpoints accept the": ". Các endpoint định dạng Anthropic chấp nhận header",
|
||||
"Health": "Sức khỏe",
|
||||
"Healthy": "Bình thường",
|
||||
"Healthy": "Khỏe mạnh",
|
||||
"HHHL AI Gateway": "Liên bang Vũ trụ",
|
||||
"Hidden — verify to reveal": "Ẩn — xác minh để hiển thị",
|
||||
"Hide": "Ẩn",
|
||||
"Hide API key": "Ẩn khóa API",
|
||||
@@ -2198,6 +2205,9 @@
|
||||
"List of models supported by this channel. Use comma to separate multiple models.": "Danh sách các mô hình được hỗ trợ bởi kênh này. Sử dụng dấu phẩy để phân tách nhiều mô hình.",
|
||||
"List of origins (one per line) allowed for Passkey registration and authentication.": "Danh sách các nguồn gốc (mỗi dòng một mục) được phép đăng ký và xác thực Passkey.",
|
||||
"List view": "Xem dạng danh sách",
|
||||
"Live capacity telemetry": "Đo lường năng lực trực tiếp",
|
||||
"Live resource telemetry": "Đo lường tài nguyên trực tiếp",
|
||||
"Live Status": "Trạng thái trực tiếp",
|
||||
"LLM Leaderboard": "Bảng xếp hạng LLM",
|
||||
"LLM prompt helper": "Trợ lý prompt LLM",
|
||||
"Load Balancing": "Tải cân bằng",
|
||||
@@ -2295,6 +2305,7 @@
|
||||
"Media pricing": "Giá phương tiện",
|
||||
"Median time-to-first-token (TTFT) sampled hourly per group": "Độ trễ token đầu tiên trung vị (TTFT) lấy mẫu mỗi giờ theo nhóm",
|
||||
"Medical Q&A, mental health support": "Hỏi đáp y tế, hỗ trợ sức khỏe tinh thần",
|
||||
"Memory Capacity": "Dung lượng bộ nhớ",
|
||||
"Memory Hits": "Lượt truy cập bộ nhớ",
|
||||
"Memory Threshold (%)": "Ngưỡng bộ nhớ (%)",
|
||||
"Merchant ID": "Mã thương gia",
|
||||
@@ -2646,6 +2657,7 @@
|
||||
"No Users Found": "Không tìm thấy người dùng nào",
|
||||
"No vendor data available": "Không có dữ liệu nhà cung cấp",
|
||||
"No X Found": "Không tìm thấy X",
|
||||
"Node": "Nút",
|
||||
"Node Name": "Tên nút",
|
||||
"Non-stream": "Không phát trực tuyến",
|
||||
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Phần thưởng mời khác 0 yêu cầu xác nhận tuân thủ trong cài đặt Cổng thanh toán.",
|
||||
@@ -3193,6 +3205,7 @@
|
||||
"Reasoning Effort": "Cường độ suy luận",
|
||||
"Receive Upstream Model Update Notifications": "Nhận thông báo cập nhật mô hình nguồn",
|
||||
"Received": "Đã nhận",
|
||||
"Received amount": "Số tiền đã nhận",
|
||||
"Recently launched models": "Các mô hình ra mắt gần đây",
|
||||
"Recently launched models gaining traction": "Mô hình mới phát hành đang được ưa chuộng",
|
||||
"Recharge": "Nạp lại",
|
||||
@@ -3209,7 +3222,6 @@
|
||||
"Redeem codes": "Đổi mã",
|
||||
"Redeemed By": "Được chuộc bởi",
|
||||
"Redeemed:": "Đã đổi:",
|
||||
"Received amount": "Số tiền đã nhận",
|
||||
"redemption code": "mã đổi thưởng",
|
||||
"Redemption Code": "Mã đổi thưởng",
|
||||
"Redemption code deleted successfully": "Mã đổi thưởng đã xóa thành công",
|
||||
@@ -3611,6 +3623,8 @@
|
||||
"Server IP": "IP máy chủ",
|
||||
"Server Log Management": "Quản lý nhật ký máy chủ",
|
||||
"Server logging is not enabled (log directory not configured)": "Nhật ký máy chủ chưa được bật (chưa cấu hình thư mục nhật ký)",
|
||||
"Server Power Core": "Lõi sức mạnh máy chủ",
|
||||
"Server Status": "Trạng thái máy chủ",
|
||||
"Server Token": "Mã thông báo máy chủ",
|
||||
"Service account JSON file(s)": "Tệp JSON tài khoản dịch vụ",
|
||||
"Session expired!": "Phiên hết hạn!",
|
||||
@@ -3828,6 +3842,7 @@
|
||||
"Sync Upstream": "Đồng bộ nguồn",
|
||||
"Sync Upstream Models": "Đồng bộ các mô hình nguồn",
|
||||
"Synchronize models and vendors from an upstream source": "Đồng bộ hóa các mô hình và nhà cung cấp từ một nguồn thượng nguồn",
|
||||
"Syncing": "Đang đồng bộ",
|
||||
"Syncing prices, please wait...": "Đang đồng bộ giá, vui lòng đợi...",
|
||||
"Syncing...": "Đang đồng bộ...",
|
||||
"System": "Hệ thống",
|
||||
@@ -4108,6 +4123,7 @@
|
||||
"Total requests made": "Tổng lượt yêu cầu",
|
||||
"Total tokens": "Tổng số token",
|
||||
"Total Tokens": "Tổng số token",
|
||||
"Total Tokens Burned": "Tổng token đã tiêu thụ",
|
||||
"Total Usage": "Tổng Mức Sử dụng",
|
||||
"Total:": "Tổng cộng:",
|
||||
"TPM": "TPM",
|
||||
@@ -4538,6 +4554,7 @@
|
||||
"Zero retention": "Không lưu dữ liệu",
|
||||
"Zhipu": "Zhipu",
|
||||
"Zhipu V4": "Zhipu V4",
|
||||
"Zoom": "Zoom"
|
||||
"Zoom": "Zoom",
|
||||
"伟大无需多言": "Vĩ đại không cần nhiều lời"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+21
-4
@@ -93,6 +93,7 @@
|
||||
"30 Days": "30 天",
|
||||
"30 days ago": "30 天前",
|
||||
"30d change": "30 天变化",
|
||||
"30s refresh": "30 秒刷新",
|
||||
"5 minutes": "5 分钟",
|
||||
"5-Hour Window": "5小时窗口",
|
||||
"50 / page": "50 条/页",
|
||||
@@ -101,6 +102,7 @@
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "计费乘数,倍率越低,API 调用费用越低。",
|
||||
"A focused home for keys, balance, routing, and service health.": "集中展示密钥、余额、路由和服务健康状态。",
|
||||
"A high-throughput AI API gateway with real-time capacity, resilient routing, and transparent token consumption at the very first glance.": "高吞吐 AI API 网关,首屏即可展示实时容量、弹性路由与透明 token 消耗。",
|
||||
"About": "关于",
|
||||
"About {{days}} days left": "约剩 {{days}} 天",
|
||||
"Accept Unpriced Models": "接受未定价模型",
|
||||
@@ -462,6 +464,7 @@
|
||||
"Availability (last 24h)": "可用率(最近 24 小时)",
|
||||
"Available": "可用",
|
||||
"Available disk space": "可用磁盘空间",
|
||||
"Available headroom": "可用余量",
|
||||
"Available Models": "可用模型",
|
||||
"Available Rewards": "可用奖励",
|
||||
"Average latency": "平均延迟",
|
||||
@@ -815,6 +818,7 @@
|
||||
"Compliance confirmation required": "需要确认合规条款",
|
||||
"Compliance confirmed": "合规已确认",
|
||||
"Compliance confirmed successfully": "合规确认成功",
|
||||
"Compute usage": "计算资源占用",
|
||||
"Concatenate channel system prompt with user's prompt": "将渠道系统提示与用户的提示连接起来",
|
||||
"Condition Path": "条件路径",
|
||||
"Condition Settings": "条件项设置",
|
||||
@@ -1348,6 +1352,7 @@
|
||||
"edit_this": "edit_this",
|
||||
"Editor mode": "编辑器模式",
|
||||
"Education": "教育",
|
||||
"Elastic compute headroom": "弹性算力余量",
|
||||
"Email": "邮箱",
|
||||
"Email (required for verification)": "电子邮件(验证必需)",
|
||||
"Email Address": "电子邮件地址",
|
||||
@@ -1528,8 +1533,8 @@
|
||||
"Exists": "存在",
|
||||
"Expand": "展开",
|
||||
"Expand All": "全部展开",
|
||||
"Expected a JSON array.": "应为 JSON 数组。",
|
||||
"Expected a JSON array of group identifiers": "应为分组标识符的 JSON 数组",
|
||||
"Expected a JSON array.": "应为 JSON 数组。",
|
||||
"Experiment with prompts and models in real time.": "实时实验提示词和模型。",
|
||||
"Expiration Time": "过期时间",
|
||||
"expired": "已过期",
|
||||
@@ -1816,6 +1821,7 @@
|
||||
"Full width": "全宽",
|
||||
"Function calling": "函数调用",
|
||||
"Functions": "函数",
|
||||
"Gateway Load": "网关负载",
|
||||
"GC Count": "GC 次数",
|
||||
"GC executed": "GC 已执行",
|
||||
"GC execution failed": "GC 执行失败",
|
||||
@@ -1924,7 +1930,8 @@
|
||||
"Header Value (supports string or JSON mapping)": "请求头值(支持字符串或 JSON 映射)",
|
||||
"header. Anthropic-formatted endpoints accept the": " 请求头。Anthropic 格式的端点也接受",
|
||||
"Health": "健康",
|
||||
"Healthy": "正常",
|
||||
"Healthy": "健康",
|
||||
"HHHL AI Gateway": "宇宙联邦",
|
||||
"Hidden — verify to reveal": "隐藏 — 验证以显示",
|
||||
"Hide": "隐藏",
|
||||
"Hide API key": "隐藏 API 密钥",
|
||||
@@ -2198,6 +2205,9 @@
|
||||
"List of models supported by this channel. Use comma to separate multiple models.": "此渠道支持的模型列表。使用逗号分隔多个模型。",
|
||||
"List of origins (one per line) allowed for Passkey registration and authentication.": "允许用于 Passkey 注册和身份验证的来源列表(每行一个)。",
|
||||
"List view": "列表视图",
|
||||
"Live capacity telemetry": "实时容量遥测",
|
||||
"Live resource telemetry": "实时资源遥测",
|
||||
"Live Status": "实时状态",
|
||||
"LLM Leaderboard": "LLM 排行榜",
|
||||
"LLM prompt helper": "LLM 辅助设计提示词",
|
||||
"Load Balancing": "负载均衡",
|
||||
@@ -2295,6 +2305,7 @@
|
||||
"Media pricing": "媒体定价",
|
||||
"Median time-to-first-token (TTFT) sampled hourly per group": "按小时采样的各分组首 token 延迟(TTFT)中位数",
|
||||
"Medical Q&A, mental health support": "医疗问答与心理健康支持",
|
||||
"Memory Capacity": "内存容量",
|
||||
"Memory Hits": "内存命中",
|
||||
"Memory Threshold (%)": "内存阈值 (%)",
|
||||
"Merchant ID": "商户 ID",
|
||||
@@ -2646,6 +2657,7 @@
|
||||
"No Users Found": "未找到用户",
|
||||
"No vendor data available": "暂无厂商数据",
|
||||
"No X Found": "未找到 X",
|
||||
"Node": "节点",
|
||||
"Node Name": "节点名称",
|
||||
"Non-stream": "非流式",
|
||||
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "非零邀请奖励需要先在支付网关设置中确认合规条款。",
|
||||
@@ -3193,6 +3205,7 @@
|
||||
"Reasoning Effort": "推理强度",
|
||||
"Receive Upstream Model Update Notifications": "接收上游模型更新通知",
|
||||
"Received": "获得",
|
||||
"Received amount": "已收额度",
|
||||
"Recently launched models": "近期发布的模型",
|
||||
"Recently launched models gaining traction": "近期发布并快速增长的模型",
|
||||
"Recharge": "充值",
|
||||
@@ -3209,7 +3222,6 @@
|
||||
"Redeem codes": "兑换码",
|
||||
"Redeemed By": "兑换人",
|
||||
"Redeemed:": "已兑换:",
|
||||
"Received amount": "已收额度",
|
||||
"redemption code": "兑换码",
|
||||
"Redemption Code": "兑换码",
|
||||
"Redemption code deleted successfully": "兑换码删除成功",
|
||||
@@ -3611,6 +3623,8 @@
|
||||
"Server IP": "服务器 IP",
|
||||
"Server Log Management": "服务器日志管理",
|
||||
"Server logging is not enabled (log directory not configured)": "服务器日志功能未启用(未配置日志目录)",
|
||||
"Server Power Core": "服务器动力核心",
|
||||
"Server Status": "服务器状态",
|
||||
"Server Token": "服务器 Token",
|
||||
"Service account JSON file(s)": "服务账号 JSON 文件",
|
||||
"Session expired!": "会话已过期!",
|
||||
@@ -3828,6 +3842,7 @@
|
||||
"Sync Upstream": "同步上游",
|
||||
"Sync Upstream Models": "同步上游模型",
|
||||
"Synchronize models and vendors from an upstream source": "从上游源同步模型和供应商",
|
||||
"Syncing": "同步中",
|
||||
"Syncing prices, please wait...": "正在同步价格,请稍候...",
|
||||
"Syncing...": "同步中...",
|
||||
"System": "系统",
|
||||
@@ -4108,6 +4123,7 @@
|
||||
"Total requests made": "总请求数",
|
||||
"Total tokens": "总 Token",
|
||||
"Total Tokens": "总 Token 数",
|
||||
"Total Tokens Burned": "总消耗 Tokens",
|
||||
"Total Usage": "总用量",
|
||||
"Total:": "总计:",
|
||||
"TPM": "TPM",
|
||||
@@ -4538,6 +4554,7 @@
|
||||
"Zero retention": "零数据保留",
|
||||
"Zhipu": "智谱",
|
||||
"Zhipu V4": "智谱 V4",
|
||||
"Zoom": "缩放"
|
||||
"Zoom": "缩放",
|
||||
"伟大无需多言": "伟大无需多言"
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -167,6 +167,12 @@ function OAuthCallback() {
|
||||
params: { code: search.code, state: search.state },
|
||||
skipBusinessError: true,
|
||||
}
|
||||
// Add PKCE code_verifier if present in sessionStorage
|
||||
const codeVerifier = sessionStorage.getItem(`pkce_verifier_${search.state}`)
|
||||
if (codeVerifier) {
|
||||
config.params.code_verifier = codeVerifier
|
||||
sessionStorage.removeItem(`pkce_verifier_${search.state}`)
|
||||
}
|
||||
const res = await api.get(`/api/oauth/${provider}`, config)
|
||||
if (res?.data?.success) {
|
||||
const { message } = res.data
|
||||
|
||||
Reference in New Issue
Block a user