Add webhook logging, pagination, middleware, migrations, and prod hardening
- Webhook event logging repo and subscription webhook idempotency - Pagination helper (echohelpers) with cursor/offset support - Request ID and structured logging middleware - Push client improvements (FCM HTTP v1, better error handling) - Task model version column, business constraint migrations, targeted indexes - Expanded categorization chain tests - Email service and config hardening - CI workflow updates, .gitignore additions, .env.example updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
54
internal/repositories/webhook_event_repo.go
Normal file
54
internal/repositories/webhook_event_repo.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// WebhookEvent represents a processed webhook event for deduplication
|
||||
type WebhookEvent struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
EventID string `gorm:"column:event_id;size:255;not null;uniqueIndex:idx_provider_event_id"`
|
||||
Provider string `gorm:"column:provider;size:20;not null;uniqueIndex:idx_provider_event_id"`
|
||||
EventType string `gorm:"column:event_type;size:100;not null"`
|
||||
ProcessedAt time.Time `gorm:"column:processed_at;autoCreateTime"`
|
||||
PayloadHash string `gorm:"column:payload_hash;size:64"`
|
||||
}
|
||||
|
||||
func (WebhookEvent) TableName() string {
|
||||
return "webhook_event_log"
|
||||
}
|
||||
|
||||
// WebhookEventRepository handles webhook event deduplication
|
||||
type WebhookEventRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewWebhookEventRepository creates a new webhook event repository
|
||||
func NewWebhookEventRepository(db *gorm.DB) *WebhookEventRepository {
|
||||
return &WebhookEventRepository{db: db}
|
||||
}
|
||||
|
||||
// HasProcessed checks if an event has already been processed
|
||||
func (r *WebhookEventRepository) HasProcessed(provider, eventID string) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&WebhookEvent{}).
|
||||
Where("provider = ? AND event_id = ?", provider, eventID).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// RecordEvent records a processed webhook event
|
||||
func (r *WebhookEventRepository) RecordEvent(provider, eventID, eventType, payloadHash string) error {
|
||||
event := &WebhookEvent{
|
||||
EventID: eventID,
|
||||
Provider: provider,
|
||||
EventType: eventType,
|
||||
PayloadHash: payloadHash,
|
||||
}
|
||||
return r.db.Create(event).Error
|
||||
}
|
||||
Reference in New Issue
Block a user