Commit Graph

119 Commits

Author SHA1 Message Date
rustmailer
02e9864343 Merge remote-tracking branch 'origin/main' into replace-fjall-with-blob 2026-07-28 21:38:44 +08:00
rustmailer
eae26d3e97 perf(blob): batch blob writes during migration to avoid per-blob fsync overhead 2026-07-28 21:13:16 +08:00
rustmailer
664ac2fe55 fix: can't set proxy for email account (IMAP) #326 2026-07-20 23:00:36 +08:00
rustmailer
c468f8be41 fix(admin): skip oversized blobs during migration instead of aborting 2026-07-16 05:22:10 +08:00
rustmailer
a88b5c84f3 Merge remote-tracking branch 'origin/main' into replace-fjall-with-blob 2026-07-15 10:48:51 +08:00
rustmailer
c6da79cdb0 feat(config): add built-in IMAP server config with shared SMTP/IMAP TLS paths 2026-07-15 09:40:44 +08:00
rustmailer
eac19ce695 Update envelope.rs 2026-07-14 07:34:56 +08:00
rustmailer
951901ac0b update 2026-07-14 05:49:54 +08:00
rustmailer
b7e757dbf7 update 2026-07-13 20:56:26 +08:00
rustmailer
85987e39fb update 2026-07-13 00:04:53 +08:00
rustmailer
adff34940c update 2026-07-10 17:24:56 +08:00
rustmailer
a8f2740973 Create README.md 2026-07-10 16:44:25 +08:00
rustmailer
36692e2091 feat: replace fjall with bichon-blob for blob storage
- use a single Engine instance for email + attachment blobs
  - add delete_batch, gc_if_needed, background flush to blob crate
  - fix Entry.raw_size storing compressed length instead of original
  - move fjall-dependent migration code from core to admin crate
  - add STORAGE_VERSION file for layout version detection
2026-07-10 03:17:38 +08:00
rustmailer
4cdf3ee5f1 refactor(blob): add delete_batch, gc_if_needed, background flush, and fix bincode compat
- Replace bincode 3.0.0 (empty crate) with bincode_reloaded 3.1.10
  - Add delete_batch for efficient grouped tombstone writes
  - Add gc_if_needed to skip GC when no segment exceeds threshold
  - Add flush() for lightweight fsync+meta checkpoint without compact
  - Add Config::flush_interval_secs to spawn a background flush thread
  - Remove per-put/per-delete fsync; persistence via background flush
  - Split gc_segments into gc_prepare/gc_finish to reduce write lock hold time
2026-07-10 01:52:04 +08:00
rustmailer
7dee5a7874 bump to v1.6.2 2026-07-08 23:55:05 +08:00
rustmailer
524201c26d docs(openapi): update attachment download param to content_hash #314 2026-07-06 09:46:29 +08:00
8times4
24fcca70a5 expand proxy provider support 2026-06-30 14:59:08 +02:00
rustmailer
2da42134d0 feat: Improve account setup UI and add post-download email filtering 2026-06-30 09:42:03 +08:00
rustmailer
40ae2d49d4 feat: web upload supports PST, configurable MBOX/PST size limits
- Make MBOX/PST upload size limits configurable via SETTINGS
    (bichon_web_mbox_upload_limit_mb defaults to 1 GB,
     bichon_web_pst_upload_limit_mb defaults to 2 GB)
