From 4eac58962536139786385cfd3d110df276c10606 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Thu, 19 Mar 2026 17:17:14 +0100 Subject: [PATCH] Cloud Connect: add scan-inbox to process existing inbox mails - POST /cloud-connect/accounts/scan-inbox: queries all inbox mails with backup_type 'cloud connect report', calls upsert_cloud_connect_report() for each, flashes a summary of accounts upserted and runs created - 'Scan inbox mails' button added to Cloud Connect Accounts page header Co-Authored-By: Claude Sonnet 4.6 --- .../backend/app/main/routes_cloud_connect.py | 62 +++++++++++++++++-- .../main/cloud_connect_accounts.html | 3 + 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/containers/backupchecks/src/backend/app/main/routes_cloud_connect.py b/containers/backupchecks/src/backend/app/main/routes_cloud_connect.py index 805c051..8889bba 100644 --- a/containers/backupchecks/src/backend/app/main/routes_cloud_connect.py +++ b/containers/backupchecks/src/backend/app/main/routes_cloud_connect.py @@ -1,13 +1,15 @@ """Veeam Cloud Connect accounts review routes. Mirrors the Cove Accounts flow: - /cloud-connect/accounts – list all accounts (unmatched first) - /cloud-connect/accounts//link – link to existing job or create new job - /cloud-connect/accounts//unlink – remove the job link + /cloud-connect/accounts – list all accounts (unmatched first) + /cloud-connect/accounts/scan-inbox – process existing inbox mails + /cloud-connect/accounts//link – link to existing job or create new job + /cloud-connect/accounts//unlink – remove the job link """ from .routes_shared import * # noqa: F401,F403 from .routes_shared import _log_admin_event -from ..models import CloudConnectAccount, Customer, Job, JobRun +from ..models import CloudConnectAccount, Customer, Job, JobRun, MailMessage +from ..cloud_connect_importer import upsert_cloud_connect_report @main_bp.route("/cloud-connect/accounts") @@ -48,6 +50,58 @@ def cloud_connect_accounts(): ) +@main_bp.route("/cloud-connect/accounts/scan-inbox", methods=["POST"]) +@login_required +@roles_required("admin", "operator") +def cloud_connect_scan_inbox(): + """Process all existing inbox mails that are Cloud Connect report emails.""" + mails = ( + MailMessage.query + .filter( + MailMessage.location == "inbox", + db.func.lower(MailMessage.backup_type) == "cloud connect report", + ) + .all() + ) + + total = len(mails) + total_accounts = 0 + total_created = 0 + errors = 0 + + for mail in mails: + try: + result = upsert_cloud_connect_report( + mail_message_id=mail.id, + html_body=(mail.html_body or ""), + ) + total_accounts += result.get("total", 0) + total_created += result.get("created", 0) + except Exception as exc: + errors += 1 + _log_admin_event( + event_type="cloud_connect_scan_error", + message=f"Failed to process mail {mail.id}: {exc}", + ) + + db.session.commit() + + _log_admin_event( + event_type="cloud_connect_scan_inbox", + message=( + f"Scanned {total} inbox mail(s): " + f"{total_accounts} accounts upserted, {total_created} new runs created, {errors} error(s)" + ), + ) + flash( + f"Scanned {total} mail(s): {total_accounts} accounts upserted, " + f"{total_created} new runs created." + + (f" {errors} error(s)." if errors else ""), + "success" if errors == 0 else "warning", + ) + return redirect(url_for("main.cloud_connect_accounts")) + + @main_bp.route("/cloud-connect/accounts//link", methods=["POST"]) @login_required @roles_required("admin", "operator") diff --git a/containers/backupchecks/src/templates/main/cloud_connect_accounts.html b/containers/backupchecks/src/templates/main/cloud_connect_accounts.html index c95ef78..64b4c9a 100644 --- a/containers/backupchecks/src/templates/main/cloud_connect_accounts.html +++ b/containers/backupchecks/src/templates/main/cloud_connect_accounts.html @@ -2,6 +2,9 @@ {% block content %}

Cloud Connect Accounts

+
+ +
{# ── Unmatched accounts ─────────────────────────────────────────────────── #}