feat: implement role management features with hooks and UI components

This commit is contained in:
twwu 2026-05-09 14:47:38 +08:00
parent a4d12efbb6
commit 35696c6b2e
14 changed files with 417 additions and 253 deletions

View File

@ -0,0 +1,27 @@
import type { RoleListGroup } from './role-list'
import type { RoleListResponse } from '@/models/access-control'
export const formatRoleGroups = (roleListResponse: RoleListResponse | undefined): RoleListGroup[] => {
if (!roleListResponse)
return []
const result: RoleListGroup[] = []
const builtinRoles = roleListResponse.data.filter(role => role.is_builtin)
const customRoles = roleListResponse.data.filter(role => !role.is_builtin)
if (builtinRoles.length > 0) {
result.push({
id: 'builtin',
category: 'global_system_default',
title: 'System Roles',
items: builtinRoles,
})
}
if (customRoles.length > 0) {
result.push({
id: 'custom',
category: 'global_custom',
title: 'Custom Roles',
items: customRoles,
})
}
return result
}

View File

@ -0,0 +1,14 @@
import type { PaginationParameters } from '@/models/access-control'
import { useWorkspaceRoleList } from '@/service/access-control/use-workspace-roles'
import { formatRoleGroups } from './helpers'
export const useRoleGroups = (params?: PaginationParameters) => {
const { data: roleList, isLoading } = useWorkspaceRoleList(params)
const roleGroups = formatRoleGroups(roleList)
return {
roleGroups,
isLoading,
}
}

View File

