feat(PROJ-28): SMTP-Out Relay — DB-Konfiguration + Admin-Tab

- smtpoutconfig.Store: AES-256-GCM verschlüsseltes Passwort in DB (id=1 Singleton)
- Mailer: Reload() für Runtime-Konfigurationswechsel (sync.RWMutex)
- API: GET/PUT/DELETE /api/admin/smtp-out + POST /api/admin/smtp-out/test
- Admin-Tab: Host, Port, User, Passwort, TLS-Switch, From, Test-Button, Status-Badge
- Startup: Lädt DB-Konfiguration und aktiviert Mailer ohne Restart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-03-31 22:36:57 +02:00
parent 7371a73b3e
commit c1a9004720
7 changed files with 698 additions and 8 deletions
+13
View File
@@ -20,6 +20,7 @@ import (
ldapcfg "github.com/archivmail/internal/ldapconfig"
"github.com/archivmail/internal/mailer"
pop3store "github.com/archivmail/internal/pop3"
"github.com/archivmail/internal/smtpoutconfig"
"github.com/archivmail/internal/smtpd"
"github.com/archivmail/internal/storage"
"github.com/archivmail/internal/tenantstore"
@@ -86,6 +87,7 @@ type Server struct {
mailer *mailer.Mailer
tokenStore *tokenstore.Store
fqdn string // from server.fqdn config (PROJ-28)
smtpOutStore *smtpoutconfig.Store
}
// SetSMTPDaemon wires the SMTP daemon into the API server after construction.
@@ -137,6 +139,11 @@ func (s *Server) SetFQDN(fqdn string) {
s.fqdn = fqdn
}
// SetSMTPOutStore wires the SMTP-Out config store into the API server.
func (s *Server) SetSMTPOutStore(store *smtpoutconfig.Store) {
s.smtpOutStore = store
}
// New creates and wires up a new API server.
func New(
cfg config.APIConfig,
@@ -209,6 +216,12 @@ func (s *Server) routes() {
s.mux.HandleFunc("GET /api/admin/retention", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleGetRetention)))
s.mux.HandleFunc("PUT /api/admin/tenant/{id}/retention", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleSetTenantRetention)))
// SMTP-Out Relay Konfiguration — superadmin only
s.mux.HandleFunc("GET /api/admin/smtp-out", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleGetSMTPOut)))
s.mux.HandleFunc("PUT /api/admin/smtp-out", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleSaveSMTPOut)))
s.mux.HandleFunc("DELETE /api/admin/smtp-out", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleDeleteSMTPOut)))
s.mux.HandleFunc("POST /api/admin/smtp-out/test", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleTestSMTPOut)))
// PROJ-29: Quotas — superadmin only
s.mux.HandleFunc("GET /api/admin/quotas", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleGetAllTenantUsage)))
s.mux.HandleFunc("GET /api/admin/tenant/{id}/quota", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleGetTenantUsage)))