feat: add multi-user RBAC with admin/viewer roles and user management
Some checks failed
CI / Check (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Release Build (push) Has been cancelled

Add complete multi-user support with role-based access control:

Backend:
- Add users CRUD endpoints (GET/POST/PUT/DELETE /api/users) with admin-only guards
- Add display_name column to users table with ALTER TABLE migration
- Fix auth to use session-based user identity (not hardcoded 'admin')
- Add POST /api/auth/logout to revoke server-side sessions
- Add require_admin() and extract_session() helpers for clean RBAC
- Guard all mutating endpoints (clients, providers, models, settings, backup)

Frontend:
- Add Users management page with create/edit/reset-password/delete modals
- Add role gating: hide edit/delete buttons for viewers on clients, providers, models
- Settings page hides auth tokens and admin actions for viewers
- Logout now revokes server session before clearing localStorage
- Sidebar shows real display_name and formatted role (Administrator/Viewer)
- Fix sidebar header: single logo with onerror fallback, renamed to 'LLM Proxy'
- Add badge and btn-action CSS classes for role pills and action buttons
- Bump cache-bust to v=7
This commit is contained in:
2026-03-02 15:58:33 -05:00
parent 5bf41be343
commit e07377adc0
17 changed files with 885 additions and 49 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LLM Proxy Gateway - Admin Dashboard</title>
<link rel="stylesheet" href="/css/dashboard.css?v=6">
<link rel="stylesheet" href="/css/dashboard.css?v=7">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="icon" href="img/logo-icon.png" type="image/png" sizes="any">
<link rel="apple-touch-icon" href="img/logo-icon.png">
@@ -57,9 +57,9 @@
<nav class="sidebar">
<div class="sidebar-header">
<div class="logo">
<img src="img/logo-icon.png" alt="LLM Proxy" class="sidebar-logo">
<i class="fas fa-shield-alt logo-fallback" style="opacity: 0.3;"></i>
<span>Gateway</span>
<img src="img/logo-icon.png" alt="LLM Proxy" class="sidebar-logo" onerror="this.style.display='none'; this.nextElementSibling.style.display='inline-block';">
<i class="fas fa-shield-alt logo-fallback" style="display: none;"></i>
<span>LLM Proxy</span>
</div>
<button class="sidebar-toggle" id="sidebar-toggle">
<i class="fas fa-bars"></i>
@@ -105,7 +105,11 @@
<div class="menu-section">
<h3 class="menu-title">SYSTEM</h3>
<a href="#settings" class="menu-item" data-page="settings" data-tooltip="System Settings">
<a href="#users" class="menu-item admin-only" data-page="users" data-tooltip="User Accounts">
<i class="fas fa-user-shield"></i>
<span>User Management</span>
</a>
<a href="#settings" class="menu-item admin-only" data-page="settings" data-tooltip="System Settings">
<i class="fas fa-cog"></i>
<span>Settings</span>
</a>
@@ -122,8 +126,8 @@
<i class="fas fa-user-circle"></i>
</div>
<div class="user-details">
<span class="user-name">Administrator</span>
<span class="user-role">Super Admin</span>
<span class="user-name">Loading...</span>
<span class="user-role">...</span>
</div>
</div>
<button class="logout-btn" id="logout-btn" title="Logout">
@@ -166,19 +170,20 @@
</div>
<!-- Scripts (cache-busted with version query params) -->
<script src="/js/api.js?v=6"></script>
<script src="/js/auth.js?v=6"></script>
<script src="/js/dashboard.js?v=6"></script>
<script src="/js/websocket.js?v=6"></script>
<script src="/js/charts.js?v=6"></script>
<script src="/js/pages/overview.js?v=6"></script>
<script src="/js/pages/analytics.js?v=6"></script>
<script src="/js/pages/costs.js?v=6"></script>
<script src="/js/pages/clients.js?v=6"></script>
<script src="/js/pages/providers.js?v=6"></script>
<script src="/js/pages/models.js?v=6"></script>
<script src="/js/pages/monitoring.js?v=6"></script>
<script src="/js/pages/settings.js?v=6"></script>
<script src="/js/pages/logs.js?v=6"></script>
<script src="/js/api.js?v=7"></script>
<script src="/js/auth.js?v=7"></script>
<script src="/js/dashboard.js?v=7"></script>
<script src="/js/websocket.js?v=7"></script>
<script src="/js/charts.js?v=7"></script>
<script src="/js/pages/overview.js?v=7"></script>
<script src="/js/pages/analytics.js?v=7"></script>
<script src="/js/pages/costs.js?v=7"></script>
<script src="/js/pages/clients.js?v=7"></script>
<script src="/js/pages/providers.js?v=7"></script>
<script src="/js/pages/models.js?v=7"></script>
<script src="/js/pages/monitoring.js?v=7"></script>
<script src="/js/pages/settings.js?v=7"></script>
<script src="/js/pages/logs.js?v=7"></script>
<script src="/js/pages/users.js?v=7"></script>
</body>
</html>