Upload of mails to report bugs #421

Closed
opened 2026-06-04 17:23:31 +00:00 by guettli · 1 comment
guettli commented 2026-06-04 17:23:31 +00:00 (Migrated from codeberg.org)

From single Mail View, the current mail should be uploadable to an API to report bugs.

Additionally the about-data will be added. Reuse the code.

The user has a text field to report his issue. Additionally files (for example screenshots) can be added.

Encourage the user to upload screenshots, so that is easier to understand the issue.

The bug report will be handled confidentially, it will not be uploaded to the public issue tracker. Tell that the user.

If you have additional ideas, feel free to add them.

We need both: a backend to receive the bug report (only API, no UI) and the code in the app to upload the data.

The user can optionaly add his email address (checkbox, if checked take address of current account, but user can change it, if the wants to)

Backend: upload must not be bigger than 20 MB. Do rate limit: only 10 uploads per minute (overall) is allowed.

Think about additional features.

Ask if unsure.

From single Mail View, the current mail should be uploadable to an API to report bugs. Additionally the about-data will be added. Reuse the code. The user has a text field to report his issue. Additionally files (for example screenshots) can be added. Encourage the user to upload screenshots, so that is easier to understand the issue. The bug report will be handled confidentially, it will not be uploaded to the public issue tracker. Tell that the user. If you have additional ideas, feel free to add them. We need both: a backend to receive the bug report (only API, no UI) and the code in the app to upload the data. The user can optionaly add his email address (checkbox, if checked take address of current account, but user can change it, if the wants to) Backend: upload must not be bigger than 20 MB. Do rate limit: only 10 uploads per minute (overall) is allowed. Think about additional features. Ask if unsure.
guettlibot commented 2026-06-04 17:30:05 +00:00 (Migrated from codeberg.org)

I have everything I need. Here is the implementation plan to post as a comment on issue #421:


Implementation Plan: Bug Report Upload Feature

Two components: a new Go HTTP backend to receive reports, and a new BugReportScreen in the Flutter app accessible from both the email detail view and the About screen.


Backend (server/bugreport/)

Language: Go (consistent with ci/main.go).

Endpoint: POST /api/v1/bug-reports, Content-Type: multipart/form-data

Field Type Required Description
description text yes User's description of the problem
about_info text yes Markdown output of buildAboutMarkdown()
email text no User's contact email
email_data text/JSON no Current email metadata (subject, from, date, body excerpt)
attachments[] file no Screenshots and other files

Constraints:

  • Body size limit: 20 MB (rejected with 413 before parsing)
  • Rate limit: 10 requests/minute global — sliding window in memory, returns 429 with Retry-After: 6 header

Storage: Each report saved as a timestamped directory under $BUGREPORT_STORAGE_DIR:

<storage_dir>/<timestamp>_<uuid>/
  report.json          ← description, email, about_info, email_data, timestamp, hashed IP
  attachment_0_foo.png ← uploaded files, named with index prefix to avoid collisions

Config (env vars):

  • BUGREPORT_STORAGE_DIR (default: ./reports)
  • BUGREPORT_PORT (default: 8090)
  • BUGREPORT_NOTIFY_EMAIL (optional — SMTP notification to developer on new report)

Returns 201 Created with {"id": "<uuid>"} on success. Raw IP is hashed (SHA-256) before storage — abuse tracking without retaining PII.


Flutter App

1. New route

Add to lib/ui/router.dart:

/bug-report → BugReportScreen(emailId: String?)

The emailId query parameter is optional. Navigation from email detail: context.push('/bug-report?emailId=${widget.emailId}'). From About screen: context.push('/bug-report').

2. New service (lib/core/services/bug_report_service.dart)

BugReportService wraps the multipart HTTP upload. Uses the existing httpClientProvider from di.dart. Provider registered as bugReportServiceProvider in di.dart.

API base URL via String.fromEnvironment('BUG_REPORT_API_URL') with a compile-time constant fallback, injected at build time like GIT_HASH is today.

Returns the report UUID on success; throws a typed BugReportException on failure.

3. New screen (lib/ui/screens/bug_report_screen.dart)

BugReportScreen({String? emailId})ConsumerStatefulWidget

