[新增] 添加 WAF 规则组及其绑定的 API 支持,更新前端页面以集成 WAF 功能

This commit is contained in:
ryan
2026-05-30 12:16:28 +08:00
parent 290ddd7b51
commit 8300d3ec1c
39 changed files with 2574 additions and 38 deletions
@@ -4,7 +4,7 @@ import "time"
const (
legacyDatabaseSchemaVersion = 1
currentDatabaseSchemaVersion = 12
currentDatabaseSchemaVersion = 13
databaseSchemaVersionRowID = 1
)
+2
View File
@@ -43,6 +43,8 @@ func registeredModels() []any {
&ManagedDomain{},
&AcmeAccount{},
&DnsAccount{},
&WAFRuleGroup{},
&WAFRuleGroupBinding{},
}
}
+66 -1
View File
@@ -1388,6 +1388,67 @@ func validateDatabaseSchemaV12(db *gorm.DB, backend string) error {
return nil
}
func ensureDefaultWAFRuleGroup(db *gorm.DB) error {
if db == nil {
return fmt.Errorf("database handle is nil")
}
if !db.Migrator().HasTable(&WAFRuleGroup{}) {
return nil
}
var count int64
if err := db.Model(&WAFRuleGroup{}).Where("is_global = ?", true).Count(&count).Error; err != nil {
return fmt.Errorf("count global waf rule groups failed: %w", err)
}
if count > 0 {
return nil
}
group := WAFRuleGroup{
Name: "全局规则组",
Enabled: true,
IsGlobal: true,
BlockStatusCode: 418,
IPWhitelist: "[]",
IPBlacklist: "[]",
CountryWhitelist: "[]",
CountryBlacklist: "[]",
RegionWhitelist: "[]",
RegionBlacklist: "[]",
BlockResponseBody: "",
}
if err := db.Create(&group).Error; err != nil {
return fmt.Errorf("create default waf rule group failed: %w", err)
}
return nil
}
// migrateV13 adds WAF rule groups and website bindings.
func migrateV13(db *gorm.DB, backend string) error {
if err := applyCurrentSchema(db, backend); err != nil {
return err
}
return ensureDefaultWAFRuleGroup(db)
}
func validateDatabaseSchemaV13(db *gorm.DB, backend string) error {
if err := validateDatabaseSchemaV12(db, backend); err != nil {
return err
}
if !db.Migrator().HasTable(&WAFRuleGroup{}) {
return fmt.Errorf("table waf_rule_groups is missing")
}
if !db.Migrator().HasTable(&WAFRuleGroupBinding{}) {
return fmt.Errorf("table waf_rule_group_bindings is missing")
}
var count int64
if err := db.Model(&WAFRuleGroup{}).Where("is_global = ?", true).Count(&count).Error; err != nil {
return fmt.Errorf("count global waf rule groups failed: %w", err)
}
if count != 1 {
return fmt.Errorf("expected exactly one global waf rule group, got %d", count)
}
return nil
}
func databaseSchemaMigrations() []databaseSchemaMigration {
return []databaseSchemaMigration{
{fromVersion: 1, toVersion: 2, migrate: migrateV2, validate: validateDatabaseSchemaV2},
@@ -1401,6 +1462,7 @@ func databaseSchemaMigrations() []databaseSchemaMigration {
{fromVersion: 9, toVersion: 10, migrate: migrateV10, validate: validateDatabaseSchemaV10},
{fromVersion: 10, toVersion: 11, migrate: migrateV11, validate: validateDatabaseSchemaV11},
{fromVersion: 11, toVersion: 12, migrate: migrateV12, validate: validateDatabaseSchemaV12},
{fromVersion: 12, toVersion: 13, migrate: migrateV13, validate: validateDatabaseSchemaV13},
}
}
@@ -1486,7 +1548,10 @@ func initializeFreshDatabaseSchema(db *gorm.DB, backend string) error {
if err := ensureDefaultGitHubAuthSource(db); err != nil {
return err
}
if err := validateDatabaseSchemaV12(db, backend); err != nil {
if err := ensureDefaultWAFRuleGroup(db); err != nil {
return err
}
if err := validateDatabaseSchemaV13(db, backend); err != nil {
return err
}
return saveDatabaseSchemaVersion(db, currentDatabaseSchemaVersion)
+71
View File
@@ -0,0 +1,71 @@
package model
import "time"
type WAFRuleGroup struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"size:255;not null"`
Enabled bool `json:"enabled" gorm:"not null;default:true"`
IsGlobal bool `json:"is_global" gorm:"not null;default:false;index"`
BlockStatusCode int `json:"block_status_code" gorm:"not null;default:418"`
BlockResponseBody string `json:"block_response_body" gorm:"type:text;not null;default:''"`
IPWhitelist string `json:"ip_whitelist" gorm:"type:text;not null;default:'[]'"`
IPBlacklist string `json:"ip_blacklist" gorm:"type:text;not null;default:'[]'"`
CountryWhitelist string `json:"country_whitelist" gorm:"type:text;not null;default:'[]'"`
CountryBlacklist string `json:"country_blacklist" gorm:"type:text;not null;default:'[]'"`
RegionWhitelist string `json:"region_whitelist" gorm:"type:text;not null;default:'[]'"`
RegionBlacklist string `json:"region_blacklist" gorm:"type:text;not null;default:'[]'"`
Remark string `json:"remark" gorm:"size:255"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type WAFRuleGroupBinding struct {
ID uint `json:"id" gorm:"primaryKey"`
RuleGroupID uint `json:"rule_group_id" gorm:"not null;uniqueIndex:idx_waf_group_route"`
ProxyRouteID uint `json:"proxy_route_id" gorm:"not null;uniqueIndex:idx_waf_group_route;index"`
CreatedAt time.Time `json:"created_at"`
}
func ListWAFRuleGroups() ([]*WAFRuleGroup, error) {
var groups []*WAFRuleGroup
err := DB.Order("is_global desc").Order("id asc").Find(&groups).Error
return groups, err
}
func GetWAFRuleGroupByID(id uint) (*WAFRuleGroup, error) {
group := &WAFRuleGroup{}
err := DB.First(group, id).Error
return group, err
}
func GetGlobalWAFRuleGroup() (*WAFRuleGroup, error) {
group := &WAFRuleGroup{}
err := DB.Where("is_global = ?", true).Order("id asc").First(group).Error
return group, err
}
func (group *WAFRuleGroup) Insert() error {
return DB.Create(group).Error
}
func (group *WAFRuleGroup) Update() error {
return DB.Model(&WAFRuleGroup{}).Where("id = ?", group.ID).Updates(map[string]any{
"name": group.Name,
"enabled": group.Enabled,
"is_global": group.IsGlobal,
"block_status_code": group.BlockStatusCode,
"block_response_body": group.BlockResponseBody,
"ip_whitelist": group.IPWhitelist,
"ip_blacklist": group.IPBlacklist,
"country_whitelist": group.CountryWhitelist,
"country_blacklist": group.CountryBlacklist,
"region_whitelist": group.RegionWhitelist,
"region_blacklist": group.RegionBlacklist,
"remark": group.Remark,
}).Error
}
func (group *WAFRuleGroup) Delete() error {
return DB.Delete(group).Error
}