import { useState, useEffect } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { MainLayout } from "@/components/layouts/main-layout"; import { useAuth } from "@/hooks/use-auth"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useToast } from "@/hooks/use-toast"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { CustomRole, InsertCustomRole, SystemRole, systemRoles } from "@shared/schema"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/components/ui/tabs"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { Textarea } from "@/components/ui/textarea"; import { Badge } from "@/components/ui/badge"; import { Loader2, MoreVertical, ShieldAlert, ShieldCheck, ShieldX, Lock, Key, FileText, Edit, Trash, Plus, Shield } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; // Available permissions for custom roles const availablePermissions = [ { id: "domain.read", label: "View Domains" }, { id: "domain.create", label: "Create Domains" }, { id: "domain.update", label: "Edit Domains" }, { id: "domain.delete", label: "Delete Domains" }, { id: "dnsrecord.read", label: "View DNS Records" }, { id: "dnsrecord.create", label: "Create DNS Records" }, { id: "dnsrecord.update", label: "Edit DNS Records" }, { id: "dnsrecord.delete", label: "Delete DNS Records" }, { id: "user.read", label: "View Users" }, { id: "user.create", label: "Create Users" }, { id: "user.update", label: "Edit Users" }, { id: "user.delete", label: "Delete Users" }, { id: "provider.read", label: "View DNS Providers" }, { id: "provider.create", label: "Add DNS Providers" }, { id: "provider.update", label: "Edit DNS Providers" }, { id: "provider.delete", label: "Remove DNS Providers" }, { id: "webhook.read", label: "View Webhooks" }, { id: "webhook.create", label: "Create Webhooks" }, { id: "webhook.update", label: "Edit Webhooks" }, { id: "webhook.delete", label: "Delete Webhooks" }, { id: "apitoken.read", label: "View API Tokens" }, { id: "apitoken.create", label: "Create API Tokens" }, { id: "apitoken.update", label: "Edit API Tokens" }, { id: "apitoken.delete", label: "Delete API Tokens" }, { id: "metrics.read", label: "View Metrics" }, { id: "history.read", label: "View DNS History" }, { id: "organization.read", label: "View Organizations" }, { id: "organization.create", label: "Create Organizations" }, { id: "organization.update", label: "Edit Organizations" }, { id: "organization.delete", label: "Delete Organizations" }, { id: "group.read", label: "View Groups" }, { id: "group.create", label: "Create Groups" }, { id: "group.update", label: "Edit Groups" }, { id: "group.delete", label: "Delete Groups" }, { id: "role.read", label: "View Roles" }, { id: "role.create", label: "Create Roles" }, { id: "role.update", label: "Edit Roles" }, { id: "role.delete", label: "Delete Roles" }, ]; // Group permissions by category const permissionCategories = [ { id: "domain", name: "Domains", permissions: availablePermissions.filter(p => p.id.startsWith("domain.")), }, { id: "dns", name: "DNS Records", permissions: availablePermissions.filter(p => p.id.startsWith("dnsrecord.")), }, { id: "user", name: "Users", permissions: availablePermissions.filter(p => p.id.startsWith("user.")), }, { id: "provider", name: "DNS Providers", permissions: availablePermissions.filter(p => p.id.startsWith("provider.")), }, { id: "webhook", name: "Webhooks", permissions: availablePermissions.filter(p => p.id.startsWith("webhook.")), }, { id: "apitoken", name: "API Tokens", permissions: availablePermissions.filter(p => p.id.startsWith("apitoken.")), }, { id: "organization", name: "Organizations", permissions: availablePermissions.filter(p => p.id.startsWith("organization.")), }, { id: "group", name: "Groups", permissions: availablePermissions.filter(p => p.id.startsWith("group.")), }, { id: "role", name: "Roles", permissions: availablePermissions.filter(p => p.id.startsWith("role.")), }, { id: "other", name: "Other", permissions: availablePermissions.filter(p => p.id.startsWith("metrics.") || p.id.startsWith("history.")), }, ]; // Zod schema for role form const roleFormSchema = z.object({ name: z.string().min(3, "Name must be at least 3 characters"), description: z.string().optional(), permissions: z.array(z.string()).min(1, "Select at least one permission"), isActive: z.boolean().default(true), }); type RoleFormValues = z.infer; // Role card component for system roles const SystemRoleCard = ({ roleName }: { roleName: SystemRole }) => { const roleInfo = { admin: { title: "Administrator", description: "Full system access with all permissions", icon: , color: "bg-destructive/10 text-destructive border-destructive/20", }, manager: { title: "Manager", description: "Can manage domains and records, but not users or system settings", icon: , color: "bg-warning/10 text-warning border-warning/20", }, user: { title: "User", description: "Can manage assigned domains and records", icon: , color: "bg-primary/10 text-primary border-primary/20", }, readonly: { title: "Read-only", description: "View-only access to assigned domains and records", icon: , color: "bg-muted/10 text-muted-foreground border-muted/20", }, }[roleName]; return (
{roleInfo.icon}
{roleInfo.title} System Role
{roleName}

