Files
Nukumizu/internal/controller/pipes/webhook.go
T
NanamiAdmin 56cc3485a7 feat(netproxy): add network proxy module to provide unified network proxy connection.
feat(internal/controller): make email, ntft, webhook, qq_napcat able to use network proxy.
2026-08-12 22:53:23 +08:00

156 lines
3.9 KiB
Go

package pipes
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
"nukumizu-backend/config"
"nukumizu-backend/internal/netproxy"
"nukumizu-backend/internal/node"
"nukumizu-backend/internal/template"
"nukumizu-backend/postLog"
)
// WebhookController handles notifications via generic HTTP webhooks.
type WebhookController struct {
cfg config.WebhookConfig
httpClient *http.Client
}
// NewWebhookController creates a new Webhook controller.
func NewWebhookController(cfg config.WebhookConfig) *WebhookController {
return &WebhookController{
cfg: cfg,
httpClient: netproxy.HTTPClient(cfg.NetworkUseProxy, 10*time.Second),
}
}
// Name returns the controller name.
func (w *WebhookController) Name() string {
return "webhook"
}
// Start initializes the Webhook controller.
func (w *WebhookController) Start() error {
if !w.cfg.Enabled {
postLog.Info("Webhook controller is disabled")
return nil
}
postLog.Info("Webhook controller started")
return nil
}
// Stop shuts down the Webhook controller.
func (w *WebhookController) Stop() {
postLog.Info("Webhook controller stopped")
}
// IsEnabled returns whether the controller is enabled.
func (w *WebhookController) IsEnabled() bool {
return w.cfg.Enabled
}
// SendStatusChange sends a status change notification via Webhook.
func (w *WebhookController) SendStatusChange(change node.StatusChange) error {
if !w.cfg.Enabled {
return nil
}
cfg := config.GetConfig()
params := template.BuildParamsFromStatusChange(change)
message := template.Render(cfg.ControllerMessage.ServerStatusChanged, params)
payload := map[string]interface{}{
"event": change.Event,
"serverName": change.Name,
"serverUUID": change.UUID,
"message": message,
"time": params.Time,
}
return w.send(payload)
}
// SendServerList sends the server list via Webhook.
func (w *WebhookController) SendServerList(onlineServers, offlineServers string) error {
if !w.cfg.Enabled {
return nil
}
cfg := config.GetConfig()
params := template.BuildParamsFromServerList()
message := template.Render(cfg.ControllerMessage.ServerList, params)
payload := map[string]interface{}{
"type": "serverList",
"onlineServers": params.OnlineServers,
"offlineServers": params.OfflineServers,
"message": message,
"time": params.Time,
}
return w.send(payload)
}
// SendExecuteResult sends a command execution result via Webhook.
func (w *WebhookController) SendExecuteResult(serverName, serverUUID, command, result string) error {
if !w.cfg.Enabled {
return nil
}
cfg := config.GetConfig()
params := template.BuildParamsFromExecResult(serverName, serverUUID, command, result)
message := template.Render(cfg.ControllerMessage.ServerExecuteResult, params)
payload := map[string]interface{}{
"type": "executeResult",
"serverName": serverName,
"serverUUID": serverUUID,
"command": command,
"result": params.Result,
"message": message,
"time": params.Time,
}
return w.send(payload)
}
func (w *WebhookController) send(payload map[string]interface{}) error {
method := w.cfg.Method
if method == "" {
method = "POST"
}
bodyJSON, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("failed to marshal webhook payload: %w", err)
}
req, err := http.NewRequest(method, w.cfg.URL, bytes.NewReader(bodyJSON))
if err != nil {
return fmt.Errorf("failed to create webhook request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
for key, value := range w.cfg.Headers {
req.Header.Set(key, value)
}
resp, err := w.httpClient.Do(req)
if err != nil {
postLog.Warning("Failed to send webhook: " + err.Error())
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
postLog.Warning(fmt.Sprintf("Webhook returned status %d", resp.StatusCode))
}
postLog.Debug("Webhook notification sent to " + w.cfg.URL)
return nil
}