feat: implement RBAC permissions system

Add role-based access control with hierarchy: super_admin > team_admin >
engineer > viewer. Adds is_super_admin boolean to User model (migration 010),
centralized backend permissions module, frontend usePermissions hook, and
UI enforcement (conditional Create/Edit buttons, editor redirect for viewers,
role badge in header). All endpoint admin checks updated from role=="admin"
to is_super_admin.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-05 02:42:44 -05:00
parent d7c5c8c9ce
commit 34daa26a67
20 changed files with 428 additions and 65 deletions

View File

@@ -1,5 +1,6 @@
import { Link, useLocation, useNavigate, Outlet } from 'react-router-dom'
import { useAuthStore } from '@/store/authStore'
import { usePermissions } from '@/hooks/usePermissions'
import { ThemeToggle } from '@/components/common/ThemeToggle'
import { BrandLogo } from '@/components/common/BrandLogo'
import { BrandWordmark } from '@/components/common/BrandWordmark'
@@ -9,6 +10,7 @@ export function AppLayout() {
const location = useLocation()
const navigate = useNavigate()
const { user, logout } = useAuthStore()
const { effectiveRole } = usePermissions()
const handleLogout = async () => {
await logout()
@@ -53,6 +55,20 @@ export function AppLayout() {
<span className="hidden text-sm text-muted-foreground sm:block">
{user?.name || user?.email}
</span>
{effectiveRole && effectiveRole !== 'engineer' && (
<span
className={cn(
'hidden rounded-full px-2 py-0.5 text-xs font-medium sm:inline-block',
effectiveRole === 'super_admin' && 'bg-red-500/10 text-red-600 dark:text-red-400',
effectiveRole === 'team_admin' && 'bg-blue-500/10 text-blue-600 dark:text-blue-400',
effectiveRole === 'viewer' && 'bg-gray-500/10 text-gray-600 dark:text-gray-400'
)}
>
{effectiveRole === 'super_admin' ? 'Super Admin' :
effectiveRole === 'team_admin' ? 'Team Admin' :
'Viewer'}
</span>
)}
<ThemeToggle />
<button
onClick={handleLogout}