Compare commits
2 Commits
066c45ab9b
...
90317c804b
| Author | SHA1 | Date | |
|---|---|---|---|
| 90317c804b | |||
| b791c43299 |
@ -1 +1 @@
|
||||
v20260112-12-vspc-company-mapping-popup-visible
|
||||
v20260112-13-vspc-company-mapping-popup-ui
|
||||
|
||||
@ -4,6 +4,8 @@ from .routes_shared import _format_datetime, _log_admin_event, _send_mail_messag
|
||||
from ..email_utils import extract_best_html_from_eml, is_effectively_blank_html
|
||||
|
||||
import time
|
||||
import re
|
||||
import html as _html
|
||||
|
||||
@main_bp.route("/inbox")
|
||||
@login_required
|
||||
@ -149,34 +151,35 @@ def inbox_message_detail(message_id: int):
|
||||
for obj in MailObject.query.filter_by(mail_message_id=msg.id).order_by(MailObject.object_name.asc()).all()
|
||||
]
|
||||
|
||||
# For VSPC Active Alarms summary messages, objects are typically not persisted until approval.
|
||||
# To enable multi-company mapping in the Inbox, we expose the company list derived from parsing
|
||||
# the stored HTML body (best-effort, without persisting objects).
|
||||
bsw = (getattr(msg, "backup_software", "") or "").strip()
|
||||
btype = (getattr(msg, "backup_type", "") or "").strip()
|
||||
jname = (getattr(msg, "job_name", "") or "").strip()
|
||||
if not objects and bsw == "Veeam" and btype == "Service Provider Console" and jname == "Active alarms summary":
|
||||
try:
|
||||
from ..parsers.veeam import _parse_vspc_active_alarms_from_html
|
||||
# VSPC multi-company emails (e.g. "Active alarms summary") may not store parsed objects yet.
|
||||
# Extract company names from the stored body so the UI can offer a dedicated mapping workflow.
|
||||
vspc_companies: list[str] = []
|
||||
try:
|
||||
bsw = (getattr(msg, "backup_software", "") or "").strip().lower()
|
||||
btype = (getattr(msg, "backup_type", "") or "").strip().lower()
|
||||
jname = (getattr(msg, "job_name", "") or "").strip().lower()
|
||||
|
||||
parsed_objs, _status, _msg = _parse_vspc_active_alarms_from_html(body_html or "")
|
||||
companies: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for o in parsed_objs or []:
|
||||
name = str((o or {}).get("name") or "")
|
||||
ix = name.find(" | ")
|
||||
if ix > 0:
|
||||
c = name[:ix].strip()
|
||||
if c and c not in seen:
|
||||
seen.add(c)
|
||||
companies.append(c)
|
||||
if companies:
|
||||
meta["vspc_companies"] = companies
|
||||
except Exception:
|
||||
# Never fail the message detail endpoint due to best-effort parsing.
|
||||
pass
|
||||
if bsw == "veeam" and btype == "service provider console" and jname == "active alarms summary":
|
||||
raw = text_body if not _is_blank_text(text_body) else (html_body or "")
|
||||
if raw:
|
||||
txt = raw
|
||||
# Best-effort HTML to text
|
||||
if "<" in txt and ">" in txt:
|
||||
txt = re.sub(r"<[^>]+>", " ", txt)
|
||||
txt = _html.unescape(txt)
|
||||
txt = re.sub(r"\s+", " ", txt).strip()
|
||||
|
||||
return jsonify({"status": "ok", "meta": meta, "body_html": body_html, "objects": objects})
|
||||
seen = set()
|
||||
for m2 in re.finditer(r"\bCompany:\s*([^\(\r\n]+?)\s*\(\s*alarms?\s*:", txt, flags=re.IGNORECASE):
|
||||
cname = (m2.group(1) or "").strip()
|
||||
if cname and cname not in seen:
|
||||
seen.add(cname)
|
||||
vspc_companies.append(cname)
|
||||
except Exception:
|
||||
vspc_companies = []
|
||||
|
||||
|
||||
return jsonify({"status": "ok", "meta": meta, "body_html": body_html, "objects": objects, "vspc_companies": vspc_companies})
|
||||
|
||||
|
||||
@main_bp.route("/inbox/message/<int:message_id>/eml")
|
||||
|
||||
@ -509,6 +509,11 @@ function findCustomerIdByName(name) {
|
||||
// reset
|
||||
mapBtn.classList.add("d-none");
|
||||
if (approveBtn) approveBtn.classList.remove("d-none");
|
||||
var ciReset = document.getElementById("msg_customer_input");
|
||||
if (ciReset) {
|
||||
ciReset.removeAttribute("disabled");
|
||||
ciReset.placeholder = "Select customer";
|
||||
}
|
||||
|
||||
var bsw = String(meta.backup_software || "").trim();
|
||||
var btype = String(meta.backup_type || "").trim();
|
||||
@ -518,14 +523,11 @@ function findCustomerIdByName(name) {
|
||||
return;
|
||||
}
|
||||
|
||||
var companies = [];
|
||||
if (Array.isArray(meta.vspc_companies) && meta.vspc_companies.length) {
|
||||
meta.vspc_companies.forEach(function (c) {
|
||||
c = String(c || "").trim();
|
||||
if (c) companies.push(c);
|
||||
});
|
||||
} else {
|
||||
// fallback: derive from parsed objects (when available)
|
||||
var companies = (data.vspc_companies || meta.vspc_companies || []);
|
||||
if (!Array.isArray(companies)) companies = [];
|
||||
|
||||
// Fallback for older stored messages where companies were embedded in object names.
|
||||
if (!companies.length) {
|
||||
var objs = data.objects || [];
|
||||
var seen = {};
|
||||
objs.forEach(function (o) {
|
||||
@ -543,6 +545,12 @@ function findCustomerIdByName(name) {
|
||||
// Show mapping button; hide regular approve
|
||||
mapBtn.classList.remove("d-none");
|
||||
if (approveBtn) approveBtn.classList.add("d-none");
|
||||
var ci = document.getElementById("msg_customer_input");
|
||||
if (ci) {
|
||||
ci.value = "";
|
||||
ci.setAttribute("disabled", "disabled");
|
||||
ci.placeholder = "Use \"Map companies\"";
|
||||
}
|
||||
|
||||
mapBtn.onclick = function () {
|
||||
var tbody = document.getElementById("vspcCompanyMapTbody");
|
||||
|
||||
@ -206,6 +206,13 @@
|
||||
- Exposed parsed VSPC company list via the inbox message detail endpoint.
|
||||
- Updated inbox popup logic to trigger the dedicated VSPC company-mapping workflow based on detected companies instead of parsed objects.
|
||||
|
||||
---
|
||||
|
||||
## v20260112-13-vspc-company-mapping-popup-ui
|
||||
- Added VSPC company extraction to the inbox message detail response so multi-company summaries expose a company list even when no objects are stored.
|
||||
- Updated the Inbox message modal to show the “Map companies” button based on the returned VSPC company list (with fallback to legacy object-name parsing).
|
||||
- Disabled the standard Customer selector when VSPC company mapping is available to avoid using the wrong approval flow.
|
||||
|
||||
================================================================================================================================================
|
||||
## v0.1.19
|
||||
This release delivers a broad set of improvements focused on reliability, transparency, and operational control across mail processing, administrative auditing, and Run Checks workflows. The changes aim to make message handling more robust, provide better insight for administrators, and give operators clearer and more flexible control when reviewing backup runs.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user