|
|
//
|
|
|
// Copyright (C) 2024 veypi <i@veypi.com>
|
|
|
// 2025-03-04 16:08:06
|
|
|
// Distributed under terms of the MIT license.
|
|
|
//
|
|
|
|
|
|
package models
|
|
|
|
|
|
import (
|
|
|
"encoding/json"
|
|
|
"fmt"
|
|
|
"strconv"
|
|
|
|
|
|
"github.com/veypi/vbase/cfg"
|
|
|
"github.com/veypi/vigo"
|
|
|
)
|
|
|
|
|
|
// Setting 系统配置表(线上配置,存储在数据库)
|
|
|
type Setting struct {
|
|
|
vigo.Model
|
|
|
Key string `gorm:"uniqueIndex;size:100;not null" json:"key"`
|
|
|
Value string `gorm:"type:text" json:"value"`
|
|
|
Type string `gorm:"size:20;default:'string'" json:"type"` // string/int/bool/json
|
|
|
Category string `gorm:"index;size:50" json:"category"`
|
|
|
Desc string `gorm:"size:200" json:"desc"`
|
|
|
UpdatedBy string `gorm:"size:36" json:"updated_by"`
|
|
|
}
|
|
|
|
|
|
// TableName 表名
|
|
|
func (Setting) TableName() string {
|
|
|
return "settings"
|
|
|
}
|
|
|
|
|
|
// 配置分类常量
|
|
|
const (
|
|
|
SettingCategoryAuth = "auth"
|
|
|
SettingCategoryEmail = "email"
|
|
|
SettingCategorySMS = "sms"
|
|
|
SettingCategorySecurity = "security"
|
|
|
SettingCategoryOAuth = "oauth"
|
|
|
SettingCategoryApp = "app"
|
|
|
)
|
|
|
|
|
|
// 配置键常量
|
|
|
const (
|
|
|
// 应用配置
|
|
|
SettingAppName = "app.name"
|
|
|
SettingAppID = "app.id"
|
|
|
|
|
|
// JWT 配置
|
|
|
SettingJWTSecret = "jwt.secret"
|
|
|
SettingJWTAccessExpiry = "jwt.access_expiry" // 单位:秒
|
|
|
SettingJWTRefreshExpiry = "jwt.refresh_expiry" // 单位:秒
|
|
|
SettingJWTIssuer = "jwt.issuer"
|
|
|
|
|
|
// 登录注册
|
|
|
SettingAuthRegRequireEmail = "auth.reg.require_email"
|
|
|
SettingAuthRegRequirePhone = "auth.reg.require_phone"
|
|
|
SettingAuthLoginMethods = "auth.login.methods" // json: ["password", "email_code", "phone_code"]
|
|
|
SettingAuthPasswordFields = "auth.login.password_fields" // json: ["username", "email", "phone"]
|
|
|
|
|
|
// 安全/验证码
|
|
|
SettingSecurityBcryptCost = "security.bcrypt_cost"
|
|
|
SettingSecurityMaxLoginAttempts = "security.max_login_attempts"
|
|
|
SettingSecurityCaptchaEnabled = "security.captcha_enabled"
|
|
|
SettingCodeExpiry = "code.expiry" // 分钟
|
|
|
SettingCodeLength = "code.length"
|
|
|
SettingCodeMaxAttempt = "code.max_attempt"
|
|
|
SettingCodeSendInterval = "code.send_interval" // 秒
|
|
|
SettingCodeMaxDailyCount = "code.max_daily_count"
|
|
|
|
|
|
// 邮件配置
|
|
|
SettingEmailEnabled = "email.enabled"
|
|
|
SettingEmailProvider = "email.provider" // smtp/sendgrid
|
|
|
SettingEmailSMTPHost = "email.smtp.host"
|
|
|
SettingEmailSMTPPort = "email.smtp.port"
|
|
|
SettingEmailSMTPUser = "email.smtp.user"
|
|
|
SettingEmailSMTPPass = "email.smtp.pass"
|
|
|
SettingEmailFrom = "email.from"
|
|
|
SettingEmailFromName = "email.from_name"
|
|
|
|
|
|
// 短信配置
|
|
|
SettingSMSEnabled = "sms.enabled"
|
|
|
SettingSMSProvider = "sms.provider" // aliyun/tencent
|
|
|
SettingSMSAccessKey = "sms.access_key"
|
|
|
SettingSMSAccessSecret = "sms.access_secret"
|
|
|
SettingSMSSignName = "sms.sign_name"
|
|
|
SettingSMSTemplateCode = "sms.template_code"
|
|
|
SettingSMSEndpoint = "sms.endpoint"
|
|
|
)
|
|
|
|
|
|
// 默认配置值
|
|
|
var defaultSettings = []Setting{
|
|
|
// 应用
|
|
|
{Key: SettingAppName, Value: "VBase IAM", Type: "string", Category: SettingCategoryApp, Desc: "应用名称"},
|
|
|
{Key: SettingAppID, Value: "vbase", Type: "string", Category: SettingCategoryApp, Desc: "应用标识"},
|
|
|
|
|
|
// JWT
|
|
|
{Key: SettingJWTSecret, Value: "", Type: "string", Category: SettingCategorySecurity, Desc: "JWT密钥(为空则使用本地key配置)"},
|
|
|
{Key: SettingJWTAccessExpiry, Value: "3600", Type: "int", Category: SettingCategorySecurity, Desc: "Access Token有效期(秒)"},
|
|
|
{Key: SettingJWTRefreshExpiry, Value: "2592000", Type: "int", Category: SettingCategorySecurity, Desc: "Refresh Token有效期(秒)"},
|
|
|
{Key: SettingJWTIssuer, Value: "vbase", Type: "string", Category: SettingCategorySecurity, Desc: "JWT签发者"},
|
|
|
|
|
|
// 登录注册
|
|
|
{Key: SettingAuthRegRequireEmail, Value: "false", Type: "bool", Category: SettingCategoryAuth, Desc: "注册是否要求邮箱"},
|
|
|
{Key: SettingAuthRegRequirePhone, Value: "false", Type: "bool", Category: SettingCategoryAuth, Desc: "注册是否要求手机号"},
|
|
|
{Key: SettingAuthLoginMethods, Value: `["password"]`, Type: "json", Category: SettingCategoryAuth, Desc: "启用的登录方式"},
|
|
|
{Key: SettingAuthPasswordFields, Value: `["username"]`, Type: "json", Category: SettingCategoryAuth, Desc: "密码登录支持的身份标识"},
|
|
|
|
|
|
// 安全
|
|
|
{Key: SettingSecurityBcryptCost, Value: "12", Type: "int", Category: SettingCategorySecurity, Desc: "bcrypt加密强度"},
|
|
|
{Key: SettingSecurityMaxLoginAttempts, Value: "5", Type: "int", Category: SettingCategorySecurity, Desc: "最大登录尝试次数"},
|
|
|
{Key: SettingSecurityCaptchaEnabled, Value: "true", Type: "bool", Category: SettingCategorySecurity, Desc: "是否启用验证码"},
|
|
|
|
|
|
// 验证码
|
|
|
{Key: SettingCodeExpiry, Value: "5", Type: "int", Category: SettingCategorySecurity, Desc: "验证码有效期(分钟)"},
|
|
|
{Key: SettingCodeLength, Value: "6", Type: "int", Category: SettingCategorySecurity, Desc: "验证码长度"},
|
|
|
{Key: SettingCodeMaxAttempt, Value: "3", Type: "int", Category: SettingCategorySecurity, Desc: "验证码最大尝试次数"},
|
|
|
{Key: SettingCodeSendInterval, Value: "60", Type: "int", Category: SettingCategorySecurity, Desc: "发送间隔(秒)"},
|
|
|
{Key: SettingCodeMaxDailyCount, Value: "100", Type: "int", Category: SettingCategorySecurity, Desc: "单日最大发送次数(0禁用,-1不限制)"},
|
|
|
|
|
|
// 邮件(默认关闭)
|
|
|
{Key: SettingEmailEnabled, Value: "false", Type: "bool", Category: SettingCategoryEmail, Desc: "是否启用邮件服务"},
|
|
|
{Key: SettingEmailProvider, Value: "smtp", Type: "string", Category: SettingCategoryEmail, Desc: "邮件服务商"},
|
|
|
{Key: SettingEmailSMTPHost, Value: "", Type: "string", Category: SettingCategoryEmail, Desc: "SMTP服务器"},
|
|
|
{Key: SettingEmailSMTPPort, Value: "587", Type: "int", Category: SettingCategoryEmail, Desc: "SMTP端口"},
|
|
|
{Key: SettingEmailSMTPUser, Value: "", Type: "string", Category: SettingCategoryEmail, Desc: "SMTP用户名"},
|
|
|
{Key: SettingEmailSMTPPass, Value: "", Type: "string", Category: SettingCategoryEmail, Desc: "SMTP密码"},
|
|
|
{Key: SettingEmailFrom, Value: "", Type: "string", Category: SettingCategoryEmail, Desc: "发件人邮箱"},
|
|
|
{Key: SettingEmailFromName, Value: "VBase", Type: "string", Category: SettingCategoryEmail, Desc: "发件人显示名"},
|
|
|
|
|
|
// 短信(默认关闭)
|
|
|
{Key: SettingSMSEnabled, Value: "false", Type: "bool", Category: SettingCategorySMS, Desc: "是否启用短信服务"},
|
|
|
{Key: SettingSMSProvider, Value: "aliyun", Type: "string", Category: SettingCategorySMS, Desc: "短信服务商"},
|
|
|
{Key: SettingSMSAccessKey, Value: "", Type: "string", Category: SettingCategorySMS, Desc: "AccessKey ID"},
|
|
|
{Key: SettingSMSAccessSecret, Value: "", Type: "string", Category: SettingCategorySMS, Desc: "AccessKey Secret"},
|
|
|
{Key: SettingSMSSignName, Value: "", Type: "string", Category: SettingCategorySMS, Desc: "短信签名"},
|
|
|
{Key: SettingSMSTemplateCode, Value: "", Type: "string", Category: SettingCategorySMS, Desc: "验证码模板CODE"},
|
|
|
{Key: SettingSMSEndpoint, Value: "", Type: "string", Category: SettingCategorySMS, Desc: "自定义接入点"},
|
|
|
}
|
|
|
|
|
|
// InitSettings 初始化默认配置
|
|
|
func InitSettings() error {
|
|
|
db := cfg.DB()
|
|
|
for _, s := range defaultSettings {
|
|
|
var count int64
|
|
|
if err := db.Model(&Setting{}).Where("`key` = ?", s.Key).Count(&count).Error; err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if count == 0 {
|
|
|
if err := db.Create(&s).Error; err != nil {
|
|
|
return fmt.Errorf("init setting %s failed: %w", s.Key, err)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// GetSetting 获取配置值
|
|
|
func GetSetting(key string) (string, error) {
|
|
|
var s Setting
|
|
|
if err := cfg.DB().Where("`key` = ?", key).First(&s).Error; err != nil {
|
|
|
return "", err
|
|
|
}
|
|
|
return s.Value, nil
|
|
|
}
|
|
|
|
|
|
// GetSettingBool 获取布尔配置
|
|
|
func GetSettingBool(key string) (bool, error) {
|
|
|
val, err := GetSetting(key)
|
|
|
if err != nil {
|
|
|
return false, err
|
|
|
}
|
|
|
return strconv.ParseBool(val)
|
|
|
}
|
|
|
|
|
|
// GetSettingInt 获取整数配置
|
|
|
func GetSettingInt(key string) (int, error) {
|
|
|
val, err := GetSetting(key)
|
|
|
if err != nil {
|
|
|
return 0, err
|
|
|
}
|
|
|
return strconv.Atoi(val)
|
|
|
}
|
|
|
|
|
|
// GetSettingJSON 获取 JSON 配置
|
|
|
func GetSettingJSON(key string, v interface{}) error {
|
|
|
val, err := GetSetting(key)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
return json.Unmarshal([]byte(val), v)
|
|
|
}
|
|
|
|
|
|
// SetSetting 设置配置值
|
|
|
func SetSetting(key, value, updatedBy string) error {
|
|
|
var s Setting
|
|
|
db := cfg.DB()
|
|
|
if err := db.Where("`key` = ?", key).First(&s).Error; err != nil {
|
|
|
return err
|
|
|
}
|
|
|
s.Value = value
|
|
|
s.UpdatedBy = updatedBy
|
|
|
return db.Save(&s).Error
|
|
|
}
|