diff --git a/backend/app/templates/mail_sources.html b/backend/app/templates/mail_sources.html index 100293f..4119bde 100644 --- a/backend/app/templates/mail_sources.html +++ b/backend/app/templates/mail_sources.html @@ -114,6 +114,17 @@ + + + + + + + + + + + + + Backfill Mail Source + ✕ + + + + + 7 days + 30 days + 90 days + + + Days to search + + + + + Cancel + + Run Backfill + + + + + @@ -508,6 +549,8 @@ function mailSourcesApp() { isSaving: false, testing: {}, fetching: {}, + backfillSource: null, + backfillDays: 30, historySource: null, importHistory: [], historyLoading: false, @@ -546,11 +589,12 @@ function mailSourcesApp() { } }, - async fetchSource(source) { + async fetchSource(source, days = 7) { this.fetching[source.id] = true; this.feedback = { message: '', type: '' }; try { - const resp = await fetch(`/api/v1/mail-sources/${source.id}/fetch`, { + const safeDays = Math.min(365, Math.max(1, Number(days) || 7)); + const resp = await fetch(`/api/v1/mail-sources/${source.id}/fetch?days=${safeDays}`, { method: 'POST', }); const result = await resp.json(); @@ -558,7 +602,7 @@ function mailSourcesApp() { throw new Error(result.detail || 'Import failed'); } this.feedback = { - message: `Import finished for ${source.name}: ${result.reports_found} report${result.reports_found === 1 ? '' : 's'}, ${result.duplicate_reports || 0} duplicate${result.duplicate_reports === 1 ? '' : 's'}.`, + message: `Import finished for ${source.name} (${safeDays} day${safeDays === 1 ? '' : 's'}): ${result.reports_found} report${result.reports_found === 1 ? '' : 's'}, ${result.duplicate_reports || 0} duplicate${result.duplicate_reports === 1 ? '' : 's'}.`, type: result.success ? 'success' : 'error', }; await this.loadSources(); @@ -573,6 +617,30 @@ function mailSourcesApp() { } }, + openBackfill(source) { + this.backfillSource = source; + this.backfillDays = 30; + this.feedback = { message: '', type: '' }; + }, + + closeBackfill() { + this.backfillSource = null; + this.backfillDays = 30; + }, + + validBackfillDays() { + const days = Number(this.backfillDays); + return Number.isInteger(days) && days >= 1 && days <= 365; + }, + + async runBackfill() { + if (!this.backfillSource || !this.validBackfillDays()) return; + const source = this.backfillSource; + const days = Number(this.backfillDays); + this.closeBackfill(); + await this.fetchSource(source, days); + }, + async loadImportHistory(source) { this.historySource = source; this.importHistory = []; diff --git a/docs/development/roadmap.md b/docs/development/roadmap.md index b5900b9..2b402de 100644 --- a/docs/development/roadmap.md +++ b/docs/development/roadmap.md @@ -29,6 +29,7 @@ Recently improved: - Mail source import history is visible in the Mail Sources UI. - Individual mail sources can be manually imported from the Mail Sources UI. - Import-history rows include sanitized per-attachment outcomes and imported report IDs. +- Mail source backfills can be launched from the UI with configurable search windows. - The current Alpine-based UI is allowed by CSP and renders dynamic tables in real browsers. Implementation note: @@ -39,7 +40,6 @@ Implementation note: Objective: make mailbox imports auditable and make report totals trustworthy. Priority tasks: -- Add mailbox search controls for date-range backfills. - Report duplicate skips separately from parse failures. - Improve source rollups so a source IP tracks pass/fail counts over time. diff --git a/docs/milestones.md b/docs/milestones.md index 3cc882e..c395481 100644 --- a/docs/milestones.md +++ b/docs/milestones.md @@ -64,10 +64,10 @@ Recently delivered: - Mail source import history is now visible from the Mail Sources UI. - A single mail source can be manually imported from the Mail Sources UI, with the result recorded in import history. - Import history now includes per-attachment details for imported reports, duplicates, parse errors, unsupported attachments, and imported report IDs. +- Mail sources can be backfilled from the UI with 7-day, 30-day, 90-day, or custom search windows. - The current Alpine-based UI can run under the configured CSP, so dynamic tables render in real browsers. Next tasks: -- Add mailbox search controls for date range/backfill without requiring code changes. - Improve source aggregation so each sender IP keeps pass/fail totals instead of only the latest result. Exit criteria: diff --git a/docs/todo.md b/docs/todo.md index 7022883..34326e8 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -93,7 +93,7 @@ This file tracks the specific implementation tasks for each milestone of the DMA - [x] Show recent import history in the Mail Sources UI - [x] Add manual import trigger per mail source - [x] Add per-import result details for duplicates, parse failures, unsupported attachments, and imported report IDs -- [ ] Add retry/backfill controls per mail source +- [x] Add retry/backfill controls per mail source ## Milestone 3: Database Integration