Files
GopherGate/src/dashboard/mod.rs
T
hobokenchicken e07377adc0
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
feat: add multi-user RBAC with admin/viewer roles and user management
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
2026-03-02 15:58:33 -05:00

124 lines
3.9 KiB
Rust

// Dashboard module for LLM Proxy Gateway
mod auth;
mod clients;
mod models;
mod providers;
pub mod sessions;
mod system;
mod usage;
mod users;
mod websocket;
use axum::{
Router,
routing::{delete, get, post, put},
};
use serde::Serialize;
use crate::state::AppState;
use sessions::SessionManager;
// Dashboard state
#[derive(Clone)]
struct DashboardState {
app_state: AppState,
session_manager: SessionManager,
}
// API Response types
#[derive(Serialize)]
struct ApiResponse<T> {
success: bool,
data: Option<T>,
error: Option<String>,
}
impl<T> ApiResponse<T> {
fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
error: None,
}
}
fn error(error: String) -> Self {
Self {
success: false,
data: None,
error: Some(error),
}
}
}
// Dashboard routes
pub fn router(state: AppState) -> Router {
let session_manager = SessionManager::new(24); // 24-hour session TTL
let dashboard_state = DashboardState {
app_state: state,
session_manager,
};
Router::new()
// Static file serving
.fallback_service(tower_http::services::ServeDir::new("static"))
// WebSocket endpoint
.route("/ws", get(websocket::handle_websocket))
// API endpoints
.route("/api/auth/login", post(auth::handle_login))
.route("/api/auth/status", get(auth::handle_auth_status))
.route("/api/auth/logout", post(auth::handle_logout))
.route("/api/auth/change-password", post(auth::handle_change_password))
.route(
"/api/users",
get(users::handle_get_users).post(users::handle_create_user),
)
.route(
"/api/users/{id}",
put(users::handle_update_user).delete(users::handle_delete_user),
)
.route("/api/usage/summary", get(usage::handle_usage_summary))
.route("/api/usage/time-series", get(usage::handle_time_series))
.route("/api/usage/clients", get(usage::handle_clients_usage))
.route("/api/usage/providers", get(usage::handle_providers_usage))
.route("/api/usage/detailed", get(usage::handle_detailed_usage))
.route("/api/analytics/breakdown", get(usage::handle_analytics_breakdown))
.route("/api/models", get(models::handle_get_models))
.route("/api/models/{id}", put(models::handle_update_model))
.route(
"/api/clients",
get(clients::handle_get_clients).post(clients::handle_create_client),
)
.route(
"/api/clients/{id}",
get(clients::handle_get_client)
.put(clients::handle_update_client)
.delete(clients::handle_delete_client),
)
.route("/api/clients/{id}/usage", get(clients::handle_client_usage))
.route(
"/api/clients/{id}/tokens",
get(clients::handle_get_client_tokens).post(clients::handle_create_client_token),
)
.route(
"/api/clients/{id}/tokens/{token_id}",
delete(clients::handle_delete_client_token),
)
.route("/api/providers", get(providers::handle_get_providers))
.route(
"/api/providers/{name}",
get(providers::handle_get_provider).put(providers::handle_update_provider),
)
.route("/api/providers/{name}/test", post(providers::handle_test_provider))
.route("/api/system/health", get(system::handle_system_health))
.route("/api/system/metrics", get(system::handle_system_metrics))
.route("/api/system/logs", get(system::handle_system_logs))
.route("/api/system/backup", post(system::handle_system_backup))
.route(
"/api/system/settings",
get(system::handle_get_settings).post(system::handle_update_settings),
)
.with_state(dashboard_state)
}