From f328474404968922e32020f3aa0c19d16882c2ea Mon Sep 17 00:00:00 2001 From: yyhuni Date: Sat, 3 Jan 2026 07:59:20 +0800 Subject: [PATCH] feat(frontend): add comprehensive mock data infrastructure for services - Add mock data modules for auth, engines, notifications, scheduled-scans, and workers - Implement mock authentication data with user profiles and login/logout responses - Create mock scan engine configurations with multiple predefined scanning profiles - Add mock notification system with various severity levels and categories - Implement mock scheduled scan data with cron expressions and run history - Add mock worker node data with status and performance metrics - Update service layer to integrate with new mock data infrastructure - Provide helper functions for filtering and paginating mock data - Enable frontend development and testing without backend API dependency --- frontend/mock/data/auth.ts | 22 ++++ frontend/mock/data/engines.ts | 78 ++++++++++++ frontend/mock/data/notifications.ts | 110 ++++++++++++++++ frontend/mock/data/scheduled-scans.ts | 132 ++++++++++++++++++++ frontend/mock/data/workers.ts | 78 ++++++++++++ frontend/mock/index.ts | 36 ++++++ frontend/services/auth.service.ts | 17 +++ frontend/services/engine.service.ts | 11 ++ frontend/services/notification.service.ts | 13 ++ frontend/services/scheduled-scan.service.ts | 11 ++ frontend/services/worker.service.ts | 11 ++ 11 files changed, 519 insertions(+) create mode 100644 frontend/mock/data/auth.ts create mode 100644 frontend/mock/data/engines.ts create mode 100644 frontend/mock/data/notifications.ts create mode 100644 frontend/mock/data/scheduled-scans.ts create mode 100644 frontend/mock/data/workers.ts diff --git a/frontend/mock/data/auth.ts b/frontend/mock/data/auth.ts new file mode 100644 index 00000000..3b777fc0 --- /dev/null +++ b/frontend/mock/data/auth.ts @@ -0,0 +1,22 @@ +import type { User, MeResponse, LoginResponse, LogoutResponse } from '@/types/auth.types' + +export const mockUser: User = { + id: 1, + username: 'admin', + isStaff: true, + isSuperuser: true, +} + +export const mockMeResponse: MeResponse = { + authenticated: true, + user: mockUser, +} + +export const mockLoginResponse: LoginResponse = { + message: 'Login successful', + user: mockUser, +} + +export const mockLogoutResponse: LogoutResponse = { + message: 'Logout successful', +} diff --git a/frontend/mock/data/engines.ts b/frontend/mock/data/engines.ts new file mode 100644 index 00000000..611c7038 --- /dev/null +++ b/frontend/mock/data/engines.ts @@ -0,0 +1,78 @@ +import type { ScanEngine } from '@/types/engine.types' + +export const mockEngines: ScanEngine[] = [ + { + id: 1, + name: 'Full Scan', + configuration: `# Full reconnaissance scan +stages: + - name: subdomain_discovery + tools: + - subfinder + - amass + - name: port_scan + tools: + - nmap + - name: web_crawling + tools: + - httpx + - katana + - name: vulnerability_scan + tools: + - nuclei +`, + createdAt: '2024-01-15T08:00:00Z', + updatedAt: '2024-12-20T10:30:00Z', + }, + { + id: 2, + name: 'Quick Scan', + configuration: `# Quick scan - subdomain and web only +stages: + - name: subdomain_discovery + tools: + - subfinder + - name: web_crawling + tools: + - httpx +`, + createdAt: '2024-02-10T09:00:00Z', + updatedAt: '2024-12-18T14:00:00Z', + }, + { + id: 3, + name: 'Vulnerability Only', + configuration: `# Vulnerability scan only +stages: + - name: vulnerability_scan + tools: + - nuclei + options: + severity: critical,high,medium +`, + createdAt: '2024-03-05T11:00:00Z', + updatedAt: '2024-12-15T16:20:00Z', + }, + { + id: 4, + name: 'Subdomain Discovery', + configuration: `# Subdomain enumeration only +stages: + - name: subdomain_discovery + tools: + - subfinder + - amass + - findomain +`, + createdAt: '2024-04-12T08:30:00Z', + updatedAt: '2024-12-10T09:00:00Z', + }, +] + +export function getMockEngines(): ScanEngine[] { + return mockEngines +} + +export function getMockEngineById(id: number): ScanEngine | undefined { + return mockEngines.find(e => e.id === id) +} diff --git a/frontend/mock/data/notifications.ts b/frontend/mock/data/notifications.ts new file mode 100644 index 00000000..97df836a --- /dev/null +++ b/frontend/mock/data/notifications.ts @@ -0,0 +1,110 @@ +import type { BackendNotification, GetNotificationsResponse } from '@/types/notification.types' + +export const mockNotifications: BackendNotification[] = [ + { + id: 1, + category: 'vulnerability', + title: 'Critical Vulnerability Found', + message: 'SQL Injection detected in retailmax.com/product endpoint', + level: 'critical', + createdAt: '2024-12-29T10:30:00Z', + isRead: false, + }, + { + id: 2, + category: 'scan', + title: 'Scan Completed', + message: 'Scan for acme.com completed successfully with 23 vulnerabilities found', + level: 'medium', + createdAt: '2024-12-29T09:00:00Z', + isRead: false, + }, + { + id: 3, + category: 'vulnerability', + title: 'High Severity Vulnerability', + message: 'XSS vulnerability found in acme.com/search', + level: 'high', + createdAt: '2024-12-28T16:45:00Z', + isRead: true, + }, + { + id: 4, + category: 'scan', + title: 'Scan Failed', + message: 'Scan for globalfinance.com failed: Connection timeout', + level: 'high', + createdAt: '2024-12-28T14:20:00Z', + isRead: true, + }, + { + id: 5, + category: 'asset', + title: 'New Subdomains Discovered', + message: '15 new subdomains discovered for techstart.io', + level: 'low', + createdAt: '2024-12-27T11:00:00Z', + isRead: true, + }, + { + id: 6, + category: 'system', + title: 'Worker Offline', + message: 'Worker node worker-03 is now offline', + level: 'medium', + createdAt: '2024-12-27T08:30:00Z', + isRead: true, + }, + { + id: 7, + category: 'scan', + title: 'Scheduled Scan Started', + message: 'Scheduled scan for Acme Corporation started', + level: 'low', + createdAt: '2024-12-26T06:00:00Z', + isRead: true, + }, + { + id: 8, + category: 'system', + title: 'System Update Available', + message: 'A new version of the scanner is available', + level: 'low', + createdAt: '2024-12-25T10:00:00Z', + isRead: true, + }, +] + +export function getMockNotifications(params?: { + page?: number + pageSize?: number + unread?: boolean +}): GetNotificationsResponse { + const page = params?.page || 1 + const pageSize = params?.pageSize || 10 + + let filtered = mockNotifications + + if (params?.unread) { + filtered = filtered.filter(n => !n.isRead) + } + + const total = filtered.length + const totalPages = Math.ceil(total / pageSize) + const start = (page - 1) * pageSize + const results = filtered.slice(start, start + pageSize) + + return { + results, + total, + page, + pageSize, + totalPages, + } +} + +export function getMockUnreadCount(): { count: number } { + return { + count: mockNotifications.filter(n => !n.isRead).length, + } +} diff --git a/frontend/mock/data/scheduled-scans.ts b/frontend/mock/data/scheduled-scans.ts new file mode 100644 index 00000000..c6e1b4c1 --- /dev/null +++ b/frontend/mock/data/scheduled-scans.ts @@ -0,0 +1,132 @@ +import type { ScheduledScan, GetScheduledScansResponse } from '@/types/scheduled-scan.types' + +export const mockScheduledScans: ScheduledScan[] = [ + { + id: 1, + name: 'Daily Acme Scan', + engineIds: [1], + engineNames: ['Full Scan'], + organizationId: 1, + organizationName: 'Acme Corporation', + targetId: null, + targetName: null, + scanMode: 'organization', + cronExpression: '0 2 * * *', + isEnabled: true, + nextRunTime: '2024-12-30T02:00:00Z', + lastRunTime: '2024-12-29T02:00:00Z', + runCount: 45, + createdAt: '2024-11-15T08:00:00Z', + updatedAt: '2024-12-29T02:00:00Z', + }, + { + id: 2, + name: 'Weekly TechStart Vuln Scan', + engineIds: [3], + engineNames: ['Vulnerability Only'], + organizationId: 2, + organizationName: 'TechStart Inc', + targetId: null, + targetName: null, + scanMode: 'organization', + cronExpression: '0 3 * * 0', + isEnabled: true, + nextRunTime: '2025-01-05T03:00:00Z', + lastRunTime: '2024-12-29T03:00:00Z', + runCount: 12, + createdAt: '2024-10-01T10:00:00Z', + updatedAt: '2024-12-29T03:00:00Z', + }, + { + id: 3, + name: 'Hourly API Monitoring', + engineIds: [2], + engineNames: ['Quick Scan'], + organizationId: null, + organizationName: null, + targetId: 12, + targetName: 'api.acme.com', + scanMode: 'target', + cronExpression: '0 * * * *', + isEnabled: true, + nextRunTime: '2024-12-29T12:00:00Z', + lastRunTime: '2024-12-29T11:00:00Z', + runCount: 720, + createdAt: '2024-12-01T00:00:00Z', + updatedAt: '2024-12-29T11:00:00Z', + }, + { + id: 4, + name: 'Monthly Full Scan - Finance', + engineIds: [1], + engineNames: ['Full Scan'], + organizationId: 3, + organizationName: 'Global Finance Ltd', + targetId: null, + targetName: null, + scanMode: 'organization', + cronExpression: '0 0 1 * *', + isEnabled: false, + nextRunTime: '2025-01-01T00:00:00Z', + lastRunTime: '2024-12-01T00:00:00Z', + runCount: 6, + createdAt: '2024-06-01T08:00:00Z', + updatedAt: '2024-12-20T15:00:00Z', + }, + { + id: 5, + name: 'RetailMax Daily Quick', + engineIds: [2, 3], + engineNames: ['Quick Scan', 'Vulnerability Only'], + organizationId: null, + organizationName: null, + targetId: 8, + targetName: 'retailmax.com', + scanMode: 'target', + cronExpression: '0 4 * * *', + isEnabled: true, + nextRunTime: '2024-12-30T04:00:00Z', + lastRunTime: '2024-12-29T04:00:00Z', + runCount: 30, + createdAt: '2024-11-29T09:00:00Z', + updatedAt: '2024-12-29T04:00:00Z', + }, +] + +export function getMockScheduledScans(params?: { + page?: number + pageSize?: number + search?: string +}): GetScheduledScansResponse { + const page = params?.page || 1 + const pageSize = params?.pageSize || 10 + const search = params?.search?.toLowerCase() || '' + + let filtered = mockScheduledScans + + if (search) { + filtered = filtered.filter( + s => + s.name.toLowerCase().includes(search) || + s.organizationName?.toLowerCase().includes(search) || + s.targetName?.toLowerCase().includes(search) + ) + } + + const total = filtered.length + const totalPages = Math.ceil(total / pageSize) + const start = (page - 1) * pageSize + const results = filtered.slice(start, start + pageSize) + + return { + results, + total, + page, + pageSize, + totalPages, + } +} + +export function getMockScheduledScanById(id: number): ScheduledScan | undefined { + return mockScheduledScans.find(s => s.id === id) +} diff --git a/frontend/mock/data/workers.ts b/frontend/mock/data/workers.ts new file mode 100644 index 00000000..8f42bf53 --- /dev/null +++ b/frontend/mock/data/workers.ts @@ -0,0 +1,78 @@ +import type { WorkerNode, WorkersResponse } from '@/types/worker.types' + +export const mockWorkers: WorkerNode[] = [ + { + id: 1, + name: 'local-worker', + ipAddress: '127.0.0.1', + sshPort: 22, + username: 'root', + status: 'online', + isLocal: true, + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z', + info: { + cpuPercent: 23.5, + memoryPercent: 45.2, + }, + }, + { + id: 2, + name: 'worker-01', + ipAddress: '192.168.1.101', + sshPort: 22, + username: 'scanner', + status: 'online', + isLocal: false, + createdAt: '2024-06-15T08:00:00Z', + updatedAt: '2024-12-29T09:30:00Z', + info: { + cpuPercent: 56.8, + memoryPercent: 72.1, + }, + }, + { + id: 3, + name: 'worker-02', + ipAddress: '192.168.1.102', + sshPort: 22, + username: 'scanner', + status: 'online', + isLocal: false, + createdAt: '2024-07-20T10:00:00Z', + updatedAt: '2024-12-29T09:45:00Z', + info: { + cpuPercent: 34.2, + memoryPercent: 58.9, + }, + }, + { + id: 4, + name: 'worker-03', + ipAddress: '192.168.1.103', + sshPort: 22, + username: 'scanner', + status: 'offline', + isLocal: false, + createdAt: '2024-08-10T14:00:00Z', + updatedAt: '2024-12-28T16:00:00Z', + }, +] + +export function getMockWorkers(page = 1, pageSize = 10): WorkersResponse { + const total = mockWorkers.length + const totalPages = Math.ceil(total / pageSize) + const start = (page - 1) * pageSize + const results = mockWorkers.slice(start, start + pageSize) + + return { + results, + total, + page, + pageSize, + } +} + +export function getMockWorkerById(id: number): WorkerNode | undefined { + return mockWorkers.find(w => w.id === id) +} diff --git a/frontend/mock/index.ts b/frontend/mock/index.ts index edbe7d15..981a4b95 100644 --- a/frontend/mock/index.ts +++ b/frontend/mock/index.ts @@ -69,3 +69,39 @@ export { getMockSubdomains, getMockSubdomainById, } from './data/subdomains' + +// Auth +export { + mockUser, + mockMeResponse, + mockLoginResponse, + mockLogoutResponse, +} from './data/auth' + +// Engines +export { + mockEngines, + getMockEngines, + getMockEngineById, +} from './data/engines' + +// Workers +export { + mockWorkers, + getMockWorkers, + getMockWorkerById, +} from './data/workers' + +// Notifications +export { + mockNotifications, + getMockNotifications, + getMockUnreadCount, +} from './data/notifications' + +// Scheduled Scans +export { + mockScheduledScans, + getMockScheduledScans, + getMockScheduledScanById, +} from './data/scheduled-scans' diff --git a/frontend/services/auth.service.ts b/frontend/services/auth.service.ts index 4484e447..9bcfe1f4 100644 --- a/frontend/services/auth.service.ts +++ b/frontend/services/auth.service.ts @@ -10,11 +10,16 @@ import type { ChangePasswordRequest, ChangePasswordResponse } from '@/types/auth.types' +import { USE_MOCK, mockDelay, mockLoginResponse, mockLogoutResponse, mockMeResponse } from '@/mock' /** * User login */ export async function login(data: LoginRequest): Promise { + if (USE_MOCK) { + await mockDelay() + return mockLoginResponse + } const res = await api.post('/auth/login/', data) return res.data } @@ -23,6 +28,10 @@ export async function login(data: LoginRequest): Promise { * User logout */ export async function logout(): Promise { + if (USE_MOCK) { + await mockDelay() + return mockLogoutResponse + } const res = await api.post('/auth/logout/') return res.data } @@ -31,6 +40,10 @@ export async function logout(): Promise { * Get current user information */ export async function getMe(): Promise { + if (USE_MOCK) { + await mockDelay() + return mockMeResponse + } const res = await api.get('/auth/me/') return res.data } @@ -39,6 +52,10 @@ export async function getMe(): Promise { * Change password */ export async function changePassword(data: ChangePasswordRequest): Promise { + if (USE_MOCK) { + await mockDelay() + return { message: 'Password changed successfully' } + } const res = await api.post('/auth/change-password/', data) return res.data } diff --git a/frontend/services/engine.service.ts b/frontend/services/engine.service.ts index dd016a24..9379f393 100644 --- a/frontend/services/engine.service.ts +++ b/frontend/services/engine.service.ts @@ -1,5 +1,6 @@ import apiClient from '@/lib/api-client' import type { ScanEngine } from '@/types/engine.types' +import { USE_MOCK, mockDelay, getMockEngines, getMockEngineById } from '@/mock' /** * Engine API service @@ -9,6 +10,10 @@ import type { ScanEngine } from '@/types/engine.types' * Get engine list */ export async function getEngines(): Promise { + if (USE_MOCK) { + await mockDelay() + return getMockEngines() + } // Engines are usually not many, get all const response = await apiClient.get('/engines/', { params: { pageSize: 1000 } @@ -21,6 +26,12 @@ export async function getEngines(): Promise { * Get engine details */ export async function getEngine(id: number): Promise { + if (USE_MOCK) { + await mockDelay() + const engine = getMockEngineById(id) + if (!engine) throw new Error('Engine not found') + return engine + } const response = await apiClient.get(`/engines/${id}/`) return response.data } diff --git a/frontend/services/notification.service.ts b/frontend/services/notification.service.ts index aa7f013e..c75d07f2 100644 --- a/frontend/services/notification.service.ts +++ b/frontend/services/notification.service.ts @@ -9,6 +9,7 @@ import type { GetNotificationsRequest, GetNotificationsResponse, } from '@/types/notification.types' +import { USE_MOCK, mockDelay, getMockNotifications, getMockUnreadCount } from '@/mock' export class NotificationService { /** @@ -18,6 +19,10 @@ export class NotificationService { static async getNotifications( params: GetNotificationsRequest = {} ): Promise { + if (USE_MOCK) { + await mockDelay() + return getMockNotifications(params) + } const response = await api.get('/notifications/', { params, }) @@ -29,6 +34,10 @@ export class NotificationService { * 后端返回: { updated: number } */ static async markAllAsRead(): Promise<{ updated: number }> { + if (USE_MOCK) { + await mockDelay() + return { updated: 2 } + } const response = await api.post<{ updated: number }>('/notifications/mark-all-as-read/') return response.data } @@ -38,6 +47,10 @@ export class NotificationService { * 后端返回: { count: number } */ static async getUnreadCount(): Promise<{ count: number }> { + if (USE_MOCK) { + await mockDelay() + return getMockUnreadCount() + } const response = await api.get<{ count: number }>('/notifications/unread-count/') return response.data } diff --git a/frontend/services/scheduled-scan.service.ts b/frontend/services/scheduled-scan.service.ts index 89ec06a9..a79c0744 100644 --- a/frontend/services/scheduled-scan.service.ts +++ b/frontend/services/scheduled-scan.service.ts @@ -5,11 +5,16 @@ import type { CreateScheduledScanRequest, UpdateScheduledScanRequest } from '@/types/scheduled-scan.types' +import { USE_MOCK, mockDelay, getMockScheduledScans, getMockScheduledScanById } from '@/mock' /** * Get scheduled scan list */ export async function getScheduledScans(params?: { page?: number; pageSize?: number; search?: string }): Promise { + if (USE_MOCK) { + await mockDelay() + return getMockScheduledScans(params) + } const res = await api.get('/scheduled-scans/', { params }) return res.data } @@ -18,6 +23,12 @@ export async function getScheduledScans(params?: { page?: number; pageSize?: num * Get scheduled scan details */ export async function getScheduledScan(id: number): Promise { + if (USE_MOCK) { + await mockDelay() + const scan = getMockScheduledScanById(id) + if (!scan) throw new Error('Scheduled scan not found') + return scan + } const res = await api.get(`/scheduled-scans/${id}/`) return res.data } diff --git a/frontend/services/worker.service.ts b/frontend/services/worker.service.ts index 075d68c5..98580507 100644 --- a/frontend/services/worker.service.ts +++ b/frontend/services/worker.service.ts @@ -9,6 +9,7 @@ import type { CreateWorkerRequest, UpdateWorkerRequest, } from '@/types/worker.types' +import { USE_MOCK, mockDelay, getMockWorkers, getMockWorkerById } from '@/mock' const BASE_URL = '/workers' @@ -17,6 +18,10 @@ export const workerService = { * Get Worker list */ async getWorkers(page = 1, pageSize = 10): Promise { + if (USE_MOCK) { + await mockDelay() + return getMockWorkers(page, pageSize) + } const response = await apiClient.get( `${BASE_URL}/?page=${page}&page_size=${pageSize}` ) @@ -27,6 +32,12 @@ export const workerService = { * Get single Worker details */ async getWorker(id: number): Promise { + if (USE_MOCK) { + await mockDelay() + const worker = getMockWorkerById(id) + if (!worker) throw new Error('Worker not found') + return worker + } const response = await apiClient.get(`${BASE_URL}/${id}/`) return response.data },