feat(frontend): add Zod schemas for threat detection API

Type-safe schemas matching backend Pydantic models for threats, stats,
models status, and WebSocket alerts.
This commit is contained in:
CarterPerez-dev 2026-02-28 11:24:03 -05:00
parent 012d849366
commit cdeb1451ef
5 changed files with 131 additions and 0 deletions

View File

@ -2,3 +2,8 @@
// © AngelaMos | 2026
// index.ts
// ===================
export * from './threats.types'
export * from './stats.types'
export * from './models.types'
export * from './websocket.types'

View File

@ -0,0 +1,29 @@
// ===================
// © AngelaMos | 2026
// models.types.ts
// ===================
import { z } from 'zod'
export const ActiveModelSchema = z.object({
model_type: z.string(),
version: z.string(),
training_samples: z.number().int(),
metrics: z.record(z.string(), z.number()),
threshold: z.number().nullable(),
})
export const ModelStatusSchema = z.object({
models_loaded: z.boolean(),
detection_mode: z.string(),
active_models: z.array(ActiveModelSchema),
})
export const RetrainResponseSchema = z.object({
status: z.string(),
job_id: z.string(),
})
export type ActiveModel = z.infer<typeof ActiveModelSchema>
export type ModelStatus = z.infer<typeof ModelStatusSchema>
export type RetrainResponse = z.infer<typeof RetrainResponseSchema>

View File

@ -0,0 +1,36 @@
// ===================
// © AngelaMos | 2026
// stats.types.ts
// ===================
import { z } from 'zod'
export const SeverityBreakdownSchema = z.object({
high: z.number().int(),
medium: z.number().int(),
low: z.number().int(),
})
export const IPStatEntrySchema = z.object({
source_ip: z.string(),
count: z.number().int(),
})
export const PathStatEntrySchema = z.object({
path: z.string(),
count: z.number().int(),
})
export const StatsResponseSchema = z.object({
time_range: z.string(),
threats_stored: z.number().int(),
threats_detected: z.number().int(),
severity_breakdown: SeverityBreakdownSchema,
top_source_ips: z.array(IPStatEntrySchema),
top_attacked_paths: z.array(PathStatEntrySchema),
})
export type SeverityBreakdown = z.infer<typeof SeverityBreakdownSchema>
export type IPStatEntry = z.infer<typeof IPStatEntrySchema>
export type PathStatEntry = z.infer<typeof PathStatEntrySchema>
export type StatsResponse = z.infer<typeof StatsResponseSchema>

View File

@ -0,0 +1,43 @@
// ===================
// © AngelaMos | 2026
// threats.types.ts
// ===================
import { z } from 'zod'
export const GeoInfoSchema = z.object({
country: z.string().nullable(),
city: z.string().nullable(),
lat: z.number().nullable(),
lon: z.number().nullable(),
})
export const ThreatEventSchema = z.object({
id: z.string().uuid(),
created_at: z.string(),
source_ip: z.string(),
request_method: z.string(),
request_path: z.string(),
status_code: z.number().int(),
response_size: z.number().int(),
user_agent: z.string(),
threat_score: z.number(),
severity: z.literal(['HIGH', 'MEDIUM', 'LOW']),
component_scores: z.record(z.string(), z.number()),
geo: GeoInfoSchema,
matched_rules: z.array(z.string()).nullable(),
model_version: z.string().nullable(),
reviewed: z.boolean(),
review_label: z.string().nullable(),
})
export const ThreatListSchema = z.object({
total: z.number().int(),
limit: z.number().int(),
offset: z.number().int(),
items: z.array(ThreatEventSchema),
})
export type GeoInfo = z.infer<typeof GeoInfoSchema>
export type ThreatEvent = z.infer<typeof ThreatEventSchema>
export type ThreatList = z.infer<typeof ThreatListSchema>

View File

@ -0,0 +1,18 @@
// ===================
// © AngelaMos | 2026
// websocket.types.ts
// ===================
import { z } from 'zod'
export const WebSocketAlertSchema = z.object({
event: z.literal('threat'),
timestamp: z.string(),
source_ip: z.string(),
request_path: z.string(),
threat_score: z.number(),
severity: z.string(),
component_scores: z.record(z.string(), z.number()),
})
export type WebSocketAlert = z.infer<typeof WebSocketAlertSchema>