重构系统配置

This commit is contained in:
RockYang
2025-08-24 12:45:51 +08:00
parent b05334bb77
commit 7fb0aad3c7
13 changed files with 186 additions and 153 deletions

View File

@@ -185,15 +185,13 @@ func (s *AppServer) Run(db *gorm.DB) error {
go func() {
info, err := host.Info()
if err == nil {
apiURL := fmt.Sprintf("%s/%s", s.Config.ApiConfig.ApiURL, "api/installs/push")
apiURL := fmt.Sprintf("%s/api/installs/push", types.GeekAPIURL)
timestamp := time.Now().Unix()
product := "geekai-plus"
signStr := fmt.Sprintf("%s#%s#%d", product, info.HostID, timestamp)
sign := utils.Sha256(signStr)
resp, err := req.C().R().SetBody(map[string]interface{}{"product": product, "device_id": info.HostID, "timestamp": timestamp, "sign": sign}).Post(apiURL)
if err != nil {
logger.Errorf("register install info failed: %v", err)
} else {
if err == nil {
logger.Debugf("register install info success: %v", resp.String())
}
}

View File

@@ -30,7 +30,6 @@ func NewDefaultConfig() *types.AppConfig {
SecretKey: utils.RandString(64),
MaxAge: 86400,
},
ApiConfig: types.ApiConfig{},
OSS: types.OSSConfig{
Active: "local",
Local: types.LocalStorageConfig{
@@ -38,7 +37,6 @@ func NewDefaultConfig() *types.AppConfig {
BasePath: "./static/upload",
},
},
AlipayConfig: types.AlipayConfig{Enabled: false, SandBox: false},
}
}

View File

@@ -17,85 +17,18 @@ type AppConfig struct {
Session Session
AdminSession Session
ProxyURL string
MysqlDns string // mysql 连接地址
StaticDir string // 静态资源目录
StaticUrl string // 静态资源 URL
Redis RedisConfig // redis 连接信息
ApiConfig ApiConfig // ChatPlus API authorization configs
SMS SMSConfig // send mobile message config
OSS OSSConfig // OSS config
SmtpConfig SmtpConfig // 邮件发送配置
XXLConfig XXLConfig
AlipayConfig AlipayConfig // 支付宝支付渠道配置
HuPiPayConfig HuPiPayConfig // 虎皮椒支付配置
GeekPayConfig GeekPayConfig // GEEK 支付配置
WechatPayConfig WechatPayConfig // 微信支付渠道配置
TikaHost string // TiKa 服务器地址
}
type SmtpConfig struct {
UseTls bool // 是否使用 TLS 发送
Host string
Port int
AppName string // 应用名称
From string // 发件人邮箱地址
Password string // 发件人邮箱密码
}
type ApiConfig struct {
ApiURL string
AppId string
Token string
JimengConfig JimengConfig // 即梦AI配置
}
type AlipayConfig struct {
Enabled bool // 是否启用该支付通道
SandBox bool // 是否沙盒环境
AppId string // 应用 ID
PrivateKey string // 商户私钥
AlipayPublicKey string // 支付宝公钥
NotifyURL string // 异步通知地址
ReturnURL string // 同步通知地址
}
type WechatPayConfig struct {
Enabled bool // 是否启用该支付通道
AppId string // 公众号的APPID,如wxd678efh567hg6787
MchId string // 直连商户的商户号,由微信支付生成并下发
SerialNo string // 商户证书的证书序列号
PrivateKey string // 商户私钥
ApiV3Key string // API V3 秘钥
NotifyURL string // 异步通知地址
}
type HuPiPayConfig struct { //虎皮椒第四方支付配置
Enabled bool // 是否启用该支付通道
AppId string // App ID
AppSecret string // app 密钥
ApiURL string // 支付网关
NotifyURL string // 异步通知地址
ReturnURL string // 同步通知地址
}
// GeekPayConfig GEEK支付配置
type GeekPayConfig struct {
Enabled bool
AppId string // 商户 ID
PrivateKey string // 私钥
ApiURL string // API 网关
NotifyURL string // 异步通知地址
ReturnURL string // 同步通知地址
Methods []string // 支付方式
}
type XXLConfig struct { // XXL 任务调度配置
Enabled bool
ServerAddr string
ExecutorIp string
ExecutorPort string
AccessToken string
RegistryKey string
MysqlDns string // mysql 连接地址
StaticDir string // 静态资源目录
StaticUrl string // 静态资源 URL
Redis RedisConfig // redis 连接信息
SMS SMSConfig // send mobile message config
OSS OSSConfig // OSS config
SmtpConfig SmtpConfig // 邮件发送配置
AlipayConfig AlipayConfig // 支付宝支付渠道配置
HuPiPayConfig HuPiPayConfig // 虎皮椒支付配置
GeekPayConfig GeekPayConfig // GEEK 支付配置
WechatPayConfig WxPayConfig // 微信支付渠道配置
TikaHost string // TiKa 服务器地址
}
type RedisConfig struct {
@@ -175,16 +108,13 @@ type SystemConfig struct {
// 配置键名常量
const (
ConfigKeySystem = "system"
ConfigKeyNotice = "notice"
ConfigKeyAgreement = "agreement"
ConfigKeyPrivacy = "privacy"
ConfigKeyApi = "api"
ConfigKeySms = "sms"
ConfigKeySmtp = "smtp"
ConfigKeyOss = "oss"
ConfigKeyAlipay = "alipay"
ConfigKeyWechat = "wechat"
ConfigKeyHuPi = "hupi"
ConfigKeyGeekpay = "geekpay"
ConfigKeySystem = "system"
ConfigKeyNotice = "notice"
ConfigKeyAgreement = "agreement"
ConfigKeyPrivacy = "privacy"
ConfigKeyGeekService = "geekai"
ConfigKeySms = "sms"
ConfigKeySmtp = "smtp"
ConfigKeyOss = "oss"
ConfigKeyPayment = "payment"
)

25
api/core/types/geekai.go Normal file
View File

@@ -0,0 +1,25 @@
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
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// GeekAI 增值服务
const GeekAPIURL = "https://sapi.geekai.me"
// CaptchaConfig 行为验证码配置
type CaptchaConfig struct {
ApiKey string `json:"api_key"`
Type string `json:"type"` // 验证码类型, 可选值: "dot" 或 "slide"
Enabled bool `json:"enabled"`
}
// WxLoginConfig 微信登录配置
type WxLoginConfig struct {
ApiKey string `json:"api_key"`
NotifyURL string `json:"notify_url"` // 登录成功回调 URL
Enabled bool `json:"enabled"` // 是否启用微信登录
}

70
api/core/types/payment.go Normal file
View File

@@ -0,0 +1,70 @@
package types
type PaymentConfig struct {
AlipayConfig AlipayConfig `json:"alipay"` // 支付宝支付渠道配置
GeekPayConfig GeekPayConfig `json:"geekpay"` // GEEK 支付配置
WxPayConfig WxPayConfig `json:"wxpay"` // 微信支付渠道配置
HuPiPayConfig HuPiPayConfig `json:"hupi"` // 虎皮椒支付渠道配置
}
type HuPiPayConfig struct { //虎皮椒第四方支付配置
Enabled bool // 是否启用该支付通道
AppId string // App ID
AppSecret string // app 密钥
ApiURL string // 支付网关
NotifyURL string // 异步通知地址
ReturnURL string // 同步通知地址
}
// AlipayConfig 支付宝支付配置
type AlipayConfig struct {
Enabled bool `json:"enabled"` // 是否启用该支付通道
SandBox bool `json:"sandbox"` // 是否沙盒环境
AppId string `json:"app_id"` // 应用 ID
PrivateKey string `json:"private_key"` // 应用私钥
AlipayPublicKey string `json:"alipay_public_key"` // 支付宝公钥
Domain string `json:"domain"` // 支付回调域名
}
func (c *AlipayConfig) Equal(other *AlipayConfig) bool {
return c.AppId == other.AppId &&
c.PrivateKey == other.PrivateKey &&
c.AlipayPublicKey == other.AlipayPublicKey &&
c.Domain == other.Domain
}
// WxPayConfig 微信支付配置
type WxPayConfig struct {
Enabled bool `json:"enabled"` // 是否启用该支付通道
AppId string `json:"app_id"` // 公众号的APPID,如wxd678efh567hg6787
MchId string `json:"mch_id"` // 直连商户的商户号,由微信支付生成并下发
SerialNo string `json:"serial_no"` // 商户证书的证书序列号
PrivateKey string `json:"private_key"` // 商户证书私钥
ApiV3Key string `json:"api_v3_key"` // API V3 秘钥
Domain string `json:"domain"` // 支付回调域名
}
func (c *WxPayConfig) Equal(other *WxPayConfig) bool {
return c.AppId == other.AppId &&
c.MchId == other.MchId &&
c.SerialNo == other.SerialNo &&
c.PrivateKey == other.PrivateKey &&
c.ApiV3Key == other.ApiV3Key &&
c.Domain == other.Domain
}
// GeekPayConfig 易支付配置
type GeekPayConfig struct {
Enabled bool `json:"enabled"` // 是否启用该支付通道
AppId string `json:"app_id"` // 商户 ID
PrivateKey string `json:"private_key"` // 私钥
ApiURL string `json:"api_url"` // z支付 API 网关
Domain string `json:"domain"` // 支付回调域名
}
func (c *GeekPayConfig) Equal(other *GeekPayConfig) bool {
return c.AppId == other.AppId &&
c.PrivateKey == other.PrivateKey &&
c.ApiURL == other.ApiURL &&
c.Domain == other.Domain
}

26
api/core/types/smtp.go Normal file
View File

@@ -0,0 +1,26 @@
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 SmtpConfig struct {
UseTls bool `json:"use_tls"` // 是否使用 TLS 发送
Host string `json:"host"` // 邮件服务器地址
Port int `json:"port"` // 邮件服务器端口
AppName string `json:"app_name"` // 应用名称
From string `json:"from"` // 发件人邮箱地址
Password string `json:"password"` // 发件人邮箱密码
}
func (s *SmtpConfig) Equal(other *SmtpConfig) bool {
return s.UseTls == other.UseTls &&
s.Host == other.Host &&
s.Port == other.Port &&
s.AppName == other.AppName &&
s.From == other.From &&
s.Password == other.Password
}

View File

@@ -24,8 +24,6 @@ require (
gorm.io/driver/mysql v1.4.7
)
require github.com/xxl-job/xxl-job-executor-go v1.2.0
require (
github.com/go-pay/gopay v1.5.101
github.com/go-rod/rod v0.116.2
@@ -69,7 +67,6 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gaukas/godicttls v0.0.3 // indirect
github.com/go-basic/ipv4 v1.0.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect

View File

@@ -46,8 +46,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-basic/ipv4 v1.0.0 h1:gjyFAa1USC1hhXTkPOwBWDPfMcUaIM+tvo1XzV9EZxs=
github.com/go-basic/ipv4 v1.0.0/go.mod h1:etLBnaxbidQfuqE6wgZQfs38nEWNmzALkxDZe4xY8Dg=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
@@ -261,8 +259,6 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/volcengine/volc-sdk-golang v1.0.23 h1:anOslb2Qp6ywnsbyq9jqR0ljuO63kg9PY+4OehIk5R8=
github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU=
github.com/xxl-job/xxl-job-executor-go v1.2.0 h1:MTl2DpwrK2+hNjRRks2k7vB3oy+3onqm9OaSarneeLQ=
github.com/xxl-job/xxl-job-executor-go v1.2.0/go.mod h1:bUFhz/5Irp9zkdYk5MxhQcDDT6LlZrI8+rv5mHtQ1mo=
github.com/ysmood/fetchup v0.3.0 h1:UhYz9xnLEVn2ukSuK3KCgcznWpHMdrmbsPpllcylyu8=
github.com/ysmood/fetchup v0.3.0/go.mod h1:hbysoq65PXL0NQeNzUczNYIKpwpkwFL4LXMDEvIQq9A=
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=

View File

@@ -74,10 +74,7 @@ func (h *ConfigHandler) Update(c *gin.Context) {
resp.ERROR(c, "您无权修改版权信息,请先联系作者获取授权")
return
}
if sys.EnabledVerify && h.App.Config.ApiConfig.AppId == "" {
resp.ERROR(c, "启用验证码服务需要先配置 GeekAI 官方 API 服务 AppId 和 Token")
return
}
}
// 使用统一配置服务写入与广播

View File

@@ -12,6 +12,7 @@ import (
"geekai/core/types"
"geekai/service"
"geekai/utils/resp"
"github.com/gin-gonic/gin"
)
@@ -20,10 +21,11 @@ import (
type CaptchaHandler struct {
App *core.AppServer
service *service.CaptchaService
config *types.CaptchaConfig
}
func NewCaptchaHandler(app *core.AppServer, s *service.CaptchaService) *CaptchaHandler {
return &CaptchaHandler{App: app, service: s}
func NewCaptchaHandler(app *core.AppServer, s *service.CaptchaService, config *types.CaptchaConfig) *CaptchaHandler {
return &CaptchaHandler{App: app, service: s, config: config}
}
// RegisterRoutes 注册路由
@@ -36,6 +38,11 @@ func (h *CaptchaHandler) RegisterRoutes() {
}
func (h *CaptchaHandler) Get(c *gin.Context) {
if !h.config.Enabled {
resp.ERROR(c, "验证码服务未启用")
return
}
data, err := h.service.Get()
if err != nil {
resp.ERROR(c, err.Error())
@@ -47,6 +54,11 @@ func (h *CaptchaHandler) Get(c *gin.Context) {
// Check verify the captcha data
func (h *CaptchaHandler) Check(c *gin.Context) {
if !h.config.Enabled {
resp.ERROR(c, "验证码服务未启用")
return
}
var data struct {
Key string `json:"key"`
Dots string `json:"dots"`
@@ -66,6 +78,11 @@ func (h *CaptchaHandler) Check(c *gin.Context) {
// SlideGet 获取滑动验证图片
func (h *CaptchaHandler) SlideGet(c *gin.Context) {
if !h.config.Enabled {
resp.ERROR(c, "验证码服务未启用")
return
}
data, err := h.service.SlideGet()
if err != nil {
resp.ERROR(c, err.Error())
@@ -77,6 +94,11 @@ func (h *CaptchaHandler) SlideGet(c *gin.Context) {
// SlideCheck 滑动验证结果校验
func (h *CaptchaHandler) SlideCheck(c *gin.Context) {
if !h.config.Enabled {
resp.ERROR(c, "验证码服务未启用")
return
}
var data struct {
Key string `json:"key"`
X int `json:"x"`

View File

@@ -31,7 +31,6 @@ import (
type FunctionHandler struct {
BaseHandler
config types.ApiConfig
uploadManager *oss.UploaderManager
dallService *dalle.Service
userService *service.UserService
@@ -49,7 +48,6 @@ func NewFunctionHandler(
App: server,
DB: db,
},
config: config.ApiConfig,
uploadManager: manager,
dallService: dallService,
userService: userService,
@@ -117,16 +115,10 @@ func (h *FunctionHandler) WeiBo(c *gin.Context) {
return
}
if h.config.Token == "" {
resp.ERROR(c, "无效的 API Token")
return
}
url := fmt.Sprintf("%s/api/weibo/fetch", h.config.ApiURL)
url := fmt.Sprintf("%s/api/weibo/fetch", types.GeekAPIURL)
var res resVo
r, err := req.C().R().
SetHeader("AppId", h.config.AppId).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.config.Token)).
SetHeader("Authorization", "Bearer geekai-plus").
SetSuccessResult(&res).Get(url)
if err != nil {
resp.ERROR(c, fmt.Sprintf("%v", err))
@@ -156,16 +148,10 @@ func (h *FunctionHandler) ZaoBao(c *gin.Context) {
return
}
if h.config.Token == "" {
resp.ERROR(c, "无效的 API Token")
return
}
url := fmt.Sprintf("%s/api/zaobao/fetch", h.config.ApiURL)
url := fmt.Sprintf("%s/api/zaobao/fetch", types.GeekAPIURL)
var res resVo
r, err := req.C().R().
SetHeader("AppId", h.config.AppId).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.config.Token)).
SetHeader("Authorization", "Bearer geekai-plus").
SetSuccessResult(&res).Get(url)
if err != nil {
resp.ERROR(c, fmt.Sprintf("%v", err))

View File

@@ -8,19 +8,19 @@ package service
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import (
"errors"
"fmt"
"geekai/core/types"
"github.com/imroc/req/v3"
"time"
"github.com/imroc/req/v3"
)
type CaptchaService struct {
config types.ApiConfig
config types.CaptchaConfig
client *req.Client
}
func NewCaptchaService(config types.ApiConfig) *CaptchaService {
func NewCaptchaService(config types.CaptchaConfig) *CaptchaService {
return &CaptchaService{
config: config,
client: req.C().SetTimeout(10 * time.Second),
@@ -28,15 +28,10 @@ func NewCaptchaService(config types.ApiConfig) *CaptchaService {
}
func (s *CaptchaService) Get() (interface{}, error) {
if s.config.Token == "" {
return nil, errors.New("无效的 API Token")
}
url := fmt.Sprintf("%s/api/captcha/get", s.config.ApiURL)
url := fmt.Sprintf("%s/api/captcha/get", types.GeekAPIURL)
var res types.BizVo
r, err := s.client.R().
SetHeader("AppId", s.config.AppId).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)).
SetSuccessResult(&res).Get(url)
if err != nil || r.IsErrorState() {
return nil, fmt.Errorf("请求 API 失败:%v", err)
@@ -50,11 +45,10 @@ func (s *CaptchaService) Get() (interface{}, error) {
}
func (s *CaptchaService) Check(data interface{}) bool {
url := fmt.Sprintf("%s/api/captcha/check", s.config.ApiURL)
url := fmt.Sprintf("%s/api/captcha/check", types.GeekAPIURL)
var res types.BizVo
r, err := s.client.R().
SetHeader("AppId", s.config.AppId).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)).
SetBodyJsonMarshal(data).
SetSuccessResult(&res).Post(url)
if err != nil || r.IsErrorState() {
@@ -69,15 +63,10 @@ func (s *CaptchaService) Check(data interface{}) bool {
}
func (s *CaptchaService) SlideGet() (interface{}, error) {
if s.config.Token == "" {
return nil, errors.New("无效的 API Token")
}
url := fmt.Sprintf("%s/api/captcha/slide/get", s.config.ApiURL)
url := fmt.Sprintf("%s/api/captcha/slide/get", types.GeekAPIURL)
var res types.BizVo
r, err := s.client.R().
SetHeader("AppId", s.config.AppId).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)).
SetSuccessResult(&res).Get(url)
if err != nil || r.IsErrorState() {
return nil, fmt.Errorf("请求 API 失败:%v", err)
@@ -91,11 +80,10 @@ func (s *CaptchaService) SlideGet() (interface{}, error) {
}
func (s *CaptchaService) SlideCheck(data interface{}) bool {
url := fmt.Sprintf("%s/api/captcha/slide/check", s.config.ApiURL)
url := fmt.Sprintf("%s/api/captcha/slide/check", types.GeekAPIURL)
var res types.BizVo
r, err := s.client.R().
SetHeader("AppId", s.config.AppId).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)).
SetBodyJsonMarshal(data).
SetSuccessResult(&res).Post(url)
if err != nil || r.IsErrorState() {

View File

@@ -21,7 +21,7 @@ import (
)
type LicenseService struct {
config types.ApiConfig
config types.GeekServiceConfig
levelDB *store.LevelDB
license *types.License
urlWhiteList []string