Layout:

  1. AppBar: "Report a Bug"
  2. Privacy notice card (outlined amber/info style):
    "Your report is handled confidentially and will not be posted to the public issue tracker."
  3. Description — required multiline TextField, autofocused
  4. If emailId != null: info chip — "The current email will be attached automatically"
  5. Screenshots (prominent):
    • OutlinedButton.icon(Icons.add_a_photo, "Add screenshots")
    • Encouraging subtext: "Screenshots help us understand the problem faster"
    • Horizontal scrollable row of removable thumbnail chips for selected files
    • File size running total with a warning when approaching 20 MB
  6. Sync log opt-in checkbox: "Include recent sync log (helps diagnose connection issues)" — calls existing syncLogRepositoryProvider to fetch last 50 entries, serializes to text
  7. Email opt-in row:
    • Checkbox: "Include my email for follow-up"
    • When checked: editable TextField pre-filled with the active account's email address (read from accountRepositoryProvider)
  8. Collapsible "System info" section: renders buildAboutMarkdown() output — reuses the existing utility from lib/ui/utils/about_markdown.dart unchanged
  9. FilledButton "Send Bug Report" (full-width)

States: idle → submitting (spinner, fields disabled) → success → error.

Success shows: "Thank you! Your report ID is <uuid>. We will look into it." with a Close button.

Errors surface as a SnackBar with a Retry action.

If emailId is set the screen pre-fills the description field with a template: "Problem with email from [from]: [subject]".

4. Trigger points

  • lib/ui/screens/email_detail_screen.dart: Add "Report a bug" item (Icons.bug_report) to the existing overflow action menu, passing the current widget.emailId
  • lib/ui/screens/about_screen.dart: Add a third button "Report a bug" in the bottom button row alongside "Copy to clipboard" and "Create issue"

Additional ideas included in scope

  • Report UUID returned to user: makes follow-up conversations unambiguous
  • Hashed IP in report.json: abuse tracking without PII storage
  • Sync log attachment (opt-in): extremely useful for diagnosing IMAP/JMAP failures; reads from existing SyncLogs table
  • File size guard: real-time running total of selected attachments, warning before the 20 MB limit is hit
  • SMTP notification: BUGREPORT_NOTIFY_EMAIL — developer gets an email when a report arrives
  • Auto-subject pre-fill: when opened from email detail, seeds the description field so users write less boilerplate

Open questions before coding starts

  1. Backend language: I assumed Go — correct, or do you prefer Dart Shelf / Python?
  2. Backend hosting: What host/URL should BUG_REPORT_API_URL point to? Is it the same server as the existing deployment?
  3. Report storage: Filesystem directories are the simplest option. Would you prefer a SQLite DB, or forwarding reports via email to a developer mailbox instead?
  4. Sync log default: Opt-in (unchecked by default) or opt-out (checked by default)?
