package handler import ( "net/http" "gpt-plus/internal/db" "github.com/gin-gonic/gin" ) type configGroup struct { Group string `json:"group"` Items []db.SystemConfig `json:"items"` } func GetConfig(c *gin.Context) { var configs []db.SystemConfig if err := db.GetDB().Order("\"group\", key").Find(&configs).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Mask password fields for i := range configs { if configs[i].Type == "password" && configs[i].Value != "" { configs[i].Value = "••••••••" } } groups := make(map[string][]db.SystemConfig) for _, cfg := range configs { groups[cfg.Group] = append(groups[cfg.Group], cfg) } c.JSON(http.StatusOK, gin.H{"groups": groups}) } type updateConfigRequest struct { Items []struct { Key string `json:"key" binding:"required"` Value string `json:"value"` } `json:"items" binding:"required"` } func UpdateConfig(c *gin.Context) { var req updateConfigRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } tx := db.GetDB().Begin() for _, item := range req.Items { // Skip masked password values if item.Value == "••••••••" { continue } var existing db.SystemConfig if err := tx.Where("key = ?", item.Key).First(&existing).Error; err != nil { tx.Rollback() c.JSON(http.StatusNotFound, gin.H{"error": "配置项不存在: " + item.Key}) return } value := item.Value if existing.Type == "password" && value != "" { encrypted, err := db.Encrypt(value) if err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "加密失败"}) return } value = encrypted } if err := tx.Model(&existing).Update("value", value).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } tx.Commit() c.JSON(http.StatusOK, gin.H{"message": "配置已更新"}) } func SeedDefaultConfigs() { defaults := []db.SystemConfig{ // Proxy group {Key: "proxy.mode", Value: "b2proxy", Group: "proxy", Label: "代理模式", Type: "string"}, {Key: "proxy.url", Value: "", Group: "proxy", Label: "代理地址", Type: "string"}, {Key: "proxy.b2proxy.enabled", Value: "true", Group: "proxy", Label: "启用 B2Proxy", Type: "bool"}, {Key: "proxy.b2proxy.api_base", Value: "http://global.rrp.b2proxy.com:8089", Group: "proxy", Label: "B2Proxy API 地址", Type: "string"}, {Key: "proxy.b2proxy.zone", Value: "custom", Group: "proxy", Label: "B2Proxy 区域", Type: "string"}, {Key: "proxy.b2proxy.proto", Value: "socks5", Group: "proxy", Label: "B2Proxy 协议", Type: "string"}, {Key: "proxy.b2proxy.sess_time", Value: "5", Group: "proxy", Label: "会话时长(分钟)", Type: "int"}, // Email group {Key: "email.gateway.base_url", Value: "https://regmail.zhengmi.org", Group: "email", Label: "邮箱网关地址", Type: "string"}, {Key: "email.gateway.api_key", Value: "Admin2026.", Group: "email", Label: "邮箱网关 API Key", Type: "password"}, {Key: "email.gateway.provider", Value: "mail", Group: "email", Label: "邮箱网关 Provider", Type: "string"}, // Card group {Key: "card.max_binds", Value: "0", Group: "card", Label: "单卡最大绑定次数(0=无限)", Type: "int"}, {Key: "card.default_name", Value: "Anna Hoover", Group: "card", Label: "默认持卡人姓名", Type: "string"}, {Key: "card.default_country", Value: "US", Group: "card", Label: "默认国家", Type: "string"}, {Key: "card.default_currency", Value: "USD", Group: "card", Label: "默认货币", Type: "string"}, {Key: "card.default_address", Value: "1208 Oakdale Street", Group: "card", Label: "默认地址", Type: "string"}, {Key: "card.default_city", Value: "Jonesboro", Group: "card", Label: "默认城市", Type: "string"}, {Key: "card.default_state", Value: "AR", Group: "card", Label: "默认州/省", Type: "string"}, {Key: "card.default_postal_code", Value: "72401", Group: "card", Label: "默认邮编", Type: "string"}, {Key: "card.api_base_url", Value: "https://yyl.ncet.top", Group: "card", Label: "开卡 API 地址", Type: "string"}, {Key: "card.api_key", Value: "", Group: "card", Label: "开卡 API 密钥", Type: "password"}, // Stripe group {Key: "stripe.build_hash", Value: "ede17ac9fd", Group: "stripe", Label: "Build Hash", Type: "string"}, {Key: "stripe.tag_version", Value: "4.5.43", Group: "stripe", Label: "Tag Version", Type: "string"}, {Key: "stripe.fingerprint_dir", Value: "./fingerprints", Group: "stripe", Label: "指纹目录", Type: "string"}, // Captcha group {Key: "captcha.provider", Value: "hcaptchasolver", Group: "captcha", Label: "验证码提供商", Type: "string"}, {Key: "captcha.api_key", Value: "Kenzx_4ba2535aaf33bd1238345f795a085853ace94e54ade70a2a", Group: "captcha", Label: "验证码 API Key", Type: "password"}, {Key: "captcha.proxy", Value: "", Group: "captcha", Label: "验证码代理", Type: "string"}, // Account group {Key: "account.password_length", Value: "16", Group: "account", Label: "密码长度", Type: "int"}, {Key: "account.locale", Value: "en-GB", Group: "account", Label: "区域设置", Type: "string"}, // Team group {Key: "team.enabled", Value: "true", Group: "team", Label: "启用 Team", Type: "bool"}, {Key: "team.workspace_prefix", Value: "Team", Group: "team", Label: "工作区前缀", Type: "string"}, {Key: "team.seat_quantity", Value: "5", Group: "team", Label: "座位数", Type: "int"}, {Key: "team.coupon", Value: "team-1-month-free", Group: "team", Label: "优惠券", Type: "string"}, {Key: "team.invite_count", Value: "0", Group: "team", Label: "邀请人数", Type: "int"}, // CPA (CLI Proxy API) group {Key: "cpa.base_url", Value: "http://127.0.0.1:8317", Group: "cpa", Label: "CPA 地址", Type: "string"}, {Key: "cpa.management_key", Value: "gptplus2026", Group: "cpa", Label: "Management Key", Type: "password"}, } d := db.GetDB() for _, cfg := range defaults { var existing db.SystemConfig res := d.Where("key = ?", cfg.Key).First(&existing) if res.Error != nil { d.Create(&cfg) } else if existing.Value == "" && cfg.Value != "" { d.Model(&existing).Updates(map[string]interface{}{ "value": cfg.Value, "label": cfg.Label, "type": cfg.Type, }) } } }