'use client' import type { ApiKeyItem } from '@dify/contracts/api/console/apps/types.gen' import { AlertDialog, AlertDialogActions, AlertDialogCancelButton, AlertDialogConfirmButton, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, } from '@langgenius/dify-ui/alert-dialog' import { Button } from '@langgenius/dify-ui/button' import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle, } from '@langgenius/dify-ui/dialog' import { IconButton } from '@langgenius/dify-ui/icon-button' import { skipToken, useMutation, useQuery } from '@tanstack/react-query' import { useAtomValue } from 'jotai' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { currentWorkspaceAtom } from '@/context/workspace-state' import { consoleQuery } from '@/service/client' import { ApiKeyTable } from './api-key-table' import { CreatedApiKeyDialog } from './created-api-key-dialog' type CreatedApiKey = Pick type ApiKeyScope = | { type: 'app'; appId: string } | { type: 'dataset' } | { type: 'environment'; appId: string; environmentId: string } type ApiKeyModalProps = { open: boolean canManage: boolean scope: ApiKeyScope onOpenChange: (open: boolean) => void } export function ApiKeyModal({ open, canManage, scope, onOpenChange }: ApiKeyModalProps) { const { t } = useTranslation() const currentWorkspace = useAtomValue(currentWorkspaceAtom) const [deleteKeyId, setDeleteKeyId] = useState() const [createdApiKey, setCreatedApiKey] = useState() const appApiKeysQuery = useQuery( consoleQuery.apps.byResourceId.apiKeys.get.queryOptions({ input: open && scope.type === 'app' ? { params: { resource_id: scope.appId } } : skipToken, }), ) const datasetApiKeysQuery = useQuery({ ...consoleQuery.datasets.apiKeys.get.queryOptions(), enabled: open && scope.type === 'dataset', }) const environmentApiKeysQuery = useQuery( consoleQuery.enterprise.appDeploy.accessService.listEnvironmentApiKeys.queryOptions({ input: open && scope.type === 'environment' ? { params: { app_id: scope.appId, environment_id: scope.environmentId } } : skipToken, }), ) const createAppApiKey = useMutation(consoleQuery.apps.byResourceId.apiKeys.post.mutationOptions()) const deleteAppApiKey = useMutation( consoleQuery.apps.byResourceId.apiKeys.byApiKeyId.delete.mutationOptions(), ) const createDatasetApiKey = useMutation(consoleQuery.datasets.apiKeys.post.mutationOptions()) const deleteDatasetApiKey = useMutation( consoleQuery.datasets.apiKeys.byApiKeyId.delete.mutationOptions(), ) const createEnvironmentApiKey = useMutation( consoleQuery.enterprise.appDeploy.accessService.createEnvironmentApiKey.mutationOptions(), ) const deleteEnvironmentApiKey = useMutation( consoleQuery.enterprise.appDeploy.accessService.deleteEnvironmentApiKey.mutationOptions(), ) const apiKeys = scope.type === 'app' ? appApiKeysQuery.data?.data : scope.type === 'dataset' ? datasetApiKeysQuery.data?.data : environmentApiKeysQuery.data?.data const isLoading = scope.type === 'app' ? appApiKeysQuery.isLoading : scope.type === 'dataset' ? datasetApiKeysQuery.isLoading : environmentApiKeysQuery.isLoading const isCreating = scope.type === 'app' ? createAppApiKey.isPending : scope.type === 'dataset' ? createDatasetApiKey.isPending : createEnvironmentApiKey.isPending const isDeleting = scope.type === 'app' ? deleteAppApiKey.isPending : scope.type === 'dataset' ? deleteDatasetApiKey.isPending : deleteEnvironmentApiKey.isPending const createDisabled = !currentWorkspace.id || !canManage const handleOpenChange = (nextOpen: boolean) => { if (!nextOpen) { setDeleteKeyId(undefined) setCreatedApiKey(undefined) } onOpenChange(nextOpen) } const handleCreate = () => { if (createDisabled || isCreating) return switch (scope.type) { case 'app': createAppApiKey.mutate( { params: { resource_id: scope.appId } }, { onSuccess: setCreatedApiKey }, ) break case 'dataset': createDatasetApiKey.mutate(undefined, { onSuccess: setCreatedApiKey }) break case 'environment': createEnvironmentApiKey.mutate( { params: { app_id: scope.appId, environment_id: scope.environmentId } }, { onSuccess: setCreatedApiKey }, ) break } } const handleDelete = () => { if (!deleteKeyId || isDeleting) return const onSuccess = () => setDeleteKeyId(undefined) switch (scope.type) { case 'app': deleteAppApiKey.mutate( { params: { resource_id: scope.appId, api_key_id: deleteKeyId } }, { onSuccess }, ) break case 'dataset': deleteDatasetApiKey.mutate({ params: { api_key_id: deleteKeyId } }, { onSuccess }) break case 'environment': deleteEnvironmentApiKey.mutate( { params: { app_id: scope.appId, environment_id: scope.environmentId, api_key_id: deleteKeyId, }, }, { onSuccess }, ) break } } return ( <>
{t(($) => $['apiKeyModal.apiSecretKey'], { ns: 'appApi' })} {t(($) => $['apiKeyModal.apiSecretKeyTips'], { ns: 'appApi' })}
$['operation.close'], { ns: 'common' })} size="lg" className="absolute inset-e-6 top-6" > } /> {isLoading && (
)} {!!apiKeys?.length && ( )}
{ if (!open && !isDeleting) setDeleteKeyId(undefined) }} >
{t(($) => $['actionMsg.deleteConfirmTitle'], { ns: 'appApi' })} {t(($) => $['actionMsg.deleteConfirmTips'], { ns: 'appApi' })}
{t(($) => $['operation.cancel'], { ns: 'common' })} {t(($) => $['operation.confirm'], { ns: 'common' })}
{ if (!nextOpen) setCreatedApiKey(undefined) }} value={createdApiKey?.token ?? ''} /> ) }