feat: support attachments when composing mail - #73
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
There are correctness issues in the mail route spec generation (z.toJSONSchema usage) and in the send flow where a null SMTP result can still return HTTP 200.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adds support for composing and sending emails with attachments by accepting multipart/form-data on the mail creation endpoint, enforcing a configurable total attachment size limit, and ensuring draft sending relays the stored raw RFC822 source to preserve MIME structure.
Changes:
- Add
multipart/form-datasupport forPOST .../mailswith attachment ingestion and a max combined attachment size limit (DLA_MAX_ATTACHMENT_SIZE_MB). - Send drafts via SMTP using the exact stored message source (
IMAPAccount.getMailSource()+SMTPAccount.sendRaw()), preserving attachments/inline parts. - Extend test coverage to validate multipart attachment creation and document the new env var in
example.env.
File summaries
| File | Description |
|---|---|
| tests/helpers/preload.ts | Sets DLA_MAX_ATTACHMENT_SIZE_MB for the test environment. |
| tests/api.routes.test.ts | Adds an integration test that creates a multipart draft with an attachment and verifies attachment metadata. |
| src/utils/mails/backends/smtp.ts | Adds sendRaw() to relay a stored RFC822 source via SMTP with an explicit envelope. |
| src/utils/mails/backends/imap.ts | Adds getMailSource() to fetch a message’s raw RFC822 source from IMAP. |
| src/utils/config.ts | Registers DLA_MAX_ATTACHMENT_SIZE_MB in the config schema. |
| src/api/versions/v1/routes/mail-accounts/mailboxes/mails/model.ts | Defines an OpenAPI schema for multipart create-mail requests with attachments. |
| src/api/versions/v1/routes/mail-accounts/mailboxes/mails/index.ts | Implements multipart parsing, size limiting, MailComposer attachment wiring, and raw-source sending for drafts. |
| example.env | Documents DLA_MAX_ATTACHMENT_SIZE_MB and its default. |
Review details
Suppressed comments (1)
src/api/versions/v1/routes/mail-accounts/mailboxes/mails/index.ts:176
z.toJSONSchema(...)is not used anywhere else in the repo and is likely not available on the Zod export; this can break OpenAPI generation or compilation. Useresolver(MailsModel.Create.Body)(fromhono-openapi) for consistency with the rest of the API spec helpers.
"application/json": { schema: z.toJSONSchema(MailsModel.Create.Body) as OpenAPIV3_1.SchemaObject },
- Files reviewed: 8/8 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| import { ConfigHandler } from "../../../../../../../utils/config"; | ||
| import type { OpenAPIV3_1 } from "openapi-types"; |
| // Send the exact stored source so MIME attachments and inline parts survive. | ||
| const source = await imap.getMailSource(mailbox.path, mailData.uid); | ||
| if (!source) return APIResponse.notFound(c, "Mail with specified UID not found"); | ||
| const result = await smtp.sendRaw(source, mailData); |
| const limit = maxAttachmentSize(); | ||
| const totalSize = files.reduce((sum, file) => sum + file.size, 0); | ||
| if (totalSize > limit) { | ||
| return { | ||
| ok: false, |
No description provided.