mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 16:46:37 +08:00
fix: 修复 WAF 规则组保存/绑定网站时报 of_waf_rule_group_bindings_pkey 冲突
This commit is contained in:
@@ -20,6 +20,8 @@ sidebar: false
|
|||||||
|
|
||||||
- 配置版本列表按 `created_at` 倒序展示,最新发布版本固定显示在列表顶部。
|
- 配置版本列表按 `created_at` 倒序展示,最新发布版本固定显示在列表顶部。
|
||||||
|
|
||||||
|
- 修复 WAF 规则组保存/绑定网站时报 `of_waf_rule_group_bindings_pkey` 冲突:PostgreSQL 在迁移导入显式 ID 后同步绑定表序列,并在写入前自动校正序列。
|
||||||
|
|
||||||
- 修复 WAF 规则组 PoW 策略发布后边缘不生效:统一 WAF 绑定站点名与 OpenResty 路由 `site_name` 解析逻辑,并为所有已启用网站生成 `site_rule_groups` 条目(含仅依赖全局规则组的站点)。
|
- 修复 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`。
|
- 收敛子代理站点标识双轨逻辑:新增 `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
|
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
|
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.
|
// ReplaceOpenFlareWAFRuleGroupBindings replaces bindings for a rule group.
|
||||||
func ReplaceOpenFlareWAFRuleGroupBindings(ctx context.Context, groupID uint, routeIDs []uint) error {
|
func ReplaceOpenFlareWAFRuleGroupBindings(ctx context.Context, groupID uint, routeIDs []uint) error {
|
||||||
conn, err := wafDB(ctx)
|
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 {
|
if err = tx.Where("rule_group_id = ?", groupID).Delete(&OpenFlareWAFRuleGroupBinding{}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
bindings := make([]OpenFlareWAFRuleGroupBinding, 0, len(routeIDs))
|
||||||
for _, routeID := range routeIDs {
|
for _, routeID := range routeIDs {
|
||||||
binding := OpenFlareWAFRuleGroupBinding{RuleGroupID: groupID, ProxyRouteID: routeID}
|
bindings = append(bindings, OpenFlareWAFRuleGroupBinding{
|
||||||
if err = tx.Create(&binding).Error; err != nil {
|
RuleGroupID: groupID,
|
||||||
return err
|
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 {
|
if err = tx.Where("proxy_route_id = ?", routeID).Delete(&OpenFlareWAFRuleGroupBinding{}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
bindings := make([]OpenFlareWAFRuleGroupBinding, 0, len(groupIDs))
|
||||||
for _, groupID := range groupIDs {
|
for _, groupID := range groupIDs {
|
||||||
binding := OpenFlareWAFRuleGroupBinding{RuleGroupID: groupID, ProxyRouteID: routeID}
|
bindings = append(bindings, OpenFlareWAFRuleGroupBinding{
|
||||||
if err = tx.Create(&binding).Error; err != nil {
|
RuleGroupID: groupID,
|
||||||
return err
|
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