From 8d7375c99fe8c20b2e08a2e5379570f507bea3fe Mon Sep 17 00:00:00 2001 From: Oliver G Date: Sat, 21 Feb 2026 13:11:43 +0100 Subject: [PATCH] feat(ui): classify publisher errors with actionable hints --- backend/app/admin_ui.py | 21 ++++++++++++++++ backend/static/admin.css | 34 ++++++++++++++++++++++++++ backend/templates/admin_dashboard.html | 12 +++++++-- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/backend/app/admin_ui.py b/backend/app/admin_ui.py index fba1b91..bde281d 100644 --- a/backend/app/admin_ui.py +++ b/backend/app/admin_ui.py @@ -169,6 +169,23 @@ def _publish_readiness(article: dict, meta: dict) -> tuple[bool, list[str]]: return len(reasons) == 0, reasons +def _classify_publish_error(error_message: str | None) -> tuple[str, str]: + text = (error_message or "").lower() + if not text.strip(): + return "ok", "-" + if "rechtsfreigabe fehlt" in text or "hauptbild nicht gesetzt" in text or "status ist nicht" in text: + return "policy", "Artikelvoraussetzungen im UI prüfen (Status/Rechtsfreigabe/Hauptbild)." + if "401" in text or "403" in text or "authorization" in text or "forbidden" in text or "unauthorized" in text: + return "auth", "WordPress Nutzer/App-Passwort prüfen." + if "404" in text and ("media" in text or "posts" in text or "wp-json" in text): + return "api", "WordPress REST-Endpunkt prüfen (`/wp-json/wp/v2`)." + if "timed out" in text or "timeout" in text or "nodename nor servname provided" in text or "name or service not known" in text: + return "dns", "DNS/Netzwerk zur WordPress-Domain prüfen." + if "media-upload fehlgeschlagen" in text or "liefert kein bild" in text or "featured_media" in text: + return "media", "Bild-URL/Format prüfen oder anderes Hauptbild auswählen." + return "unknown", "Fehlerdetails prüfen und bei Bedarf Job erneut starten." + + def _legal_checklist(article: dict, feed: dict | None) -> list[dict[str, str]]: meta = article.get("meta", {}) extraction = meta.get("extraction") if isinstance(meta.get("extraction"), dict) else {} @@ -289,6 +306,10 @@ def admin_dashboard(request: Request): feeds = list_feeds() runs = list_runs(limit=30) publish_jobs = list_publish_jobs(limit=30) + for job in publish_jobs: + category, hint = _classify_publish_error(job.get("error_message")) + job["error_category"] = category + job["error_hint"] = hint status_filter = request.query_params.get("status_filter") if status_filter in {"new", "rewrite", "review", "approved", "published", "error"}: articles = list_articles(limit=100, status_filter=status_filter) diff --git a/backend/static/admin.css b/backend/static/admin.css index 16d55be..053263a 100644 --- a/backend/static/admin.css +++ b/backend/static/admin.css @@ -131,6 +131,40 @@ button.secondary { color: #991b1b; } +.badge.errcat { + margin-bottom: 4px; +} + +.badge.errcat-policy { + background: #fee2e2; + color: #991b1b; +} + +.badge.errcat-auth { + background: #ffedd5; + color: #9a3412; +} + +.badge.errcat-dns { + background: #dbeafe; + color: #1e40af; +} + +.badge.errcat-media { + background: #fef9c3; + color: #854d0e; +} + +.badge.errcat-api { + background: #ede9fe; + color: #5b21b6; +} + +.badge.errcat-unknown { + background: #e2e8f0; + color: #334155; +} + .alert { margin-bottom: 12px; padding: 10px; diff --git a/backend/templates/admin_dashboard.html b/backend/templates/admin_dashboard.html index fe811a5..f318355 100644 --- a/backend/templates/admin_dashboard.html +++ b/backend/templates/admin_dashboard.html @@ -256,7 +256,7 @@

Publish Jobs

- + {% for j in publish_jobs %} @@ -274,7 +274,15 @@ - {% endif %} - + + {% endfor %}
IDArtikelStatusAttemptsWP PostFehler
IDArtikelStatusAttemptsWP PostFehlerHinweis
{{ j.error_message or "-" }} + {% if j.error_message %} + {{ j.error_category }} +
{{ j.error_message }}
+ {% else %} + - + {% endif %} +
{{ j.error_hint or "-" }}