fix: 按评审报告止血支付漏洞并加固授权链路

废除浏览器回跳自助结算,通用 webhook 强制密钥鉴权,Waffo 支持验签与未知事件忽略;重构订单状态机/激活占座/SQLite WAL,并补备份、readyz、限流与前端导出/登录页安全修复。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-12 21:26:58 +08:00
1 parent 8cc2c83f9d
commit bac72e6945
24 files changed
+682 -391

No files matched your search

+34 -15
View File
@@ -4,24 +4,43 @@ import (
"backend/config"
"backend/internal/model"
"fmt"
"strings"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Init 初始化数据库连接并执行迁移 // Logan.Li
// Init 初始化数据库连接并执行迁移(当前交付形态以 SQLite 为准) // Logan.Li
func Init(cfg *config.Config) (*gorm.DB, error) {
logLevel := getLogLevel(cfg.Mode)
fmt.Printf("[数据库] 使用 SQLite: %s\n", cfg.DBName)
db, err := gorm.Open(sqlite.Open(cfg.DBName), &gorm.Config{
driver := strings.ToLower(strings.TrimSpace(cfg.DBDriver))
if driver != "" && driver != "sqlite" && driver != "sqlite3" {
return nil, fmt.Errorf("当前仅支持 SQLite(收到 DB_DRIVER=%s);MySQL/PostgreSQL 尚未接入", cfg.DBDriver)
}
dsn := cfg.DBName
if !strings.Contains(dsn, "?") {
dsn += "?_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL&_foreign_keys=on"
}
fmt.Printf("[数据库] 使用 SQLite(WAL): %s\n", cfg.DBName)
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logLevel),
})
if err != nil {
return nil, fmt.Errorf("SQLite 连接失败: %v", err)
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxOpenConns(1) // SQLite 写串行化,WAL 下读不阻塞 // Logan.Li
sqlDB.SetMaxIdleConns(1)
sqlDB.SetConnMaxLifetime(time.Hour)
return migrateDatabase(db)
}
@@ -37,21 +56,21 @@ func getLogLevel(mode string) logger.LogLevel {
func migrateDatabase(db *gorm.DB) (*gorm.DB, error) {
err := db.AutoMigrate(
// 认证与系统
&model.User{}, // 管理员用户 // Logan.Li
&model.Setting{}, // 系统设置 // Logan.Li
&model.AuditLog{}, // 操作日志 // Logan.Li
&model.LicenseTemplate{}, // License 模板 // Logan.Li
&model.PaymentGatewayConfig{}, // 支付网关配置 // Logan.Li
&model.User{}, // 管理员用户 // Logan.Li
&model.Setting{}, // 系统设置 // Logan.Li
&model.AuditLog{}, // 操作日志 // Logan.Li
&model.LicenseTemplate{}, // License 模板 // Logan.Li
&model.PaymentGatewayConfig{}, // 支付网关配置 // Logan.Li
// 核心授权域
&model.Product{}, // 产品 // Logan.Li
&model.Customer{}, // 客户 // Logan.Li
&model.License{}, // License // Logan.Li
&model.Activation{}, // 激活记录 // Logan.Li
&model.Product{}, // 产品 // Logan.Li
&model.Customer{}, // 客户 // Logan.Li
&model.License{}, // License // Logan.Li
&model.Activation{}, // 激活记录 // Logan.Li
// 定价与订单
&model.Plan{}, // 定价计划 // Logan.Li
&model.Order{}, // 订单 // Logan.Li
&model.Plan{}, // 定价计划 // Logan.Li
&model.Order{}, // 订单 // Logan.Li
// 防重复购买
&model.CustomerEntitlement{}, // 客户权益 // Logan.Li
&model.CustomerEntitlement{}, // 客户权益 // Logan.Li
)
if err != nil {
return nil, fmt.Errorf("数据库迁移失败: %v", err)
+18 -37
View File
@@ -239,37 +239,26 @@ func ensureOpsXShellPlanPricing(db *gorm.DB) error {
for _, u := range updates {
var plan model.Plan
err := db.Where("product_id = ? AND plan_type = ?", product.ID, u.planType).First(&plan).Error
if err != nil {
plan = model.Plan{
ProductID: product.ID,
Name: u.name,
PlanType: u.planType,
Price: u.price,
Currency: u.currency,
Duration: u.duration,
MaxDevices: u.maxDevices,
Features: u.features,
SortOrder: u.sortOrder,
Status: 1,
}
if err := db.Create(&plan).Error; err != nil {
return err
}
continue
if err == nil {
continue // 已存在:尊重管理员运行时修改,不再覆盖 // Logan.Li
}
plan.Name = u.name
plan.Price = u.price
plan.Currency = u.currency
plan.Duration = u.duration
plan.MaxDevices = u.maxDevices
plan.Features = u.features
plan.SortOrder = u.sortOrder
plan.Status = 1
if err := db.Save(&plan).Error; err != nil {
plan = model.Plan{
ProductID: product.ID,
Name: u.name,
PlanType: u.planType,
Price: u.price,
Currency: u.currency,
Duration: u.duration,
MaxDevices: u.maxDevices,
Features: u.features,
SortOrder: u.sortOrder,
Status: 1,
}
if err := db.Create(&plan).Error; err != nil {
return err
}
}
log.Printf("[初始化] OpsXShell 套餐已同步: 包年¥99/2台, 永久¥199/5台, 体验180天/1台")
log.Printf("[初始化] OpsXShell 套餐已确保存在(仅新建,不覆盖已有价格)")
return nil
}
@@ -288,21 +277,13 @@ func ensureWaffoPaymentGateway(db *gorm.DB) error {
if err := db.Create(&gw).Error; err != nil {
return err
}
} else {
existing.Enabled = true
existing.SandboxMode = true
existing.Currency = "CNY"
existing.Priority = 0
if err := db.Save(&existing).Error; err != nil {
return err
}
log.Println("[初始化] 已创建 Waffo 支付网关(默认沙箱,可在后台改 live)")
}
// Lower priority for other enabled gateways (ASC pick prefers Waffo).
// 已存在则不覆盖 Enabled/SandboxMode,尊重管理员配置 // Logan.Li
if err := db.Model(&model.PaymentGatewayConfig{}).
Where("gateway <> ? AND enabled = ?", "waffo", true).
Update("priority", 100).Error; err != nil {
return err
}
log.Println("[初始化] 支付网关已优先 Waffo(包年/永久均为 onetime)")
return nil
}