vasyansk b6e68bdd90 Add activity tracking to prevent stall timeouts during message transfers
Add OnActivity callback to CopyDeps to prevent stall timeouts during large message transfers
Implement touchReader and touchWriter wrappers to call OnActivity during FETCH and APPEND operations
Add slow message logging to identify performance bottlenecks
Add test case to verify activity reporting during message transfers
Clean up orchestrator account reset code formatting
2026-07-21 04:38:32 +07:00
2026-07-03 11:18:40 +07:00
2026-07-17 11:42:34 +07:00
2026-07-17 11:42:34 +07:00
2026-07-03 11:18:40 +07:00
2026-07-08 13:34:53 +07:00
2026-07-03 11:18:40 +07:00
2026-07-08 13:33:14 +07:00

imap-copier

Single-binary Go server (embeds the React SPA) that copies IMAP mailboxes between a source and a destination account. Non-destructive (copy only, never deletes), deduplicated by Message-ID, resumable/idempotent re-runs.

Screenshots

Task list — create migration jobs and track their status:

Migration tasks list

Task detail — run control, per-account credentials, live event log and progress:

Task detail

Quick start (plain HTTP on :80)

cp .env.example .env
# generate a real 32-byte key for ENC_KEY:
sed -i '' "s|ENC_KEY=|ENC_KEY=$(openssl rand -base64 32)|" .env
# edit .env: set POSTGRES_PASSWORD, AUTH_USER, AUTH_PASS, SESSION_SECRET

docker compose build
docker compose up -d
curl -fsS http://localhost/healthz

The app is served through Caddy on port 80 by default — no domain or TLS required. Login with AUTH_USER/AUTH_PASS from .env.

Enabling HTTPS (Let's Encrypt)

Set a real, publicly resolvable domain and an ACME contact email in .env:

DOMAIN=copier.example.com
ACME_EMAIL=you@example.com

Then start with the TLS override instead of the plain compose file:

docker compose -f docker-compose.yml -f docker-compose.tls.yml up -d
# or: make up-tls

This swaps Caddy's config to Caddyfile.tls, which requests and renews a Let's Encrypt certificate automatically for DOMAIN (ports 80/443 must be reachable from the internet for the ACME HTTP-01 challenge). Switch back to plain HTTP with docker compose -f docker-compose.yml up -d (or make up).

Environment variables (.env)

Var Required Notes
POSTGRES_PASSWORD yes Postgres password, also used in DATABASE_URL
AUTH_USER / AUTH_PASS yes Single operator login (no user table)
ENC_KEY yes 32 bytes, base64: openssl rand -base64 32
SESSION_SECRET yes Signs the session cookie
WORKER_CONCURRENCY no (default 4) Parallel accounts copied per run
HTTP_PORT no (default 80) Host port Caddy binds for HTTP
DOMAIN / ACME_EMAIL only for HTTPS See above

Makefile targets

make build    # docker compose build
make up       # docker compose up -d (plain HTTP)
make up-tls   # docker compose up -d with the Let's Encrypt override
make down     # docker compose down
make logs     # docker compose logs -f
make test     # go test ./...
make e2e      # scripts/e2e.sh (full-stack E2E against greenmail)

E2E test

scripts/e2e.sh builds and starts the full stack (postgres, app, caddy) plus a throwaway greenmail IMAP server acting as both the source and destination mailbox host, then drives the real REST API: login, create endpoints/task/account, /test, /run, and a second /run to prove idempotency (nothing is re-copied). It tears everything down afterwards.

bash scripts/e2e.sh
# or: make e2e

Known limitations

Deduplication key is UNIQUE(account_id, message_key) without folder. If the same message appears in multiple source folders (e.g. Gmail INBOX + [Gmail]/All Mail + labels-as-folders), it is copied only into whichever destination folder is processed first; folder placement for such duplicated messages is not guaranteed. This is intentional per the design spec.

Architecture

  • cmd/server — entrypoint: runs DB migrations, then serves HTTP.
  • internal/httpapi — REST API + WebSocket + embedded SPA (webdist/).
  • internal/orchestrator — test/run coordination, per-account concurrency.
  • internal/imapx — IMAP connect/list/copy primitives.
  • internal/store — Postgres access (endpoints, tasks, accounts, runs).
  • web/ — React/Vite SPA, built into internal/httpapi/webdist at image build time (see Dockerfile).
S
Description
No description provided
Readme
953 KiB
Languages
Go 60.6%
TypeScript 27.9%
CSS 7.3%
Shell 3.7%
Dockerfile 0.2%
Other 0.2%