From aab60d686681fbbae724f72b6121a8df377412c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 May 2026 18:53:52 +0000 Subject: [PATCH] feat: frontend clear-error button, debug trace viewer, debug_logging toggle, docs updates Agent-Logs-Url: https://github.com/christianlouis/InboxConverge/sessions/5d2918de-630e-4a66-8355-1b036c620b1c Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- CHANGELOG.md | 50 +++++++++++++ backend/app/api/v1/endpoints/mail_accounts.py | 1 + backend/tests/unit/test_mail_accounts.py | 1 + docs/TODO.md | 19 +++++ frontend/src/app/accounts/page.tsx | 26 ++++++- frontend/src/app/dashboard/page.tsx | 22 +++++- frontend/src/app/logs/page.tsx | 74 +++++++++++++++++-- .../src/components/AddMailAccountModal.tsx | 16 ++++ frontend/src/lib/api.ts | 8 ++ 9 files changed, 204 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 302b72c..ccbf9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [Unreleased] + +### Added + +- **Friendly error messages**: Introduced `_format_connection_error()` helper in + `mail_processor.py` that translates raw OS/socket/SSL/POP3/IMAP exceptions into + human-readable sentences including the host:port and actionable guidance (DNS + failure, TLS error, connection timeout, authentication rejection, etc.). The + helper is applied at every `raise MailFetchError` / `raise MailConnectionError` + site and in both `_test_pop3_connection` and `_test_imap_connection`. + +- **Per-account debug logging** (`debug_logging` column on `MailAccount`): when + enabled, the next processing run records a structured connection trace + (connect timing, TLS details, auth, INBOX selection, message UIDs/sizes, + elapsed milliseconds per phase) via the new `MailDebugRecorder` class. The + trace is persisted as a `ProcessingLog` row with `level="DEBUG"` and surfaced + in the "Mailbox Activity" logs page as a collapsible "Connection trace" panel. + Debug logging auto-disables after 5 completed runs in a 24-hour window. + +- **"Clear error" button**: new `POST /api/v1/mail-accounts/{id}/clear-error` + endpoint that nulls `last_error_message`/`last_error_at` and resets `status` + to `ACTIVE` when currently `ERROR`. Wired into the error banners on both the + Accounts page and the Mailbox Activity (Logs) page. + +- **Debug-logging toggle** in the account edit form (Add/Edit Account modal): + checkbox labelled "Debug logging (auto-disables after 5 runs)". + +- Alembic migration `0002_add_debug_logging.py` adding the `debug_logging` + boolean column to `mail_accounts` (idempotent via `ADD COLUMN IF NOT EXISTS`). + +### Fixed + +- **Empty IMAP error messages** — `IMAP fetch error:` with a blank suffix was + caused by `asyncio.TimeoutError` and `aioimaplib.Abort` having an empty + `str()`. The new `_format_connection_error()` helper always produces a + non-empty, human-readable message. + +- **Cryptic DNS error** — `POP3 fetch error: [Errno -5] No address associated + with hostname` is now surfaced as `Could not resolve hostname 'pop.web.de' — + check that the server address is correct (DNS lookup failed: …)`. + +- **Sticky ERROR status after transient fetch failures**: the `tasks.py` + processing loop previously set `account.status = ERROR` and + `last_error_message = "{N} emails failed to forward"` even when the + connection and fetch succeeded but some individual email-forward operations + failed. Now, a successful fetch (no exception from `fetch_emails`) always + clears `last_error_message`/`last_error_at` and sets `status = ACTIVE`, + regardless of per-email forwarding failures. Per-email failures continue to + be tracked in `ProcessingLog` and the run's `emails_failed` counter. + ## v0.8.0 (2026-05-03) ### Bug Fixes diff --git a/backend/app/api/v1/endpoints/mail_accounts.py b/backend/app/api/v1/endpoints/mail_accounts.py index c8a5dee..a738c27 100644 --- a/backend/app/api/v1/endpoints/mail_accounts.py +++ b/backend/app/api/v1/endpoints/mail_accounts.py @@ -104,6 +104,7 @@ async def create_mail_account( check_interval_minutes=account_in.check_interval_minutes, max_emails_per_check=account_in.max_emails_per_check, delete_after_forward=account_in.delete_after_forward, + debug_logging=account_in.debug_logging, provider_name=account_in.provider_name, ) diff --git a/backend/tests/unit/test_mail_accounts.py b/backend/tests/unit/test_mail_accounts.py index d3e5ef5..be5f145 100644 --- a/backend/tests/unit/test_mail_accounts.py +++ b/backend/tests/unit/test_mail_accounts.py @@ -81,6 +81,7 @@ def _make_account(**overrides) -> MagicMock: check_interval_minutes=5, max_emails_per_check=50, delete_after_forward=True, + debug_logging=False, provider_name="Gmail", auto_detected=False, total_emails_processed=100, diff --git a/docs/TODO.md b/docs/TODO.md index f19c0fd..58113c4 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -4,6 +4,25 @@ Comprehensive task breakdown for repository improvements and production readines ## ✅ Recently Completed +- [x] **IMAP/POP3 diagnostics — Step 1: Friendly error messages**: Added + `_format_connection_error()` helper that converts raw OS/socket/SSL/POP3/IMAP + exceptions into human-readable sentences with host:port context. Applied at + every `raise MailFetchError`/`MailConnectionError` site. Fixes blank "IMAP + fetch error:" messages and cryptic DNS errno strings. + +- [x] **IMAP/POP3 diagnostics — Step 2: Auto-clear stale error state**: Successful + fetches now always clear `last_error_message`/`last_error_at` and set + `status=ACTIVE`, even when some individual email forwards fail. Added + `POST /mail-accounts/{id}/clear-error` endpoint and "Clear" buttons on the + Accounts and Mailbox Activity pages. + +- [x] **IMAP/POP3 diagnostics — Step 3: Per-account debug logging**: Added + `debug_logging` boolean column (migration `0002`), `MailDebugRecorder` class, + instrumented all connection phases (connect, auth, select, search, fetch UIDs, + per-message fetch, logout), persisted as `ProcessingLog[level=DEBUG]`. + Auto-disables after 5 runs in 24 h. Toggle in account edit form. Connection + trace viewer in Mailbox Activity logs page. + - [x] **Google OAuth consent screen legal compliance**: Added English Privacy Policy (`/privacy`) with Google API Limited Use Disclosure, Terms of Service (`/terms`), legal footer links on the home page (resolves Google's "homepage has no privacy policy link" verification rejection), login page, and register page (consent text). Cross-link from `/datenschutz` to `/privacy` added. - [x] **Fix Pydantic V2 deprecation warnings**: Replaced `.dict()` with `.model_dump()` in `admin.py` and `notifications.py`. Fixed `RuntimeWarning: coroutine never awaited` for `db.add()` in test mocks (`test_tasks.py`, `test_config_service.py`). diff --git a/frontend/src/app/accounts/page.tsx b/frontend/src/app/accounts/page.tsx index 6409e1a..03eadd4 100644 --- a/frontend/src/app/accounts/page.tsx +++ b/frontend/src/app/accounts/page.tsx @@ -4,7 +4,7 @@ import { AuthGuard } from '@/components/AuthGuard'; import { DashboardLayout } from '@/components/DashboardLayout'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { mailAccountsApi, MailAccount } from '@/lib/api'; -import { Plus, Edit2, Trash2, CheckCircle, XCircle, AlertTriangle, Power, RefreshCw } from 'lucide-react'; +import { Plus, Edit2, Trash2, CheckCircle, XCircle, AlertTriangle, Power, RefreshCw, RotateCcw } from 'lucide-react'; import { useState } from 'react'; import Image from 'next/image'; import { AddMailAccountModal } from '@/components/AddMailAccountModal'; @@ -115,6 +115,13 @@ export default function AccountsPage() { }, }); + const clearErrorMutation = useMutation({ + mutationFn: mailAccountsApi.clearError, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['mail-accounts'] }); + }, + }); + const handleEdit = (account: MailAccount) => { setEditingAccount(account); setIsModalOpen(true); @@ -253,9 +260,20 @@ export default function AccountsPage() { {account.last_error_message && (
{account.last_error_message}
+{account.last_error_message}
+{account.last_error_message}
+{account.last_error_message}
+| {formatDate(log.timestamp)} @@ -100,7 +143,7 @@ function RunDetailRow({ run }: { run: ProcessingRun }) { ))} |
No per-email logs for this run.
- )} + ) : null}{account.last_error_message}
+{account.last_error_message}
+