mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-04-24 12:04:31 +08:00
更新配置代码
This commit is contained in:
@@ -27,6 +27,10 @@ func NewCaptchaService(captchaConfig types.CaptchaConfig) *CaptchaService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CaptchaService) UpdateConfig(config types.CaptchaConfig) {
|
||||
s.config = config
|
||||
}
|
||||
|
||||
func (s *CaptchaService) Get() (interface{}, error) {
|
||||
url := fmt.Sprintf("%s/api/captcha/get", types.GeekAPIURL)
|
||||
var res types.BizVo
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
package service
|
||||
|
||||
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// 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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"geekai/store/model"
|
||||
"sync"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ConfigService 统一的配置访问、缓存与通知服务
|
||||
type ConfigService struct {
|
||||
db *gorm.DB
|
||||
rdb *redis.Client
|
||||
mu sync.RWMutex
|
||||
cache map[string]json.RawMessage
|
||||
watchers map[string][]chan struct{}
|
||||
}
|
||||
|
||||
func NewConfigService(db *gorm.DB, rdb *redis.Client) *ConfigService {
|
||||
s := &ConfigService{
|
||||
db: db,
|
||||
rdb: rdb,
|
||||
cache: make(map[string]json.RawMessage),
|
||||
watchers: make(map[string][]chan struct{}),
|
||||
}
|
||||
go s.subscribe()
|
||||
return s
|
||||
}
|
||||
|
||||
// Get 以原始 JSON 获取配置(带本地缓存)
|
||||
func (s *ConfigService) Get(key string) (json.RawMessage, error) {
|
||||
s.mu.RLock()
|
||||
if v, ok := s.cache[key]; ok {
|
||||
s.mu.RUnlock()
|
||||
return v, nil
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
|
||||
var cfg model.Config
|
||||
if err := s.db.Where("name", key).First(&cfg).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.cache[key] = json.RawMessage(cfg.Value)
|
||||
s.mu.Unlock()
|
||||
return json.RawMessage(cfg.Value), nil
|
||||
}
|
||||
|
||||
// GetInto 将配置解析进传入结构体
|
||||
func (s *ConfigService) GetInto(key string, dest interface{}) error {
|
||||
data, err := s.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, dest)
|
||||
}
|
||||
|
||||
// Set 设置配置并写入数据库,同时触发通知
|
||||
func (s *ConfigService) Set(key string, config json.RawMessage) error {
|
||||
value := string(config)
|
||||
cfg := model.Config{Name: key, Value: value}
|
||||
if err := s.db.Where("name", key).FirstOrCreate(&cfg, model.Config{Name: key}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if cfg.Id > 0 {
|
||||
cfg.Value = value
|
||||
if err := s.db.Updates(&cfg).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.cache[key] = json.RawMessage(value)
|
||||
s.mu.Unlock()
|
||||
s.notifyLocal(key)
|
||||
s.publish(key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Watch 返回一个通道,当指定 key 发生变化时收到事件
|
||||
func (s *ConfigService) Watch(key string) <-chan struct{} {
|
||||
ch := make(chan struct{}, 1)
|
||||
s.mu.Lock()
|
||||
s.watchers[key] = append(s.watchers[key], ch)
|
||||
s.mu.Unlock()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *ConfigService) notifyLocal(key string) {
|
||||
s.mu.RLock()
|
||||
list := s.watchers[key]
|
||||
s.mu.RUnlock()
|
||||
for _, ch := range list {
|
||||
select { // 非阻塞通知
|
||||
case ch <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 通过 Redis 发布配置变更,便于多实例同步
|
||||
func (s *ConfigService) publish(key string) {
|
||||
if s.rdb == nil {
|
||||
return
|
||||
}
|
||||
channel := "config:changed"
|
||||
if err := s.rdb.Publish(context.Background(), channel, key).Err(); err != nil {
|
||||
logger.Warnf("publish config change failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConfigService) subscribe() {
|
||||
if s.rdb == nil {
|
||||
return
|
||||
}
|
||||
channel := "config:changed"
|
||||
sub := s.rdb.Subscribe(context.Background(), channel)
|
||||
for msg := range sub.Channel() {
|
||||
key := msg.Payload
|
||||
logger.Infof("config changed: %s", key)
|
||||
// 失效本地缓存并本地广播
|
||||
s.mu.Lock()
|
||||
delete(s.cache, key)
|
||||
s.mu.Unlock()
|
||||
s.notifyLocal(key)
|
||||
}
|
||||
}
|
||||
|
||||
// Test 预留统一测试入口,根据 key 执行连通性检查
|
||||
func (s *ConfigService) Test(key string) (string, error) {
|
||||
// TODO: 实现各配置类型的测试逻辑
|
||||
return fmt.Sprintf("%s ok", key), nil
|
||||
}
|
||||
@@ -53,7 +53,7 @@ type License struct {
|
||||
}
|
||||
|
||||
// ActiveLicense 激活 License
|
||||
func (s *LicenseService) ActiveLicense(license string, machineId string) error {
|
||||
func (s *LicenseService) ActiveLicense(license string) error {
|
||||
var res struct {
|
||||
Code types.BizCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
@@ -61,7 +61,7 @@ func (s *LicenseService) ActiveLicense(license string, machineId string) error {
|
||||
}
|
||||
apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/active")
|
||||
response, err := req.C().R().
|
||||
SetBody(map[string]string{"license": license, "machine_id": machineId}).
|
||||
SetBody(map[string]string{"license": license, "machine_id": s.machineId}).
|
||||
SetSuccessResult(&res).Post(apiURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("发送激活请求失败: %v", err)
|
||||
@@ -81,7 +81,7 @@ func (s *LicenseService) ActiveLicense(license string, machineId string) error {
|
||||
|
||||
s.license = &types.License{
|
||||
Key: license,
|
||||
MachineId: machineId,
|
||||
MachineId: s.machineId,
|
||||
Configs: res.Data.Configs,
|
||||
ExpiredAt: res.Data.ExpiredAt,
|
||||
IsActive: true,
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
)
|
||||
|
||||
type QinNiuOss struct {
|
||||
type QiNiuOss struct {
|
||||
config *types.QiNiuOssConfig
|
||||
mac *qbox.Mac
|
||||
putPolicy storage.PutPolicy
|
||||
@@ -33,8 +33,8 @@ type QinNiuOss struct {
|
||||
proxyURL string
|
||||
}
|
||||
|
||||
func NewQiNiuOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) *QinNiuOss {
|
||||
s := &QinNiuOss{
|
||||
func NewQiNiuOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) *QiNiuOss {
|
||||
s := &QiNiuOss{
|
||||
proxyURL: appConfig.ProxyURL,
|
||||
}
|
||||
if sysConfig.OSS.Active == QiNiu {
|
||||
@@ -43,7 +43,7 @@ func NewQiNiuOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) *Qin
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *QinNiuOss) UpdateConfig(config *types.QiNiuOssConfig) {
|
||||
func (s *QiNiuOss) UpdateConfig(config *types.QiNiuOssConfig) {
|
||||
zone, ok := storage.GetRegionByID(storage.RegionID(config.Zone))
|
||||
if !ok {
|
||||
zone = storage.ZoneHuanan
|
||||
@@ -61,7 +61,7 @@ func (s *QinNiuOss) UpdateConfig(config *types.QiNiuOssConfig) {
|
||||
s.uploader = formUploader
|
||||
s.bucket = storage.NewBucketManager(mac, &storeConfig)
|
||||
}
|
||||
func (s QinNiuOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
func (s QiNiuOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
// 解析表单
|
||||
file, err := ctx.FormFile(name)
|
||||
if err != nil {
|
||||
@@ -94,7 +94,7 @@ func (s QinNiuOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
|
||||
}
|
||||
|
||||
func (s QinNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string, error) {
|
||||
func (s QiNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string, error) {
|
||||
var fileData []byte
|
||||
var err error
|
||||
if useProxy {
|
||||
@@ -123,7 +123,7 @@ func (s QinNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string
|
||||
return fmt.Sprintf("%s/%s", s.config.Domain, ret.Key), nil
|
||||
}
|
||||
|
||||
func (s QinNiuOss) PutBase64(base64Img string) (string, error) {
|
||||
func (s QiNiuOss) PutBase64(base64Img string) (string, error) {
|
||||
imageData, err := base64.StdEncoding.DecodeString(base64Img)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error decoding base64:%v", err)
|
||||
@@ -139,7 +139,7 @@ func (s QinNiuOss) PutBase64(base64Img string) (string, error) {
|
||||
return fmt.Sprintf("%s/%s", s.config.Domain, ret.Key), nil
|
||||
}
|
||||
|
||||
func (s QinNiuOss) Delete(fileURL string) error {
|
||||
func (s QiNiuOss) Delete(fileURL string) error {
|
||||
var objectKey string
|
||||
if strings.HasPrefix(fileURL, "http") {
|
||||
filename := filepath.Base(fileURL)
|
||||
@@ -151,4 +151,4 @@ func (s QinNiuOss) Delete(fileURL string) error {
|
||||
return s.bucket.Delete(s.config.Bucket, objectKey)
|
||||
}
|
||||
|
||||
var _ Uploader = QinNiuOss{}
|
||||
var _ Uploader = QiNiuOss{}
|
||||
|
||||
@@ -20,11 +20,11 @@ type UploaderManager struct {
|
||||
local *LocalStorage
|
||||
aliyun *AliYunOss
|
||||
mini *MiniOss
|
||||
qiniu *QinNiuOss
|
||||
qiniu *QiNiuOss
|
||||
config *types.OSSConfig
|
||||
}
|
||||
|
||||
func NewUploaderManager(sysConfig *types.SystemConfig, local *LocalStorage, aliyun *AliYunOss, mini *MiniOss, qiniu *QinNiuOss) (*UploaderManager, error) {
|
||||
func NewUploaderManager(sysConfig *types.SystemConfig, local *LocalStorage, aliyun *AliYunOss, mini *MiniOss, qiniu *QiNiuOss) (*UploaderManager, error) {
|
||||
if sysConfig.OSS.Active == "" {
|
||||
sysConfig.OSS.Active = Local
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ func NewSmtpService(appConfig *types.AppConfig) *SmtpService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SmtpService) UpdateConfig(config *types.SmtpConfig) {
|
||||
s.config = config
|
||||
}
|
||||
|
||||
func (s *SmtpService) SendVerifyCode(to string, code int) error {
|
||||
subject := fmt.Sprintf("%s 注册验证码", s.config.AppName)
|
||||
body := fmt.Sprintf("【%s】:您的验证码为 %d,请不要告诉他人。如非本人操作,请忽略此邮件。", s.config.AppName, code)
|
||||
|
||||
Reference in New Issue
Block a user