{roleInfo.description}

Permissions

{roleName === "admin" ? (
All permissions
) : (
{roleName === "readonly" ? ( Read-only access ) : roleName === "manager" ? ( <> Domain management DNS record management Webhook management ) : ( <> Domain access DNS record management )}
)}
); }; export default function RolesPage() { const { toast } = useToast(); const { user } = useAuth(); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [selectedRole, setSelectedRole] = useState(null); const [activeTab, setActiveTab] = useState("system-roles"); // Fetch custom roles const { data: customRoles = [], isLoading, error } = useQuery({ queryKey: ["/api/roles/custom"], enabled: !!user && user.role === "admin", }); // Create role form const createRoleForm = useForm({ resolver: zodResolver(roleFormSchema), defaultValues: { name: "", description: "", permissions: [], isActive: true, }, }); // Edit role form const editRoleForm = useForm({ resolver: zodResolver(roleFormSchema), defaultValues: { name: "", description: "", permissions: [], isActive: true, }, }); // Create role mutation const createRoleMutation = useMutation({ mutationFn: async (data: RoleFormValues) => { const res = await apiRequest("POST", "/api/roles/custom", { ...data, createdBy: user?.id, }); return await res.json(); }, onSuccess: () => { setIsCreateDialogOpen(false); createRoleForm.reset(); queryClient.invalidateQueries({ queryKey: ["/api/roles/custom"] }); toast({ title: "Role created", description: "The custom role has been created successfully.", }); }, onError: (error: Error) => { toast({ title: "Error creating role", description: error.message, variant: "destructive", }); }, }); // Update role mutation const updateRoleMutation = useMutation({ mutationFn: async ({ id, data }: { id: string; data: Partial }) => { const res = await apiRequest("PUT", `/api/roles/custom/${id}`, data); return await res.json(); }, onSuccess: () => { setIsEditDialogOpen(false); editRoleForm.reset(); setSelectedRole(null); queryClient.invalidateQueries({ queryKey: ["/api/roles/custom"] }); toast({ title: "Role updated", description: "The custom role has been updated successfully.", }); }, onError: (error: Error) => { toast({ title: "Error updating role", description: error.message, variant: "destructive", }); }, }); // Delete role mutation const deleteRoleMutation = useMutation({ mutationFn: async (id: string) => { await apiRequest("DELETE", `/api/roles/custom/${id}`); }, onSuccess: () => { setIsDeleteDialogOpen(false); setSelectedRole(null); queryClient.invalidateQueries({ queryKey: ["/api/roles/custom"] }); toast({ title: "Role deleted", description: "The custom role has been deleted successfully.", }); }, onError: (error: Error) => { toast({ title: "Error deleting role", description: error.message, variant: "destructive", }); }, }); // Handle role edit const handleEditRole = (role: CustomRole) => { setSelectedRole(role); editRoleForm.reset({ name: role.name, description: role.description || "", permissions: role.permissions, isActive: role.isActive, }); setIsEditDialogOpen(true); }; // Handle role deletion const handleDeleteRole = (role: CustomRole) => { setSelectedRole(role); setIsDeleteDialogOpen(true); }; // Submit handlers const onCreateSubmit = (data: RoleFormValues) => { createRoleMutation.mutate(data); }; const onEditSubmit = (data: RoleFormValues) => { if (selectedRole) { updateRoleMutation.mutate({ id: selectedRole.id, data }); } }; const confirmDeleteRole = () => { if (selectedRole) { deleteRoleMutation.mutate(selectedRole.id); } }; // Check if current user is admin if (user?.role !== "admin") { return (

Access Restricted

You don't have permission to access this page. Only administrators can manage roles and permissions.

); } return (

Role Management

Define user roles and their permissions

System Roles Custom Roles {/* System Roles Tab */}
{systemRoles.map((role) => ( ))}
{/* Custom Roles Tab */} {isLoading ? (
) : customRoles.length === 0 ? (

No Custom Roles

Create custom roles to define specific permissions for users in your organization.

) : (
{customRoles.map((role) => (
{role.name} {role.description || "Custom role"}
handleEditRole(role)}> Edit handleDeleteRole(role)} > Delete
Status {role.isActive ? "Active" : "Inactive"}
Created {formatDistanceToNow(new Date(role.createdAt), { addSuffix: true })}

Permissions ({role.permissions.length})

{role.permissions.slice(0, 5).map((permission) => ( {permission} ))} {role.permissions.length > 5 && ( +{role.permissions.length - 5} more )}
))}
)}
{/* Create Role Dialog */} Create Custom Role Define a new role with custom permissions.
( Role Name )} /> ( Description