Phase 7: monitoring UI — send log, queue, mail.log tail

Three HTMX-polled monitoring screens (spec 7.2.11-13): send log with
server-side domain/application filters and pagination, Postfix queue
(postqueue -p), and a mail.log tail. Fragment endpoints return HTML
snippets, not JSON (spec 7.1); all output is auto-escaped via
html/template (spec 7.6.7).

Adds store.QuerySendLog/CountSendLog/ListApplicationLogins,
postfix.Queue(), and logtail.TailLines (a point-in-time reverse read,
independent of the background follow loop). Verified on the dev server:
gofmt/vet/test green, docker build green, container e2e (filters,
60-row pagination, <script> escaping, real postqueue/mail.log output,
existing Reload button unaffected).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 23:22:08 +03:00
parent ab25d24706
commit cb25923a7b
16 changed files with 558 additions and 16 deletions
+14
View File
@@ -27,6 +27,10 @@ type Config struct {
// to true (spec 7.6.6); it exists as a knob only so the panel can be tested
// over plain HTTP in development, never for production.
CookieSecure bool
// MailLogPath is where Postfix's delivery log lives, read by the mail.log
// monitoring view (spec 7.2.13). It is the same path the log-tailer role
// follows in cmd/panel.
MailLogPath string
}
// Server is the panel HTTP application.
@@ -106,6 +110,16 @@ func (s *Server) Handler() http.Handler {
authed.HandleFunc("POST /applications/{aid}/password", s.handleRegenPassword)
authed.HandleFunc("POST /applications/{aid}/delete", s.handleDeleteApplication)
authed.HandleFunc("POST /reload", s.handleReload)
// Monitoring screens (spec 7.2.11-13): each page and its HTMX polling
// fragment (spec 7.1 — the /rows and /body endpoints return HTML, not JSON).
authed.HandleFunc("GET /sendlog", s.handleSendLog)
authed.HandleFunc("GET /sendlog/rows", s.handleSendLogRows)
authed.HandleFunc("GET /queue", s.handleQueue)
authed.HandleFunc("GET /queue/body", s.handleQueueBody)
authed.HandleFunc("GET /logtail", s.handleLogTail)
authed.HandleFunc("GET /logtail/body", s.handleLogTailBody)
mux.Handle("/", s.requireAuth(authed))
return mux