second commit
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package webhook
|
||||
|
||||
// TikTok Gin webhook adapter — bridges HTTP requests from the Gin router
|
||||
// to the TikTok channel package's WebhookHandler and IncomingProcessor.
|
||||
// Reference: Facebook webhook adapter pattern (facebook_webhook.go)
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
tiktokchannel "github.com/gochat/gochat/internal/channel/tiktok"
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
applogger "github.com/gochat/gochat/pkg/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TikTokWebhookHandler adapts TikTok webhook handling to Gin HTTP requests.
|
||||
type TikTokWebhookHandler struct {
|
||||
tiktokWebhook *tiktokchannel.WebhookHandler
|
||||
pipeline *tiktokchannel.IncomingProcessor
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewTikTokWebhookHandler creates a Gin-compatible TikTok webhook handler.
|
||||
func NewTikTokWebhookHandler(tiktokWebhook *tiktokchannel.WebhookHandler, pipeline *tiktokchannel.IncomingProcessor, db *gorm.DB) *TikTokWebhookHandler {
|
||||
return &TikTokWebhookHandler{
|
||||
tiktokWebhook: tiktokWebhook,
|
||||
pipeline: pipeline,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleTikTokWebhook processes incoming TikTok webhook HTTP requests.
|
||||
func (h *TikTokWebhookHandler) HandleTikTokWebhook(c *gin.Context) {
|
||||
inboxIDStr := c.Param("inbox_id")
|
||||
inboxID, err := strconv.ParseUint(inboxIDStr, 10, 32)
|
||||
if err != nil {
|
||||
applogger.L().Warnf("TikTok webhook: invalid inbox_id %s", inboxIDStr)
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ignored"})
|
||||
return
|
||||
}
|
||||
|
||||
// Lookup inbox from database
|
||||
inbox, err := h.lookupInbox(uint(inboxID))
|
||||
if err != nil {
|
||||
applogger.L().Warnf("TikTok webhook: inbox lookup failed for id %d: %v", inboxID, err)
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ignored"})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the webhook event using the channel-level handler
|
||||
event, err := h.tiktokWebhook.HandleWebhookRequest(c.Request)
|
||||
if err != nil {
|
||||
applogger.L().Errorf("TikTok webhook: parse request failed for inbox %d: %v", inboxID, err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Process the event via the pipeline
|
||||
if err := h.pipeline.ProcessUpdate(c.Request.Context(), inbox, *event); err != nil {
|
||||
applogger.L().Errorf("TikTok webhook: process event failed for inbox %d: %v", inboxID, err)
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ignored"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"status": "processed"})
|
||||
}
|
||||
|
||||
// HandleTikTokVerification handles TikTok webhook verification (challenge-response).
|
||||
func (h *TikTokWebhookHandler) HandleTikTokVerification(c *gin.Context) {
|
||||
inboxIDStr := c.Param("inbox_id")
|
||||
inboxID, err := strconv.ParseUint(inboxIDStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid inbox_id"})
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to read body"})
|
||||
return
|
||||
}
|
||||
defer c.Request.Body.Close()
|
||||
|
||||
// Parse verification challenge
|
||||
var verifyReq map[string]interface{}
|
||||
if err := json.Unmarshal(body, &verifyReq); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid JSON"})
|
||||
return
|
||||
}
|
||||
|
||||
challenge, _ := verifyReq["challenge"].(string)
|
||||
applogger.L().Infof("TikTok webhook verification: inbox=%d challenge=%s", inboxID, challenge)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"challenge": challenge})
|
||||
}
|
||||
|
||||
// lookupInbox fetches an Inbox record from the database.
|
||||
func (h *TikTokWebhookHandler) lookupInbox(inboxID uint) (*model.Inbox, error) {
|
||||
var inbox model.Inbox
|
||||
if err := h.db.Where("id = ?", inboxID).First(&inbox).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &inbox, nil
|
||||
}
|
||||
|
||||
// parseChannelConfig parses the JSON-encoded ChannelConfig string into a map.
|
||||
func (h *TikTokWebhookHandler) parseChannelConfig(inbox *model.Inbox) map[string]interface{} {
|
||||
if inbox.ChannelConfig == "" {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
var config map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(inbox.ChannelConfig), &config); err != nil {
|
||||
applogger.L().Warnf("TikTok: Failed to parse ChannelConfig for inbox %d: %v", inbox.ID, err)
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
return config
|
||||
}
|
||||
Reference in New Issue
Block a user