From c8de2620c868c20068b1cba111094d476585a23c Mon Sep 17 00:00:00 2001 From: D Stephenson Date: Tue, 12 May 2026 20:22:01 +0000 Subject: [PATCH] Add scan concurrency and login delay sliders Serialised logins now sleep `login_delay` seconds between each SSH auth to prevent AD/LDAP lockout. Both max sessions (1-10) and login delay (0-15s) are configurable via UI sliders in the header and passed as JSON to all scan endpoints. Co-Authored-By: Claude Sonnet 4.6 --- app.py | 25 ++++++++++++++----- index.html | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-- scanner.py | 4 +-- ssh_client.py | 13 +++++++--- 4 files changed, 95 insertions(+), 14 deletions(-) diff --git a/app.py b/app.py index ec8d006..67d0014 100644 --- a/app.py +++ b/app.py @@ -34,10 +34,12 @@ def _reschedule(interval_minutes): logger.info(f"Auto-scan scheduled every {interval_minutes} minutes") -def _trigger_scan_background(dept: str = None): +def _trigger_scan_background(dept: str = None, workers: int = 5, login_delay: int = 3): """Run scan in a background thread. dept=None → all switches.""" if not scanner.scan_state["running"]: - t = threading.Thread(target=scanner.run_scan, kwargs={"dept": dept}, daemon=True) + t = threading.Thread(target=scanner.run_scan, + kwargs={"dept": dept, "workers": workers, "login_delay": login_delay}, + daemon=True) t.start() @@ -55,12 +57,20 @@ def index(): return render_template("index.html") +def _scan_params(): + body = request.get_json(silent=True) or {} + workers = max(1, min(10, int(body.get("workers", 5)))) + login_delay = max(0, min(30, int(body.get("login_delay", 3)))) + return workers, login_delay + + @app.route("/api/scan", methods=["POST"]) def api_scan(): """Scan all active switches (no dept filter).""" if scanner.scan_state["running"]: return jsonify({"error": "Scan already running"}), 409 - _trigger_scan_background(dept=None) + workers, login_delay = _scan_params() + _trigger_scan_background(dept=None, workers=workers, login_delay=login_delay) return jsonify({"status": "started", "dept": None}) @@ -74,7 +84,8 @@ def api_scan_clear(): conn.execute("DELETE FROM links") conn.commit() conn.close() - _trigger_scan_background(dept=None) + workers, login_delay = _scan_params() + _trigger_scan_background(dept=None, workers=workers, login_delay=login_delay) return jsonify({"status": "started", "cleared": True}) @@ -83,7 +94,8 @@ def api_scan_elec(): """Scan only ELEC department switches.""" if scanner.scan_state["running"]: return jsonify({"error": "Scan already running"}), 409 - _trigger_scan_background(dept="ELEC") + workers, login_delay = _scan_params() + _trigger_scan_background(dept="ELEC", workers=workers, login_delay=login_delay) return jsonify({"status": "started", "dept": "ELEC"}) @@ -92,7 +104,8 @@ def api_scan_gw(): """Scan only GW department switches.""" if scanner.scan_state["running"]: return jsonify({"error": "Scan already running"}), 409 - _trigger_scan_background(dept="GW") + workers, login_delay = _scan_params() + _trigger_scan_background(dept="GW", workers=workers, login_delay=login_delay) return jsonify({"status": "started", "dept": "GW"}) diff --git a/index.html b/index.html index a7f9b1a..027abd4 100644 --- a/index.html +++ b/index.html @@ -151,6 +151,44 @@ var Xr=function(e){if(!(this instanceof Xr))return new Xr(e);this.id="Thenable/1 cursor: pointer; } + .scan-settings-wrap { + display: flex; + align-items: center; + gap: 8px; + background: var(--surface2); + border: 1px solid var(--border); + border-radius: 8px; + padding: 6px 12px; + font-size: 12px; + color: var(--text-dim); + } + .scan-settings-wrap input[type=range] { + -webkit-appearance: none; + width: 70px; + height: 4px; + background: var(--border); + border-radius: 2px; + outline: none; + cursor: pointer; + } + .scan-settings-wrap input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none; + width: 13px; height: 13px; + border-radius: 50%; + background: var(--accent); + cursor: pointer; + } + .scan-settings-wrap .setting-val { + min-width: 22px; + color: var(--text); + font-weight: 600; + } + .scan-settings-sep { + width: 1px; height: 16px; + background: var(--border); + margin: 0 4px; + } + .btn { display: flex; align-items: center; @@ -424,6 +462,16 @@ var Xr=function(e){if(!(this instanceof Xr))return new Xr(e);this.id="Thenable/1 +
+ Sessions + + 5 +
+ Login delay + + 3s +
+