Adds GET /admin/mailboxes — paginated platform-wide mailbox list that
joins email_accounts → users → organizations so the table answers
"whose mailbox is this and where does it live" without N+1 fetches.
Search covers mailbox email / owner email / org name; status filter
defaults to active so the active surface shows first ("inactive" /
"all" both available). Provider filter speeds up "show me every Gmail
mailbox" investigations. Cursor pagination matches the rest of the
admin lists.
Frontend page surfaces warmup-on/off, send budget, and last-sync time
with red-when-never / amber-when-stale-over-24h tone so an
investigator can spot dead mailboxes fast. Mailbox email links into
the owning user's detail page; org name links into the workspace
admin so the pivot path stays one click in either direction.
Gated on AdminPermViewUsers since mailbox triage is tightly coupled to
user/org context today; a dedicated bit can be carved later if
mailbox-specific actions land.
Add users.ban_scope INT NOT NULL DEFAULT 0 in migration 000045 so admins
can describe what a ban concretely stops (login / workspace creation /
outbound send) instead of relying on a single boolean banned_at flag
that meant "everything".
Wire flags in the BanScope enum (kept in sync with the migration) plus
a CHECK constraint guaranteeing non-negative values. Existing bans
backfill to BanScopeLogin so the historical "you can't log in"
semantics is preserved exactly — no behaviour changes silently at
deployment.
BanUserRequest gains an optional scope field, BanUser threads it through
the service to the repo write, and the UserBanDialog grows a checkbox
group with one option per flag. Reason still required; at least one
scope must be picked. Audit details now include the scope bitmask.
Runtime enforcement (refusing login when BanScopeLogin is set, etc.) is
intentionally separate from this commit — the existing codebase doesn't
yet have an active ban check anywhere, so wiring that lives across the
auth middleware, org-create handler, and emailsend service. This slice
ships the schema, the audit story, and the UI vocabulary so the
enforcement PR can land without database churn.
The audit calls added in the last commit went to AuditService.LogAction,
which writes to the general user-facing audit log (Cassandra). The admin
audit-log viewer at /admin/audit-logs queries the admin_audit_log table
in Postgres, so worker / credentials / release actions never showed up.
Add a public AdminService.LogAdminAction that wraps the existing private
logAction (writes to admin_audit_log with the same shape as ban_user /
update_worker / etc.). Repoint h.audit() at it.
Actions now visible in the audit viewer:
test, install, restart, upgrade, uninstall, rotate_keys, apply,
assign, system_update, reboot, check_releases (plus the existing
create/update/delete across workers, AWS creds, and profiles).