Files
infoscreen/dashboard/src/apiAuth.ts
RobbStarkAustria a7df3c2708 feat(dashboard): header user dropdown (Syncfusion) + proper logout; docs: clarify architecture; build: add splitbuttons; bump alpha.10
Dashboard

Add top-right user dropdown using Syncfusion DropDownButton: shows username + role; menu entries “Profil” and “Abmelden”.
Replace custom dropdown logic with Syncfusion component; position at header’s right edge.
Update /logout page to call backend logout and redirect to /login (reliable user switching).
Build/Config

Add @syncfusion/ej2-react-splitbuttons and @syncfusion/ej2-splitbuttons dependencies.
Update Vite optimizeDeps.include to pre-bundle splitbuttons and avoid import-analysis errors.
Docs

README: Rework Architecture Overview with clearer data flow:
Listener consumes MQTT (discovery/heartbeats) and updates API.
Scheduler reads from API and publishes events via MQTT to clients.
Clients send via MQTT and receive via MQTT.
Worker receives commands directly from API and reports results back (no MQTT).
Explicit note: MariaDB is accessed exclusively by the API Server; Dashboard never talks to DB directly.
README: Add SplitButtons to “Syncfusion Components Used”; add troubleshooting steps for @syncfusion/ej2-react-splitbuttons import issues (optimizeDeps + volume reset).
Copilot instructions: Document header user menu and splitbuttons technical notes (deps, optimizeDeps, dev-container node_modules volume).
Program info

Bump to 2025.1.0-alpha.10 with changelog:
UI: Header user menu (DropDownButton with username/role; Profil/Abmelden).
Frontend: Syncfusion SplitButtons integration + Vite pre-bundling config.
Fix: Added README guidance for splitbuttons import errors.
No breaking changes.
2025-10-15 16:33:35 +00:00

163 lines
3.6 KiB
TypeScript

/**
* Authentication API client for the dashboard.
*
* Provides functions to interact with auth endpoints including login,
* logout, and fetching current user information.
*/
export interface User {
id: number;
username: string;
role: 'user' | 'editor' | 'admin' | 'superadmin';
is_active: boolean;
}
export interface LoginRequest {
username: string;
password: string;
}
export interface LoginResponse {
message: string;
user: {
id: number;
username: string;
role: string;
};
}
export interface AuthCheckResponse {
authenticated: boolean;
role?: string;
}
/**
* Authenticate a user with username and password.
*
* @param username - The user's username
* @param password - The user's password
* @returns Promise<LoginResponse>
* @throws Error if login fails
*/
export async function login(username: string, password: string): Promise<LoginResponse> {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include', // Important for session cookies
body: JSON.stringify({ username, password }),
});
const data = await res.json();
if (!res.ok || data.error) {
throw new Error(data.error || 'Login failed');
}
return data;
}
/**
* Log out the current user.
*
* @returns Promise<void>
* @throws Error if logout fails
*/
export async function logout(): Promise<void> {
const res = await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'include',
});
const data = await res.json();
if (!res.ok || data.error) {
throw new Error(data.error || 'Logout failed');
}
}
/**
* Fetch the current authenticated user's information.
*
* @returns Promise<User>
* @throws Error if not authenticated or request fails
*/
export async function fetchCurrentUser(): Promise<User> {
const res = await fetch('/api/auth/me', {
method: 'GET',
credentials: 'include',
});
const data = await res.json();
if (!res.ok || data.error) {
throw new Error(data.error || 'Failed to fetch current user');
}
return data as User;
}
/**
* Quick check if user is authenticated (lighter than fetchCurrentUser).
*
* @returns Promise<AuthCheckResponse>
*/
export async function checkAuth(): Promise<AuthCheckResponse> {
const res = await fetch('/api/auth/check', {
method: 'GET',
credentials: 'include',
});
const data = await res.json();
if (!res.ok) {
throw new Error('Failed to check authentication status');
}
return data;
}
/**
* Helper function to check if a user has a specific role.
*
* @param user - The user object
* @param role - The role to check for
* @returns boolean
*/
export function hasRole(user: User | null, role: string): boolean {
if (!user) return false;
return user.role === role;
}
/**
* Helper function to check if a user has any of the specified roles.
*
* @param user - The user object
* @param roles - Array of roles to check for
* @returns boolean
*/
export function hasAnyRole(user: User | null, roles: string[]): boolean {
if (!user) return false;
return roles.includes(user.role);
}
/**
* Helper function to check if user is superadmin.
*/
export function isSuperadmin(user: User | null): boolean {
return hasRole(user, 'superadmin');
}
/**
* Helper function to check if user is admin or higher.
*/
export function isAdminOrHigher(user: User | null): boolean {
return hasAnyRole(user, ['admin', 'superadmin']);
}
/**
* Helper function to check if user is editor or higher.
*/
export function isEditorOrHigher(user: User | null): boolean {
return hasAnyRole(user, ['editor', 'admin', 'superadmin']);
}