// // Copyright (C) 2024 veypi // 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 }