Commit Graph
1801 Commits
Author SHA1 Message Date
Linus Rath 68d64f5018 fix: pair the policy with its own version and keep config reloads and saves from undoing each other #1066
Fixes from the review of the admin part:
- GET /api/admin/policy reads the policy and its version together; an
  identity check waiting on the store let another replica's change pair
  the old policy with the new ETag, and a save with it then won.
- A policy save answers with the stored policy and its ETag, and the
  policy, organisation, plugins and themes pages continue from that
  instead of reading it back from an instance that may not have it yet.
- A stored policy that doesn't parse keeps its version, so a save can
  replace it instead of answering 409 forever.
- A reload of another replica's change throws when this replica's writes
  kept landing while it read, so the change poller tries again; a write
  whose reload raced it reads the document again instead of applying its
  older result.
- Dropping a deleted plugin from the policy rereads policy.json first in
  local mode, as every other write does.
- New updateAdminConfig(key, change) builds a value on the stored one.

The policy route's test mocks gain getPolicyWithVersion.
2026-10-31 23:55:17 +01:00
Linus Rath 698b881f1f refactor: use the shared store's own outage check in the admin code #1066 2026-10-31 23:45:53 +01:00
Linus Rath d6e98e207a Merge branch 'feat/multi-instance' into feat/multi-instance-admin 2026-10-31 23:45:00 +01:00
Linus Rath 9e2b59847f refactor: recognise a shared-store outage by name, so every bundle's module copy sees it 2026-10-31 23:31:02 +01:00
Linus Rath 4983325f32 feat: bring the admin config, password, state and audit log along when moving to a shared store #1066 2026-10-31 23:26:52 +01:00
Linus Rath f531ef63b1 feat: print the same setup token on every instance #1066 2026-10-31 23:25:10 +01:00
Linus Rath ade95013f8 feat: keep one admin audit log for every instance #1066 2026-10-31 23:23:00 +01:00
Linus Rath 291b6364c9 feat: count admin login attempts across every instance and refuse sign-in while the shared store is down #1066
The three admin login limiters (5 per address and 50 overall in one
reservation, 5 per known device) keep their numbers and still don't count
refused attempts, but live in the store: memory in this process as before,
or the shared store with several replicas. checkRateLimit and
checkDeviceRateLimit are async now; the login route awaits them and
answers 503 "Sign-in is temporarily unavailable" when the store can't be
reached. The two limiter tests gain await and reset the store's counters,
which vi.resetModules no longer clears.

The admin code tells a store outage by the error's name, since the store
may come from another bundle's copy of the module.
2026-10-31 23:18:40 +01:00
Linus Rath ee5a107649 fix: answer 409 for a stale policy save also when another bundle made the config manager #1066 2026-10-31 23:17:36 +01:00
Linus Rath a048689179 feat: check admin sign-outs and password changes against the shared store on every instance #1066 2026-10-31 23:12:23 +01:00
Linus Rath cde02f5a48 fix: refuse the old admin password everywhere right after it was changed 2026-10-31 23:07:11 +01:00
Linus Rath 7a482a4d0e feat: share admin config and policy between instances and spread changes within seconds #1066 2026-10-31 23:03:57 +01:00
Linus Rath 25408b2ac2 fix: refuse a policy save from a page opened before someone else saved #1066
The admin pages save the whole policy from the draft they loaded. A
save from a page opened before another tab's or another admin's save,
or before a deleted plugin was dropped from the policy, used to put the
older policy back. GET /api/admin/policy now sends the policy version as
an ETag; the policy, organisation, plugins and themes pages send it back
as If-Match, and a save based on an older version answers 409 and keeps
the draft on the page. A PUT without If-Match replaces the policy as
before. Deleting a plugin or theme changes only its id in the stored
policy, and a policy import compares against what it replaces.

