feat: scan history browser, user-scoped viewer tokens, export fixes, email fixes (v1.6.20)
- Scan history browser (history.js, GET /api/db/sessions, get_sessions(), get_session_items(ref_scan_id)) — review any past session without rescanning - User-scoped viewer tokens (#34) — scope by individual employee across M365 and GWS; autocomplete from Accounts list; dual-email support - Fix: GWS scan never marked finished (end_scan → finish_scan) and emitted wrong SSE event (scan_done → google_scan_done), excluding GWS items from all exports - Fix: file scan begin_scan called with wrong keyword args (TypeError swallowed), so local/SMB items were never written to DB - Fix: Graph sendMail reported failure on success — _post() now returns {} on empty 202 response instead of raising JSONDecodeError - Fix: Graph error hidden behind generic "No SMTP host" message when both Graph and SMTP were unavailable - Fix: Gmail vs Google Workspace SMTP error messages distinguished by username domain; Workspace errors point to admin console, not personal security settings - Docs: update README, MANUAL-EN, MANUAL-DA, CLAUDE.md, TODO.md, CHANGELOG.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
e64d7eb958
commit
c9aab19a97
24 files changed
+753
-70
No files matched your search
+17
-1
@@ -70,6 +70,13 @@ def db_scans():
|
||||
return jsonify(_get_db().scans_list())
|
||||
|
||||
|
||||
@bp.route("/api/db/sessions")
|
||||
def db_sessions():
|
||||
"""List scan sessions (grouped concurrent scans), newest first."""
|
||||
if not DB_OK: return jsonify([])
|
||||
return jsonify(_get_db().get_sessions())
|
||||
|
||||
|
||||
@bp.route("/api/db/subject", methods=["POST"])
|
||||
def db_subject_lookup():
|
||||
"""Find all items containing a given CPR number.
|
||||
@@ -154,13 +161,22 @@ def db_flagged_items():
|
||||
from flask import session as _session
|
||||
scope = _session.get("viewer_scope", {})
|
||||
role_filt = scope.get("role", "") if isinstance(scope, dict) else ""
|
||||
items = _get_db().get_session_items()
|
||||
# user may be a list of emails (current) or a legacy single string
|
||||
raw_user = scope.get("user", "") if isinstance(scope, dict) else ""
|
||||
if isinstance(raw_user, list):
|
||||
user_filt = set(e.lower() for e in raw_user if e)
|
||||
else:
|
||||
user_filt = {raw_user.lower()} if raw_user else set()
|
||||
ref_scan_id = request.args.get("ref", type=int)
|
||||
items = _get_db().get_session_items(ref_scan_id=ref_scan_id)
|
||||
# Normalise JSON-encoded columns the same way scan_engine does for SSE cards
|
||||
import json as _json
|
||||
out = []
|
||||
for row in items:
|
||||
if role_filt and row.get("role", "") != role_filt:
|
||||
continue
|
||||
if user_filt and (row.get("account_id", "") or "").lower() not in user_filt:
|
||||
continue
|
||||
row["special_category"] = _json.loads(row.get("special_category") or "[]") if isinstance(row.get("special_category"), str) else row.get("special_category", [])
|
||||
row["exif"] = _json.loads(row.get("exif_json") or "{}") if isinstance(row.get("exif_json"), str) else row.get("exif", {})
|
||||
row.pop("exif_json", None)
|
||||
|
||||
Reference in new issue
Block a user