diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/api/types/threat.types.ts b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/api/types/threat.types.ts new file mode 100644 index 00000000..ece0d22f --- /dev/null +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/api/types/threat.types.ts @@ -0,0 +1,67 @@ +// ©AngelaMos | 2026 +// threat.types.ts + +import { z } from 'zod' + +export const ransomwareVictimSchema = z.object({ + post_title: z.string(), + group_name: z.string(), + discovered: z.string(), + country: z.string().optional(), + activity: z.string().optional(), + website: z.string().optional(), + description: z.string().optional(), +}) + +export type RansomwareVictim = z.infer + +export const dshieldPortSchema = z.object({ + rank: z.number(), + targetport: z.number(), + records: z.number(), + targets: z.number(), + sources: z.number(), +}) + +export type DShieldPort = z.infer + +export const dshieldSourceSchema = z.object({ + rank: z.number(), + source: z.string(), + reports: z.number(), + targets: z.number(), +}) + +export type DShieldSource = z.infer + +export const dshieldDailySummarySchema = z.object({ + date: z.string(), + records: z.number(), + sources: z.number(), + targets: z.number(), +}) + +export type DShieldDailySummary = z.infer + +export const dshieldDataSchema = z.object({ + topports: z + .union([z.record(z.string(), z.unknown()), z.array(z.unknown())]) + .optional(), + topips: z.array(dshieldSourceSchema).optional(), + dailysummary: z.array(dshieldDailySummarySchema).optional(), + ts: z.string().optional(), +}) + +export type DShieldData = z.infer + +export const isValidRansomwareVictim = ( + data: unknown +): data is RansomwareVictim => { + if (data === null || data === undefined || typeof data !== 'object') return false + return ransomwareVictimSchema.safeParse(data).success +} + +export const isValidDShieldData = (data: unknown): data is DShieldData => { + if (data === null || data === undefined || typeof data !== 'object') return false + return dshieldDataSchema.safeParse(data).success +} diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/DShieldPanel.tsx b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/DShieldPanel.tsx index 30964cec..233ca464 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/DShieldPanel.tsx +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/DShieldPanel.tsx @@ -1,7 +1,8 @@ // ©AngelaMos | 2026 // DShieldPanel.tsx -import { useSnapshot } from '@/api/snapshot' +import { type DShieldDailySummary, type DShieldPort } from '@/api/types' +import { useDShieldData } from '@/api/hooks' import styles from './DShieldPanel.module.scss' import { Panel } from './Panel' @@ -10,39 +11,11 @@ const SOURCE_ROW_LIMIT = 8 const THOUSAND = 1_000 const MILLION = 1_000_000 -interface DShieldPort { - rank: number - targetport: number - records: number - targets: number - sources: number -} - -interface DShieldSource { - rank: number - source: string - reports: number - targets: number -} - -interface DShieldDailySummary { - date: string - records: number - sources: number - targets: number -} - -interface DShieldData { - topports?: Record | DShieldPort[] - topips?: DShieldSource[] - dailysummary?: DShieldDailySummary[] -} - export function DShieldPanel(): React.ReactElement { - const { data } = useSnapshot() - const ds = (data?.scan_firehose as DShieldData | undefined) ?? {} + const ds = useDShieldData() const ports = toArray(ds.topports) + .filter(isPort) .slice() .sort((a, b) => a.rank - b.rank) .slice(0, PORT_ROW_LIMIT) @@ -119,6 +92,17 @@ function toArray(v: Record | T[] | undefined): T[] { return Object.values(v) } +function isPort(v: unknown): v is DShieldPort { + if (!v || typeof v !== 'object') return false + const p = v as Record + return ( + typeof p.targetport === 'number' && + typeof p.records === 'number' && + typeof p.sources === 'number' && + typeof p.rank === 'number' + ) +} + function pickLatestSummary( list: DShieldDailySummary[] | undefined ): DShieldDailySummary | undefined {