You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.7 KiB
85 lines
2.7 KiB
package server |
|
|
|
import ( |
|
"encoding/json" |
|
"io" |
|
"log/slog" |
|
"net/http" |
|
|
|
"github.com/gin-gonic/gin" |
|
|
|
"git/palnet/slack-notifier/internal/gitea" |
|
"git/palnet/slack-notifier/internal/security" |
|
) |
|
|
|
func (s *Server) handleGitea(c *gin.Context) { |
|
raw, err := io.ReadAll(c.Request.Body) |
|
if err != nil { |
|
c.JSON(http.StatusBadRequest, gin.H{"error": "cannot read body"}) |
|
return |
|
} |
|
|
|
// 들어온 원본 페이로드 전체를 기록 (검증 전 — 모든 수신 요청 확인용). |
|
slog.Info("gitea webhook received", |
|
"event", c.GetHeader("X-Gitea-Event"), |
|
"delivery", c.GetHeader("X-Gitea-Delivery"), |
|
"signature", c.GetHeader("X-Gitea-Signature"), |
|
"body", string(raw)) |
|
|
|
if !security.VerifyHMACSHA256(s.cfg.GiteaWebhookSecret, raw, c.GetHeader("X-Gitea-Signature")) { |
|
slog.Warn("gitea signature verification failed") |
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid signature"}) |
|
return |
|
} |
|
|
|
event := c.GetHeader("X-Gitea-Event") |
|
notes, err := gitea.BuildNotifications(event, raw) |
|
if err != nil { |
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"}) |
|
return |
|
} |
|
|
|
sent := s.deliver(c.Request.Context(), notes) |
|
c.JSON(http.StatusOK, gin.H{"event": event, "matched": len(notes), "sent": sent}) |
|
} |
|
|
|
func (s *Server) handleNotion(c *gin.Context) { |
|
raw, err := io.ReadAll(c.Request.Body) |
|
if err != nil { |
|
c.JSON(http.StatusBadRequest, gin.H{"error": "cannot read body"}) |
|
return |
|
} |
|
|
|
// 들어온 원본 페이로드 전체를 기록 (검증 전 — 모든 수신 요청 확인용). |
|
slog.Info("notion webhook received", |
|
"signature", c.GetHeader("X-Notion-Signature"), |
|
"body", string(raw)) |
|
|
|
// 1) Subscription verification handshake: Notion POSTs a verification_token once. |
|
// Capture it from logs and paste into NOTION_VERIFICATION_TOKEN. |
|
var probe struct { |
|
VerificationToken string `json:"verification_token"` |
|
} |
|
_ = json.Unmarshal(raw, &probe) |
|
if probe.VerificationToken != "" { |
|
slog.Warn("notion verification_token received — set this in NOTION_VERIFICATION_TOKEN", "verification_token", probe.VerificationToken) |
|
c.JSON(http.StatusOK, gin.H{"verification_token": probe.VerificationToken}) |
|
return |
|
} |
|
|
|
// 2) Normal events: verify signature against the verification token. |
|
if !security.VerifyHMACSHA256(s.cfg.NotionVerificationToken, raw, c.GetHeader("X-Notion-Signature")) { |
|
slog.Warn("notion signature verification failed") |
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid signature"}) |
|
return |
|
} |
|
|
|
notes, err := s.notion.BuildNotifications(c.Request.Context(), raw) |
|
if err != nil { |
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"}) |
|
return |
|
} |
|
|
|
sent := s.deliver(c.Request.Context(), notes) |
|
c.JSON(http.StatusOK, gin.H{"matched": len(notes), "sent": sent}) |
|
}
|
|
|