feat: add customer authentication and scoped RBAC

This commit is contained in:
lingniu
2026-07-16 13:58:28 +08:00
parent 6d6c9ce534
commit a1195fb97d
28 changed files with 1738 additions and 97 deletions

View File

@@ -1,5 +1,6 @@
import type {
ApiEnvelope,
AdminUser,
AccessQuery,
AccessSummary,
AccessThresholdConfig,
@@ -41,6 +42,8 @@ import type {
RealtimeLocationRow,
SourceReadinessPlan,
SessionInfo,
LoginResponse,
CustomerUserInput,
TrackPlaybackResponse,
VehicleRealtimeRow,
VehicleCoverageRow,
@@ -171,6 +174,20 @@ function withTraceID(message: string, traceID?: string) {
export const api = {
session: (signal?: AbortSignal) => request<SessionInfo>('/api/v2/session', withSignal(undefined, signal)),
login: (credentials: { username: string; password: string }) => request<LoginResponse>('/api/v2/auth/login', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials)
}),
logout: () => request<{ loggedOut: boolean }>('/api/v2/auth/logout', { method: 'POST' }),
changePassword: (input: { currentPassword: string; newPassword: string }) => request<{ changed: boolean }>('/api/v2/auth/password', {
method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(input)
}),
adminUsers: (signal?: AbortSignal) => request<AdminUser[]>('/api/v2/admin/users', withSignal(undefined, signal)),
createCustomerUser: (input: CustomerUserInput) => request<{ id: number }>('/api/v2/admin/users', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(input)
}),
updateCustomerUser: (id: number, input: CustomerUserInput) => request<{ id: number }>(`/api/v2/admin/users/${id}`, {
method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(input)
}),
monitorSummary: (params = new URLSearchParams(), signal?: AbortSignal) => request<MonitorSummary>(`/api/v2/monitor/summary?${params.toString()}`, signal ? { signal } : undefined),
monitorMap: (params = new URLSearchParams(), signal?: AbortSignal) => request<MonitorMapResponse>(`/api/v2/monitor/map?${params.toString()}`, signal ? { signal } : undefined),
monitorWorkspace: (params = new URLSearchParams(), signal?: AbortSignal) => request<MonitorWorkspaceResponse>(`/api/v2/monitor/workspace?${params.toString()}`, signal ? { signal } : undefined),

View File

@@ -5,11 +5,53 @@ export interface ApiEnvelope<T> {
}
export interface SessionInfo {
subjectId?: string;
name: string;
role: 'viewer' | 'operator' | 'admin';
username?: string;
role: 'viewer' | 'operator' | 'admin' | 'customer';
userType?: 'viewer' | 'operator' | 'admin' | 'customer';
authProvider?: 'local' | 'legacy-token' | 'disabled' | string;
menuKeys?: string[];
vehicleCount?: number;
customerRef?: string;
tenantRef?: string;
authMode: 'disabled' | 'enforce';
}
export interface LoginResponse {
accessToken: string;
expiresAt: string;
session: Omit<SessionInfo, 'authMode'>;
}
export interface AdminUser {
id: number;
username: string;
displayName: string;
userType: 'admin' | 'customer';
status: 'enabled' | 'disabled';
customerRef: string;
tenantRef: string;
authProvider: string;
externalSubject?: string;
menuKeys: string[];
vehicleVins: string[];
lastLoginAt?: string;
createdAt: string;
updatedAt: string;
}
export interface CustomerUserInput {
username?: string;
displayName: string;
password?: string;
status: 'enabled' | 'disabled';
customerRef: string;
tenantRef: string;
menuKeys: string[];
vehicleVins: string[];
}
export interface MonitorSummary {
totalVehicles: number;
onlineVehicles: number;