Add comprehensive backend features and mobile UI improvements

Backend:
- Add 2FA authentication with TOTP support
- Add API keys management system
- Add audit logging for security events
- Add file upload/management system
- Add notifications system with preferences
- Add session management
- Add webhooks integration
- Add analytics endpoints
- Add export functionality
- Add password policy enforcement
- Add new database migrations for core tables

Frontend:
- Add module position system (top/bottom sidebar sections)
- Add search and notifications module configuration tabs
- Add mobile logo replacing hamburger menu
- Center page title absolutely when no tabs present
- Align sidebar footer toggles with navigation items
- Add lighter icon color in dark theme for mobile
- Add API keys management page
- Add notifications page with context
- Add admin analytics and audit logs pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-17 22:27:32 +01:00
parent f698aa4d51
commit 8c4a555b88
76 changed files with 9751 additions and 323 deletions

View File

@@ -31,6 +31,11 @@ export const authAPI = {
return response.data;
},
verify2fa: async (data: { temp_token: string; code: string }): Promise<Token> => {
const response = await api.post<Token>('/auth/verify-2fa', data);
return response.data;
},
register: async (data: RegisterRequest): Promise<User> => {
const response = await api.post<User>('/auth/register', data);
return response.data;
@@ -40,6 +45,38 @@ export const authAPI = {
const response = await api.get<User>('/auth/me');
return response.data;
},
logout: async (): Promise<void> => {
await api.post('/auth/logout');
},
};
// 2FA / TOTP endpoints
export const twoFactorAPI = {
getStatus: async (): Promise<{ enabled: boolean; has_backup_codes: boolean }> => {
const response = await api.get<{ enabled: boolean; has_backup_codes: boolean }>('/2fa/status');
return response.data;
},
setup: async (): Promise<{ secret: string; uri: string; qr_code: string }> => {
const response = await api.post<{ secret: string; uri: string; qr_code: string }>('/2fa/setup');
return response.data;
},
verify: async (code: string): Promise<{ message: string; backup_codes: string[] }> => {
const response = await api.post<{ message: string; backup_codes: string[] }>('/2fa/verify', { code });
return response.data;
},
disable: async (data: { password: string; code: string }): Promise<{ message: string }> => {
const response = await api.post<{ message: string }>('/2fa/disable', data);
return response.data;
},
regenerateBackupCodes: async (code: string): Promise<{ backup_codes: string[] }> => {
const response = await api.post<{ backup_codes: string[] }>('/2fa/regenerate-backup-codes', { code });
return response.data;
},
};
// Settings endpoints
@@ -64,8 +101,8 @@ export const settingsAPI = {
return response.data;
},
updateModules: async (data: Record<string, boolean | string[]>): Promise<Record<string, boolean | string[]>> => {
const response = await api.put<Record<string, boolean | string[]>>('/settings/modules', data);
updateModules: async (data: Record<string, boolean | string | string[]>): Promise<Record<string, boolean | string | string[]>> => {
const response = await api.put<Record<string, boolean | string | string[]>>('/settings/modules', data);
return response.data;
},
@@ -128,4 +165,173 @@ export const usersAPI = {
},
};
// API Keys endpoints
export interface ApiKeyItem {
id: string;
user_id: string;
name: string;
key_prefix: string;
scopes: string[] | null;
is_active: boolean;
last_used_at: string | null;
last_used_ip: string | null;
usage_count: number;
expires_at: string | null;
created_at: string;
updated_at: string;
}
export const apiKeysAPI = {
create: async (data: { name: string; scopes?: string[]; expires_at?: string | null }): Promise<ApiKeyItem & { key: string }> => {
const response = await api.post<ApiKeyItem & { key: string }>('/api-keys', data);
return response.data;
},
list: async (): Promise<{ items: ApiKeyItem[]; total: number }> => {
const response = await api.get<{ items: ApiKeyItem[]; total: number }>('/api-keys');
return response.data;
},
revoke: async (keyId: string): Promise<ApiKeyItem> => {
const response = await api.post<ApiKeyItem>(`/api-keys/${keyId}/revoke`);
return response.data;
},
delete: async (keyId: string): Promise<void> => {
await api.delete(`/api-keys/${keyId}`);
},
};
// Sessions endpoints
export interface UserSession {
id: string;
user_id: string;
device_name: string | null;
device_type: string | null;
browser: string | null;
os: string | null;
ip_address: string | null;
location: string | null;
is_active: boolean;
is_current: boolean;
created_at: string;
last_active_at: string;
expires_at: string | null;
}
export const sessionsAPI = {
list: async (): Promise<{ items: UserSession[]; total: number; active_count: number }> => {
const response = await api.get<{ items: UserSession[]; total: number; active_count: number }>('/sessions');
return response.data;
},
revoke: async (sessionId: string): Promise<UserSession> => {
const response = await api.post<UserSession>(`/sessions/${sessionId}/revoke`);
return response.data;
},
revokeAllOther: async (): Promise<{ revoked: number }> => {
const response = await api.post<{ revoked: number }>('/sessions/revoke-all');
return response.data;
},
};
// Notifications endpoints
export interface NotificationItem {
id: string;
user_id: string;
title: string;
message: string | null;
type: 'info' | 'success' | 'warning' | 'error' | 'system';
link: string | null;
metadata: Record<string, unknown> | null;
is_read: boolean;
created_at: string;
read_at: string | null;
}
export const notificationsAPI = {
list: async (params?: { skip?: number; limit?: number; unread_only?: boolean }): Promise<{ items: NotificationItem[]; total: number; unread_count: number }> => {
const response = await api.get<{ items: NotificationItem[]; total: number; unread_count: number }>('/notifications', { params });
return response.data;
},
unreadCount: async (): Promise<{ unread_count: number }> => {
const response = await api.get<{ unread_count: number }>('/notifications/unread-count');
return response.data;
},
markAsRead: async (notificationId: string): Promise<NotificationItem> => {
const response = await api.post<NotificationItem>(`/notifications/${notificationId}/read`);
return response.data;
},
markAllAsRead: async (): Promise<{ marked_as_read: number }> => {
const response = await api.post<{ marked_as_read: number }>('/notifications/read-all');
return response.data;
},
delete: async (notificationId: string): Promise<void> => {
await api.delete(`/notifications/${notificationId}`);
},
deleteAllRead: async (): Promise<{ deleted: number }> => {
const response = await api.delete<{ deleted: number }>('/notifications/read/all');
return response.data;
},
};
// Analytics endpoints (admin)
export type AnalyticsOverview = {
users: { total: number; active: number; new_today: number; new_this_week: number; new_this_month: number };
sessions: { active: number };
api_keys: { total: number; active: number };
security: { logins_24h: number; failed_logins_24h: number };
notifications: { unread_total: number };
generated_at: string;
};
export const analyticsAPI = {
overview: async (): Promise<AnalyticsOverview> => {
const response = await api.get<AnalyticsOverview>('/analytics/overview');
return response.data;
},
userActivity: async (days: number = 7): Promise<{ daily_stats: { date: string; active_users: number; new_users: number }[] }> => {
const response = await api.get<{ daily_stats: { date: string; active_users: number; new_users: number }[] }>('/analytics/users/activity', {
params: { days },
});
return response.data;
},
actionsBreakdown: async (hours: number = 24): Promise<{ period_hours: number; actions: { action: string; count: number }[] }> => {
const response = await api.get<{ period_hours: number; actions: { action: string; count: number }[] }>('/analytics/actions/breakdown', {
params: { hours },
});
return response.data;
},
};
// Audit logs endpoints (admin)
export type AuditLogItem = {
id: string;
user_id: string | null;
username: string | null;
action: string;
resource_type: string | null;
resource_id: string | null;
details: string | null;
ip_address: string | null;
user_agent: string | null;
status: string;
created_at: string;
};
export const auditAPI = {
list: async (params?: Record<string, unknown>): Promise<{ items: AuditLogItem[]; total: number; page: number; page_size: number; total_pages: number }> => {
const response = await api.get<{ items: AuditLogItem[]; total: number; page: number; page_size: number; total_pages: number }>('/audit', { params });
return response.data;
},
};
export default api;