| 1 | import type { FlaskBaseResponse } from "@/types/flask.d" |
| 2 | import type { |
| 3 | CaseEvent, |
| 4 | CaseTask, |
| 5 | CaseTaskCreatePayload, |
| 6 | CaseTaskUpdatePayload, |
| 7 | CaseTemplate, |
| 8 | CaseTemplateCreatePayload, |
| 9 | CaseTemplateLibraryListResponse, |
| 10 | CaseTemplateLibraryRefreshResponse, |
| 11 | CaseTemplateTask, |
| 12 | CaseTemplateTaskCreatePayload, |
| 13 | CaseTemplateTaskUpdatePayload, |
| 14 | CaseTemplateUpdatePayload |
| 15 | } from "@/types/incidentManagement/caseTemplates.d" |
| 16 | import { HttpClient } from "../../httpClient" |
| 17 | |
| 18 | // --------------------------------------------------------------------------- |
| 19 | // Case template management (admin/analyst only — backend gates by scope) |
| 20 | // --------------------------------------------------------------------------- |
| 21 | |
| 22 | export interface CaseTemplateListFilters { |
| 23 | customerCode?: string |
| 24 | source?: string |
| 25 | includeGlobal?: boolean |
| 26 | } |
| 27 | |
| 28 | export default { |
| 29 | listTemplates(filters: CaseTemplateListFilters = {}) { |
| 30 | const params: Record<string, string | boolean> = {} |
| 31 | if (filters.customerCode !== undefined) params.customer_code = filters.customerCode |
| 32 | if (filters.source !== undefined) params.source = filters.source |
| 33 | if (filters.includeGlobal !== undefined) params.include_global = filters.includeGlobal |
| 34 | |
| 35 | return HttpClient.get<FlaskBaseResponse & { templates: CaseTemplate[] }>(`/incidents/case_templates`, { |
| 36 | params |
| 37 | }) |
| 38 | }, |
| 39 | getTemplate(templateId: number) { |
| 40 | return HttpClient.get<FlaskBaseResponse & { template: CaseTemplate | null }>( |
| 41 | `/incidents/case_templates/${templateId}` |
| 42 | ) |
| 43 | }, |
| 44 | createTemplate(payload: CaseTemplateCreatePayload) { |
| 45 | return HttpClient.post<FlaskBaseResponse & { template: CaseTemplate | null }>( |
| 46 | `/incidents/case_templates`, |
| 47 | payload |
| 48 | ) |
| 49 | }, |
| 50 | updateTemplate(templateId: number, payload: CaseTemplateUpdatePayload) { |
| 51 | return HttpClient.patch<FlaskBaseResponse & { template: CaseTemplate | null }>( |
| 52 | `/incidents/case_templates/${templateId}`, |
| 53 | payload |
| 54 | ) |
| 55 | }, |
| 56 | deleteTemplate(templateId: number) { |
| 57 | return HttpClient.delete<FlaskBaseResponse & { template: CaseTemplate | null }>( |
| 58 | `/incidents/case_templates/${templateId}` |
| 59 | ) |
| 60 | }, |
| 61 | |
| 62 | // Template tasks |
| 63 | addTemplateTask(templateId: number, payload: CaseTemplateTaskCreatePayload) { |
| 64 | return HttpClient.post<FlaskBaseResponse & { task: CaseTemplateTask | null }>( |
| 65 | `/incidents/case_templates/${templateId}/tasks`, |
| 66 | payload |
| 67 | ) |
| 68 | }, |
| 69 | updateTemplateTask(taskId: number, payload: CaseTemplateTaskUpdatePayload) { |
| 70 | return HttpClient.patch<FlaskBaseResponse & { task: CaseTemplateTask | null }>( |
| 71 | `/incidents/case_templates/tasks/${taskId}`, |
| 72 | payload |
| 73 | ) |
| 74 | }, |
| 75 | deleteTemplateTask(taskId: number) { |
| 76 | return HttpClient.delete<FlaskBaseResponse & { task: CaseTemplateTask | null }>( |
| 77 | `/incidents/case_templates/tasks/${taskId}` |
| 78 | ) |
| 79 | }, |
| 80 | reorderTemplateTasks(templateId: number, orderedTaskIds: number[]) { |
| 81 | return HttpClient.post<FlaskBaseResponse & { template: CaseTemplate | null }>( |
| 82 | `/incidents/case_templates/${templateId}/tasks/reorder`, |
| 83 | orderedTaskIds |
| 84 | ) |
| 85 | }, |
| 86 | |
| 87 | // --------------------------------------------------------------------------- |
| 88 | // Per-case tasks (visible to admin/analyst/customer_user; writes admin/analyst only) |
| 89 | // --------------------------------------------------------------------------- |
| 90 | listCaseTasks(caseId: number) { |
| 91 | return HttpClient.get<FlaskBaseResponse & { tasks: CaseTask[] }>( |
| 92 | `/incidents/db_operations/case/${caseId}/tasks` |
| 93 | ) |
| 94 | }, |
| 95 | addCaseTask(caseId: number, payload: CaseTaskCreatePayload) { |
| 96 | return HttpClient.post<FlaskBaseResponse & { task: CaseTask | null }>( |
| 97 | `/incidents/db_operations/case/${caseId}/tasks`, |
| 98 | payload |
| 99 | ) |
| 100 | }, |
| 101 | updateCaseTask(taskId: number, payload: CaseTaskUpdatePayload, signal?: AbortSignal) { |
| 102 | return HttpClient.patch<FlaskBaseResponse & { task: CaseTask | null }>( |
| 103 | `/incidents/db_operations/case/tasks/${taskId}`, |
| 104 | payload, |
| 105 | { signal } |
| 106 | ) |
| 107 | }, |
| 108 | deleteCaseTask(taskId: number) { |
| 109 | return HttpClient.delete<FlaskBaseResponse & { task: CaseTask | null }>( |
| 110 | `/incidents/db_operations/case/tasks/${taskId}` |
| 111 | ) |
| 112 | }, |
| 113 | applyTemplateToCase(caseId: number, templateId: number, alertId?: number | null) { |
| 114 | const params: Record<string, number> = {} |
| 115 | if (alertId !== undefined && alertId !== null) params.alert_id = alertId |
| 116 | return HttpClient.post<FlaskBaseResponse & { tasks_added: number }>( |
| 117 | `/incidents/db_operations/case/${caseId}/apply-template/${templateId}`, |
| 118 | undefined, |
| 119 | { params } |
| 120 | ) |
| 121 | }, |
| 122 | |
| 123 | // --------------------------------------------------------------------------- |
| 124 | // Timeline (read-only for everyone with case access) |
| 125 | // --------------------------------------------------------------------------- |
| 126 | getCaseTimeline(caseId: number, limit = 500, offset = 0) { |
| 127 | return HttpClient.get<FlaskBaseResponse & { case_id: number; events: CaseEvent[] }>( |
| 128 | `/incidents/db_operations/case/${caseId}/timeline`, |
| 129 | { params: { limit, offset } } |
| 130 | ) |
| 131 | }, |
| 132 | |
| 133 | // --------------------------------------------------------------------------- |
| 134 | // Case Template Library (admin/analyst only — backend gates by scope) |
| 135 | // Read-only catalog of YAML playbooks from |
| 136 | // https://github.com/socfortress/CoPilot-Case-Templates. |
| 137 | // --------------------------------------------------------------------------- |
| 138 | getLibrary() { |
| 139 | return HttpClient.get<FlaskBaseResponse & CaseTemplateLibraryListResponse>(`/incidents/case_templates/library`) |
| 140 | }, |
| 141 | refreshLibrary() { |
| 142 | return HttpClient.post<FlaskBaseResponse & CaseTemplateLibraryRefreshResponse>( |
| 143 | `/incidents/case_templates/library/refresh` |
| 144 | ) |
| 145 | }, |
| 146 | importLibraryEntry(key: string) { |
| 147 | // Backend returns the standard CaseTemplateOperationResponse on success, |
| 148 | // or HTTP 409 if a CaseTemplate with the same name already exists. |
| 149 | return HttpClient.post<FlaskBaseResponse & { template: CaseTemplate | null }>( |
| 150 | `/incidents/case_templates/library/${encodeURIComponent(key)}/import` |
| 151 | ) |
| 152 | } |
| 153 | } |