- Config: try ENV_FILE, .env, ../.env for loading; trim trailing slash from BaseURL - Log BASE_URL at server startup for verification - .env.example: document BASE_URL - Tasks, projects, tags, migrations and related API/handlers Made-with: Cursor
74 lines
1.7 KiB
Go
74 lines
1.7 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/calendarapi/internal/models"
|
|
"github.com/calendarapi/internal/repository"
|
|
"github.com/calendarapi/internal/utils"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type TaskWebhookService struct {
|
|
queries *repository.Queries
|
|
}
|
|
|
|
func NewTaskWebhookService(queries *repository.Queries) *TaskWebhookService {
|
|
return &TaskWebhookService{queries: queries}
|
|
}
|
|
|
|
func (s *TaskWebhookService) Deliver(ctx context.Context, ownerID uuid.UUID, event string, task *models.Task) {
|
|
webhooks, err := s.queries.ListTaskWebhooksByOwner(ctx, utils.ToPgUUID(ownerID))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
payload := map[string]interface{}{
|
|
"event": event,
|
|
"task": task,
|
|
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
for _, wh := range webhooks {
|
|
if !s.webhookSubscribedTo(wh.Events, event) {
|
|
continue
|
|
}
|
|
secret := ""
|
|
if wh.Secret.Valid {
|
|
secret = wh.Secret.String
|
|
}
|
|
go s.postWebhook(wh.Url, body, []byte(secret))
|
|
}
|
|
}
|
|
|
|
func (s *TaskWebhookService) webhookSubscribedTo(eventsJSON []byte, event string) bool {
|
|
var events []string
|
|
if err := json.Unmarshal(eventsJSON, &events); err != nil {
|
|
return false
|
|
}
|
|
for _, e := range events {
|
|
if e == event {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *TaskWebhookService) postWebhook(url string, body []byte, secret []byte) {
|
|
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
|
|
if err != nil {
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
if len(secret) > 0 {
|
|
req.Header.Set("X-Webhook-Signature", string(secret))
|
|
}
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
_, _ = client.Do(req)
|
|
}
|