Replaces the gotoolkit helpers with the standard library and the libraries they wrapped: - gotoolkit/encoding TOML wrapper -> pelletier/go-toml/v2 directly, behind a new smtp2shoutrrr.LoadConfig shared by both commands. - gotoolkit/service + gotoolkit/model.Server -> signal.NotifyContext and a concrete *Server whose Start shuts the listener down gracefully when its context is cancelled. Also drops the unused ANONYMOUS SASL client from cmd/sendmail, bumps alpine, golangci-lint and the CI actions, and adds coverage for config loading and the server lifecycle. Closes FMG-2 Co-authored-by: multica-agent <github@multica.ai>
70 lines
1.6 KiB
Go
70 lines
1.6 KiB
Go
package smtp2shoutrrr
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"github.com/emersion/go-smtp"
|
|
)
|
|
|
|
// shutdownTimeout bounds how long in-flight sessions are given to finish once
|
|
// the server is asked to stop.
|
|
const shutdownTimeout = 10 * time.Second
|
|
|
|
type Server struct {
|
|
backend *smtp.Server
|
|
}
|
|
|
|
// Start accepts connections until ctx is cancelled or Stop is called, then
|
|
// shuts the listener down gracefully. It returns nil on a clean shutdown.
|
|
func (s *Server) Start(ctx context.Context) error {
|
|
slog.Info("Started SMTP server", slog.String("addr", s.backend.Addr))
|
|
|
|
served := make(chan error, 1)
|
|
go func() {
|
|
served <- s.backend.ListenAndServe()
|
|
}()
|
|
|
|
select {
|
|
case err := <-served:
|
|
return err
|
|
case <-ctx.Done():
|
|
}
|
|
|
|
// The incoming context is already cancelled, so the shutdown deadline has
|
|
// to be derived from a live one.
|
|
shutdownCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), shutdownTimeout)
|
|
defer cancel()
|
|
|
|
if err := s.Stop(shutdownCtx); err != nil && !errors.Is(err, smtp.ErrServerClosed) {
|
|
return err
|
|
}
|
|
|
|
return <-served
|
|
}
|
|
|
|
func (s *Server) Stop(ctx context.Context) error {
|
|
slog.Info("Stopping SMTP server")
|
|
return s.backend.Shutdown(ctx)
|
|
}
|
|
|
|
func NewSMTPServer(config *Config) *Server {
|
|
be := &Backend{
|
|
config: config,
|
|
}
|
|
|
|
smtpBackend := smtp.NewServer(be)
|
|
smtpBackend.Addr = fmt.Sprintf(":%d", config.Port)
|
|
smtpBackend.WriteTimeout = 10 * time.Second
|
|
smtpBackend.ReadTimeout = 10 * time.Second
|
|
smtpBackend.MaxMessageBytes = 1024 * 1024
|
|
smtpBackend.MaxRecipients = 50
|
|
smtpBackend.AllowInsecureAuth = true
|
|
|
|
return &Server{
|
|
backend: smtpBackend,
|
|
}
|
|
}
|