dify/web/app/components/datasets/access-config/index.tsx
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

136 lines
5.7 KiB
TypeScript

'use client'
import type { ResourceOpenScope } from '@/models/access-control'
import { ScrollArea } from '@langgenius/dify-ui/scroll-area'
import { useAtomValue } from 'jotai'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AccessRulesEditor from '@/app/components/access-rules-editor'
import Loading from '@/app/components/base/loading'
import { userProfileIdAtom } from '@/context/account-state'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { useLocale } from '@/context/i18n'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { datasetRbacEnabledAtom } from '@/context/system-features-state'
import { getAccessControlTemplateLanguage } from '@/i18n-config/language'
import {
useDatasetAccessRules,
useDatasetUserAccessSettings,
useRemoveDatasetAccessPolicyMemberBindings,
useUpdateDatasetOpenScope,
useUpdateDatasetUserAccessSettings,
} from '@/service/access-control/use-dataset-access-config'
import { getDatasetACLCapabilities } from '@/utils/permission'
type DatasetAccessConfigPageProps = {
datasetId: string
}
const DatasetAccessConfigPage = ({ datasetId }: DatasetAccessConfigPageProps) => {
const { t } = useTranslation()
const locale = useLocale()
const language = useMemo(() => getAccessControlTemplateLanguage(locale), [locale])
const dataset = useDatasetDetailContextWithSelector((state) => state.dataset)
const currentUserId = useAtomValue(userProfileIdAtom)
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const isRbacEnabled = useAtomValue(datasetRbacEnabledAtom)
const canAccessConfig = getDatasetACLCapabilities(dataset?.permission_keys, {
currentUserId,
resourceMaintainer: dataset?.maintainer,
workspacePermissionKeys,
isRbacEnabled,
}).canAccessConfig
const { data: datasetAccessRulesResponse, isLoading: isLoadingDatasetAccessRules } =
useDatasetAccessRules(datasetId, language, { enabled: canAccessConfig })
const { data: datasetUserAccessSettingsResponse, isLoading: isLoadingDatasetUserAccessSettings } =
useDatasetUserAccessSettings(datasetId, language, { enabled: canAccessConfig })
const { mutate: updateDatasetOpenScope, isPending: isUpdatingDatasetOpenScope } =
useUpdateDatasetOpenScope(datasetId)
const { mutate: updateDatasetUserAccessSettings } = useUpdateDatasetUserAccessSettings(datasetId)
const { mutate: removeDatasetAccessPolicyMemberBindings } =
useRemoveDatasetAccessPolicyMemberBindings(datasetId)
const [optimisticOpenScope, setOptimisticOpenScope] = useState<ResourceOpenScope | null>(null)
const [updatingAccountId, setUpdatingAccountId] = useState<string | null>(null)
const datasetAccessRules = datasetAccessRulesResponse?.items || []
const datasetUserAccessSettings = datasetUserAccessSettingsResponse?.data || []
const openScope = optimisticOpenScope || datasetUserAccessSettingsResponse?.scope
const handleOpenScopeChange = useCallback(
(nextOpenScope: ResourceOpenScope) => {
if (!canAccessConfig) return
if (nextOpenScope === openScope) return
const previousOptimisticOpenScope = optimisticOpenScope
setOptimisticOpenScope(nextOpenScope)
updateDatasetOpenScope(nextOpenScope, {
onError: () => setOptimisticOpenScope(previousOptimisticOpenScope),
})
},
[canAccessConfig, openScope, optimisticOpenScope, updateDatasetOpenScope],
)
const handleUserAccessPoliciesChange = useCallback(
(accountId: string, accessPolicyIds: string[]) => {
if (!canAccessConfig) return
setUpdatingAccountId(accountId)
updateDatasetUserAccessSettings(
{ accountId, accessPolicyIds },
{ onSettled: () => setUpdatingAccountId(null) },
)
},
[canAccessConfig, updateDatasetUserAccessSettings],
)
const handleRemoveAccessPolicyMemberBinding = useCallback(
(accountId: string, accessPolicyId: string) => {
if (!canAccessConfig) return
setUpdatingAccountId(accountId)
removeDatasetAccessPolicyMemberBindings(
{ accessPolicyId, accountIds: [accountId] },
{ onSettled: () => setUpdatingAccountId(null) },
)
},
[canAccessConfig, removeDatasetAccessPolicyMemberBindings],
)
if (!canAccessConfig) return <Loading type="app" />
return (
<ScrollArea
className="h-full bg-background-default-subtle"
slotClassNames={{ viewport: 'overscroll-contain' }}
>
<header className="flex min-h-15.5 flex-col justify-center px-6 py-3">
<h1 className="system-sm-semibold text-text-primary">
{t(($) => $['settings.resourceAccess'], { ns: 'common' })}
</h1>
<p className="mt-0.5 system-xs-regular text-text-tertiary">
{t(($) => $['accessRule.datasetDescription'], { ns: 'permission' })}
</p>
</header>
<main className="w-full px-6 pt-8 pb-10">
<AccessRulesEditor
className="w-full max-w-200"
rules={datasetAccessRules}
userAccessSettings={datasetUserAccessSettings}
isLoadingRules={isLoadingDatasetAccessRules}
isLoadingUserAccessSettings={isLoadingDatasetUserAccessSettings}
openScope={openScope}
isUpdatingOpenScope={isLoadingDatasetUserAccessSettings || isUpdatingDatasetOpenScope}
updatingAccountId={updatingAccountId}
maintainerId={dataset?.maintainer}
onOpenScopeChange={handleOpenScopeChange}
onUserAccessPoliciesChange={handleUserAccessPoliciesChange}
onRemoveAccessPolicyMemberBinding={handleRemoveAccessPolicyMemberBinding}
onAddAccessSubject={handleUserAccessPoliciesChange}
/>
</main>
</ScrollArea>
)
}
export default DatasetAccessConfigPage