fix: 修复 WAF 规则组保存/绑定网站时报 of_waf_rule_group_bindings_pkey 冲突

This commit is contained in:
ryan
2026-06-20 21:33:46 +08:00
parent b04a358e5e
commit 99f6f3231a
6 changed files with 114 additions and 10 deletions
+2
View File
@@ -20,6 +20,8 @@ sidebar: false
- 配置版本列表按 `created_at` 倒序展示,最新发布版本固定显示在列表顶部。
- 修复 WAF 规则组保存/绑定网站时报 `of_waf_rule_group_bindings_pkey` 冲突:PostgreSQL 在迁移导入显式 ID 后同步绑定表序列,并在写入前自动校正序列。
- 修复 WAF 规则组 PoW 策略发布后边缘不生效:统一 WAF 绑定站点名与 OpenResty 路由 `site_name` 解析逻辑,并为所有已启用网站生成 `site_rule_groups` 条目(含仅依赖全局规则组的站点)。
- 收敛子代理站点标识双轨逻辑:新增 `routeidentity` 统一包,`proxy_route`、`config_version`、`uptimekuma`、`flared` 与 OpenResty 渲染共用 `ResolveSiteName` / `DecodeDomains`;移除废弃 `RenderPoWConfig`;PoW Lua 与 WAF 一致仅依赖 `$openflare_waf_site`。
@@ -98,6 +98,17 @@ func up202606200006(ctx context.Context, tx *sql.Tx) error {
}
}
if dialect == dialectPostgres {
if _, err := tx.ExecContext(ctx, `
SELECT setval(
pg_get_serial_sequence('of_waf_rule_group_bindings', 'id'),
COALESCE((SELECT MAX(id) FROM of_waf_rule_group_bindings), 0)
)
`); err != nil {
return fmt.Errorf("sync of_waf_rule_group_bindings sequence failed: %w", err)
}
}
return nil
}
@@ -0,0 +1,8 @@
-- +goose Up
SELECT setval(
pg_get_serial_sequence('of_waf_rule_group_bindings', 'id'),
COALESCE((SELECT MAX(id) FROM of_waf_rule_group_bindings), 0)
);
-- +goose Down
SELECT 1;
@@ -0,0 +1,5 @@
-- +goose Up
SELECT 1;
-- +goose Down
SELECT 1;
+34 -10
View File
@@ -312,6 +312,28 @@ func ListOpenFlareWAFRuleGroupBindingsByRouteID(ctx context.Context, routeID uin
return bindings, nil
}
func syncWAFBindingIDSequence(tx *gorm.DB) error {
if tx == nil || tx.Dialector.Name() != "postgres" {
return nil
}
return tx.Exec(`
SELECT setval(
pg_get_serial_sequence('of_waf_rule_group_bindings', 'id'),
COALESCE((SELECT MAX(id) FROM of_waf_rule_group_bindings), 0)
)
`).Error
}
func insertOpenFlareWAFRuleGroupBindings(tx *gorm.DB, bindings []OpenFlareWAFRuleGroupBinding) error {
if len(bindings) == 0 {
return nil
}
if err := syncWAFBindingIDSequence(tx); err != nil {
return err
}
return tx.Create(&bindings).Error
}
// ReplaceOpenFlareWAFRuleGroupBindings replaces bindings for a rule group.
func ReplaceOpenFlareWAFRuleGroupBindings(ctx context.Context, groupID uint, routeIDs []uint) error {
conn, err := wafDB(ctx)
@@ -322,13 +344,14 @@ func ReplaceOpenFlareWAFRuleGroupBindings(ctx context.Context, groupID uint, rou
if err = tx.Where("rule_group_id = ?", groupID).Delete(&OpenFlareWAFRuleGroupBinding{}).Error; err != nil {
return err
}
bindings := make([]OpenFlareWAFRuleGroupBinding, 0, len(routeIDs))
for _, routeID := range routeIDs {
binding := OpenFlareWAFRuleGroupBinding{RuleGroupID: groupID, ProxyRouteID: routeID}
if err = tx.Create(&binding).Error; err != nil {
return err
}
bindings = append(bindings, OpenFlareWAFRuleGroupBinding{
RuleGroupID: groupID,
ProxyRouteID: routeID,
})
}
return nil
return insertOpenFlareWAFRuleGroupBindings(tx, bindings)
})
}
@@ -342,13 +365,14 @@ func ReplaceOpenFlareWAFSiteRuleGroupBindings(ctx context.Context, routeID uint,
if err = tx.Where("proxy_route_id = ?", routeID).Delete(&OpenFlareWAFRuleGroupBinding{}).Error; err != nil {
return err
}
bindings := make([]OpenFlareWAFRuleGroupBinding, 0, len(groupIDs))
for _, groupID := range groupIDs {
binding := OpenFlareWAFRuleGroupBinding{RuleGroupID: groupID, ProxyRouteID: routeID}
if err = tx.Create(&binding).Error; err != nil {
return err
}
bindings = append(bindings, OpenFlareWAFRuleGroupBinding{
RuleGroupID: groupID,
ProxyRouteID: routeID,
})
}
return nil
return insertOpenFlareWAFRuleGroupBindings(tx, bindings)
})
}
@@ -0,0 +1,54 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package model
import (
"context"
"testing"
"github.com/Rain-kl/Wavelet/internal/db"
"github.com/glebarez/sqlite"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
func setupWAFBindingsTestDB(t *testing.T) func() {
t.Helper()
sqliteDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
require.NoError(t, err)
require.NoError(t, sqliteDB.AutoMigrate(&OpenFlareWAFRuleGroupBinding{}))
db.SetDB(sqliteDB)
return func() {
db.SetDB(nil)
}
}
func TestReplaceOpenFlareWAFRuleGroupBindingsAfterExplicitHighID(t *testing.T) {
cleanup := setupWAFBindingsTestDB(t)
defer cleanup()
ctx := context.Background()
conn := db.DB(ctx)
require.NotNil(t, conn)
require.NoError(t, conn.Create(&OpenFlareWAFRuleGroupBinding{
ID: 50,
RuleGroupID: 1,
ProxyRouteID: 1,
}).Error)
require.NoError(t, ReplaceOpenFlareWAFRuleGroupBindings(ctx, 2, []uint{2, 3}))
var bindings []OpenFlareWAFRuleGroupBinding
require.NoError(t, conn.Where("rule_group_id = ?", 2).Order("proxy_route_id asc").Find(&bindings).Error)
require.Len(t, bindings, 2)
assert.Equal(t, uint(2), bindings[0].ProxyRouteID)
assert.Equal(t, uint(3), bindings[1].ProxyRouteID)
assert.Greater(t, bindings[0].ID, uint(50))
assert.Greater(t, bindings[1].ID, bindings[0].ID)
}