package main import ( "context" "errors" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "internal-mail-skill/internal/api" "internal-mail-skill/internal/config" "internal-mail-skill/internal/db" "internal-mail-skill/internal/pop3" "internal-mail-skill/internal/service" mailsmtp "internal-mail-skill/internal/smtp" ) func main() { if err := run(); err != nil { slog.Error("server stopped", "error", err) os.Exit(1) } } func run() error { cfgPath := os.Getenv("CONFIG_PATH") if cfgPath == "" { cfgPath = ".env" } cfg, err := config.Load(cfgPath) if err != nil { return err } if err := ensureDataDirs(cfg); err != nil { return err } store, err := db.Open(cfg.Data.DatabasePath) if err != nil { return err } defer store.Close() ctx := context.Background() if err := store.Migrate(ctx); err != nil { return err } popFetcher := pop3.NewFetcher(cfg.POP3) smtpSender := mailsmtp.NewSender(cfg.SMTP) mailService := service.NewWithOptions(store, popFetcher, smtpSender, cfg.AccountID, service.Options{ RawDir: cfg.Data.RawMailDir, AttachmentDir: cfg.Data.AttachmentDir, FromAddress: cfg.SMTP.FromAddress, FromName: cfg.SMTP.FromName, }) apiServer := api.New(cfg, store, mailService) httpServer := &http.Server{ Addr: api.ListenAddr(cfg), Handler: apiServer.Handler(), ReadHeaderTimeout: 10 * time.Second, } errCh := make(chan error, 1) go func() { slog.Info("internal mail skill listening", "addr", httpServer.Addr, "account_id", cfg.AccountID) errCh <- httpServer.ListenAndServe() }() stopCh := make(chan os.Signal, 1) signal.Notify(stopCh, syscall.SIGINT, syscall.SIGTERM) select { case sig := <-stopCh: slog.Info("shutdown requested", "signal", sig.String()) shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() return httpServer.Shutdown(shutdownCtx) case err := <-errCh: if errors.Is(err, http.ErrServerClosed) { return nil } return err } } func ensureDataDirs(cfg *config.Config) error { for _, dir := range []string{cfg.Data.DataDir, cfg.Data.RawMailDir, cfg.Data.AttachmentDir} { if err := os.MkdirAll(dir, 0750); err != nil { return err } } return nil }