mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-06 17:46:23 +00:00
+50
-35
@@ -26,6 +26,7 @@ import {
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Circle,
|
||||
Copy,
|
||||
CreditCard,
|
||||
FileText,
|
||||
KeyRound,
|
||||
@@ -38,13 +39,14 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { motion, useReducedMotion } from 'motion/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { toast } from 'sonner'
|
||||
import { useAuthStore } from '@/stores/auth-store'
|
||||
import { getUserModels } from '@/lib/api'
|
||||
import { MOTION_TRANSITION } from '@/lib/motion'
|
||||
import { ROLE } from '@/lib/roles'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { CopyButton } from '@/components/copy-button'
|
||||
import {
|
||||
CardStaggerContainer,
|
||||
CardStaggerItem,
|
||||
@@ -104,8 +106,8 @@ interface RequestExample {
|
||||
endpoint: string
|
||||
model: string
|
||||
keyName: string
|
||||
keyId?: number
|
||||
displayKey: string
|
||||
curl: string
|
||||
ready: boolean
|
||||
}
|
||||
|
||||
@@ -179,7 +181,7 @@ function SetupGuideBackdrop(props: { compact?: boolean }) {
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-0 bg-[linear-gradient(112deg,oklch(0.97_0.04_250/.92)_0%,oklch(0.95_0.08_315/.82)_38%,oklch(0.96_0.12_92/.78)_74%,oklch(0.94_0.1_132/.62)_100%)] dark:opacity-25',
|
||||
'pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_48%_120%_at_78%_0%,color-mix(in_oklch,var(--primary)_8%,transparent)_0%,transparent_62%),linear-gradient(112deg,color-mix(in_oklch,var(--card)_98%,var(--primary)_2%)_0%,color-mix(in_oklch,var(--card)_94%,var(--muted)_6%)_48%,color-mix(in_oklch,var(--background)_92%,var(--accent)_8%)_100%)] dark:opacity-65',
|
||||
props.compact
|
||||
? '[mask-image:linear-gradient(90deg,black_0%,black_48%,transparent_74%)] opacity-55'
|
||||
: 'opacity-85'
|
||||
@@ -188,7 +190,7 @@ function SetupGuideBackdrop(props: { compact?: boolean }) {
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-y-0 right-0 hidden overflow-hidden font-mono text-lime-100/75 sm:block dark:text-lime-200/25',
|
||||
'text-foreground/5 pointer-events-none absolute inset-y-0 right-0 hidden overflow-hidden font-mono sm:block dark:text-foreground/8',
|
||||
props.compact ? 'w-1/2 opacity-45' : 'w-[58%] opacity-75'
|
||||
)}
|
||||
aria-hidden='true'
|
||||
@@ -275,12 +277,41 @@ function RequestPreview(props: {
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const shouldReduceMotion = useReducedMotion()
|
||||
const previewLines = props.example.curl.split('\n').map((line) => {
|
||||
if (line.includes('Authorization: Bearer')) {
|
||||
return ` -H "Authorization: Bearer ${props.example.displayKey}" \\`
|
||||
}
|
||||
return line
|
||||
const [isCopying, setIsCopying] = useState(false)
|
||||
const { copyToClipboard } = useCopyToClipboard({ notify: false })
|
||||
const previewCurl = buildCurlCommand({
|
||||
endpoint: props.example.endpoint,
|
||||
apiKey: props.example.displayKey,
|
||||
model: props.example.model,
|
||||
})
|
||||
const previewLines = previewCurl.split('\n')
|
||||
const handleCopyRequest = async () => {
|
||||
if (!props.example.keyId || isCopying) return
|
||||
|
||||
setIsCopying(true)
|
||||
try {
|
||||
const result = await fetchTokenKey(props.example.keyId)
|
||||
const key = result.success && result.data?.key ? result.data.key : ''
|
||||
if (!key) {
|
||||
toast.error(result.message || t('Failed to copy to clipboard'))
|
||||
return
|
||||
}
|
||||
|
||||
const realCurl = buildCurlCommand({
|
||||
endpoint: props.example.endpoint,
|
||||
apiKey: `sk-${key}`,
|
||||
model: props.example.model,
|
||||
})
|
||||
const copied = await copyToClipboard(realCurl)
|
||||
if (copied) {
|
||||
toast.success(t('Copied to clipboard'))
|
||||
} else {
|
||||
toast.error(t('Failed to copy to clipboard'))
|
||||
}
|
||||
} finally {
|
||||
setIsCopying(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
@@ -315,17 +346,17 @@ function RequestPreview(props: {
|
||||
</div>
|
||||
</div>
|
||||
{props.example.ready ? (
|
||||
<CopyButton
|
||||
value={props.example.curl}
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='h-7 gap-1.5 px-2 text-xs'
|
||||
tooltip={t('Copy ready-to-run curl')}
|
||||
successTooltip={t('Copied!')}
|
||||
disabled={isCopying}
|
||||
onClick={handleCopyRequest}
|
||||
aria-label={t('Copy ready-to-run curl')}
|
||||
>
|
||||
{t('Copy')}
|
||||
</CopyButton>
|
||||
<Copy data-icon='inline-start' />
|
||||
{isCopying ? t('Loading') : t('Copy')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button size='sm' variant='outline' render={<Link to='/keys' />}>
|
||||
{t('Create API Key')}
|
||||
@@ -463,17 +494,6 @@ export function OverviewDashboard() {
|
||||
[apiKeysQuery.data]
|
||||
)
|
||||
|
||||
const realKeyQuery = useQuery({
|
||||
queryKey: ['dashboard', 'overview', 'token-key', preferredKey?.id],
|
||||
queryFn: async () => {
|
||||
if (!preferredKey?.id) return ''
|
||||
const result = await fetchTokenKey(preferredKey.id)
|
||||
return result.success && result.data?.key ? `sk-${result.data.key}` : ''
|
||||
},
|
||||
enabled: Boolean(preferredKey?.id),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
|
||||
const startSteps = useMemo<StartStep[]>(
|
||||
() => [
|
||||
{
|
||||
@@ -561,23 +581,18 @@ export function OverviewDashboard() {
|
||||
const requestExample = useMemo<RequestExample>(() => {
|
||||
const endpoint = normalizeEndpoint(apiInfoItems[0]?.url)
|
||||
const model = modelsQuery.data?.[0] ?? 'gpt-4o-mini'
|
||||
const apiKey = realKeyQuery.data ?? ''
|
||||
const keyName = preferredKey?.name ?? t('No API key yet')
|
||||
const ready = Boolean(apiKey && model)
|
||||
const ready = Boolean(preferredKey?.id && model)
|
||||
|
||||
return {
|
||||
endpoint,
|
||||
model,
|
||||
keyName,
|
||||
displayKey: formatDisplayKey(apiKey),
|
||||
keyId: preferredKey?.id,
|
||||
displayKey: preferredKey ? formatDisplayKey(`sk-${preferredKey.key}`) : 'sk-...',
|
||||
ready,
|
||||
curl: buildCurlCommand({
|
||||
endpoint,
|
||||
apiKey: apiKey || 'sk-...',
|
||||
model,
|
||||
}),
|
||||
}
|
||||
}, [apiInfoItems, modelsQuery.data, preferredKey, realKeyQuery.data, t])
|
||||
}, [apiInfoItems, modelsQuery.data, preferredKey, t])
|
||||
|
||||
const completedStepCount = startSteps.filter((step) => step.completed).length
|
||||
const setupComplete = completedStepCount === startSteps.length
|
||||
|
||||
Reference in New Issue
Block a user