diff --git a/api/core/config.go b/api/core/config.go index 4d4699f4..a9032d9c 100644 --- a/api/core/config.go +++ b/api/core/config.go @@ -148,14 +148,24 @@ func LoadSystemConfig(db *gorm.DB) *types.SystemConfig { logger.Error("load payment config error: ", err) } + // 加载文本审查配置 + var moderationConfig types.ModerationConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyModeration).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &moderationConfig) + if err != nil { + logger.Error("load moderation config error: ", err) + } + return &types.SystemConfig{ - Base: baseConfig, - License: license, - SMS: smsConfig, - OSS: ossConfig, - SMTP: smtpConfig, - Payment: paymentConfig, - Captcha: captchaConfig, - WxLogin: wxLoginConfig, + Base: baseConfig, + License: license, + SMS: smsConfig, + OSS: ossConfig, + SMTP: smtpConfig, + Payment: paymentConfig, + Captcha: captchaConfig, + WxLogin: wxLoginConfig, + Moderation: moderationConfig, } } diff --git a/api/core/types/config.go b/api/core/types/config.go index 10edddbb..9c903b8c 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -100,29 +100,31 @@ type BaseConfig struct { } type SystemConfig struct { - Base BaseConfig - Payment PaymentConfig - OSS OSSConfig - SMS SMSConfig - SMTP SmtpConfig - Captcha CaptchaConfig - WxLogin WxLoginConfig - Jimeng JimengConfig - License License + Base BaseConfig + Payment PaymentConfig + OSS OSSConfig + SMS SMSConfig + SMTP SmtpConfig + Captcha CaptchaConfig + WxLogin WxLoginConfig + Jimeng JimengConfig + License License + Moderation ModerationConfig } // 配置键名常量 const ( - ConfigKeySystem = "system" - ConfigKeyNotice = "notice" - ConfigKeyAgreement = "agreement" - ConfigKeyPrivacy = "privacy" - ConfigKeyMarkMap = "mark_map" - ConfigKeyCaptcha = "captcha" - ConfigKeyWxLogin = "wx_login" - ConfigKeyLicense = "license" - ConfigKeySms = "sms" - ConfigKeySmtp = "smtp" - ConfigKeyOss = "oss" - ConfigKeyPayment = "payment" + ConfigKeySystem = "system" + ConfigKeyNotice = "notice" + ConfigKeyAgreement = "agreement" + ConfigKeyPrivacy = "privacy" + ConfigKeyMarkMap = "mark_map" + ConfigKeyCaptcha = "captcha" + ConfigKeyWxLogin = "wx_login" + ConfigKeyLicense = "license" + ConfigKeySms = "sms" + ConfigKeySmtp = "smtp" + ConfigKeyOss = "oss" + ConfigKeyPayment = "payment" + ConfigKeyModeration = "moderation" ) diff --git a/api/core/types/moderation.go b/api/core/types/moderation.go new file mode 100644 index 00000000..835c9224 --- /dev/null +++ b/api/core/types/moderation.go @@ -0,0 +1,55 @@ +package types + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +// 文本审查 +type ModerationConfig struct { + Enable bool `json:"enable"` // 是否启用文本审查 + Active string `json:"active"` + GuidePrompt string `json:"guide_prompt"` // 模型引导提示词 + Gitee ModerationGiteeConfig `json:"gitee"` + Baidu ModerationBaiduConfig `json:"baidu"` + Tencent ModerationTencentConfig `json:"tencent"` +} + +const ( + ModerationGitee = "gitee" + ModerationBaidu = "baidu" + ModerationTencent = "tencent" +) + +// GiteeAI 文本审查配置 +type ModerationGiteeConfig struct { + ApiKey string `json:"api_key"` + Model string `json:"model"` // 文本审核模型 +} + +// 百度文本审查配置 +type ModerationBaiduConfig struct { + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` +} + +// 腾讯云文本审查配置 +type ModerationTencentConfig struct { + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` +} + +type ModerationResult struct { + Flagged bool `json:"flagged"` + Categories map[string]bool `json:"categories"` + CategoryScores map[string]float64 `json:"category_scores"` +} + +var ModerationCategories = map[string]string{ + "politic": "内容涉及人物、事件或敏感的政治观点", + "porn": "明确的色情内容", + "insult": "具有侮辱、攻击性语言、人身攻击或冒犯性表达", + "violence": "包含暴力、血腥、攻击行为或煽动暴力的言论", +} diff --git a/api/handler/admin/config_handler.go b/api/handler/admin/config_handler.go index baa6daab..48cfd34c 100644 --- a/api/handler/admin/config_handler.go +++ b/api/handler/admin/config_handler.go @@ -8,11 +8,13 @@ package admin // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "fmt" "geekai/core" "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/service" + "geekai/service/moderation" "geekai/service/oss" "geekai/service/payment" "geekai/service/sms" @@ -26,21 +28,17 @@ import ( type ConfigHandler struct { handler.BaseHandler - licenseService *service.LicenseService - sysConfig *types.SystemConfig - alipayService *payment.AlipayService - wxpayService *payment.WxPayService - epayService *payment.EPayService - smsAliyun *sms.AliYunSmsService - smsBao *sms.BaoSmsService - smsManager *sms.SmsManager - localOss *oss.LocalStorage - qiniuOss *oss.QiNiuOss - aliyunOss *oss.AliYunOss - minioOss *oss.MiniOss - smtpService *service.SmtpService - captchaService *service.CaptchaService - wxLoginService *service.WxLoginService + licenseService *service.LicenseService + sysConfig *types.SystemConfig + alipayService *payment.AlipayService + wxpayService *payment.WxPayService + epayService *payment.EPayService + smsManager *sms.SmsManager + uploaderManager *oss.UploaderManager + smtpService *service.SmtpService + captchaService *service.CaptchaService + wxLoginService *service.WxLoginService + moderationManager *moderation.ServiceManager } func NewConfigHandler( @@ -51,34 +49,26 @@ func NewConfigHandler( alipayService *payment.AlipayService, wxpayService *payment.WxPayService, epayService *payment.EPayService, - smsAliyun *sms.AliYunSmsService, - smsBao *sms.BaoSmsService, smsManager *sms.SmsManager, - localOss *oss.LocalStorage, - qiniuOss *oss.QiNiuOss, - aliyunOss *oss.AliYunOss, - minioOss *oss.MiniOss, + uploaderManager *oss.UploaderManager, smtpService *service.SmtpService, captchaService *service.CaptchaService, wxLoginService *service.WxLoginService, + moderationManager *moderation.ServiceManager, ) *ConfigHandler { return &ConfigHandler{ - BaseHandler: handler.BaseHandler{App: app, DB: db}, - licenseService: licenseService, - sysConfig: sysConfig, - alipayService: alipayService, - wxpayService: wxpayService, - epayService: epayService, - smsAliyun: smsAliyun, - smsBao: smsBao, - smsManager: smsManager, - localOss: localOss, - qiniuOss: qiniuOss, - aliyunOss: aliyunOss, - minioOss: minioOss, - smtpService: smtpService, - captchaService: captchaService, - wxLoginService: wxLoginService, + BaseHandler: handler.BaseHandler{App: app, DB: db}, + licenseService: licenseService, + sysConfig: sysConfig, + alipayService: alipayService, + wxpayService: wxpayService, + epayService: epayService, + smsManager: smsManager, + uploaderManager: uploaderManager, + moderationManager: moderationManager, + smtpService: smtpService, + captchaService: captchaService, + wxLoginService: wxLoginService, } } @@ -101,6 +91,8 @@ func (h *ConfigHandler) RegisterRoutes() { rg.POST("update/sms", h.UpdateSms) rg.POST("update/oss", h.UpdateOss) rg.POST("update/smtp", h.UpdateStmp) + rg.POST("update/moderation", h.UpdateModeration) + rg.POST("moderation/test", h.TestModeration) rg.GET("get", h.Get) rg.POST("license/active", h.Active) rg.GET("license/get", h.GetLicense) @@ -280,14 +272,7 @@ func (h *ConfigHandler) UpdatePayment(c *gin.Context) { return } - var config model.Config - oldData := types.PaymentConfig{} - err := h.DB.Where("name", types.ConfigKeyPayment).First(&config).Error - if err == nil { - utils.JsonDecode(config.Value, &oldData) - } - - err = h.Update(types.ConfigKeyPayment, data) + err := h.Update(types.ConfigKeyPayment, data) if err != nil { resp.ERROR(c, err.Error()) return @@ -324,32 +309,14 @@ func (h *ConfigHandler) UpdateSms(c *gin.Context) { return } - var config model.Config - oldData := types.SMSConfig{} - err := h.DB.Where("name", types.ConfigKeySms).First(&config).Error - if err == nil { - utils.JsonDecode(config.Value, &oldData) - } - - err = h.Update(types.ConfigKeySms, data) + err := h.Update(types.ConfigKeySms, data) if err != nil { resp.ERROR(c, err.Error()) return } // 更新服务配置 - switch data.Active { - case sms.Ali: - err = h.smsAliyun.UpdateConfig(&data.Ali) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - case sms.Bao: - h.smsBao.UpdateConfig(&data.Bao) - } - - h.smsManager.SetActive(data.Active) + h.smsManager.UpdateConfig(data) resp.SUCCESS(c, data) } @@ -362,39 +329,14 @@ func (h *ConfigHandler) UpdateOss(c *gin.Context) { return } - var config model.Config - oldData := types.OSSConfig{} - err := h.DB.Where("name", types.ConfigKeyOss).First(&config).Error - if err == nil { - utils.JsonDecode(config.Value, &oldData) - } - - err = h.Update("oss", data) + err := h.Update(types.ConfigKeyOss, data) if err != nil { resp.ERROR(c, err.Error()) return } // 更新服务配置 - switch data.Active { - case oss.Local: - h.localOss.UpdateConfig(&data.Local) - case oss.QiNiu: - h.qiniuOss.UpdateConfig(&data.QiNiu) - case oss.AliYun: - err := h.aliyunOss.UpdateConfig(&data.AliYun) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - case oss.Minio: - err := h.minioOss.UpdateConfig(&data.Minio) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - } - + h.uploaderManager.UpdateConfig(data) h.sysConfig.OSS = data resp.SUCCESS(c, data) @@ -408,24 +350,14 @@ func (h *ConfigHandler) UpdateStmp(c *gin.Context) { return } - var config model.Config - oldData := types.SmtpConfig{} - err := h.DB.Where("name", types.ConfigKeySmtp).First(&config).Error - if err == nil { - utils.JsonDecode(config.Value, &oldData) - } - - err = h.Update(types.ConfigKeySmtp, data) + err := h.Update(types.ConfigKeySmtp, data) if err != nil { resp.ERROR(c, err.Error()) return } - // 配置发生改变时更新服务配置 - if !data.Equal(&oldData) { - h.smtpService.UpdateConfig(&data) - } - + // 更新服务配置 + h.smtpService.UpdateConfig(&data) h.sysConfig.SMTP = data resp.SUCCESS(c, data) } @@ -519,4 +451,89 @@ func (h *ConfigHandler) GetLicense(c *gin.Context) { resp.SUCCESS(c, license) } -// +// UpdateModeration 更新文本审查配置 +func (h *ConfigHandler) UpdateModeration(c *gin.Context) { + var data types.ModerationConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyModeration, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + h.moderationManager.UpdateConfig(data) + h.sysConfig.Moderation = data + + resp.SUCCESS(c, data) +} + +// 测试结果类型,用于前端显示 +type ModerationTestResult struct { + IsAbnormal bool `json:"isAbnormal"` + Details []ModerationTestDetail `json:"details"` +} + +type ModerationTestDetail struct { + Category string `json:"category"` + Description string `json:"description"` + Confidence string `json:"confidence"` + IsCategory bool `json:"isCategory"` +} + +// TestModeration 测试文本审查服务 +func (h *ConfigHandler) TestModeration(c *gin.Context) { + var data struct { + Text string `json:"text"` + Service string `json:"service"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if data.Text == "" { + resp.ERROR(c, "测试文本不能为空") + return + } + + // 检查是否启用了文本审查 + if !h.sysConfig.Moderation.Enable { + resp.ERROR(c, "文本审查服务未启用") + return + } + + // 获取当前激活的审核服务 + service := h.moderationManager.GetService() + // 执行文本审核 + result, err := service.Moderate(data.Text) + if err != nil { + resp.ERROR(c, "审核服务调用失败: "+err.Error()) + return + } + + // 转换为前端需要的格式 + testResult := ModerationTestResult{ + IsAbnormal: result.Flagged, + Details: make([]ModerationTestDetail, 0), + } + + // 构建详细信息 + for category, description := range types.ModerationCategories { + score := result.CategoryScores[category] + isCategory := result.Categories[category] + + testResult.Details = append(testResult.Details, ModerationTestDetail{ + Category: category, + Description: description, + Confidence: fmt.Sprintf("%.2f", score), + IsCategory: isCategory, + }) + } + + resp.SUCCESS(c, testResult) +} diff --git a/api/main.go b/api/main.go index 6f213700..7c308f76 100644 --- a/api/main.go +++ b/api/main.go @@ -19,6 +19,7 @@ import ( "geekai/service/dalle" "geekai/service/jimeng" "geekai/service/mj" + "geekai/service/moderation" "geekai/service/oss" "geekai/service/payment" "geekai/service/sd" @@ -241,6 +242,12 @@ func main() { // 用户服务 fx.Provide(service.NewUserService), + // 文本审查服务 + fx.Provide(moderation.NewGiteeAIModeration), + fx.Provide(moderation.NewBaiduAIModeration), + fx.Provide(moderation.NewTencentAIModeration), + fx.Provide(moderation.NewServiceManager), + // 注册路由 fx.Invoke(func(s *core.AppServer, h *handler.ChatAppHandler) { h.RegisterRoutes() diff --git a/api/service/moderation/baidu_moderation.go b/api/service/moderation/baidu_moderation.go new file mode 100644 index 00000000..b213d469 --- /dev/null +++ b/api/service/moderation/baidu_moderation.go @@ -0,0 +1,33 @@ +package moderation + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "errors" + "geekai/core/types" +) + +type BaiduAIModeration struct { + config types.ModerationBaiduConfig +} + +func NewBaiduAIModeration(sysConfig *types.SystemConfig) *BaiduAIModeration { + return &BaiduAIModeration{ + config: sysConfig.Moderation.Baidu, + } +} + +func (s *BaiduAIModeration) UpdateConfig(config types.ModerationBaiduConfig) { + s.config = config +} + +func (s *BaiduAIModeration) Moderate(text string) (types.ModerationResult, error) { + return types.ModerationResult{}, errors.New("not implemented") +} + +var _ Service = (*BaiduAIModeration)(nil) diff --git a/api/service/moderation/gitee_moderation.go b/api/service/moderation/gitee_moderation.go new file mode 100644 index 00000000..8e7ed14c --- /dev/null +++ b/api/service/moderation/gitee_moderation.go @@ -0,0 +1,58 @@ +package moderation + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "errors" + "geekai/core/types" + + "github.com/imroc/req/v3" +) + +type GiteeAIModeration struct { + config types.ModerationGiteeConfig + apiURL string +} + +func NewGiteeAIModeration(sysConfig *types.SystemConfig) *GiteeAIModeration { + return &GiteeAIModeration{ + config: sysConfig.Moderation.Gitee, + apiURL: "https://ai.gitee.com/v1/moderations", + } +} + +func (s *GiteeAIModeration) UpdateConfig(config types.ModerationGiteeConfig) { + s.config = config +} + +type GiteeAIModerationResult struct { + ID string `json:"id"` + Model string `json:"model"` + Results []types.ModerationResult `json:"results"` +} + +func (s *GiteeAIModeration) Moderate(text string) (types.ModerationResult, error) { + + body := map[string]any{ + "input": text, + "model": s.config.Model, + } + var res GiteeAIModerationResult + r, err := req.C().R().SetHeader("Authorization", "Bearer "+s.config.ApiKey).SetBody(body).SetSuccessResult(&res).Post(s.apiURL) + if err != nil { + return types.ModerationResult{}, err + } + + if r.IsErrorState() { + return types.ModerationResult{}, errors.New(r.String()) + } + + return res.Results[0], nil +} + +var _ Service = (*GiteeAIModeration)(nil) diff --git a/api/service/moderation/moderation_manager.go b/api/service/moderation/moderation_manager.go new file mode 100644 index 00000000..25125a93 --- /dev/null +++ b/api/service/moderation/moderation_manager.go @@ -0,0 +1,58 @@ +package moderation + +import ( + "geekai/core/types" + + logger2 "geekai/logger" +) + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +var logger = logger2.GetLogger() + +type Service interface { + Moderate(text string) (types.ModerationResult, error) +} + +type ServiceManager struct { + gitee *GiteeAIModeration + baidu *BaiduAIModeration + tencent *TencentAIModeration + active string +} + +func NewServiceManager(gitee *GiteeAIModeration, baidu *BaiduAIModeration, tencent *TencentAIModeration) *ServiceManager { + return &ServiceManager{ + gitee: gitee, + baidu: baidu, + tencent: tencent, + } +} + +func (s *ServiceManager) GetService() Service { + switch s.active { + case types.ModerationBaidu: + return s.baidu + case types.ModerationTencent: + return s.tencent + default: + return s.gitee + } +} + +func (s *ServiceManager) UpdateConfig(config types.ModerationConfig) { + switch config.Active { + case types.ModerationGitee: + s.gitee.UpdateConfig(config.Gitee) + case types.ModerationBaidu: + s.baidu.UpdateConfig(config.Baidu) + case types.ModerationTencent: + s.tencent.UpdateConfig(config.Tencent) + } + s.active = config.Active +} diff --git a/api/service/moderation/tencent_moderation.go b/api/service/moderation/tencent_moderation.go new file mode 100644 index 00000000..75bee579 --- /dev/null +++ b/api/service/moderation/tencent_moderation.go @@ -0,0 +1,33 @@ +package moderation + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "errors" + "geekai/core/types" +) + +type TencentAIModeration struct { + config types.ModerationTencentConfig +} + +func NewTencentAIModeration(sysConfig *types.SystemConfig) *TencentAIModeration { + return &TencentAIModeration{ + config: sysConfig.Moderation.Tencent, + } +} + +func (s *TencentAIModeration) UpdateConfig(config types.ModerationTencentConfig) { + s.config = config +} + +func (s *TencentAIModeration) Moderate(text string) (types.ModerationResult, error) { + return types.ModerationResult{}, errors.New("not implemented") +} + +var _ Service = (*TencentAIModeration)(nil) diff --git a/api/service/oss/aliyun_oss.go b/api/service/oss/aliyun_oss.go index 20ff31b8..8c9b6602 100644 --- a/api/service/oss/aliyun_oss.go +++ b/api/service/oss/aliyun_oss.go @@ -23,7 +23,7 @@ import ( ) type AliYunOss struct { - config *types.AliYunOssConfig + config types.AliYunOssConfig bucket *oss.Bucket proxyURL string } @@ -33,7 +33,7 @@ func NewAliYunOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) (*A proxyURL: appConfig.ProxyURL, } if sysConfig.OSS.Active == AliYun { - err := s.UpdateConfig(&sysConfig.OSS.AliYun) + err := s.UpdateConfig(sysConfig.OSS.AliYun) if err != nil { logger.Errorf("阿里云OSS初始化失败: %v", err) } @@ -42,7 +42,7 @@ func NewAliYunOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) (*A } -func (s *AliYunOss) UpdateConfig(config *types.AliYunOssConfig) error { +func (s *AliYunOss) UpdateConfig(config types.AliYunOssConfig) error { client, err := oss.New(config.Endpoint, config.AccessKey, config.AccessSecret) if err != nil { return err diff --git a/api/service/oss/localstorage.go b/api/service/oss/localstorage.go index b2337e8c..5b3bef82 100644 --- a/api/service/oss/localstorage.go +++ b/api/service/oss/localstorage.go @@ -21,18 +21,18 @@ import ( ) type LocalStorage struct { - config *types.LocalStorageConfig + config types.LocalStorageConfig proxyURL string } func NewLocalStorage(sysConfig *types.SystemConfig, appConfig *types.AppConfig) *LocalStorage { return &LocalStorage{ - config: &sysConfig.OSS.Local, + config: sysConfig.OSS.Local, proxyURL: appConfig.ProxyURL, } } -func (s *LocalStorage) UpdateConfig(config *types.LocalStorageConfig) { +func (s *LocalStorage) UpdateConfig(config types.LocalStorageConfig) { s.config = config } diff --git a/api/service/oss/minio_oss.go b/api/service/oss/minio_oss.go index f7465b78..0dcebf21 100644 --- a/api/service/oss/minio_oss.go +++ b/api/service/oss/minio_oss.go @@ -24,7 +24,7 @@ import ( ) type MiniOss struct { - config *types.MiniOssConfig + config types.MiniOssConfig client *minio.Client proxyURL string } @@ -33,7 +33,7 @@ func NewMiniOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) (*Min s := &MiniOss{proxyURL: appConfig.ProxyURL} if sysConfig.OSS.Active == Minio { - err := s.UpdateConfig(&sysConfig.OSS.Minio) + err := s.UpdateConfig(sysConfig.OSS.Minio) if err != nil { logger.Errorf("MinioOSS初始化失败: %v", err) } @@ -41,7 +41,7 @@ func NewMiniOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) (*Min return s, nil } -func (s *MiniOss) UpdateConfig(config *types.MiniOssConfig) error { +func (s *MiniOss) UpdateConfig(config types.MiniOssConfig) error { minioClient, err := minio.New(config.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4(config.AccessKey, config.AccessSecret, ""), Secure: config.UseSSL, diff --git a/api/service/oss/qiniu_oss.go b/api/service/oss/qiniu_oss.go index 3623c648..c3ded24d 100644 --- a/api/service/oss/qiniu_oss.go +++ b/api/service/oss/qiniu_oss.go @@ -25,7 +25,7 @@ import ( ) type QiNiuOss struct { - config *types.QiNiuOssConfig + config types.QiNiuOssConfig mac *qbox.Mac putPolicy storage.PutPolicy uploader *storage.FormUploader @@ -38,12 +38,12 @@ func NewQiNiuOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) *QiN proxyURL: appConfig.ProxyURL, } if sysConfig.OSS.Active == QiNiu { - s.UpdateConfig(&sysConfig.OSS.QiNiu) + s.UpdateConfig(sysConfig.OSS.QiNiu) } return s } -func (s *QiNiuOss) UpdateConfig(config *types.QiNiuOssConfig) { +func (s *QiNiuOss) UpdateConfig(config types.QiNiuOssConfig) { zone, ok := storage.GetRegionByID(storage.RegionID(config.Zone)) if !ok { zone = storage.ZoneHuanan diff --git a/api/service/oss/uploader_manager.go b/api/service/oss/uploader_manager.go index 64c05b85..2f81c937 100644 --- a/api/service/oss/uploader_manager.go +++ b/api/service/oss/uploader_manager.go @@ -21,7 +21,7 @@ type UploaderManager struct { aliyun *AliYunOss mini *MiniOss qiniu *QiNiuOss - config *types.OSSConfig + active string } func NewUploaderManager(sysConfig *types.SystemConfig, local *LocalStorage, aliyun *AliYunOss, mini *MiniOss, qiniu *QiNiuOss) (*UploaderManager, error) { @@ -31,7 +31,7 @@ func NewUploaderManager(sysConfig *types.SystemConfig, local *LocalStorage, aliy sysConfig.OSS.Active = strings.ToLower(sysConfig.OSS.Active) return &UploaderManager{ - config: &sysConfig.OSS, + active: sysConfig.OSS.Active, local: local, aliyun: aliyun, mini: mini, @@ -40,7 +40,7 @@ func NewUploaderManager(sysConfig *types.SystemConfig, local *LocalStorage, aliy } func (m *UploaderManager) GetUploadHandler() Uploader { - switch m.config.Active { + switch m.active { case Local: return m.local case AliYun: @@ -52,3 +52,17 @@ func (m *UploaderManager) GetUploadHandler() Uploader { } return m.local } + +func (m *UploaderManager) UpdateConfig(config types.OSSConfig) { + switch config.Active { + case Local: + m.local.UpdateConfig(config.Local) + case AliYun: + m.aliyun.UpdateConfig(config.AliYun) + case Minio: + m.mini.UpdateConfig(config.Minio) + case QiNiu: + m.qiniu.UpdateConfig(config.QiNiu) + } + m.active = config.Active +} diff --git a/api/service/sms/aliyun.go b/api/service/sms/aliyun.go index 858263dd..db9d7c4e 100644 --- a/api/service/sms/aliyun.go +++ b/api/service/sms/aliyun.go @@ -15,14 +15,14 @@ import ( ) type AliYunSmsService struct { - config *types.SmsConfigAli + config types.SmsConfigAli client *dysmsapi.Client domain string zoneId string } func NewAliYunSmsService(sysConfig *types.SystemConfig) (*AliYunSmsService, error) { - config := &sysConfig.SMS.Ali + config := sysConfig.SMS.Ali domain := "dysmsapi.aliyuncs.com" zoneId := "cn-hangzhou" @@ -40,7 +40,7 @@ func NewAliYunSmsService(sysConfig *types.SystemConfig) (*AliYunSmsService, erro return &s, nil } -func (s *AliYunSmsService) UpdateConfig(config *types.SmsConfigAli) error { +func (s *AliYunSmsService) UpdateConfig(config types.SmsConfigAli) error { client, err := dysmsapi.NewClientWithAccessKey( s.zoneId, config.AccessKey, diff --git a/api/service/sms/bao.go b/api/service/sms/bao.go index 19a1537c..a220180d 100644 --- a/api/service/sms/bao.go +++ b/api/service/sms/bao.go @@ -19,18 +19,18 @@ import ( ) type BaoSmsService struct { - config *types.SmsConfigBao + config types.SmsConfigBao domain string } func NewBaoSmsService(sysConfig *types.SystemConfig) *BaoSmsService { return &BaoSmsService{ - config: &sysConfig.SMS.Bao, + config: sysConfig.SMS.Bao, domain: "api.smsbao.com", } } -func (s *BaoSmsService) UpdateConfig(config *types.SmsConfigBao) { +func (s *BaoSmsService) UpdateConfig(config types.SmsConfigBao) { s.config = config } diff --git a/api/service/sms/service_manager.go b/api/service/sms/sms_manager.go similarity index 76% rename from api/service/sms/service_manager.go rename to api/service/sms/sms_manager.go index 3e2ac652..32921827 100644 --- a/api/service/sms/service_manager.go +++ b/api/service/sms/sms_manager.go @@ -30,12 +30,25 @@ func NewSmsManager(sysConfig *types.SystemConfig, aliyun *AliYunSmsService, bao } func (m *SmsManager) GetService() Service { - if m.active == Ali { + switch m.active { + case Ali: return m.aliyun + case Bao: + return m.bao } - return m.bao + return nil } func (m *SmsManager) SetActive(active string) { m.active = active } + +func (m *SmsManager) UpdateConfig(config types.SMSConfig) { + switch config.Active { + case Ali: + m.aliyun.UpdateConfig(config.Ali) + case Bao: + m.bao.UpdateConfig(config.Bao) + } + m.active = config.Active +} diff --git a/web/src/assets/iconfont/iconfont.css b/web/src/assets/iconfont/iconfont.css index 72ee9c84..f8aff2db 100644 --- a/web/src/assets/iconfont/iconfont.css +++ b/web/src/assets/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4125778 */ - src: url('iconfont.woff2?t=1754626711656') format('woff2'), - url('iconfont.woff?t=1754626711656') format('woff'), - url('iconfont.ttf?t=1754626711656') format('truetype'); + src: url('iconfont.woff2?t=1756631578371') format('woff2'), + url('iconfont.woff?t=1756631578371') format('woff'), + url('iconfont.ttf?t=1756631578371') format('truetype'); } .iconfont { @@ -13,6 +13,18 @@ -moz-osx-font-smoothing: grayscale; } +.icon-tencent:before { + content: "\e655"; +} + +.icon-baidu:before { + content: "\e656"; +} + +.icon-moderation:before { + content: "\e6c6"; +} + .icon-back-bold:before { content: "\e654"; } diff --git a/web/src/assets/iconfont/iconfont.js b/web/src/assets/iconfont/iconfont.js index 53fdf836..0f79bc47 100644 --- a/web/src/assets/iconfont/iconfont.js +++ b/web/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,v(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function v(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(v,50)}p()}})(window); \ No newline at end of file +window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,v(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function v(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(v,50)}p()}})(window); \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.json b/web/src/assets/iconfont/iconfont.json index 7dce4c65..d1f01f31 100644 --- a/web/src/assets/iconfont/iconfont.json +++ b/web/src/assets/iconfont/iconfont.json @@ -5,6 +5,27 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "3547761", + "name": "tencent", + "font_class": "tencent", + "unicode": "e655", + "unicode_decimal": 58965 + }, + { + "icon_id": "26267540", + "name": "baidu", + "font_class": "baidu", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "31975090", + "name": "Content Moderation", + "font_class": "moderation", + "unicode": "e6c6", + "unicode_decimal": 59078 + }, { "icon_id": "27025075", "name": "返回", diff --git a/web/src/assets/iconfont/iconfont.ttf b/web/src/assets/iconfont/iconfont.ttf index f8155a03..7834ecc8 100644 Binary files a/web/src/assets/iconfont/iconfont.ttf and b/web/src/assets/iconfont/iconfont.ttf differ diff --git a/web/src/assets/iconfont/iconfont.woff b/web/src/assets/iconfont/iconfont.woff index 6d1cb8d9..8ed592be 100644 Binary files a/web/src/assets/iconfont/iconfont.woff and b/web/src/assets/iconfont/iconfont.woff differ diff --git a/web/src/assets/iconfont/iconfont.woff2 b/web/src/assets/iconfont/iconfont.woff2 index 546aad3f..9d77c06d 100644 Binary files a/web/src/assets/iconfont/iconfont.woff2 and b/web/src/assets/iconfont/iconfont.woff2 differ diff --git a/web/src/components/ChatPrompt.vue b/web/src/components/ChatPrompt.vue index 9ab8a2c7..6fe20889 100644 --- a/web/src/components/ChatPrompt.vue +++ b/web/src/components/ChatPrompt.vue @@ -36,7 +36,14 @@ {{ dateFormat(data.created_at) }} - tokens: {{ finalTokens }} + + + + + @@ -81,7 +88,14 @@ {{ dateFormat(data.created_at) }} - + + + + + @@ -90,6 +104,7 @@