82 lines
2.6 KiB
Go
82 lines
2.6 KiB
Go
// Command server runs the notification microservice: a pure RabbitMQ
|
|
// consumer with no gRPC server and no database of its own — it sends
|
|
// email in reaction to events published by iam. Replaces the email-
|
|
// sending half of internal/handlers/auth.go + internal/mail/mail.go.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"enkly/pkg/envkit"
|
|
"enkly/pkg/eventbus"
|
|
"enkly/pkg/mailkit"
|
|
"enkly/services/notification/internal/application"
|
|
)
|
|
|
|
func main() {
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
|
|
|
if err := envkit.LoadDotEnv(".env"); err != nil {
|
|
logger.Error("load .env", "err", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
var (
|
|
rabbitURL = envkit.String("ENKLY_RABBITMQ_URL", "amqp://enkly:enkly@localhost:5672/")
|
|
healthAddr = envkit.String("ENKLY_NOTIFICATION_HEALTH_ADDR", ":9007")
|
|
|
|
smtpHost = envkit.String("ENKLY_SMTP_HOST", "smtp.gmail.com")
|
|
smtpPort = envkit.String("ENKLY_SMTP_PORT", "587")
|
|
smtpUsername = envkit.String("ENKLY_SMTP_USERNAME", "")
|
|
smtpPassword = envkit.String("ENKLY_SMTP_PASSWORD", "")
|
|
smtpFrom = envkit.String("ENKLY_SMTP_FROM", "")
|
|
)
|
|
|
|
mailer := mailkit.New(mailkit.Config{
|
|
Host: smtpHost, Port: smtpPort, Username: smtpUsername, Password: smtpPassword, From: smtpFrom,
|
|
})
|
|
if !mailer.Configured() {
|
|
logger.Warn("mail is not configured — verification codes and reset links will be logged, not emailed",
|
|
"hint", "set ENKLY_SMTP_USERNAME and ENKLY_SMTP_PASSWORD (a Gmail App Password) in .env")
|
|
}
|
|
|
|
bus, err := eventbus.Connect(rabbitURL, logger)
|
|
if err != nil {
|
|
logger.Error("connect rabbitmq", "err", err)
|
|
os.Exit(1)
|
|
}
|
|
defer bus.Close()
|
|
|
|
svc := &application.Service{Mailer: mailer, Logger: logger}
|
|
|
|
// A minimal HTTP health endpoint — this service has no gRPC API
|
|
// (nothing calls it synchronously), but docker-compose and an
|
|
// orchestrator still want something to poll.
|
|
go func() {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
|
|
lis, err := net.Listen("tcp", healthAddr)
|
|
if err != nil {
|
|
logger.Error("health listen", "err", err)
|
|
return
|
|
}
|
|
_ = http.Serve(lis, mux)
|
|
}()
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
|
defer stop()
|
|
|
|
logger.Info("notification service consuming", "queue", eventbus.NotificationQueueBindings.Queue, "mail_configured", mailer.Configured())
|
|
if err := bus.Consume(ctx, eventbus.NotificationQueueBindings, svc.HandleEvent); err != nil && ctx.Err() == nil {
|
|
logger.Error("consume", "err", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Info("shutting down")
|
|
}
|