@ -1,118 +1,15 @@
'use client'
import type { Role, RoleListGroup } from './role-list'
import type { RoleModalMode } from './role-modal'
import type { RoleModalMode, submitRoleData } from './role-modal'
import type { Role } from '@/models/access-control'
import { Button } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useState } from 'react'
import { useCreateWorkspaceRole, useUpdateWorkspaceRole } from '@/service/access-control/use-workspace-roles'
import { useRoleGroups } from './hooks'
import RoleList from './role-list'
import RoleModal from './role-modal'
const MOCK_ROLE_GROUPS: RoleListGroup[] = [
{
id: 'system',
type: 'system',
title: 'System roles',
items: [
{
id: 'owner',
name: 'Owner',
description: 'Full access to all workspace features and settings.',
permissions: [
'manage_model_providers',
'manage_members',
'manage_roles_permissions',
'manage_billing',
'manage_data_sources',
'manage_api_extensions',
'create_apps',
'view_all_apps',
'delete_any_app',
'create_knowledge_bases',
'view_all_knowledge_bases',
'delete_any_knowledge_base',
'view_all_app_logs',
'cross_app_log_access',
'view_sensitive_fields',
'install_plugins',
'uninstall_plugins',
],
},
{
id: 'admin',
name: 'Admin',
description: 'Manage apps, update settings, manage members and permissions.',
permissions: [
'manage_members',
'manage_roles_permissions',
'manage_data_sources',
'create_apps',
'view_all_apps',
'create_knowledge_bases',
'view_all_knowledge_bases',
],
},
{
id: 'editor',
name: 'Editor',
description: 'Create and edit resources (knowledge bases, apps, plugins) without workspace settings access.',
permissions: [
'create_apps',
'view_all_apps',
'create_knowledge_bases',
'view_all_knowledge_bases',
'install_plugins',
],
},
{
id: 'member',
name: 'Member',
description: 'Limited permissions within the workspace.',
permissions: ['view_all_apps', 'view_all_knowledge_bases'],
},
{
id: 'none',
name: 'No Permission',
description: 'Default role with no permissions assigned.',
permissions: [],
},
],
},
{
id: 'custom',
type: 'custom',
title: 'Custom roles',
items: [
{
id: 'executive',
name: 'Executive',
description: 'Unrestricted access to all workspace operations.',
permissions: [
'manage_model_providers',
'manage_members',
'manage_roles_permissions',
'manage_billing',
'create_apps',
'view_all_apps',
'create_knowledge_bases',
'view_all_knowledge_bases',
],
},
{
id: 'employee',
name: 'Employee',
description: 'Access to payroll bot and internal project knowledge bases.',
permissions: ['view_all_apps', 'view_all_knowledge_bases'],
},
{
id: 'partner',
name: 'Partner',
description: 'View external-facing apps: product info, feedback forms, and visitor registration.',
permissions: ['view_all_apps'],
},
],
},
]
type ModalState = {
mode: RoleModalMode
role?: Role
@ -121,6 +18,11 @@ type ModalState = {
const PermissionsPage = () => {
const [modalState, setModalState] = useState<ModalState>(null)
const { roleGroups } = useRoleGroups()
const { mutateAsync: createWorkspaceRole } = useCreateWorkspaceRole()
const { mutateAsync: updateWorkspaceRole } = useUpdateWorkspaceRole()
const openCreate = useCallback(() => {
setModalState({ mode: 'create' })
}, [])
@ -136,10 +38,28 @@ const PermissionsPage = () => {
const closeModal = useCallback(() => setModalState(null), [])
const handleSubmit = useCallback(
(_data: { name: string, description: string, permissions: string[] }) => {
// TODO: wire up to API when backend is ready
(data: submitRoleData) => {
const { name, description, permissionKeys } = data
const mode = modalState?.mode ?? ''
const roleId = modalState?.role?.id ?? ''
if (mode === 'create') {
createWorkspaceRole({ name, description, permission_keys: permissionKeys }, {
onSuccess: () => {
toast.success('Role created successfully')
closeModal()
},
})
}
else if (mode === 'edit') {
updateWorkspaceRole({ id: roleId, name, description, permission_keys: permissionKeys }, {
onSuccess: () => {
toast.success('Role updated successfully')
closeModal()
},
})
}
},
[],
[createWorkspaceRole, updateWorkspaceRole, closeModal, modalState],
)
return (
@ -165,7 +85,7 @@ const PermissionsPage = () => {
</div>
</div>
<RoleList
groups={MOCK_ROLE_GROUPS}
groups={roleGroups}
onView={handleView}
onEdit={handleEdit}
/>

View File

@ -1,20 +1,12 @@
'use client'
import type { Role, RoleCategory } from '@/models/access-control'
import { cn } from '@langgenius/dify-ui/cn'
import Row from './row'
export type Role = {
id: string
name: string
description: string
permissions?: string[]
}
export type RoleType = 'system' | 'custom'
export type RoleListGroup = {
id: string
type: RoleType
category: RoleCategory
title: string
items: Role[]
}
@ -24,7 +16,6 @@ export type RoleListProps = {
className?: string
onView?: (role: Role) => void
onEdit?: (role: Role) => void
onDelete?: (role: Role) => void
}
const RoleList = ({
@ -32,7 +23,6 @@ const RoleList = ({
className,
onView,
onEdit,
onDelete,
}: RoleListProps) => {
return (
<div className={cn('flex flex-col', className)}>
@ -53,11 +43,10 @@ const RoleList = ({
)}
name={row.name}
description={row.description}
roleType={group.type}
roleCategory={group.category}
role={row}
onView={onView}
onEdit={onEdit}
onDelete={onDelete}
/>
))}
</div>

View File

@ -1,5 +1,5 @@
'use client'
import type { Role, RoleType } from '.'
import type { Role, RoleCategory } from '@/models/access-control'
import {
DropdownMenu,
DropdownMenuContent,
@ -7,26 +7,28 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useState } from 'react'
import ActionButton from '@/app/components/base/action-button'
import { useDeleteWorkspaceRole } from '@/service/access-control/use-workspace-roles'
type RowMenuProps = {
roleType: RoleType
roleCategory: RoleCategory
role: Role
onView?: (role: Role) => void
onEdit?: (role: Role) => void
onDelete?: (role: Role) => void
}
const RowMenu = ({
roleType,
roleCategory,
role,
onView,
onEdit,
onDelete,
}: RowMenuProps) => {
const [open, setOpen] = useState(false)
const { mutateAsync: deleteRole } = useDeleteWorkspaceRole()
const handleView = useCallback(() => onView?.(role), [onView, role])
const handleEdit = useCallback(() => onEdit?.(role), [onEdit, role])
@ -35,7 +37,14 @@ const RowMenu = ({
// TODO: wire up to API when backend is ready
}, [])
const handleDelete = useCallback(() => onDelete?.(role), [onDelete, role])
const handleDelete = useCallback(() => {
deleteRole(role.id, {
onSuccess: () => {
toast.success('Role deleted successfully')
setOpen(false)
},
})
}, [deleteRole, role.id])
return (
<DropdownMenu open={open} onOpenChange={setOpen}>
@ -44,14 +53,14 @@ const RowMenu = ({
</DropdownMenuTrigger>
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="min-w-[160px]">
{
roleType === 'system' && (
roleCategory === 'global_system_default' && (
<DropdownMenuItem className="system-sm-semibold text-text-secondary" onClick={handleView}>
View
</DropdownMenuItem>
)
}
{
roleType === 'custom' && (
roleCategory === 'global_custom' && (
<>
<DropdownMenuItem className="system-sm-semibold text-text-secondary" onClick={handleEdit}>
Edit

View File

@ -1,4 +1,4 @@
import type { Role, RoleType } from '.'
import type { Role, RoleCategory } from '@/models/access-control'
import { cn } from '@langgenius/dify-ui/cn'
import { memo } from 'react'
import RowMenu from './row-menu'
@ -7,22 +7,20 @@ type RowProps = {
className?: string
name: string
description: string
roleType: RoleType
roleCategory: RoleCategory
role: Role
onView?: (role: Role) => void
onEdit?: (role: Role) => void
onDelete?: (role: Role) => void
}
const Row = ({
className,
name,
description,
roleType,
roleCategory,
role,
onView,
onEdit,
onDelete,
}: RowProps) => {
return (
<div
@ -36,15 +34,14 @@ const Row = ({
{name}
</div>
<p className="mt-1 system-sm-regular text-text-tertiary">
{description}
{description || 'No description'}
</p>
</div>
<RowMenu
roleType={roleType}
roleCategory={roleCategory}
role={role}
onView={onView}
onEdit={onEdit}
onDelete={onDelete}
/>
</div>
)

View File

@ -0,0 +1,18 @@
import { useWorkspacePermissionCatalog } from '@/service/access-control/use-permission-catalog'
export const useWorkspacePermissionGroups = () => {
const { data: workspacePermissionCatalog } = useWorkspacePermissionCatalog()
const groups = workspacePermissionCatalog?.groups || []
const allPermissions = groups.flatMap(g => g.permissions) || []
const permissionMap = Object.fromEntries(
allPermissions.map(p => [p.key, p]),
)
return {
groups,
permissionMap,
}
}

View File

@ -1,6 +1,6 @@
'use client'
import type { Role } from '../role-list'
import type { Role } from '@/models/access-control'
import { Button } from '@langgenius/dify-ui/button'
import {
Dialog,
@ -9,23 +9,25 @@ import {
DialogDescription,
DialogTitle,
} from '@langgenius/dify-ui/dialog'
import { useState } from 'react'
import { useCallback, useState } from 'react'
import Input from '@/app/components/base/input'
import Textarea from '@/app/components/base/textarea'
import PermissionField from './permission-field'
export type RoleModalMode = 'create' | 'view' | 'edit'
export type RoleModalRole = Role & {
permissions?: string[]
export type submitRoleData = {
name: string
description?: string
permissionKeys?: string[]
}
export type RoleModalProps = {
mode: RoleModalMode
open: boolean
role?: RoleModalRole
role?: Role
onClose: () => void
onSubmit?: (data: { name: string, description: string, permissions: string[] }) => void
onSubmit?: (data: submitRoleData) => void
}
const TITLES: Record<RoleModalMode, { title: string, description: string }> = {
@ -52,13 +54,21 @@ const RoleModal = ({
}: RoleModalProps) => {
const [name, setName] = useState(role?.name ?? '')
const [desc, setDesc] = useState(role?.description ?? '')
const [permissions, setPermissions] = useState<string[]>(role?.permissions ?? [])
const [permissionKeys, setPermissionKeys] = useState<string[]>(role?.permission_keys ?? [])
const readonly = mode === 'view'
const { title, description } = TITLES[mode]
const onRoleNameChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value)
}, [])
const onRoleDescChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
setDesc(e.target.value)
}, [])
const handleSubmit = () => {
onSubmit?.({ name: name.trim(), description: desc.trim(), permissions })
onSubmit?.({ name: name.trim(), description: desc.trim(), permissionKeys })
onClose()
}
@ -94,7 +104,7 @@ const RoleModal = ({
<Input
id="role-name"
value={name}
onChange={e => setName(e.target.value)}
onChange={onRoleNameChange}
placeholder="e.g. Marketing Lead"
disabled={readonly}
/>
@ -106,15 +116,15 @@ const RoleModal = ({
<Textarea
id="role-description"
value={desc}
onChange={e => setDesc(e.target.value)}
onChange={onRoleDescChange}
placeholder="Describe what this role is responsible for"
disabled={readonly}
className="min-h-24 resize-none"
/>
</div>
<PermissionField
value={permissions}
onChange={setPermissions}
value={permissionKeys}
onChange={setPermissionKeys}
readonly={readonly}
/>
</div>

View File

@ -1,8 +1,8 @@
'use client'
import { cn } from '@langgenius/dify-ui/cn'
import { useWorkspacePermissionGroups } from './hooks'
import PermissionPicker from './permission-picker'
import { PERMISSION_MAP } from './permissions-data'
export type PermissionFieldProps = {
value: string[]
@ -15,6 +15,8 @@ const PermissionField = ({
onChange,
readonly = false,
}: PermissionFieldProps) => {
const { permissionMap } = useWorkspacePermissionGroups()
const handleRemove = (id: string) => {
onChange(value.filter(p => p !== id))
}
@ -24,13 +26,13 @@ const PermissionField = ({
<div className="system-sm-medium text-text-secondary">Permissions</div>
{value.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{value.map((id) => {
const p = PERMISSION_MAP[id]
{value.map((key) => {
const p = permissionMap[key]
if (!p)
return null
return (
<span
key={id}
key={key}
className={cn(
'inline-flex items-center gap-1 rounded-md bg-util-colors-indigo-indigo-50 px-1.5 py-0.5 system-xs-medium text-text-accent',
'border-[0.5px] border-components-panel-border',
@ -42,7 +44,7 @@ const PermissionField = ({
type="button"
className="flex h-3.5 w-3.5 items-center justify-center rounded hover:bg-state-base-hover"
aria-label={`Remove ${p.name}`}
onClick={() => handleRemove(id)}
onClick={() => handleRemove(key)}
>
<span aria-hidden className="i-ri-close-line h-3 w-3" />
</button>
@ -52,6 +54,13 @@ const PermissionField = ({
})}
</div>
)}
{
value.length === 0 && (
<div className="system-sm-regular text-text-tertiary">
No permissions assigned yet
</div>
)
}
{!readonly && (
<PermissionPicker value={value} onChange={onChange} />
)}

View File

@ -1,6 +1,6 @@
'use client'
import type { PermissionGroup } from './permissions-data'
import type { PermissionGroup } from '@/models/access-control'
import { cn } from '@langgenius/dify-ui/cn'
import {
DropdownMenu,
@ -12,7 +12,7 @@ import {
} from '@langgenius/dify-ui/dropdown-menu'
import { useEffect, useMemo, useRef, useState } from 'react'
import Checkbox from '@/app/components/base/checkbox'
import { PERMISSION_GROUPS } from './permissions-data'
import { useWorkspacePermissionGroups } from './hooks'
type PermissionPickerProps = {
value: string[]
@ -37,17 +37,19 @@ const PermissionPicker = ({ value, onChange, className }: PermissionPickerProps)
return () => clearTimeout(timer)
}, [open])
const { groups } = useWorkspacePermissionGroups()
const filteredGroups = useMemo<PermissionGroup[]>(() => {
const q = search.trim().toLowerCase()
if (!q)
return PERMISSION_GROUPS
return PERMISSION_GROUPS
return groups
return groups
.map(group => ({
...group,
items: group.items.filter(i => i.name.toLowerCase().includes(q)),
permissions: group.permissions.filter(i => i.name.toLowerCase().includes(q)),
}))
.filter(group => group.items.length > 0)
}, [search])
.filter(group => group.permissions.length > 0)
}, [search, groups])
const selectedSet = useMemo(() => new Set(value), [value])
@ -59,19 +61,19 @@ const PermissionPicker = ({ value, onChange, className }: PermissionPickerProps)
}
const getGroupState = (group: PermissionGroup) => {
const checkedCount = group.items.reduce(
(acc, i) => acc + (selectedSet.has(i.id) ? 1 : 0),
const checkedCount = group.permissions.reduce(
(acc, i) => acc + (selectedSet.has(i.key) ? 1 : 0),
0,
)
return {
allChecked: checkedCount > 0 && checkedCount === group.items.length,
indeterminate: checkedCount > 0 && checkedCount < group.items.length,
allChecked: checkedCount > 0 && checkedCount === group.permissions.length,
indeterminate: checkedCount > 0 && checkedCount < group.permissions.length,
}
}
const toggleGroup = (group: PermissionGroup) => {
const { allChecked, indeterminate } = getGroupState(group)
const ids = group.items.map(i => i.id)
const ids = group.permissions.map(i => i.key)
if (allChecked || indeterminate) {
const idSet = new Set(ids)
onChange(value.filter(v => !idSet.has(v)))
@ -130,7 +132,7 @@ const PermissionPicker = ({ value, onChange, className }: PermissionPickerProps)
{filteredGroups.map((group, groupIndex) => {
const { allChecked, indeterminate } = getGroupState(group)
return (
<DropdownMenuGroup key={group.id}>
<DropdownMenuGroup key={group.group_key}>
{groupIndex > 0 && <DropdownMenuSeparator />}
<button
type="button"
@ -143,16 +145,16 @@ const PermissionPicker = ({ value, onChange, className }: PermissionPickerProps)
className="pointer-events-none"
/>
<span className="system-xs-medium-uppercase tracking-wide text-text-tertiary">
{group.label}
{group.group_name}
</span>
</button>
{group.items.map((item) => {
const checked = selectedSet.has(item.id)
{group.permissions.map((item) => {
const checked = selectedSet.has(item.key)
return (
<DropdownMenuCheckboxItem
key={item.id}
key={item.key}
checked={checked}
onCheckedChange={() => togglePermission(item.id)}
onCheckedChange={() => togglePermission(item.key)}
className="gap-2 pl-6"
>
<Checkbox checked={checked} className="pointer-events-none" />

View File

@ -1,66 +0,0 @@
export type Permission = {
id: string
name: string
}
export type PermissionGroup = {
id: string
label: string
items: Permission[]
}
export const PERMISSION_GROUPS: PermissionGroup[] = [
{
id: 'general',
label: 'General',
items: [
{ id: 'manage_model_providers', name: 'Manage model providers' },
{ id: 'manage_members', name: 'Manage members' },
{ id: 'manage_roles_permissions', name: 'Manage roles & permissions' },
{ id: 'manage_billing', name: 'Manage billing' },
{ id: 'manage_data_sources', name: 'Manage data sources' },
{ id: 'manage_api_extensions', name: 'Manage API extensions' },
],
},
{
id: 'apps',
label: 'Apps',
items: [
{ id: 'create_apps', name: 'Create apps' },
{ id: 'view_all_apps', name: 'View all apps' },
{ id: 'delete_any_app', name: 'Delete any app' },
],
},
{
id: 'knowledge',
label: 'Knowledge',
items: [
{ id: 'create_knowledge_bases', name: 'Create knowledge bases' },
{ id: 'view_all_knowledge_bases', name: 'View all knowledge bases' },
{ id: 'delete_any_knowledge_base', name: 'Delete any knowledge base' },
],
},
{
id: 'logs_audit',
label: 'Logs & Audit',
items: [
{ id: 'view_all_app_logs', name: 'View all app logs' },
{ id: 'cross_app_log_access', name: 'Cross-app log access' },
{ id: 'view_sensitive_fields', name: 'View sensitive fields' },
],
},
{
id: 'plugins',
label: 'Plugins',
items: [
{ id: 'install_plugins', name: 'Install plugins' },
{ id: 'uninstall_plugins', name: 'Uninstall plugins' },
],
},
]
export const ALL_PERMISSIONS: Permission[] = PERMISSION_GROUPS.flatMap(g => g.items)
export const PERMISSION_MAP: Record<string, Permission> = Object.fromEntries(
ALL_PERMISSIONS.map(p => [p.id, p]),
)

View File

@ -1,14 +1,18 @@
export enum SubjectType {
GROUP = 'group',
ACCOUNT = 'account',
}
export const SubjectType = {
GROUP: 'group',
ACCOUNT: 'account',
} as const
export enum AccessMode {
PUBLIC = 'public',
SPECIFIC_GROUPS_MEMBERS = 'private',
ORGANIZATION = 'private_all',
EXTERNAL_MEMBERS = 'sso_verified',
}
export type SubjectType = typeof SubjectType[keyof typeof SubjectType]
export const AccessMode = {
PUBLIC: 'public',
SPECIFIC_GROUPS_MEMBERS: 'private',
ORGANIZATION: 'private_all',
EXTERNAL_MEMBERS: 'sso_verified',
} as const
export type AccessMode = typeof AccessMode[keyof typeof AccessMode]
export type AccessControlGroup = {
id: 'string'
@ -28,3 +32,147 @@ export type SubjectGroup = { subjectId: string, subjectType: SubjectType, groupD
export type SubjectAccount = { subjectId: string, subjectType: SubjectType, accountData: AccessControlAccount }
export type Subject = SubjectGroup | SubjectAccount
export type Permission = {
key: string
name: string
description: string
}
export type PermissionGroup = {
group_key: string
group_name: string
description: string
permissions: Permission[]
}
export type PermissionGroups = {
groups: PermissionGroup[]
}
export type PermissionKey = string
export type RoleType = 'workspace' | 'app' | 'dataset'
export type RoleCategory = 'global_system_default' | 'global_custom'
export type Role = {
id: string
tenant_id: string
type: RoleType
category: RoleCategory
name: string
description: string
is_builtin: boolean
permission_keys: PermissionKey[]
}
export type Pagination = {
total_count: number
per_page: number
current_page: number
total_pages: number
}
export type PaginationParameters = {
page?: number
limit?: number
reverse?: boolean
}
export type RoleListResponse = {
data: Role[]
pagination: Pagination
}
export type CreateRoleRequest = {
name: string
description?: string
permission_keys?: PermissionKey[]
}
export type UpdateRolesRequest = {
id: string
name: string
description?: string
permission_keys?: PermissionKey[]
}
export type AccessPolicyResourceType = 'app' | 'dataset'
export type AccessPolicyCategory = 'global_system_default' | 'global_custom'
export type AccessPolicy = {
id: string
tenant_id: string
resource_type: AccessPolicyResourceType
policy_key: string
name: string
description: string
permission_keys: PermissionKey[]
is_builtin: boolean
category: AccessPolicyCategory
created_at: string
updated_at: string
}
export type GetAccessPoliciesRequest = {
resource_type?: AccessPolicyResourceType
} & PaginationParameters
export type GetAccessPoliciesResponse = {
data: AccessPolicy[]
pagination: Pagination
}
export type CreateAccessPolicyRequest = {
resource_type: AccessPolicyResourceType
name: string
description?: string
permission_keys?: PermissionKey[]
}
export type UpdateAccessPolicyRequest = {
id: string
name: string
description?: string
permission_keys?: PermissionKey[]
}
export type Bindings = {
role_ids: string[]
account_ids: string[]
}
export type AccessPolicyWithBindings = {
policy: AccessPolicy
} & Bindings
export type GetAppAccessPolicyByAppIdResponse = {
app_id: string
items: AccessPolicyWithBindings[]
}
export type GetDatasetAccessPolicyByDatasetIdResponse = {
dataset_id: string
items: AccessPolicyWithBindings[]
}
export type GetAppAccessPoliciesResponse = {
items: AccessPolicyWithBindings[]
pagination: Pagination
}
export type GetDatasetAccessPoliciesResponse = {
items: AccessPolicyWithBindings[]
pagination: Pagination
}
export type UpdateRolesOfMemberRequest = {
member_id: string
role_ids: string[]
}
export type UpdateRolesOfMemberResponse = {
account_id: string
roles: Role[]
}

View File

@ -0,0 +1,28 @@
import type {
PermissionGroups,
} from '@/models/access-control'
import { useQuery } from '@tanstack/react-query'
import { get } from '../base'
const NAME_SPACE = 'rbac-permission-catalog'
export const useWorkspacePermissionCatalog = () => {
return useQuery({
queryKey: [NAME_SPACE, 'workspace'],
queryFn: () => get<PermissionGroups>('/workspaces/current/rbac/role-permissions/catalog'),
})
}
export const useAppPermissionCatalog = () => {
return useQuery({
queryKey: [NAME_SPACE, 'app'],
queryFn: () => get<PermissionGroups>('/workspaces/current/rbac/role-permissions/catalog/app'),
})
}
export const useDatasetPermissionCatalog = () => {
return useQuery({
queryKey: [NAME_SPACE, 'dataset'],
queryFn: () => get<PermissionGroups>('/workspaces/current/rbac/role-permissions/catalog/dataset'),
})
}

View File

@ -0,0 +1,59 @@
import type {
CreateRoleRequest,
PaginationParameters,
RoleListResponse,
} from '@/models/access-control'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { del, get, post, put } from '../base'
const NAME_SPACE = 'rbac-role-management'
export const useWorkspaceRoleList = (params?: PaginationParameters) => {
return useQuery({
queryKey: [NAME_SPACE, 'workspace-role-list', params],
queryFn: () => get<RoleListResponse>('/workspaces/current/rbac/roles', { params }),
})
}
export const useCreateWorkspaceRole = () => {
const queryClient = useQueryClient()
return useMutation({
mutationKey: [NAME_SPACE, 'create-workspace-role'],
mutationFn: (data: CreateRoleRequest) =>
post<RoleListResponse>('/workspaces/current/rbac/roles', {
body: { ...data },
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [NAME_SPACE, 'workspace-role-list'] })
},
})
}
export const useUpdateWorkspaceRole = () => {
const queryClient = useQueryClient()
return useMutation({
mutationKey: [NAME_SPACE, 'update-workspace-role'],
mutationFn: (data: CreateRoleRequest & { id: string }) =>
put<RoleListResponse>(`/workspaces/current/rbac/roles/${data.id}`, {
body: { ...data },
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [NAME_SPACE, 'workspace-role-list'] })
},
})
}
export const useDeleteWorkspaceRole = () => {
const queryClient = useQueryClient()
return useMutation({
mutationKey: [NAME_SPACE, 'delete-workspace-role'],
mutationFn: (id: string) =>
del(`/workspaces/current/rbac/roles/${id}`),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [NAME_SPACE, 'workspace-role-list'] })
},
})
}