Implement role and user management enhancements
CI / Backend (build + test) (push) Successful in 1m14s
CI / Frontend (lint + typecheck + build) (push) Successful in 30s

- Added MaxIpLimit to roles, allowing for the configuration of simultaneous IP limits for users.
- Updated role creation and update commands to include MaxIpLimit, ensuring proper handling in the application logic.
- Enhanced user management by introducing a DELETE endpoint for user accounts, with appropriate checks to prevent self-deletion.
- Updated documentation to reflect changes in role and user management, clarifying the new IP limit functionality and user deletion process.
- Adjusted related tests to cover new functionality and ensure robust validation of role and user management features.
This commit is contained in:
Leonid Pershin
2026-07-13 07:18:13 +03:00
parent 48e8d06a41
commit 24d9ea1099
48 changed files with 1240 additions and 171 deletions
@@ -9,10 +9,12 @@ import { Label } from '@/shared/ui/label'
import { Badge } from '@/shared/ui/badge'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { listRoles } from '@/features/admin/roles/api'
import { useAuthStore } from '@/features/auth/store'
import type { UserSummaryDto } from '@/shared/api/types'
import {
blockUser,
changeUserRole,
deleteUser,
forceRevokeConfig,
getUserConfigs,
resetUserPassword,
@@ -23,6 +25,7 @@ export function UserManageDialog({ user, open, onOpenChange }: { user: UserSumma
const { t } = useTranslation()
const queryClient = useQueryClient()
const [newPassword, setNewPassword] = useState('')
const currentUserId = useAuthStore((state) => state.user?.id)
const rolesQuery = useQuery({ queryKey: ['admin-roles'], queryFn: listRoles, enabled: open })
const configsQuery = useQuery({ queryKey: ['admin-user-configs', user.id], queryFn: () => getUserConfigs(user.id), enabled: open })
@@ -65,6 +68,16 @@ export function UserManageDialog({ user, open, onOpenChange }: { user: UserSumma
onError: () => toast.error(t('auth.genericError')),
})
const deleteMutation = useMutation({
mutationFn: () => deleteUser(user.id),
onSuccess: async () => {
toast.success(t('admin.users.deleted'))
onOpenChange(false)
await invalidateUsers()
},
onError: () => toast.error(t('auth.genericError')),
})
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-lg">
@@ -147,6 +160,18 @@ export function UserManageDialog({ user, open, onOpenChange }: { user: UserSumma
</div>
))}
</div>
{user.id !== currentUserId && (
<Button
variant="destructive"
disabled={deleteMutation.isPending}
onClick={() => {
if (confirm(t('admin.users.confirmDelete'))) deleteMutation.mutate()
}}
>
{t('admin.users.delete')}
</Button>
)}
</div>
</DialogContent>
</Dialog>