2026-06-28 09:43:50 +08:00
rustmailer
cfb8172607 fix: sliding token expiry and silent 401 redirect 2026-06-26 18:29:18 +08:00
rustmailer
8641bb4b56 fix: Proxy does not support formats from proxy providers #307 2026-06-26 18:20:19 +08:00
rustmailer
cdf27f2dd4 feat: add web upload for EML/MBOX files #260 2026-06-26 17:49:35 +08:00
rustmailer
db36272bae feat: add in-browser attachment preview for images, PDFs, and text files #303 2026-06-25 04:51:05 +08:00
rustmailer
cad447275f feat: search for an email address simultaneously in to, cc and bcc #304 2026-06-25 02:36:24 +08:00
rustmailer
8542ba0d28 feat: Display Name / Alias for IMAP Accounts #306 2026-06-25 02:00:20 +08:00
rustmailer
bba1ea5cc7 feat: add per-account FilterRule, ExtractionRules and ArchiveRules with regex validation 2026-06-24 21:46:32 +08:00
rustmailer
9e55026f12 feat: add SSO/OIDC support to user model and settings 2026-06-24 17:32:14 +08:00
rustmailer
b0f229618c refactor(blob): add NFS-safe file I/O layer 2026-06-24 00:03:56 +08:00
rustmailer
ff59d47a0d fix(blob): fsync meta before rename and hold write_mutex during GC 2026-06-23 23:57:34 +08:00
rustmailer
b6957c8ceb test(blob): add concurrent access and crash recovery integration tests 2026-06-23 23:29:48 +08:00
rustmailer
cd98c050b6 fix(blob): invalidate FilePool after GC to prevent stale reads 2026-06-23 23:28:33 +08:00
rustmailer
11e40750fe feat(blob): add FilePool read cache and fix BucketCache TOCTOU 2026-06-23 23:23:03 +08:00
rustmailer
b0374517e8 fix(blob): address review issues - visibility, unused param 2026-06-23 23:20:24 +08:00
rustmailer
4e12ae5457 refactor(blob): replace global RwLock with per-account Arc<AccountHandle> 2026-06-23 23:13:39 +08:00
rustmailer
495c91b7ae fix(blob): address code review issues - CRC guard, tests, clone 2026-06-23 23:10:15 +08:00
rustmailer
e442db4a2d feat(blob): replace JSON metadata with bincode + CRC32 binary format 2026-06-23 23:01:32 +08:00
rustmailer
cc54063669 chore(blob): add bincode dependency and new meta error variants 2026-06-23 22:55:13 +08:00
rustmailer
04745458bc feat(blob): add embedded KV storage engine for email archiving 2026-06-23 21:38:31 +08:00
rustmailer
220aa268c1 feat(imap): handle UIDVALIDITY changes via Message-ID comparison instead of full rebuild 2026-06-23 10:46:36 +08:00
rustmailer
e3fd9d2f29 fix: purge DedupCache entries on account/mailbox/envelope removal 2026-06-22 17:11:20 +08:00
rustmailer
89557700ae Merge branch 'main' of https://github.com/rustmailer/bichon 2026-06-11 09:23:00 +08:00
rustmailer
2f5de48c6a fix(smtp): reject journaling attempts to non-local accounts 2026-06-11 09:22:57 +08:00
Josh
ce3f8944a3 fix(smtp): don't clobber the IMAP-owned INBOX uid_validity on journal ingest
When SMTP journaling ingests a message, parse_email() upserts the account's
INBOX MailBox row so the journaled envelope has a row to attach to. But it
built the row with uid_validity/highest_uid/uid_next = None and called
batch_upsert, which replaces the WHOLE row. The INBOX row id
(create_hash(account_id, "INBOX")) is the same id the IMAP sync maintains,
so every journaled delivery reset the IMAP-maintained uid_validity to None.

On the next reconcile, local_mailbox.uid_validity != Some(remote) is then
true, so the mailbox is treated as invalid and wiped + rebuilt. For a large,
UID-sparse INBOX whose rebuild gets interrupted, the local copy is silently
lost and never restored (the incremental fetch resumes past all existing
UIDs). See #297 for the full diagnosis and DB evidence.

Fix: only create the INBOX row when it does not already exist; otherwise
leave the IMAP-owned row untouched. The journaling path only needs the row
to exist so the envelope can attach to it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 11:36:38 -07:00
rustmailer
c3a725770c fix: reconnect and retry IMAP batch on BrokenPipe/network errors 2026-06-07 18:18:37 +08:00
rustmailer
c736afffb0 fix: account deletion times out #291 2026-06-07 15:34:38 +08:00
rustmailer
aebb94ee4e fix: preserve non-stored search fields when updating envelope tags
update_envelope_tags lost f_body, f_from_text, f_to_text, f_cc_text,
  f_bcc_text, f_attachment_name_text and f_attachment_name_exact because
  they are not STORED and field_values() skipped them during delete+add.
  Rebuild these from stored counterparts and the blob-store EML.
2026-06-05 09:13:25 +08:00
rustmailer
42861f6cc9 Merge pull request #288 from Korov/fix/tencent-mail-uidvalidity
fix: Add fallback UIDVALIDITY support for non-compliant IMAP servers
2026-06-04 23:45:40 +08:00
rustmailer
327a3f39d9 Merge pull request #287 from fama/dedup-cache-fix
fix: open NewIndexWriter once across all migration segments
2026-06-04 10:24:22 +08:00
fama
427f7248d2 fix: open NewIndexWriter once across all migration segments
Previously, do_migrate_segment created a fresh NewIndexWriter (and
therefore a new Fjall Database) on every call, meaning the Fjall
database at bichon-storage/ was opened and closed once per segment.

This caused the migration to fail mid-way through (observed at segment
9/16) with:

  Storage(InvalidTag(("ChecksumType", 171)))

Root cause: after segment N writes email blobs via Fjall's ingestion
API (start_ingestion / write / finish), those SSTables and KV-separated
blob files are flushed to disk and the Database is dropped. When segment
N+1 calls Database::builder(storage_dir).open(), Fjall must discover and
catalog all on-disk files produced by the previous segments. During that
discovery it reads SSTable or blob-file block headers and encounters a
ChecksumType discriminant byte (171 / 0xAB) that lsm-tree 3.1.4 does
not recognise, causing the fatal error.

The first N segments succeed because the cumulative set of ingested
SSTables stays small enough that Fjall does not need to read the
offending headers during reopen. Once enough data has accumulated the
reopen triggers a manifest or compaction read that exposes the mismatch.

Fix: open NewIndexWriter once, before the segment loop, and pass a
&mut reference into each do_migrate_segment call. finish_writers() is
called a single time after all segments complete. The Fjall Database
stays open for the entire migration and is never closed and reopened,
eliminating the incompatible-reopen path entirely.
2026-06-03 15:10:03 -06:00
rustmailer
a2a51a2037 feat(imap): add message size check before download 2026-06-03 21:17:35 +08:00