dify/web/app/components/access-rules-editor/index.tsx

126 lines
4.0 KiB
TypeScript

'use client'
import type { AccessPolicyWithBindings, RemoveBindingPayload } from '@/models/access-control'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useState } from 'react'
import AccessRuleRow from '@/app/components/header/account-setting/access-rules-page/access-rule-row'
import AddRuleTargetsModal from '@/app/components/header/account-setting/access-rules-page/add-rule-targets-modal'
import { useParams } from '@/next/navigation'
import { useUpdateAppAccessRuleBindings } from '@/service/access-control/use-app-access-config'
import { useUpdateDatasetAccessRuleBindings } from '@/service/access-control/use-dataset-access-config'
export type AccessRulesEditorProps = {
rules: AccessPolicyWithBindings[]
className?: string
}
const AccessRulesEditor = ({
rules,
className,
}: AccessRulesEditorProps) => {
const { appId } = useParams() as { appId: string }
const [currentRule, setCurrentRule] = useState<AccessPolicyWithBindings | null>(null)
const handleAddRole = useCallback((rule: AccessPolicyWithBindings) => {
setCurrentRule(rule)
}, [])
const handleCloseAddModal = useCallback(() => {
setCurrentRule(null)
}, [])
const { mutateAsync: updateAppAccessRuleBindings } = useUpdateAppAccessRuleBindings()
const { mutateAsync: updateDatasetAccessRuleBindings } = useUpdateDatasetAccessRuleBindings()
const handleAddSubmit = useCallback(
(selection: { roleIds: string[], memberIds: string[] }) => {
const { policy } = currentRule || {}
const { id: policyId, resource_type } = policy || {}
if (resource_type === 'app') {
updateAppAccessRuleBindings({
appId,
policyId: policyId || '',
role_ids: selection.roleIds,
account_ids: selection.memberIds,
}, {
onSuccess: () => {
toast.success('Rule binding updated successfully')
},
})
}
else if (resource_type === 'dataset') {
updateDatasetAccessRuleBindings({
datasetId: appId,
policyId: policyId || '',
role_ids: selection.roleIds,
account_ids: selection.memberIds,
}, {
onSuccess: () => {
toast.success('Rule binding updated successfully')
},
})
}
},
[appId, currentRule, updateAppAccessRuleBindings, updateDatasetAccessRuleBindings],
)
const handleRemoveRole = useCallback(
(payload: RemoveBindingPayload) => {
const { policy_id, role_ids, account_ids, resource_type } = payload
if (resource_type === 'app') {
updateAppAccessRuleBindings({
appId,
policyId: policy_id,
role_ids,
account_ids,
}, {
onSuccess: () => {
toast.success('Rule binding removed successfully')
},
})
}
else if (resource_type === 'dataset') {
updateDatasetAccessRuleBindings({
datasetId: appId,
policyId: policy_id,
role_ids,
account_ids,
}, {
onSuccess: () => {
toast.success('Rule binding removed successfully')
},
})
}
},
[appId, updateAppAccessRuleBindings, updateDatasetAccessRuleBindings],
)
return (
<div className={cn('flex flex-col', className)}>
{rules.map((rule, index) => (
<AccessRuleRow
key={rule.policy.id}
rule={rule}
showMenu={false}
onAddRole={handleAddRole}
onRemove={handleRemoveRole}
className={cn(index > 0 && 'border-t border-divider-subtle')}
/>
))}
{currentRule && (
<AddRuleTargetsModal
ruleName={currentRule.policy.name}
initialRoleIds={currentRule.roles.map(role => role.role_id)}
initialMemberIds={currentRule.accounts.map(account => account.account_id)}
onClose={handleCloseAddModal}
onSubmit={handleAddSubmit}
/>
)}
</div>
)
}
export default AccessRulesEditor