From 9c9802205a2b50d41540a7c36f83aa866ff3359a Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Mon, 13 Apr 2026 17:12:14 +0200 Subject: [PATCH] Fix site filter in Job Details returning no results Site filter performed client-side against a 1000-row limited deviation set, causing "No permission deviations found" for any site whose deviations fell outside that window. Backend now accepts ?site_url= for server-side filtering without limit; frontend calls the API on filter change instead of filtering the cached data. Co-Authored-By: Claude Sonnet 4.6 --- containers/clearview/site/app.js | 14 ++++++++-- .../clearview/src/clearview_app/main.py | 26 +++++++++++-------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/containers/clearview/site/app.js b/containers/clearview/site/app.js index d2bbd6a..e48462a 100644 --- a/containers/clearview/site/app.js +++ b/containers/clearview/site/app.js @@ -884,8 +884,18 @@ }); }); - els.jobSiteFilter.addEventListener('change', function () { - if (state.selectedJobData) { + els.jobSiteFilter.addEventListener('change', async function () { + if (!state.selectedJobId) return; + const siteFilter = els.jobSiteFilter.value; + if (siteFilter) { + // Fetch server-side filtered data (no 1000-row limit) + const filtered = await requestJson( + '/api/scan-jobs/' + encodeURIComponent(state.selectedJobId) + + '?site_url=' + encodeURIComponent(siteFilter) + ); + renderJobTables(filtered); + } else { + // "All sites" — use the already-loaded full job data renderJobTables(state.selectedJobData); } }); diff --git a/containers/clearview/src/clearview_app/main.py b/containers/clearview/src/clearview_app/main.py index 9545b1d..ecd621b 100644 --- a/containers/clearview/src/clearview_app/main.py +++ b/containers/clearview/src/clearview_app/main.py @@ -419,23 +419,27 @@ def export_scan_job(job_id: str, site_url: str | None = None) -> StreamingRespon @app.get("/api/scan-jobs/{job_id}", response_model=ScanJobDetail) -def get_scan_job(job_id: str) -> ScanJobDetail: +def get_scan_job(job_id: str, site_url: str | None = None) -> ScanJobDetail: with SessionLocal() as db: job = db.get(ScanJob, job_id, options=[joinedload(ScanJob.tenant_profile)]) if not job: raise HTTPException(status_code=404, detail="Job not found") - targets = list( - db.execute(select(ScanTarget).where(ScanTarget.job_id == job.id).order_by(ScanTarget.id.asc())).scalars() - ) - deviations = list( - db.execute( - select(PermissionDeviation) - .where(PermissionDeviation.job_id == job.id) - .order_by(PermissionDeviation.id.desc()) - .limit(1000) - ).scalars() + targets_q = select(ScanTarget).where(ScanTarget.job_id == job.id).order_by(ScanTarget.id.asc()) + if site_url: + targets_q = targets_q.where(ScanTarget.site_url == site_url) + targets = list(db.execute(targets_q).scalars()) + + deviations_q = ( + select(PermissionDeviation) + .where(PermissionDeviation.job_id == job.id) + .order_by(PermissionDeviation.site_url.asc(), PermissionDeviation.object_url.asc(), PermissionDeviation.id.asc()) ) + if site_url: + deviations_q = deviations_q.where(PermissionDeviation.site_url == site_url) + else: + deviations_q = deviations_q.limit(1000) + deviations = list(db.execute(deviations_q).scalars()) return ScanJobDetail( **_to_job_summary(job).model_dump(),