package server import ( "context" "embed" "html/template" "net/http" "github.com/gin-gonic/gin" "git/palnet/slack-notifier/internal/config" "git/palnet/slack-notifier/internal/logging" "git/palnet/slack-notifier/internal/mapping" "git/palnet/slack-notifier/internal/notify" "git/palnet/slack-notifier/internal/notion" "git/palnet/slack-notifier/internal/slack" ) //go:embed templates/*.html var templatesFS embed.FS // Server wires together config, the Slack client, the mapping store and Notion client. type Server struct { cfg config.Config slack *slack.Client mappings *mapping.Store notion *notion.Client } func New(cfg config.Config, sl *slack.Client, m *mapping.Store, nt *notion.Client) *Server { return &Server{cfg: cfg, slack: sl, mappings: m, notion: nt} } // Router builds the Gin engine with all routes registered. func (s *Server) Router() *gin.Engine { if s.cfg.IsProduction() { gin.SetMode(gin.ReleaseMode) } r := gin.New() r.Use(logging.GinMiddleware(), gin.Recovery()) _ = r.SetTrustedProxies(nil) // we don't sit behind a trusted proxy by default tmpl := template.Must(template.ParseFS(templatesFS, "templates/*.html")) r.SetHTMLTemplate(tmpl) r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "ok"}) }) r.POST("/webhooks/gitea", s.handleGitea) r.POST("/webhooks/notion", s.handleNotion) admin := r.Group("/admin") { admin.GET("/mappings", s.adminMappings) admin.POST("/mappings", s.adminAddMapping) admin.DELETE("/mappings/:source", s.adminDeleteMapping) } return r } // deliver resolves each notification's recipient via the mapping store and DMs them. // Returns how many were sent successfully. func (s *Server) deliver(ctx context.Context, notes []notify.Notification) int { sent := 0 for _, n := range notes { email := s.mappings.Resolve(n.Email, n.Login) if s.slack.SendDM(ctx, email, n.Text, n.Blocks, s.cfg.DefaultSlackChannel) { sent++ } } return sent }