Commit Graph
13 Commits
Author SHA1 Message Date
Hieu Nguyen (Jack) c5cfa1be6d Added Spam filter 2026-09-08 15:42:33 +07:00
Hieu Nguyen (Jack) dca90d67ed Merge origin/main into domain sending setup 2026-09-08 12:04:24 +07:00
Hieu Nguyen (Jack) c170b8a1e0 Add switch setting for sending 2026-09-08 11:59:21 +07:00
Oday BakkourandClaude Fable 5.1 c6a38ae8e5 feat(mail): conversation view, forwarding with attachments, rich-text composer
Completes the three follow-ups from the threading PR.

Conversation view
- `GET /api/messages?group=thread` returns one row per conversation (its
  newest message matching the filter) via a latest-per-thread subquery,
  with `threadCount`, `threadUnread`, and `threadMessageIds` (the ids the
  row stands for within the current filter).
- Lists default to conversation view, remembered per browser, with a header
  toggle. Row and bulk actions, drags, and selection expand to the whole
  conversation; a row is bold while any message in it is unread. Drafts are
  never grouped.

Forwarding
- Forward builds a "---------- Forwarded message ---------" block from the
  original and creates a draft with `forwardOfMessageId`; the server copies
  the original's attachments onto the draft (new R2 objects, since keys are
  unique per row). The composer shows them as removable chips.
- `/api/send` accepts `draftId` and sends the draft's stored files with
  whatever was uploaded in the request. Deleting a draft now removes its R2
  objects too, and `DELETE /api/drafts/[id]/attachments/[attachmentId]`
  drops one file.

Rich-text composer
- contentEditable editor with bold/italic/underline/strikethrough, lists,
  quote, link (⌘K) and clear formatting; pasted content is flattened to text.
- The body is one HTML string; the text/plain part is derived from it, with
  bullets, numbering and "> " quote prefixes so text-only clients and the
  reader's collapse heuristics still work.
- Reply and forward quotes are HTML wrapped in a marked block that both the
  composer (•••) and the reader ("Quoted text") fold, so rich replies keep
  their formatting instead of falling back to text. Signature handling and
  draft round-trips work on the HTML.
- Shared `.email-body` styles restore list markers and blockquotes that
  Tailwind preflight strips from rendered mail.

Verified on a fresh local D1: grouped inbox collapses 4 messages to 1 row
with the right ids; a forward from an imported message carried its PDF onto
the draft, survived a draft reload, sent with the file, and joined the
original's thread; deleting the draft dropped its R2 object. tsc (11) and
eslint (96) unchanged from baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGeWDVSkXtVEmDAN2AtB9h
2026-09-06 15:13:40 +03:00
Oday BakkourandClaude Fable 5.1 a73017ef69 feat(mail): conversation threading, Cc/Bcc recipients, and reply-all
Mailflare stored one recipient per message and never used the threading
headers: send accepted a single `to`, replies carried no In-Reply-To, and
`threadId` was always the message's own Message-ID, so nothing ever grouped.

Threading
- `resolveThreadId` (src/lib/email/threading.ts) looks up the parent named
  by In-Reply-To/References among stored messages in the same mailbox and
  reuses its thread; otherwise the message's own Message-ID seeds a new one.
  Applied to inbound mail and to .eml/.mbox/IMAP import.
- Outbound replies write In-Reply-To and References and file the sent copy
  under the parent's thread; a fresh send is keyed by the Message-ID
  Cloudflare returns, which is what any reply will reference.
- New `GET /api/messages/[id]/thread` returns the conversation (drafts and
  trash excluded). The reader shows earlier and later messages as collapsed
  cards that expand in place; list rows show a "(n)" count.

Recipients
- `to`, `cc`, `bcc` accept a header string or an array (composer, /api/send,
  /api/v1/send). Full To and Cc lines are stored on inbound messages so
  reply-all can address everyone; Bcc is stored on sent mail only.
- Composer gains a chip input (comma, Enter, paste, blur commit; Backspace
  edits the last chip; invalid entries highlighted) with Cc/Bcc toggles.
- Reply-all appears only when it would reach someone a plain reply would
  not, and drops the mailbox's own addresses.
- Reader header shows every To/Cc/Bcc recipient; Sent rows read "Name, +2".

Schema: migration 0025 adds cc_addr, bcc_addr, in_reply_to,
references_header and two indexes; bootstrap SQL and MIGRATION_NAMES
updated. mbox export now writes Cc/In-Reply-To/References.

Verified against a fresh local D1 with the seeded demo data: a reply sent
through /api/send, an .eml reply imported with In-Reply-To pointing at the
Cloudflare-assigned Message-ID, and a reply-all from the composer all landed
in one 4-message thread. tsc (11) and eslint (96) unchanged from baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGeWDVSkXtVEmDAN2AtB9h
2026-09-06 14:54:17 +03:00
Hieu Nguyen (Jack) a0bbaf3310 replace workflow with cron 2026-09-06 11:12:45 +07:00
Hieu Nguyen (Jack) 9d5c1bc63a Merge PR #13 with current routing and settings 2026-09-03 17:21:46 +07:00
Hieu Nguyen (Jack) 46327ddbcb Update deployment.md 2026-09-02 13:54:14 +07:00
Hieu Nguyen (Jack) dbd07802c6 update readme 2026-09-02 03:00:55 +07:00
Hieu Nguyen (Jack) d5a08b0140 added migration to build script 2026-09-02 02:05:22 +07:00
Hieu Nguyen (Jack) d600403aaa Update readme #15 2026-08-31 17:30:11 +07:00
Oday BakkourandClaude Opus 5 caf48d7f07 docs: check off both roadmap items and document routing rules and webhooks
Adds README sections covering the three-phase domain rule evaluation order, the
available match fields and operators, the keep-a-copy forwarding behaviour, the
webhook retry/backoff model, the signing headers, and the new API surface, plus
screenshots of both pages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGeWDVSkXtVEmDAN2AtB9h
2026-08-30 10:18:44 +03:00
Hieu Nguyen (Jack) 0dfb15d82b Update README.md 2026-06-19 13:47:19 +07:00