Test mocks of the config manager gain getPolicyVersion and updatePolicy,
which the route and the extension cleanup now call.
2026-10-31 22:57:57 +01:00
Linus Rath c20ecd4b1a refactor: let each store bring its own step for the shared-store import 2026-10-31 22:42:14 +01:00
Linus Rath 1b2d81be27 feat: hold each instance back until it has loaded from the shared store and add a readiness check #1066 2026-10-31 22:40:51 +01:00
Linus Rath df90492258 feat: add a shared store that keeps today's files by default and can use a redis-protocol server #1066 2026-10-31 22:40:44 +01:00
Linus Rath 0571b8f2ec fix: show pictures apple mail sends inline as attachments #1478 2026-10-31 22:08:56 +01:00
Linus Rath 521b0aedab fix: stop naming a jmap metadata property in the demo client 2026-10-31 18:52:29 +01:00
Linus Rath 088a982218 test: expect 30 locales now that vietnamese is in 2026-10-31 18:50:55 +01:00
Linus Rath d23a2869d1 Merge remote-tracking branch 'origin/main' into fix/found-fix-server
# Conflicts:
#	Dockerfile
#	docker-compose.yml
2026-10-31 18:17:39 +01:00
Linus Rath f884ffe441 Merge branch 'fix/found-fix-ui' into fix/found-fix-server 2026-10-31 18:17:13 +01:00
Linus Rath 5fe7d1a65a fix: keep existing compose volumes and add the two that were missing 2026-10-31 18:12:00 +01:00
Linus Rath 136e6be8fd refactor: drop the unused draft autosave interval setting 2026-10-31 18:07:16 +01:00
Linus Rath fa9aad95b4 docs: describe where the push relay sends the account label 2026-10-31 18:06:08 +01:00
Linus Rath 9f0413d50b docs: say in the Lite readme that sign-in with SSO works when Stalwart serves Lite 2026-10-31 18:04:57 +01:00
Linus Rath 88351b2333 fix: point the outgoing hosts list at the current usage stats and updates pages 2026-10-31 18:02:01 +01:00
Linus Rath 6fc3994aac fix: keep plugin credentials and update-check state across container recreates 2026-10-31 18:00:13 +01:00
Linus Rath 100f6db309 fix: skip the setup wizard in demo mode without a mail server 2026-10-31 18:00:13 +01:00
Linus Rath ddc24e4fc7 fix: let a browser an admin signed in from before past the shared admin login limit 2026-10-31 17:55:29 +01:00
Linus Rath e15c335bec fix: pick up admin changes saved by another replica without a restart 2026-10-31 17:54:40 +01:00
Linus Rath 825d6e76f8 fix: let admins switch off usage stats by clearing the collector address 2026-10-31 17:51:09 +01:00
Linus Rath 6aa48c300d fix: keep plugin credentials when the docker container is recreated 2026-10-31 17:50:07 +01:00
Linus Rath 072a4219be fix: drop the connector address setting that nothing used 2026-10-31 17:49:54 +01:00
Linus Rath f11ddf82dd fix: tell banner plugins when an admin opened the mailbox through impersonation 2026-10-31 17:48:29 +01:00
Linus Rath a9e93881b5 feat: match filter conditions against a regular expression #885 2026-10-31 17:45:26 +01:00
Linus Rath dcc785690c fix: apply the log level and format set in the setup wizard or admin console 2026-10-31 17:45:06 +01:00
Linus Rath 65ec66b8ef fix: send embedded-mode messages to the parent origin set in the admin console 2026-10-31 17:43:03 +01:00
Linus Rath 73a7bb7bcd fix: send the headers plugins add to an outgoing message instead of dropping them 2026-10-31 17:40:20 +01:00
Linus Rath 961c176401 Merge remote-tracking branch 'origin/main' into feat/audience-shots 2026-10-31 17:06:43 +01:00
Linus Rath ee53eb8d2c test: add a flight booking with a card to the demo 2026-10-31 16:58:40 +01:00
Linus Rath 9d529ca98f feat: reply from the address a message was delivered to when to, cc and bcc have no match #1353 2026-10-31 16:55:10 +01:00
Linus Rath ea604ab8a1 fix: keep the open folder regular weight in mountain view so bold only means unread #1469 2026-10-31 16:31:15 +01:00
Linus Rath 7341e981c3 fix: limit how often a plugin can open a message in the reader #1466 2026-10-31 16:25:07 +01:00
Linus Rath b8223a4492 Merge pull request #1466 from HOLOEYE-JZ/feat/plugin-open-email
feat(plugins): api.ui.openEmail opens a message in the reader
2026-10-31 16:25:05 +01:00
Linus Rath c09be1cdd3 Merge pull request #1456 from HOLOEYE-JZ/feat/plugin-frame-activity
feat(plugins): input in a plugin frame counts as activity for the idle sign-out
2026-10-31 16:25:03 +01:00
Linus Rath e5ed50dc92 Merge pull request #1467 from HOLOEYE-JZ/feat/plugin-hooks-account-and-composer
feat(plugins): star and after-send hooks name the mail's account
2026-10-31 16:25:00 +01:00
Linus Rath 2ceeef3fb9 Merge pull request #1464 from VasilisNtovolos/fix/xhr-upload-inactivity-timeout
fix: time out stalled XHR uploads after inactivity
2026-10-31 16:24:56 +01:00
Linus Rath 4c53609b1e Merge pull request #1465 from HOLOEYE-JZ/fix/tasks-shared-account-without-calendars
fix(tasks): skip a shared account without calendar access when listing tasks
2026-10-31 16:24:54 +01:00
Linus Rath 1e462fc67e Merge pull request #1436 from shukiv/fix/impersonation-expired-app-passwords
fix: let a mailbox be opened through the handoff more than five times
2026-10-31 16:24:52 +01:00
Linus Rath 2daffe1364 fix: complete the vietnamese strings and register them for plugin controls #1448 2026-10-31 16:24:49 +01:00