mirror of
https://github.com/marcredhat/SIEM-toolkit-patched
synced 2026-06-08 12:33:51 +00:00
Fix Ingest Dashboard timeout causing failed to fetch
- daily-volume: run per-day PowerQueries in parallel with asyncio.gather instead of sequentially with sleeps — 3 days now completes in ~16s vs 140s+ - Default view changed from 7d to 3d; day buttons updated to [3, 5, 7] - igLoad: fire daily-volume and top-sources simultaneously with Promise.allSettled so both panels load in parallel rather than one after the other - Each panel shows "Querying data lake…" spinner while loading - Each panel renders independently — one failure doesn't block the other Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+14
-12
@@ -39,24 +39,26 @@ async def get_by_event_type(days: int = Query(7, ge=1, le=90)):
|
||||
|
||||
|
||||
@router.get("/daily-volume")
|
||||
async def get_daily_volume(days: int = Query(7, ge=1, le=14)):
|
||||
"""Total event count per day."""
|
||||
async def get_daily_volume(days: int = Query(5, ge=1, le=7)):
|
||||
"""Total event count per day — queries run in parallel."""
|
||||
import asyncio
|
||||
results = []
|
||||
|
||||
now = datetime.utcnow()
|
||||
points = min(days, 7)
|
||||
for i in range(points):
|
||||
day_from = (datetime.utcnow() - timedelta(days=i + 1)).strftime("%Y-%m-%dT00:00:00.000Z")
|
||||
day_to = (datetime.utcnow() - timedelta(days=i)).strftime("%Y-%m-%dT00:00:00.000Z")
|
||||
label = (datetime.utcnow() - timedelta(days=i + 1)).strftime("%Y-%m-%d")
|
||||
|
||||
async def _fetch_day(i: int) -> dict:
|
||||
day_from = (now - timedelta(days=i + 1)).strftime("%Y-%m-%dT00:00:00.000Z")
|
||||
day_to = (now - timedelta(days=i)).strftime("%Y-%m-%dT00:00:00.000Z")
|
||||
label = (now - timedelta(days=i + 1)).strftime("%Y-%m-%d")
|
||||
try:
|
||||
result = await s1_client.run_powerquery("| group total=count()", day_from, day_to)
|
||||
events_list = result.get("events") if isinstance(result, dict) else []
|
||||
count = events_list[0].get("total", 0) if isinstance(events_list, list) and events_list else 0
|
||||
events_list = result.get("events", []) if isinstance(result, dict) else []
|
||||
count = events_list[0].get("total", 0) if events_list else 0
|
||||
except Exception:
|
||||
count = 0
|
||||
results.append({"date": label, "events": count})
|
||||
if i < points - 1:
|
||||
await asyncio.sleep(3)
|
||||
return {"date": label, "events": count}
|
||||
|
||||
results = await asyncio.gather(*[_fetch_day(i) for i in range(points)])
|
||||
return list(reversed(results))
|
||||
|
||||
|
||||
|
||||
+24
-13
@@ -249,7 +249,7 @@ function cvSetFilter(f) {
|
||||
|
||||
// ── Ingest ────────────────────────────────────────────────────────────────
|
||||
|
||||
let igDays = 7
|
||||
let igDays = 3
|
||||
|
||||
function renderIngest() {
|
||||
set(`<div class="p-8 max-w-6xl">
|
||||
@@ -259,7 +259,7 @@ function renderIngest() {
|
||||
<p class="text-sm text-gray-400 mt-1">Event volume · cost projection · filter simulator</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
${[7,14,30].map(d=>`<button onclick="igSetDays(${d})" id="ig-d${d}"
|
||||
${[3,5,7].map(d=>`<button onclick="igSetDays(${d})" id="ig-d${d}"
|
||||
class="px-3 py-1.5 text-xs rounded-lg border border-gray-700 text-gray-400 hover:border-gray-500">${d}d</button>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
@@ -291,7 +291,7 @@ function renderIngest() {
|
||||
|
||||
function igSetDays(d) {
|
||||
igDays = d
|
||||
;[7,14,30].forEach(n => {
|
||||
;[3,5,7].forEach(n => {
|
||||
const b = document.getElementById(`ig-d${n}`)
|
||||
if (b) b.className = `px-3 py-1.5 text-xs rounded-lg border transition-colors ${n===d ? 'bg-purple-700 border-purple-600 text-white' : 'border-gray-700 text-gray-400 hover:border-gray-500'}`
|
||||
})
|
||||
@@ -299,14 +299,25 @@ function igSetDays(d) {
|
||||
}
|
||||
|
||||
async function igLoad() {
|
||||
try {
|
||||
const daily = await apiGet(`/api/ingest/daily-volume?days=${igDays}`)
|
||||
document.getElementById('ig-chart').innerHTML = barChart(daily, 'date', 'events')
|
||||
} catch(e) {
|
||||
document.getElementById('ig-chart').innerHTML = `<p class="text-red-400 text-sm">${esc(e.message)}</p>`
|
||||
const spinner = `<p class="text-gray-500 text-sm animate-pulse">Querying data lake…</p>`
|
||||
document.getElementById('ig-chart').innerHTML = spinner
|
||||
document.getElementById('ig-sources').innerHTML = spinner
|
||||
|
||||
const [dailyResult, sourcesResult] = await Promise.allSettled([
|
||||
apiGet(`/api/ingest/daily-volume?days=${igDays}`),
|
||||
apiGet(`/api/ingest/top-sources?days=${igDays}`)
|
||||
])
|
||||
|
||||
// Daily volume chart
|
||||
if (dailyResult.status === 'fulfilled') {
|
||||
document.getElementById('ig-chart').innerHTML = barChart(dailyResult.value, 'date', 'events')
|
||||
} else {
|
||||
document.getElementById('ig-chart').innerHTML = `<p class="text-red-400 text-sm">${esc(dailyResult.reason?.message ?? 'Error')}</p>`
|
||||
}
|
||||
try {
|
||||
const { data = [] } = await apiGet(`/api/ingest/top-sources?days=${igDays}`)
|
||||
|
||||
// Top sources table
|
||||
if (sourcesResult.status === 'fulfilled') {
|
||||
const data = sourcesResult.value?.data ?? []
|
||||
const rows = data.map(r => {
|
||||
const name = r['dataSource.name'] || r.name || 'unknown'
|
||||
const evts = r.events || 0
|
||||
@@ -324,9 +335,9 @@ async function igLoad() {
|
||||
<th class="pb-2 text-right font-medium">Est. GB</th>
|
||||
</tr></thead>
|
||||
<tbody>${rows.join('')}</tbody>
|
||||
</table>` : `<p class="text-gray-500 text-sm">No data — check that S1_BASE_URL points to your SDL-enabled tenant.</p>`
|
||||
} catch(e) {
|
||||
document.getElementById('ig-sources').innerHTML = `<p class="text-red-400 text-sm">${esc(e.message)}</p>`
|
||||
</table>` : `<p class="text-gray-500 text-sm">No data in this period.</p>`
|
||||
} else {
|
||||
document.getElementById('ig-sources').innerHTML = `<p class="text-red-400 text-sm">${esc(sourcesResult.reason?.message ?? 'Error')}</p>`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user