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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user