package service import ( "bytes" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "net/http" "one-api/dto" "one-api/setting" "time" ) // WebhookPayload webhook 通知的负载数据 type WebhookPayload struct { Type string `json:"type"` Title string `json:"title"` Content string `json:"content"` Values []interface{} `json:"values,omitempty"` Timestamp int64 `json:"timestamp"` } // generateSignature 生成 webhook 签名 func generateSignature(secret string, payload []byte) string { h := hmac.New(sha256.New, []byte(secret)) h.Write(payload) return hex.EncodeToString(h.Sum(nil)) } // SendWebhookNotify 发送 webhook 通知 func SendWebhookNotify(webhookURL string, secret string, data dto.Notify) error { // 处理占位符 content := data.Content for _, value := range data.Values { content = fmt.Sprintf(content, value) } // 构建 webhook 负载 payload := WebhookPayload{ Type: data.Type, Title: data.Title, Content: content, Values: data.Values, Timestamp: time.Now().Unix(), } // 序列化负载 payloadBytes, err := json.Marshal(payload) if err != nil { return fmt.Errorf("failed to marshal webhook payload: %v", err) } // 创建 HTTP 请求 var req *http.Request var resp *http.Response if setting.EnableWorker() { // 构建worker请求数据 workerReq := &WorkerRequest{ URL: webhookURL, Key: setting.WorkerValidKey, Method: http.MethodPost, Headers: map[string]string{ "Content-Type": "application/json", }, Body: payloadBytes, } // 如果有secret,添加签名到headers if secret != "" { signature := generateSignature(secret, payloadBytes) workerReq.Headers["X-Webhook-Signature"] = signature workerReq.Headers["Authorization"] = "Bearer " + secret } resp, err = DoWorkerRequest(workerReq) if err != nil { return fmt.Errorf("failed to send webhook request through worker: %v", err) } defer resp.Body.Close() // 检查响应状态 if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("webhook request failed with status code: %d", resp.StatusCode) } } else { req, err = http.NewRequest(http.MethodPost, webhookURL, bytes.NewBuffer(payloadBytes)) if err != nil { return fmt.Errorf("failed to create webhook request: %v", err) } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 如果有 secret,生成签名 if secret != "" { signature := generateSignature(secret, payloadBytes) req.Header.Set("X-Webhook-Signature", signature) } // 发送请求 client := GetHttpClient() resp, err = client.Do(req) if err != nil { return fmt.Errorf("failed to send webhook request: %v", err) } defer resp.Body.Close() // 检查响应状态 if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("webhook request failed with status code: %d", resp.StatusCode) } } return nil }