* feat(mail): support gzip compressed email storage in D1 raw_blob column
Add ENABLE_MAIL_GZIP env var to optionally gzip-compress incoming emails
into a new raw_blob BLOB column, saving D1 storage space. Reading is
backward-compatible: prioritizes raw_blob (decompress) with fallback to
plaintext raw field. Includes DB migration v0.0.7, docs, and changelogs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: gzip fallback on missing column + decouple resolve from handleListQuery
- email/index.ts: gzip INSERT failure now falls back to plaintext INSERT
instead of silently losing the email (P1: data loss prevention)
- common.ts: add handleMailListQuery for raw_mails-specific list queries
with resolveRawEmailList, keeping handleListQuery generic
- Replace handleListQuery → handleMailListQuery in mails_api, admin_mail_api,
user_mail_api (only raw_mails callers)
- Add e2e test infrastructure: worker-gzip service, wrangler.toml.e2e.gzip,
api-gzip playwright project, mail-gzip.spec.ts with 4 test cases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback for gzip feature
- Use destructuring in resolveRawEmailRow to truly remove raw_blob key
- Narrow fallback scope: only fallback to plaintext on compression failure
or missing raw_blob column, re-throw other DB errors
- Clean unused imports in e2e gzip test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add try-catch in resolveRawEmail to prevent single corrupt blob from failing entire list
A corrupted raw_blob would cause decompressBlob to throw, which with
Promise.all in resolveRawEmailList would reject the entire batch query.
Now catches decompression errors and falls back to row.raw field.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(mail): align sendAdminInternalMail with gzip storage path
sendAdminInternalMail now respects ENABLE_MAIL_GZIP: compresses to
raw_blob when enabled, with fallback to plaintext on failure.
Added e2e test verifying admin internal mail is readable under gzip.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(e2e): match admin internal mail by body content instead of encoded subject
mimetext base64-encodes the Subject header, so the raw MIME string
does not contain the literal subject text. Match on body content
(balance: 99) which is plaintext.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(e2e): add WORKER_GZIP_URL guard and length assertions in gzip tests
Address CodeRabbit feedback:
- Skip gzip tests when WORKER_GZIP_URL is not set to prevent false positives
- Assert results array length before accessing [0] for clearer error messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(mail): narrow gzip fallback scope and fix webhook query compatibility
- sendAdminInternalMail: separate compress vs DB error handling, only
fallback to plaintext on compression failure or missing raw_blob
column, rethrow other DB errors (aligns with email/index.ts)
- Webhook test endpoints: use SELECT * instead of explicit raw_blob
column reference, so pre-migration databases don't 500
- Docs/changelog: clarify that db_migration must run before enabling
ENABLE_MAIL_GZIP
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(telegram): use generic Record type for raw_mails query result
Align with other query sites — avoid hardcoding raw_blob in the
TypeScript type annotation so the query works with or without the
column after migration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(models): add RawMailRow type and unify raw_mails query typing
Add RawMailRow type to models with raw_blob as optional field, replacing
ad-hoc Record<string, unknown> and inline type annotations across
webhook test endpoints, telegram API, and gzip utilities.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Add source_meta field to address table for tracking creation source
- Web: records client IP address (with fallback to 'web:unknown')
- Telegram: records 'tg:{userId}'
- Admin: records 'admin'
- Add database migration with field existence check
- Add frontend display in admin Account page
- Backward compatible: fallback if field doesn't exist
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
* feat: optimize email filtering with frontend-only search
- Remove backend keyword parameter from mail APIs (breaking change)
- Implement frontend filtering on current page (20-100 items)
- Add message_id database index for UPDATE performance
- Support desktop and mobile responsive layouts
- Update API documentation and CHANGELOG
BREAKING CHANGE: /admin/mails and /user_api/mails no longer accept keyword parameter
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: restore Mail ID query input in Index.vue
- Keep showMailIdQuery UI input for querying specific mail by ID
- Triggered when URL contains mail_id parameter
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add AI-powered email content extraction feature using Cloudflare Workers AI to automatically identify and extract important information from emails including verification codes, authentication links, service links, and subscription links.
Features:
- AI extraction with priority-based logic (auth_code > auth_link > service_link > subscription_link > other_link)
- Admin allowlist configuration with wildcard support (*@example.com)
- Frontend display in both email list (compact) and detail view (full mode)
- Bilingual documentation (Chinese/English)
- Database migration: add metadata field to raw_mails (v0.0.3 -> v0.0.4)
Technical highlights:
- Proper regex escaping for wildcard pattern matching
- Content truncation to avoid AI token limits
- Error handling that won't affect email receiving
- JSON schema validation for AI responses
- Type-safe TypeScript implementation
- Vue I18n support with special character escaping
References:
- Inspired by Alle Project: https://github.com/bestruirui/Alle
- Uses Cloudflare Workers AI JSON Mode
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>