gitea-github-proxy/api/webhook_manager.go

134 lines
3.6 KiB
Go
Raw Normal View History

package api
import (
"bytes"
giteaTypes "code.gitea.io/gitea/modules/structs"
"encoding/json"
"git.lumeweb.com/LumeWeb/gitea-github-proxy/config"
"git.lumeweb.com/LumeWeb/gitea-github-proxy/db/model"
"go.uber.org/zap"
"gorm.io/gorm"
"io"
"net/http"
"regexp"
"strings"
)
type WebhookManager struct {
config *config.Config
db *gorm.DB
logger *zap.Logger
}
type IncomingWebhookData struct {
Headers http.Header
}
func NewWebhookManager(cfg *config.Config, db *gorm.DB, logger *zap.Logger) *WebhookManager {
return &WebhookManager{config: cfg, db: db, logger: logger}
}
func (whm *WebhookManager) HandlePullRequest(request *giteaTypes.PullRequestPayload, r *http.Request) {
ghEvent := convertPullRequestEvent(request)
githubAction := translatePrAction(request.Action)
r.Header.Set("X-GitHub-Event", githubAction)
whm.sendWebhooks(ghEvent, r)
}
func (whm *WebhookManager) sendWebhooks(request interface{}, r *http.Request) {
var apps []model.Apps
result := whm.db.Find(&apps)
if result.Error != nil {
whm.logger.Error("Failed to query apps", zap.Error(result.Error))
return
}
for _, app := range apps {
go func(app model.Apps) {
payloadBytes, err := json.Marshal(request)
if err != nil {
whm.logger.Error("Failed to marshal payload", zap.Error(err))
return
}
var rawMap map[string]interface{}
err = json.Unmarshal(payloadBytes, &rawMap)
if err != nil {
whm.logger.Error("Failed to unmarshal payload", zap.Error(err))
return
}
rawMap["installation"] = struct {
ID uint `json:"id"`
}{ID: app.ID}
2024-02-12 04:25:43 +00:00
payloadBytes, err = json.Marshal(rawMap)
if err != nil {
whm.logger.Error("Failed to marshal payload", zap.Error(err))
return
}
req, err := http.NewRequest("POST", app.WebhookUrl, bytes.NewBuffer(payloadBytes))
if err != nil {
whm.logger.Error("Failed to create request", zap.Error(err), zap.String("url", app.WebhookUrl))
return
}
req.Header.Set("Content-Type", "application/json")
for key, values := range r.Header {
for _, value := range values {
req.Header.Add(key, value)
}
}
payload := toNormalizedJson(payloadBytes)
signature := generatePayloadSignature(payload, app.WebhookSecret)
2024-02-11 11:20:18 +00:00
req.Header.Set("X-Hub-Signature-256", signature)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
whm.logger.Error("Failed to send webhook", zap.Error(err), zap.String("url", app.WebhookUrl))
return
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
whm.logger.Error("Failed to close response body", zap.Error(err))
}
}(resp.Body)
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
whm.logger.Info("Webhook sent successfully", zap.String("url", app.WebhookUrl))
} else {
whm.logger.Error("Webhook failed", zap.String("url", app.WebhookUrl), zap.Int("status", resp.StatusCode))
}
}(app)
}
}
/*
* Ported from https://github.com/octokit/webhooks.js/blob/984f3f51e077dbc7637aa55406c3bb114dbb7f82/src/to-normalized-json-string.ts
*/
func toNormalizedJson(payload []byte) []byte {
jsonString := string(payload)
// Regex to find Unicode escape sequences
re := regexp.MustCompile(`\\u([\da-fA-F]{4})`)
// Function to convert found sequences to uppercase
replaceFunc := func(match string) string {
parts := strings.Split(match, "\\u")
// Convert the Unicode sequence part to uppercase
return parts[0] + "\\u" + strings.ToUpper(parts[1])
}
// Replace the matches in the jsonString
normalizedString := re.ReplaceAllStringFunc(jsonString, replaceFunc)
return []byte(normalizedString)
}