feat: Add Playbook Manager, Saved Searches, and Timeline View components

- Implemented PlaybookManager for creating and managing investigation playbooks with templates.
- Added SavedSearches component for managing bookmarked queries and recurring scans.
- Introduced TimelineView for visualizing forensic event timelines with zoomable charts.
- Enhanced backend processing with auto-queued jobs for dataset uploads and improved database concurrency.
- Updated frontend components for better user experience and performance optimizations.
- Documented changes in update log for future reference.
This commit is contained in:
2026-02-23 14:23:07 -05:00
parent 37a9584d0c
commit 5a2ad8ec1c
110 changed files with 10537 additions and 1185 deletions

View File

@@ -0,0 +1,75 @@
from pathlib import Path
p=Path(r'd:/Projects/Dev/ThreatHunt/frontend/src/components/AUPScanner.tsx')
t=p.read_text(encoding='utf-8')
# default selection when hunt changes: first 3 datasets instead of all
old=''' datasets.list(0, 500, selectedHuntId).then(res => {
if (cancelled) return;
setDsList(res.datasets);
setSelectedDs(new Set(res.datasets.map(d => d.id)));
}).catch(() => {});
'''
new=''' datasets.list(0, 500, selectedHuntId).then(res => {
if (cancelled) return;
setDsList(res.datasets);
setSelectedDs(new Set(res.datasets.slice(0, 3).map(d => d.id)));
}).catch(() => {});
'''
if old not in t:
raise SystemExit('hunt-change dataset init block not found')
t=t.replace(old,new)
# insert dataset scope multi-select under hunt info
anchor=''' {!selectedHuntId && (
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: 'block' }}>
All datasets will be scanned if no hunt is selected
</Typography>
)}
</Box>
{/* Theme selector */}
'''
insert=''' {!selectedHuntId && (
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: 'block' }}>
Select a hunt to enable scoped scanning
</Typography>
)}
<FormControl size="small" fullWidth sx={{ mt: 1.2 }} disabled={!selectedHuntId || dsList.length === 0}>
<InputLabel id="aup-dataset-label">Datasets</InputLabel>
<Select
labelId="aup-dataset-label"
multiple
value={Array.from(selectedDs)}
label="Datasets"
renderValue={(selected) => `${(selected as string[]).length} selected`}
onChange={(e) => setSelectedDs(new Set(e.target.value as string[]))}
>
{dsList.map(d => (
<MenuItem key={d.id} value={d.id}>
<Checkbox size="small" checked={selectedDs.has(d.id)} />
<Typography variant="body2" sx={{ ml: 0.5 }}>
{d.name} ({d.row_count.toLocaleString()} rows)
</Typography>
</MenuItem>
))}
</Select>
</FormControl>
{selectedHuntId && dsList.length > 0 && (
<Stack direction="row" spacing={1} sx={{ mt: 1 }}>
<Button size="small" onClick={() => setSelectedDs(new Set(dsList.slice(0, 3).map(d => d.id)))}>Top 3</Button>
<Button size="small" onClick={() => setSelectedDs(new Set(dsList.map(d => d.id)))}>All</Button>
<Button size="small" onClick={() => setSelectedDs(new Set())}>Clear</Button>
</Stack>
)}
</Box>
{/* Theme selector */}
'''
if anchor not in t:
raise SystemExit('dataset scope anchor not found')
t=t.replace(anchor,insert)
p.write_text(t,encoding='utf-8')
print('added AUP dataset multi-select scoping and safer defaults')