A live run could only be stopped one account at a time, and stopping it
at all meant losing the queue: the accounts that had not started yet
stayed idle with no record that they were meant to run.
A run now carries a handle holding the context that stops every account
under it plus the reason it was stopped. Pause and cancel take the same
path and differ only in the status left behind — paused accounts are
what Resume re-runs, and the migration journal makes each one continue
where it stopped instead of re-copying. Accounts still queued when the
stop lands get the same status as the interrupted ones, so the whole
remainder is resumable after a pause and cancelled after a cancel.
Database writes keep using the uncancellable context, so statuses and
counters survive the stop.
The scheduler skips paused tasks: auto-starting a full run would defeat
the pause. An operator stopping a run no longer trips the schedule
breaker either — that is for failures, not for intent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An imported account with a wrong password could only be deleted and
re-added, which loses its folder mapping and its migration journal.
Clicking either FAIL badge now opens a dialog for both logins and both
passwords.
Passwords are never sent to the browser, so the password fields start
empty and an empty field keeps the stored ciphertext — one side can be
corrected without retyping the other. Saving resets both test verdicts
to unknown: they described the previous credentials, and the run gate
requires a passing test on both sides, so the account cannot start on an
unverified password.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mapping the Exchange/Kerio special folders onto mailcow's by hand is
repetitive work that scales with the number of accounts. "By default"
maps Deleted Items to Trash, Junk E-mail to Junk, Sent Items to Sent and
unchecks Public Folders, which mailcow has no counterpart for.
The destination select only offered the source folder as a name to
create, so a target missing on the destination could not be selected at
all. It now also offers the current selection, and marks any name absent
from the destination as "(create)".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Only the plain four-column format had a downloadable example, leaving
the Kerio route undocumented in the UI. Each import button now carries
its own sample link underneath: the plain comma-separated layout and a
Kerio export with the Name;FullName;Description;Enable header, a
disabled row included to show what the import skips.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The endpoints screen could only create and edit servers, so a mistyped
or retired endpoint stayed in the list forever.
Tasks reference endpoints without ON DELETE CASCADE, so a referenced
endpoint is refused with 409 and a count of the tasks using it rather
than cascading away migration history. The foreign-key violation is
mapped to the same status to cover a task created between check and
delete.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pg_isready without -d connects to a database named after the user, but
the database is imapcopier, so every probe logged a FATAL and the
healthcheck only passed because pg_isready treats "server rejects the
connection" as reachable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Introduce Client wrapper with socket deadline support
Add reconnection logic for body read timeouts
Implement test cases for underflow scenarios
Update orchestrator to handle reconnections
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
Improve account display by showing source/destination in a stacked format
when they differ, and add fixed sizing for live progress rows to prevent
resizing during scans. Also update the table header to use a single
"Account" column.
Accounts finishing done_with_errors showed only a count and a single
last_error. This adds a modal listing every concrete error of the
account's most recent run.
- migration 0005: account_errors table (kind folder|message|account,
folder, message_ref, error, created_at; ON DELETE CASCADE; indexed)
- store: AddAccountError / ClearAccountErrors / ListAccountErrors
- copy: OnError callback captures per-message error text (previously
only counted), with a "UID N: subject" reference
- orchestrator: clear errors at run start; persist folder/message/
account errors; cap 500 rows/account/run with a suppressed-note row
- api: GET /api/tasks/{id}/accounts/{accountId}/errors
- web: AccountErrorsModal, clickable ERRORS count, api + styles
Verified: migration applies on Postgres 18; store add/list/clear and
cascade tests pass against real pg; backend build/vet/test green; web
tsc+vite build and oxlint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U9Eq4JtWjyNTv5qat3B3mM
Introduce idleConn wrapper to prevent wedged connections
Add test for silent server timeout behavior
Implement proper TLS handshake and greeting handling
Set explicit dial and read timeouts
A 30s in-process scheduler auto-runs tasks on a recurring interval
(1/3/6/12/24h). Interval is measured from the last run's completion; a task
already running is never re-triggered. Enabling a schedule requires all
accounts tested OK. A breaker disables the schedule and flags the task broken
(red badge in list + detail) when a scheduled run errors or panics. Next-run
time (browser-local) and a run-log modal (per-run status/totals) are shown.
Migration 0004 adds tasks.schedule_interval_seconds/schedule_anchor/broken and
runs.trigger; task delete cascades runs as before.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move folder config from task-level to per-account (accounts.folder_mapping +
excluded_folders, migrating existing task mapping). Add per-account folders
button that re-probes an account and opens the mapping modal with sync
checkboxes to exclude folders. Orchestrator builds each account's plan via
planFolders honoring per-account mapping/exclusions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>