Spaces:
Build error
Build error
| package service | |
| import ( | |
| "fmt" | |
| "net/http" | |
| "net/url" | |
| "one-api/common" | |
| "one-api/dto" | |
| "one-api/model" | |
| "one-api/setting/system_setting" | |
| "strings" | |
| ) | |
| func NotifyRootUser(t string, subject string, content string) { | |
| user := model.GetRootUser().ToBaseUser() | |
| err := NotifyUser(user.Id, user.Email, user.GetSetting(), dto.NewNotify(t, subject, content, nil)) | |
| if err != nil { | |
| common.SysLog(fmt.Sprintf("failed to notify root user: %s", err.Error())) | |
| } | |
| } | |
| func NotifyUser(userId int, userEmail string, userSetting dto.UserSetting, data dto.Notify) error { | |
| notifyType := userSetting.NotifyType | |
| if notifyType == "" { | |
| notifyType = dto.NotifyTypeEmail | |
| } | |
| // Check notification limit | |
| canSend, err := CheckNotificationLimit(userId, data.Type) | |
| if err != nil { | |
| common.SysLog(fmt.Sprintf("failed to check notification limit: %s", err.Error())) | |
| return err | |
| } | |
| if !canSend { | |
| return fmt.Errorf("notification limit exceeded for user %d with type %s", userId, notifyType) | |
| } | |
| switch notifyType { | |
| case dto.NotifyTypeEmail: | |
| // check setting email | |
| userEmail = userSetting.NotificationEmail | |
| if userEmail == "" { | |
| common.SysLog(fmt.Sprintf("user %d has no email, skip sending email", userId)) | |
| return nil | |
| } | |
| return sendEmailNotify(userEmail, data) | |
| case dto.NotifyTypeWebhook: | |
| webhookURLStr := userSetting.WebhookUrl | |
| if webhookURLStr == "" { | |
| common.SysLog(fmt.Sprintf("user %d has no webhook url, skip sending webhook", userId)) | |
| return nil | |
| } | |
| // 获取 webhook secret | |
| webhookSecret := userSetting.WebhookSecret | |
| return SendWebhookNotify(webhookURLStr, webhookSecret, data) | |
| case dto.NotifyTypeBark: | |
| barkURL := userSetting.BarkUrl | |
| if barkURL == "" { | |
| common.SysLog(fmt.Sprintf("user %d has no bark url, skip sending bark", userId)) | |
| return nil | |
| } | |
| return sendBarkNotify(barkURL, data) | |
| } | |
| return nil | |
| } | |
| func sendEmailNotify(userEmail string, data dto.Notify) error { | |
| // make email content | |
| content := data.Content | |
| // 处理占位符 | |
| for _, value := range data.Values { | |
| content = strings.Replace(content, dto.ContentValueParam, fmt.Sprintf("%v", value), 1) | |
| } | |
| return common.SendEmail(data.Title, userEmail, content) | |
| } | |
| func sendBarkNotify(barkURL string, data dto.Notify) error { | |
| // 处理占位符 | |
| content := data.Content | |
| for _, value := range data.Values { | |
| content = strings.Replace(content, dto.ContentValueParam, fmt.Sprintf("%v", value), 1) | |
| } | |
| // 替换模板变量 | |
| finalURL := strings.ReplaceAll(barkURL, "{{title}}", url.QueryEscape(data.Title)) | |
| finalURL = strings.ReplaceAll(finalURL, "{{content}}", url.QueryEscape(content)) | |
| // 发送GET请求到Bark | |
| var req *http.Request | |
| var resp *http.Response | |
| var err error | |
| if system_setting.EnableWorker() { | |
| // 使用worker发送请求 | |
| workerReq := &WorkerRequest{ | |
| URL: finalURL, | |
| Key: system_setting.WorkerValidKey, | |
| Method: http.MethodGet, | |
| Headers: map[string]string{ | |
| "User-Agent": "OneAPI-Bark-Notify/1.0", | |
| }, | |
| } | |
| resp, err = DoWorkerRequest(workerReq) | |
| if err != nil { | |
| return fmt.Errorf("failed to send bark request through worker: %v", err) | |
| } | |
| defer resp.Body.Close() | |
| // 检查响应状态 | |
| if resp.StatusCode < 200 || resp.StatusCode >= 300 { | |
| return fmt.Errorf("bark request failed with status code: %d", resp.StatusCode) | |
| } | |
| } else { | |
| // SSRF防护:验证Bark URL(非Worker模式) | |
| fetchSetting := system_setting.GetFetchSetting() | |
| if err := common.ValidateURLWithFetchSetting(finalURL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.DomainFilterMode, fetchSetting.IpFilterMode, fetchSetting.DomainList, fetchSetting.IpList, fetchSetting.AllowedPorts, fetchSetting.ApplyIPFilterForDomain); err != nil { | |
| return fmt.Errorf("request reject: %v", err) | |
| } | |
| // 直接发送请求 | |
| req, err = http.NewRequest(http.MethodGet, finalURL, nil) | |
| if err != nil { | |
| return fmt.Errorf("failed to create bark request: %v", err) | |
| } | |
| // 设置User-Agent | |
| req.Header.Set("User-Agent", "OneAPI-Bark-Notify/1.0") | |
| // 发送请求 | |
| client := GetHttpClient() | |
| resp, err = client.Do(req) | |
| if err != nil { | |
| return fmt.Errorf("failed to send bark request: %v", err) | |
| } | |
| defer resp.Body.Close() | |
| // 检查响应状态 | |
| if resp.StatusCode < 200 || resp.StatusCode >= 300 { | |
| return fmt.Errorf("bark request failed with status code: %d", resp.StatusCode) | |
| } | |
| } | |
| return nil | |
| } | |