75f3dd141c
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CI and Release / lint-frontend (push) Has been cancelled
CI and Release / dockerfile-scan (push) Has been cancelled
CI and Release / test-frontend (push) Has been cancelled
CI and Release / lint-verification-agent (push) Has been cancelled
CI and Release / test-verification-agent (push) Has been cancelled
CI and Release / e2e-verification-agent (push) Has been cancelled
CI and Release / test-backend (push) Has been cancelled
CI and Release / build-dev-image (push) Has been cancelled
CI and Release / push-dev-image (push) Has been cancelled
CI and Release / build-image (push) Has been cancelled
CI and Release / build-verification-image (push) Has been cancelled
CI and Release / determine-version (push) Has been cancelled
CI and Release / push-image (push) Has been cancelled
CI and Release / push-verification-image (12) (push) Has been cancelled
CI and Release / lint-backend (push) Has been cancelled
CI and Release / push-verification-image (17) (push) Has been cancelled
CI and Release / push-verification-image (18) (push) Has been cancelled
CI and Release / release (push) Has been cancelled
CI and Release / publish-helm-chart (push) Has been cancelled
CI and Release / push-verification-image (13) (push) Has been cancelled
CI and Release / push-verification-image (14) (push) Has been cancelled
CI and Release / push-verification-image (15) (push) Has been cancelled
CI and Release / push-verification-image (16) (push) Has been cancelled
136 lines
3.0 KiB
Go
136 lines
3.0 KiB
Go
package teams_notifier
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"databasus-backend/internal/util/encryption"
|
|
)
|
|
|
|
type TeamsNotifier struct {
|
|
NotifierID uuid.UUID `gorm:"type:uuid;primaryKey;column:notifier_id" json:"notifierId"`
|
|
WebhookURL string `gorm:"type:text;not null;column:power_automate_url" json:"powerAutomateUrl"`
|
|
}
|
|
|
|
func (TeamsNotifier) TableName() string {
|
|
return "teams_notifiers"
|
|
}
|
|
|
|
func (n *TeamsNotifier) Validate(encryptor encryption.FieldEncryptor) error {
|
|
if n.WebhookURL == "" {
|
|
return errors.New("webhook_url is required")
|
|
}
|
|
|
|
webhookURL, err := encryptor.Decrypt(n.WebhookURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decrypt webhook URL: %w", err)
|
|
}
|
|
|
|
u, err := url.Parse(webhookURL)
|
|
if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
|
|
return errors.New("invalid webhook_url")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type cardAttachment struct {
|
|
ContentType string `json:"contentType"`
|
|
Content any `json:"content"`
|
|
}
|
|
|
|
type payload struct {
|
|
Title string `json:"title"`
|
|
Text string `json:"text"`
|
|
Attachments []cardAttachment `json:"attachments,omitzero"`
|
|
}
|
|
|
|
func (n *TeamsNotifier) Send(
|
|
encryptor encryption.FieldEncryptor,
|
|
logger *slog.Logger,
|
|
heading, message string,
|
|
) error {
|
|
if err := n.Validate(encryptor); err != nil {
|
|
return err
|
|
}
|
|
|
|
webhookURL, err := encryptor.Decrypt(n.WebhookURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decrypt webhook URL: %w", err)
|
|
}
|
|
|
|
card := map[string]any{
|
|
"type": "AdaptiveCard",
|
|
"version": "1.4",
|
|
"body": []any{
|
|
map[string]any{
|
|
"type": "TextBlock",
|
|
"size": "Medium",
|
|
"weight": "Bolder",
|
|
"text": heading,
|
|
},
|
|
map[string]any{"type": "TextBlock", "wrap": true, "text": message},
|
|
},
|
|
}
|
|
|
|
p := payload{
|
|
Title: heading,
|
|
Text: message,
|
|
Attachments: []cardAttachment{
|
|
{ContentType: "application/vnd.microsoft.card.adaptive", Content: card},
|
|
},
|
|
}
|
|
|
|
body, _ := json.Marshal(p)
|
|
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, webhookURL, bytes.NewReader(body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
if closeErr := resp.Body.Close(); closeErr != nil {
|
|
logger.Error("failed to close response body", "error", closeErr)
|
|
}
|
|
}()
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
return fmt.Errorf("teams webhook returned status %d", resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (n *TeamsNotifier) HideSensitiveData() {
|
|
n.WebhookURL = ""
|
|
}
|
|
|
|
func (n *TeamsNotifier) Update(incoming *TeamsNotifier) {
|
|
if incoming.WebhookURL != "" {
|
|
n.WebhookURL = incoming.WebhookURL
|
|
}
|
|
}
|
|
|
|
func (n *TeamsNotifier) EncryptSensitiveData(encryptor encryption.FieldEncryptor) error {
|
|
if n.WebhookURL != "" {
|
|
encrypted, err := encryptor.Encrypt(n.WebhookURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to encrypt webhook URL: %w", err)
|
|
}
|
|
n.WebhookURL = encrypted
|
|
}
|
|
return nil
|
|
}
|