dify/web/features/deployments/detail/access-tab/developer-api-section.tsx
Stephen Zhou 605dca6431
tweaks
2026-05-08 16:17:46 +08:00

129 lines
4.6 KiB
TypeScript

'use client'
import type { ConsoleEnvironment, DeveloperApiKeyRow } from '@dify/contracts/enterprise/types.gen'
import { Switch } from '@langgenius/dify-ui/switch'
import { useMutation } from '@tanstack/react-query'
import { useAtomValue, useSetAtom } from 'jotai'
import { useTranslation } from 'react-i18next'
import { consoleQuery } from '@/service/client'
import { createdDeveloperApiTokenAtom } from '../../store'
import { ApiKeyGenerateMenu, ApiKeyList } from './api-keys'
import { CopyPill, Section } from './common'
type DeveloperApiSectionProps = {
appId: string
apiEnabled: boolean
apiUrl?: string
environments: ConsoleEnvironment[]
apiKeys: DeveloperApiKeyRow[]
}
export function DeveloperApiSection({
appId,
apiEnabled,
apiUrl,
environments,
apiKeys,
}: DeveloperApiSectionProps) {
const { t } = useTranslation('deployments')
const createdApiToken = useAtomValue(createdDeveloperApiTokenAtom)
const setCreatedApiToken = useSetAtom(createdDeveloperApiTokenAtom)
const toggleDeveloperAPI = useMutation(consoleQuery.enterprise.appDeploy.updateDeveloperApi.mutationOptions())
function handleToggle(enabled: boolean) {
toggleDeveloperAPI.mutate({
params: { appInstanceId: appId },
body: { enabled },
})
}
const visibleCreatedApiToken = createdApiToken?.appId === appId
? createdApiToken.token
: undefined
return (
<Section
title={t('access.api.developerTitle')}
description={t('access.api.description')}
action={(
<Switch
checked={apiEnabled}
onCheckedChange={handleToggle}
/>
)}
>
{apiEnabled
? (
<div className="flex flex-col gap-2">
{apiUrl && (
<CopyPill
label={t('access.api.endpoint')}
value={apiUrl}
/>
)}
<div className="flex items-center justify-between gap-3">
<div className="flex min-w-0 flex-col">
<span className="system-sm-medium text-text-primary">
{t('access.api.backendTitle')}
</span>
<span className="system-xs-regular text-text-tertiary">
{t('access.api.keyList')}
</span>
</div>
<ApiKeyGenerateMenu
appId={appId}
environments={environments}
apiKeys={apiKeys}
/>
</div>
{visibleCreatedApiToken && (
<div className="flex flex-col gap-2 rounded-lg border border-components-panel-border bg-components-panel-bg-blur p-3">
<div className="flex items-start justify-between gap-3">
<div className="flex min-w-0 flex-col">
<span className="system-sm-medium text-text-primary">
{t('access.api.newTokenTitle')}
</span>
<span className="system-xs-regular text-text-tertiary">
{t('access.api.newTokenDescription')}
</span>
</div>
<button
type="button"
onClick={() => setCreatedApiToken(undefined)}
aria-label={t('access.api.dismissToken')}
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
>
<span className="i-ri-close-line h-3.5 w-3.5" />
</button>
</div>
<CopyPill
label={t('access.api.newTokenLabel')}
value={visibleCreatedApiToken}
/>
</div>
)}
{apiKeys.length === 0
? (
<div className="rounded-lg border border-dashed border-components-panel-border bg-components-panel-bg-blur px-4 py-6 text-center system-sm-regular text-text-tertiary">
{environments.length === 0
? t('access.api.empty')
: t('access.api.noKeys')}
</div>
)
: (
<ApiKeyList
appId={appId}
apiKeys={apiKeys}
/>
)}
</div>
)
: (
<div className="system-xs-regular text-text-tertiary">
{t('access.api.disabled')}
</div>
)}
</Section>
)
}