fix(logtail): keep mail.log in /data and reconcile stuck rows (v1.x closure phase 2)
test / test (push) Has been cancelled

Move the delivery log from the ephemeral /var/log to /data/log/mail.log so
the lines that resolve a queued send-log row survive a container recreate.
postlogd writes it as postfix, the panel reads it through the selfpost group
(dir 2750, file 0640, normalised every start); backups exclude log/.

Close the residual gap with a queue sweep: rows queued for over two minutes
whose id postqueue -p no longer lists are marked bounced. The sweep waits
until the tailer has read the log to its end and does nothing when the queue
cannot be listed, so a message in flight is never touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mixeme
2026-08-08 11:35:27 +03:00
parent c839f68c57
commit 9d6d89c79d
25 changed files with 549 additions and 72 deletions
+24 -15
View File
@@ -36,7 +36,7 @@
## Общий чек-лист прогресса
- [x] **Фаза 1** — адаптивный опрос мониторинга
- [ ] **Фаза 2** — mail.log в `/data` + postqueue reconcile
- [x] **Фаза 2** — mail.log в `/data` + postqueue reconcile
- [ ] **Фаза 3** — docs: development.md, README, удаление планов и `docs/archive/`
- [ ] **Фаза 4** — релизный коммит `1.0.0` (по явной команде)
- [ ] **Фаза 5** — tag `v1.0.0` + push (по явной команде)
@@ -76,26 +76,35 @@
### 2.1 `mail.log` → `/data/log/mail.log`
- [ ] `build/postfix-config.sh``maillog_file`
- [ ] `build/logrotate-mail.conf` — путь
- [ ] `build/entrypoint.sh``mkdir`, права (postfix write, panel read)
- [ ] `cmd/panel/main.go` — default `MAIL_LOG`
- [ ] `test/e2e/mail_helpers.go` — путь в контейнере
- [ ] `guide.md``MAIL_LOG`, рост диска `/data/log`
- [ ] `envdoc_test` — при изменении env
- [x] `build/postfix-config.sh``maillog_file` из `MAIL_LOG`
- [x] `build/logrotate-mail.conf` — путь + `create 0640 postfix selfpost`
- [x] `build/entrypoint.sh``mkdir`, права (`2750 postfix:selfpost`, файл `0640`),
исключение `/data/log` из общего `chown` на `panel`
- [x] `cmd/panel/main.go` — default `MAIL_LOG`
- [x] `internal/backup` — исключить `log/` из архива (+ тест)
- [x] `test/e2e/mail_helpers.go` — путь в контейнере
- [x] `guide.md``MAIL_LOG`, System log, бэкап `./data`; README — рост диска
- [x] `envdoc_test``MAIL_LOG` в `buildScriptKeys`
### 2.2 Postqueue reconcile
- [ ] `internal/postfix` — парсер queue-id из `postqueue -p` + тест
- [ ] `internal/store``ListQueuedOlderThan(grace)`
- [ ] `internal/logtail` — periodic sweep, grace ~2 min → `bounced`
- [ ] Тесты reconcile
- [x] `internal/postfix` `QueueIDs` / парсер queue-id из `postqueue -p` + тест
- [x] `internal/store``ListQueuedOlderThan(cutoff)`
- [x] `internal/logtail` — periodic sweep (5 min), grace 2 min → `bounced`,
старт только после того, как tailer дочитал лог до конца
- [x] Тесты reconcile
### 2.3 Документация и риски
- [ ] `architecture.md``/data/log`, reconcile; убрать gap «container recreate»
- [ ] `security.md` снять принятый риск «вечный queued»
- [ ] CHANGELOG `[Unreleased]`
- [x] `architecture.md``/data/log`, reconcile; gap «container recreate» убран
- [x] `security.md` — риск «вечный queued» снят; на его месте — ложный `bounced`
и права на лог
- [x] CHANGELOG `[Unreleased]`
**Стенд (остаток, для оператора):** Docker на машине разработки недоступен —
сборка образа и старт контейнера не проверены. Проверить при выкате: `/data/log`
создаётся с нужными правами, панель читает `mail.log`, logrotate проворачивает
файл, `postqueue -p` читается из-под `panel`.
---