Make Stop stop: no replay, no restart, immediate feedback
Build — Windows, Linux & macOS / GDPRScanner / linux (push) Has been cancelled
Build — Windows, Linux & macOS / GDPRScanner / macos (push) Has been cancelled
Build — Windows, Linux & macOS / GDPRScanner / windows (push) Has been cancelled
Build — Windows, Linux & macOS / Create GitHub Release (push) Has been cancelled
Build — Windows, Linux & macOS / GDPRScanner / linux (push) Has been cancelled
Build — Windows, Linux & macOS / GDPRScanner / macos (push) Has been cancelled
Build — Windows, Linux & macOS / GDPRScanner / windows (push) Has been cancelled
Build — Windows, Linux & macOS / Create GitHub Release (push) Has been cancelled
Pressing Stop could look like the scan restarting, and for scheduled jobs it really did restart the Google stage. - Scheduler: check the abort flag between stages and never clear the Google abort after a Stop (that started a fresh all-users Google scan). A stopped job is recorded as "cancelled" and skips email/retention; scheduler_done carries cancelled=true. - SSE: scan_cancelled and file_scan_done now end the current scan like scan_done, and sse.replay_messages() never replays a finished scan. Previously a stopped M365 scan (and every file scan) stayed "current", so each reconnect replayed it and re-showed Stop + progress. - Frontend: scan_cancelled clears the M365 running flag and only tears down the UI when no Google/file scan is still running; scheduler_done / scheduler_error clear all running flags. - run_scan: Stop during collection now ends the scan (checkpoint, finish, scan_cancelled) instead of announcing scan_start / "Scanning…". - Stop button: switches to a disabled "Stopping…" state on click; all show/hide goes through _setStopBtnDisplay(), which resets it when hidden or when a new scan starts. Adds tests/test_stop_scan.py (10 tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit was merged in pull request #2.
This commit is contained in:
1 parent
9cf23da907
commit
606d8e944b
12 files changed
+372
-64
No files matched your search
+27
-12
@@ -351,6 +351,9 @@ class ScanScheduler:
|
||||
)
|
||||
_fs_extra = {k: _profile_opts[k] for k in _FS_OPT_KEYS if k in _profile_opts}
|
||||
for fs in options.get("file_sources", []):
|
||||
# Stop pressed during an earlier stage — don't start more stages
|
||||
if _m._scan_abort.is_set():
|
||||
break
|
||||
# Resolve string IDs to full source dicts
|
||||
if isinstance(fs, str):
|
||||
fs = _all_file_sources.get(fs, {"path": fs, "label": fs})
|
||||
@@ -374,7 +377,12 @@ class ScanScheduler:
|
||||
# Legacy profiles store everything in sources[]
|
||||
google_sources = [s for s in options.get("sources", [])
|
||||
if s in ("gmail", "gdrive")]
|
||||
if google_sources and state.google_connector:
|
||||
# POST /api/scan/stop sets both abort events. _scan_abort was cleared
|
||||
# when this job started, so if it is set now the user pressed Stop
|
||||
# during an earlier stage — the Google stage must not start, and its
|
||||
# abort event must not be cleared (that turned Stop into a restart).
|
||||
stopped = _m._scan_abort.is_set()
|
||||
if google_sources and state.google_connector and not stopped:
|
||||
from routes.google_scan import (
|
||||
_run_google_scan as _rgs,
|
||||
_scan_lock as _gsl,
|
||||
@@ -383,12 +391,15 @@ class ScanScheduler:
|
||||
if _gsl.acquire(blocking=False):
|
||||
try:
|
||||
_gsa.clear()
|
||||
logger.info("[scheduler] Starting Google scan — sources=%s", google_sources)
|
||||
_rgs({
|
||||
"sources": google_sources,
|
||||
"user_emails": [], # empty → scan all workspace users
|
||||
"options": options.get("options", {}),
|
||||
})
|
||||
# Stop sets _scan_abort first, so re-check after clearing:
|
||||
# a Stop landing in between must still win.
|
||||
if not _m._scan_abort.is_set():
|
||||
logger.info("[scheduler] Starting Google scan — sources=%s", google_sources)
|
||||
_rgs({
|
||||
"sources": google_sources,
|
||||
"user_emails": [], # empty → scan all workspace users
|
||||
"options": options.get("options", {}),
|
||||
})
|
||||
except Exception as _ge:
|
||||
import traceback as _tb3
|
||||
logger.error("[scheduler] Google scan error: %s\n%s", _ge, _tb3.format_exc())
|
||||
@@ -399,18 +410,21 @@ class ScanScheduler:
|
||||
|
||||
run["flagged"] = len(_m.flagged_items)
|
||||
run["scanned"] = _m.scan_meta.get("total_scanned", 0)
|
||||
run["status"] = "completed"
|
||||
logger.info("[scheduler] Job '%s' completed — %d flagged, %d scanned",
|
||||
job_cfg.get("name", ""), run["flagged"], run["scanned"])
|
||||
stopped = _m._scan_abort.is_set()
|
||||
run["status"] = "cancelled" if stopped else "completed"
|
||||
logger.info("[scheduler] Job '%s' %s — %d flagged, %d scanned",
|
||||
job_cfg.get("name", ""), run["status"], run["flagged"], run["scanned"])
|
||||
|
||||
if job_cfg.get("auto_email") and state.flagged_items:
|
||||
# A stopped job's results are partial — don't email them or apply
|
||||
# retention based on them.
|
||||
if not stopped and job_cfg.get("auto_email") and state.flagged_items:
|
||||
try:
|
||||
self._send_email_report(job_cfg)
|
||||
run["emailed"] = 1
|
||||
except Exception as e:
|
||||
run["error"] = f"Scan OK, email failed: {e}"
|
||||
|
||||
if job_cfg.get("auto_retention") and job_cfg.get("retention_years"):
|
||||
if not stopped and job_cfg.get("auto_retention") and job_cfg.get("retention_years"):
|
||||
try:
|
||||
self._run_retention(job_cfg)
|
||||
except Exception as e:
|
||||
@@ -420,6 +434,7 @@ class ScanScheduler:
|
||||
_m.broadcast("scheduler_done", {
|
||||
"flagged": run["flagged"], "scanned": run["scanned"],
|
||||
"emailed": run["emailed"], "job_name": job_cfg.get("name", ""),
|
||||
"cancelled": stopped,
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in new issue
Block a user