I have everything I need. Here is the implementation plan to post as a comment on issue #421: --- ## Implementation Plan: Bug Report Upload Feature Two components: a new **Go HTTP backend** to receive reports, and a new **`BugReportScreen`** in the Flutter app accessible from both the email detail view and the About screen. --- ### Backend (`server/bugreport/`) **Language**: Go (consistent with `ci/main.go`). **Endpoint**: `POST /api/v1/bug-reports`, `Content-Type: multipart/form-data` | Field | Type | Required | Description | |---|---|---|---| | `description` | text | yes | User's description of the problem | | `about_info` | text | yes | Markdown output of `buildAboutMarkdown()` | | `email` | text | no | User's contact email | | `email_data` | text/JSON | no | Current email metadata (subject, from, date, body excerpt) | | `attachments[]` | file | no | Screenshots and other files | **Constraints**: - Body size limit: 20 MB (rejected with 413 before parsing) - Rate limit: 10 requests/minute global — sliding window in memory, returns 429 with `Retry-After: 6` header **Storage**: Each report saved as a timestamped directory under `$BUGREPORT_STORAGE_DIR`: ``` <storage_dir>/<timestamp>_<uuid>/ report.json ← description, email, about_info, email_data, timestamp, hashed IP attachment_0_foo.png ← uploaded files, named with index prefix to avoid collisions ``` **Config (env vars)**: - `BUGREPORT_STORAGE_DIR` (default: `./reports`) - `BUGREPORT_PORT` (default: `8090`) - `BUGREPORT_NOTIFY_EMAIL` (optional — SMTP notification to developer on new report) Returns `201 Created` with `{"id": "<uuid>"}` on success. Raw IP is hashed (SHA-256) before storage — abuse tracking without retaining PII. --- ### Flutter App #### 1. New route Add to `lib/ui/router.dart`: ``` /bug-report → BugReportScreen(emailId: String?) ``` The `emailId` query parameter is optional. Navigation from email detail: `context.push('/bug-report?emailId=${widget.emailId}')`. From About screen: `context.push('/bug-report')`. #### 2. New service (`lib/core/services/bug_report_service.dart`) `BugReportService` wraps the multipart HTTP upload. Uses the existing `httpClientProvider` from `di.dart`. Provider registered as `bugReportServiceProvider` in `di.dart`. API base URL via `String.fromEnvironment('BUG_REPORT_API_URL')` with a compile-time constant fallback, injected at build time like `GIT_HASH` is today. Returns the report UUID on success; throws a typed `BugReportException` on failure. #### 3. New screen (`lib/ui/screens/bug_report_screen.dart`) `BugReportScreen({String? emailId})` — `ConsumerStatefulWidget` **Layout**: 1. AppBar: "Report a Bug" 2. **Privacy notice card** (outlined amber/info style): _"Your report is handled confidentially and will not be posted to the public issue tracker."_ 3. **Description** — required multiline `TextField`, autofocused 4. If `emailId != null`: info chip — "The current email will be attached automatically" 5. **Screenshots** (prominent): - `OutlinedButton.icon(Icons.add_a_photo, "Add screenshots")` - Encouraging subtext: "Screenshots help us understand the problem faster" - Horizontal scrollable row of removable thumbnail chips for selected files - File size running total with a warning when approaching 20 MB 6. **Sync log** opt-in checkbox: "Include recent sync log (helps diagnose connection issues)" — calls existing `syncLogRepositoryProvider` to fetch last 50 entries, serializes to text 7. **Email opt-in** row: - Checkbox: "Include my email for follow-up" - When checked: editable `TextField` pre-filled with the active account's email address (read from `accountRepositoryProvider`) 8. **Collapsible "System info"** section: renders `buildAboutMarkdown()` output — reuses the existing utility from `lib/ui/utils/about_markdown.dart` unchanged 9. `FilledButton` "Send Bug Report" (full-width) **States**: idle → submitting (spinner, fields disabled) → success → error. Success shows: _"Thank you! Your report ID is `<uuid>`. We will look into it."_ with a Close button. Errors surface as a `SnackBar` with a Retry action. If `emailId` is set the screen pre-fills the description field with a template: `"Problem with email from [from]: [subject]"`. #### 4. Trigger points - **`lib/ui/screens/email_detail_screen.dart`**: Add "Report a bug" item (`Icons.bug_report`) to the existing overflow action menu, passing the current `widget.emailId` - **`lib/ui/screens/about_screen.dart`**: Add a third button "Report a bug" in the bottom button row alongside "Copy to clipboard" and "Create issue" --- ### Additional ideas included in scope - **Report UUID returned to user**: makes follow-up conversations unambiguous - **Hashed IP in `report.json`**: abuse tracking without PII storage - **Sync log attachment (opt-in)**: extremely useful for diagnosing IMAP/JMAP failures; reads from existing `SyncLogs` table - **File size guard**: real-time running total of selected attachments, warning before the 20 MB limit is hit - **SMTP notification**: `BUGREPORT_NOTIFY_EMAIL` — developer gets an email when a report arrives - **Auto-subject pre-fill**: when opened from email detail, seeds the description field so users write less boilerplate --- ### Open questions before coding starts 1. **Backend language**: I assumed Go — correct, or do you prefer Dart Shelf / Python? 2. **Backend hosting**: What host/URL should `BUG_REPORT_API_URL` point to? Is it the same server as the existing deployment? 3. **Report storage**: Filesystem directories are the simplest option. Would you prefer a SQLite DB, or forwarding reports via email to a developer mailbox instead? 4. **Sync log default**: Opt-in (unchecked by default) or opt-out (checked by default)?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: guettli/sharedinbox#421