feat: add admin dashboard, audit logs, token expiry check and test suite

Frontend Features:
- Add ProtectedRoute component with token expiry validation
- Create AdminDashboardPage with system statistics and user management
- Create AuditLogsPage with filtering and pagination
- Add admin-only navigation (Shield icon) for ymirliu@panjit.com.tw
- Add admin API methods to apiV2 service
- Add admin type definitions (SystemStats, AuditLog, etc.)

Token Management:
- Auto-redirect to login on token expiry
- Check authentication on route change
- Show loading state during auth check
- Admin privilege verification

Backend Testing:
- Add pytest configuration (pytest.ini)
- Create test fixtures (conftest.py)
- Add unit tests for auth, tasks, and admin endpoints
- Add integration tests for complete workflows
- Test user isolation and admin access control

Documentation:
- Add TESTING.md with comprehensive testing guide
- Include test running instructions
- Document fixtures and best practices

Routes:
- /admin - Admin dashboard (admin only)
- /admin/audit-logs - Audit logs viewer (admin only)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
egg
2025-11-16 18:01:50 +08:00
parent fd98018ddd
commit 8f94191914
13 changed files with 1554 additions and 45 deletions

View File

@@ -24,6 +24,12 @@ import type {
TaskListResponse,
TaskStats,
SessionInfo,
SystemStats,
UserWithStats,
TopUser,
AuditLog,
AuditLogListResponse,
UserActivitySummary,
} from '@/types/apiV2'
/**
@@ -426,6 +432,66 @@ class ApiClientV2 {
link.click()
window.URL.revokeObjectURL(link.href)
}
// ==================== Admin APIs ====================
/**
* Get system statistics (admin only)
*/
async getSystemStats(): Promise<SystemStats> {
const response = await this.client.get<SystemStats>('/admin/stats')
return response.data
}
/**
* Get user list with statistics (admin only)
*/
async listUsers(params: {
page?: number
page_size?: number
} = {}): Promise<{ users: UserWithStats[]; total: number; page: number; page_size: number }> {
const response = await this.client.get('/admin/users', { params })
return response.data
}
/**
* Get top users by metric (admin only)
*/
async getTopUsers(params: {
metric?: 'tasks' | 'completed_tasks'
limit?: number
} = {}): Promise<TopUser[]> {
const response = await this.client.get<TopUser[]>('/admin/users/top', { params })
return response.data
}
/**
* Get audit logs (admin only)
*/
async getAuditLogs(params: {
user_id?: number
category?: string
action?: string
success?: boolean
date_from?: string
date_to?: string
page?: number
page_size?: number
} = {}): Promise<AuditLogListResponse> {
const response = await this.client.get<AuditLogListResponse>('/admin/audit-logs', { params })
return response.data
}
/**
* Get user activity summary (admin only)
*/
async getUserActivitySummary(userId: number, days: number = 30): Promise<UserActivitySummary> {
const response = await this.client.get<UserActivitySummary>(
`/admin/audit-logs/user/${userId}/summary`,
{ params: { days } }
)
return response.data
}
}
// Export singleton instance