mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 14:06:36 +08:00
fix: 修复 WAF 规则组保存/绑定网站时报 of_waf_rule_group_bindings_pkey 冲突
This commit is contained in:
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user