[优化] go 引用调整

This commit is contained in:
ryan
2026-06-06 10:26:20 +08:00
parent ee1110b752
commit 3cfefb4367
552 changed files with 1642 additions and 2185 deletions
@@ -0,0 +1,4 @@
package migrate
// Versions 1 through 7 are treated as the historical baseline. There are no
// supported deployments below v8, so new upgrades start from this base version.
@@ -0,0 +1,54 @@
package migrate
import (
"sort"
"gorm.io/gorm"
)
const BaseDatabaseSchemaVersion = 7
type Context interface {
ApplyCurrentSchema(db *gorm.DB, backend string) error
ApplyCurrentSchemaExcept(db *gorm.DB, backend string, excludedTables ...string) error
BackfillOriginsFromProxyRoutes(db *gorm.DB) error
BackfillProxyRouteSiteFields(db *gorm.DB) error
EnsureProxyRouteSiteNameUniqueIndex(db *gorm.DB) error
BackfillProxyRouteCertificateFields(db *gorm.DB) error
BackfillProxyRouteDomainCertificateFields(db *gorm.DB) error
EnsureDefaultGitHubAuthSource(db *gorm.DB) error
EnsureDefaultWAFRuleGroup(db *gorm.DB) error
DropLegacyNodeColumns(db *gorm.DB, backend string) error
ValidateDatabaseSchemaVersion(db *gorm.DB, backend string, version int) error
}
type Migration struct {
FromVersion int
ToVersion int
Migrate func(ctx Context, db *gorm.DB, backend string) error
Validate func(ctx Context, db *gorm.DB, backend string) error
}
var registeredMigrations []Migration
func Register(migration Migration) {
registeredMigrations = append(registeredMigrations, migration)
}
func Migrations() []Migration {
migrations := append([]Migration{}, registeredMigrations...)
sort.Slice(migrations, func(i int, j int) bool {
return migrations[i].FromVersion < migrations[j].FromVersion
})
return migrations
}
func CurrentVersion() int {
version := BaseDatabaseSchemaVersion
for _, migration := range registeredMigrations {
if migration.ToVersion > version {
version = migration.ToVersion
}
}
return version
}
@@ -0,0 +1,23 @@
package migrate
import "testing"
func TestMigrationsAreContinuousFromBaseVersion(t *testing.T) {
migrations := Migrations()
if len(migrations) == 0 {
t.Fatal("expected at least one registered migration")
}
expectedFrom := BaseDatabaseSchemaVersion
for _, migration := range migrations {
if migration.FromVersion != expectedFrom {
t.Fatalf("expected migration from v%d, got v%d -> v%d", expectedFrom, migration.FromVersion, migration.ToVersion)
}
if migration.ToVersion != migration.FromVersion+1 {
t.Fatalf("expected one-step migration, got v%d -> v%d", migration.FromVersion, migration.ToVersion)
}
expectedFrom = migration.ToVersion
}
if CurrentVersion() != expectedFrom {
t.Fatalf("unexpected current version: got %d want %d", CurrentVersion(), expectedFrom)
}
}
+26
View File
@@ -0,0 +1,26 @@
// v10 升级内容:新增可配置认证源与第三方账号绑定,并迁移旧 GitHub 登录配置。
// 背景说明:登录体系从固定 GitHub OAuth 字段演进为通用认证源模型,需要创建 auth_sources、external_accounts,并把旧用户 GitHub 绑定迁移到新表。
package migrate
import "gorm.io/gorm"
func init() {
Register(V10())
}
func V10() Migration {
return Migration{
FromVersion: 9,
ToVersion: 10,
Migrate: migrateV10,
Validate: validateV10,
}
}
func migrateV10(ctx Context, db *gorm.DB, backend string) error {
return ctx.EnsureDefaultGitHubAuthSource(db)
}
func validateV10(ctx Context, db *gorm.DB, backend string) error {
return ctx.ValidateDatabaseSchemaVersion(db, backend, 10)
}
+26
View File
@@ -0,0 +1,26 @@
// v11 升级内容:新增 ACME 账户、DNS 账户,并扩展证书 provider 字段。
// 背景说明:证书申请能力从单一手工导入扩展到自动签发,需要持久化 ACME/DNS 凭据,并标记证书来源。
package migrate
import "gorm.io/gorm"
func init() {
Register(V11())
}
func V11() Migration {
return Migration{
FromVersion: 10,
ToVersion: 11,
Migrate: migrateV11,
Validate: validateV11,
}
}
func migrateV11(ctx Context, db *gorm.DB, backend string) error {
return nil
}
func validateV11(ctx Context, db *gorm.DB, backend string) error {
return ctx.ValidateDatabaseSchemaVersion(db, backend, 11)
}
+26
View File
@@ -0,0 +1,26 @@
// v12 升级内容:为 proxy_routes 增加 Basic Auth 相关字段。
// 背景说明:站点级访问控制需要支持基础认证,因此在代理路由配置中持久化 Basic Auth 开关与凭据配置。
package migrate
import "gorm.io/gorm"
func init() {
Register(V12())
}
func V12() Migration {
return Migration{
FromVersion: 11,
ToVersion: 12,
Migrate: migrateV12,
Validate: validateV12,
}
}
func migrateV12(ctx Context, db *gorm.DB, backend string) error {
return nil
}
func validateV12(ctx Context, db *gorm.DB, backend string) error {
return ctx.ValidateDatabaseSchemaVersion(db, backend, 12)
}
+26
View File
@@ -0,0 +1,26 @@
// v13 升级内容:新增 WAF 规则组与站点绑定表,并创建默认全局规则组。
// 背景说明:WAF 配置从零散站点字段演进为可复用规则组,需要全局规则组作为默认入口,并支持站点与规则组绑定。
package migrate
import "gorm.io/gorm"
func init() {
Register(V13())
}
func V13() Migration {
return Migration{
FromVersion: 12,
ToVersion: 13,
Migrate: migrateV13,
Validate: validateV13,
}
}
func migrateV13(ctx Context, db *gorm.DB, backend string) error {
return ctx.EnsureDefaultWAFRuleGroup(db)
}
func validateV13(ctx Context, db *gorm.DB, backend string) error {
return ctx.ValidateDatabaseSchemaVersion(db, backend, 13)
}
+26
View File
@@ -0,0 +1,26 @@
// v14 升级内容:为 WAF 规则组增加 PoW 策略字段。
// 背景说明:PoW 能力从站点路由侧沉淀到 WAF 规则组中,便于统一按规则组管理人机挑战策略。
package migrate
import "gorm.io/gorm"
func init() {
Register(V14())
}
func V14() Migration {
return Migration{
FromVersion: 13,
ToVersion: 14,
Migrate: migrateV14,
Validate: validateV14,
}
}
func migrateV14(ctx Context, db *gorm.DB, backend string) error {
return ctx.EnsureDefaultWAFRuleGroup(db)
}
func validateV14(ctx Context, db *gorm.DB, backend string) error {
return ctx.ValidateDatabaseSchemaVersion(db, backend, 14)
}
+52
View File
@@ -0,0 +1,52 @@
// v15 升级内容:为 nodes 增加 ip_manual_override 字段。
// 背景说明:管理端手动指定节点 IP 后,Agent 心跳不应继续覆盖该值,因此需要在节点表中记录 IP 是否由管理端锁定。
package migrate
import (
"fmt"
"gorm.io/gorm"
)
type nodeV15 struct {
IPManualOverride bool `gorm:"column:ip_manual_override;not null;default:false"`
}
func init() {
Register(V15())
}
func V15() Migration {
return Migration{
FromVersion: 14,
ToVersion: 15,
Migrate: migrateV15,
Validate: validateV15,
}
}
func (nodeV15) TableName() string {
return "nodes"
}
func migrateV15(ctx Context, db *gorm.DB, backend string) error {
if db == nil {
return fmt.Errorf("database handle is nil")
}
if !db.Migrator().HasColumn(&nodeV15{}, "ip_manual_override") {
if err := db.Migrator().AddColumn(&nodeV15{}, "IPManualOverride"); err != nil {
return fmt.Errorf("add nodes.ip_manual_override: %w", err)
}
}
return nil
}
func validateV15(ctx Context, db *gorm.DB, backend string) error {
if err := ctx.ValidateDatabaseSchemaVersion(db, backend, 14); err != nil {
return err
}
if db == nil || !db.Migrator().HasColumn(&nodeV15{}, "ip_manual_override") {
return fmt.Errorf("column nodes.ip_manual_override is missing")
}
return nil
}
+185
View File
@@ -0,0 +1,185 @@
// v16 is the first database migration after the V15 formal release baseline.
// It folds the previously drafted v16-v21 schema work into a single official
// upgrade: tunnel-relay fields, WAF IP groups, current node identity/version
// columns, and split node observation tables. The migration also backfills
// legacy node columns and removes obsolete pre-release tunnel metadata when
// present, so V15 deployments can upgrade directly to the new formal schema.
package migrate
import (
"fmt"
"log/slog"
"gorm.io/gorm"
)
func (nodeV16) TableName() string {
return "nodes"
}
func (tunnelV16) TableName() string {
return "tunnels"
}
func (proxyRouteV16) TableName() string {
return "proxy_routes"
}
type nodeV16 struct{}
type tunnelV16 struct{}
type proxyRouteV16 struct{}
type wafIPGroupV16 struct{}
type wafRuleGroupV16 struct{}
func (wafIPGroupV16) TableName() string {
return "waf_ip_groups"
}
func (wafRuleGroupV16) TableName() string {
return "waf_rule_groups"
}
func init() {
Register(V16())
}
func V16() Migration {
return Migration{
FromVersion: 15,
ToVersion: 16,
Migrate: migrateV16,
Validate: validateV16,
}
}
func migrateV16(ctx Context, db *gorm.DB, backend string) error {
if err := ctx.ApplyCurrentSchema(db, backend); err != nil {
return err
}
migrator := db.Migrator()
if migrator.HasColumn(&nodeV16{}, "agent_token") {
if err := db.Exec(`UPDATE nodes SET access_token = agent_token WHERE access_token IS NULL OR access_token = ''`).Error; err != nil {
return fmt.Errorf("backfill nodes.access_token from agent_token: %w", err)
}
}
if migrator.HasColumn(&nodeV16{}, "agent_version") {
if err := db.Exec(`UPDATE nodes SET version = agent_version WHERE version = '' OR version IS NULL`).Error; err != nil {
return fmt.Errorf("backfill nodes.version from agent_version: %w", err)
}
}
if migrator.HasColumn(&nodeV16{}, "nginx_version") {
if err := db.Exec(`UPDATE nodes SET ext_version = nginx_version WHERE ext_version IS NULL OR ext_version = ''`).Error; err != nil {
return fmt.Errorf("backfill nodes.ext_version from nginx_version: %w", err)
}
}
if err := ctx.DropLegacyNodeColumns(db, backend); err != nil {
return err
}
if err := db.Exec("UPDATE nodes SET node_type = 'edge_node' WHERE node_type = '' OR node_type IS NULL").Error; err != nil {
return fmt.Errorf("backfill nodes.node_type: %w", err)
}
if err := db.Exec("UPDATE proxy_routes SET upstream_type = 'direct' WHERE upstream_type = '' OR upstream_type IS NULL").Error; err != nil {
return fmt.Errorf("backfill proxy_routes.upstream_type: %w", err)
}
if migrator.HasColumn(&proxyRouteV16{}, "tunnel_id") {
if err := db.Model(&proxyRouteV16{}).Where("upstream_type = ?", "tunnel").Update("upstream_type", "direct").Error; err != nil {
return fmt.Errorf("reset pre-release tunnel proxy routes: %w", err)
}
// Drop the legacy index idx_proxy_routes_tunnel_id if it exists, to avoid errors on dropping the tunnel_id column (especially on SQLite).
if migrator.HasIndex(&proxyRouteV16{}, "idx_proxy_routes_tunnel_id") {
if err := migrator.DropIndex(&proxyRouteV16{}, "idx_proxy_routes_tunnel_id"); err != nil {
return fmt.Errorf("drop index idx_proxy_routes_tunnel_id failed: %w", err)
}
}
if err := migrator.DropColumn(&proxyRouteV16{}, "tunnel_id"); err != nil {
return fmt.Errorf("drop pre-release proxy_routes.tunnel_id: %w", err)
}
}
if migrator.HasTable(&tunnelV16{}) {
if err := migrator.DropTable(&tunnelV16{}); err != nil {
return fmt.Errorf("drop pre-release tunnels table: %w", err)
}
slog.Info("dropped pre-release tunnels table during v16 migration")
}
return nil
}
func validateV16(ctx Context, db *gorm.DB, backend string) error {
if err := ctx.ValidateDatabaseSchemaVersion(db, backend, 15); err != nil {
return err
}
if db == nil {
return fmt.Errorf("database handle is nil")
}
migrator := db.Migrator()
for _, column := range []string{
"access_token",
"version",
"ext_version",
"node_type",
"relay_bind_port",
"relay_vhost_http_port",
"relay_auth_token",
"relay_agent_access_addr",
"relay_client_access_addr",
"relay_client_proxy_url",
"relay_status",
} {
if !migrator.HasColumn(&nodeV16{}, column) {
return fmt.Errorf("column nodes.%s is missing", column)
}
}
for _, column := range []string{
"upstream_type",
"tunnel_node_id",
"tunnel_target_addr",
"tunnel_target_protocol",
} {
if !migrator.HasColumn(&proxyRouteV16{}, column) {
return fmt.Errorf("column proxy_routes.%s is missing", column)
}
}
if migrator.HasColumn(&proxyRouteV16{}, "tunnel_id") {
return fmt.Errorf("column proxy_routes.tunnel_id should not exist in v16")
}
if migrator.HasTable(&tunnelV16{}) {
return fmt.Errorf("table tunnels should not exist in v16")
}
for _, column := range []string{
"agent_token",
"agent_version",
"nginx_version",
"relay_version",
"relay_frp_version",
"relay_frps_connections",
"relay_frps_proxy_count",
} {
if migrator.HasColumn(&nodeV16{}, column) {
return fmt.Errorf("column nodes.%s should not exist in v16", column)
}
}
if !migrator.HasTable(&wafIPGroupV16{}) {
return fmt.Errorf("table waf_ip_groups is missing")
}
for _, column := range []string{
"ip_whitelist_groups",
"ip_blacklist_groups",
} {
if !migrator.HasColumn(&wafRuleGroupV16{}, column) {
return fmt.Errorf("column waf_rule_groups.%s is missing", column)
}
}
if !migrator.HasColumn(&wafIPGroupV16{}, "ext_ips") {
return fmt.Errorf("column waf_ip_groups.ext_ips is missing")
}
return nil
}
+58
View File
@@ -0,0 +1,58 @@
package migrate
import (
"fmt"
"gorm.io/gorm"
)
type nodeV17 struct{}
func (nodeV17) TableName() string {
return "nodes"
}
func init() {
Register(V17())
}
func V17() Migration {
return Migration{
FromVersion: 16,
ToVersion: 17,
Migrate: migrateV17,
Validate: validateV17,
}
}
func migrateV17(ctx Context, db *gorm.DB, backend string) error {
if err := ctx.ApplyCurrentSchema(db, backend); err != nil {
return err
}
return nil
}
func validateV17(ctx Context, db *gorm.DB, backend string) error {
if err := ctx.ValidateDatabaseSchemaVersion(db, backend, 16); err != nil {
return err
}
if db == nil {
return fmt.Errorf("database handle is nil")
}
migrator := db.Migrator()
if !migrator.HasColumn(&nodeV17{}, "relay_web_server_enabled") {
return fmt.Errorf("column nodes.relay_web_server_enabled is missing")
}
// Validate columns on a sharded partition table
for _, shard := range []string{"node_observation_frps_00"} {
for _, column := range []string{"frps_client_count", "frps_proxies"} {
if !migrator.HasColumn(shard, column) {
return fmt.Errorf("column %s.%s is missing", shard, column)
}
}
}
return nil
}
+41
View File
@@ -0,0 +1,41 @@
// v8 升级内容:为 proxy_routes 增加域名级证书绑定字段 domain_cert_ids,并回填已有站点的证书映射。
// 背景说明:v1-v7 已作为历史初始基线合并;v8 是当前保留逐版本升级链的起点,用于把早期站点级证书列表扩展为每个域名可独立绑定证书。
package migrate
import "gorm.io/gorm"
func init() {
Register(V8())
}
func V8() Migration {
return Migration{
FromVersion: 7,
ToVersion: 8,
Migrate: migrateV8,
Validate: validateV8,
}
}
func migrateV8(ctx Context, db *gorm.DB, backend string) error {
if err := ctx.ApplyCurrentSchema(db, backend); err != nil {
return err
}
if err := ctx.BackfillOriginsFromProxyRoutes(db); err != nil {
return err
}
if err := ctx.BackfillProxyRouteSiteFields(db); err != nil {
return err
}
if err := ctx.EnsureProxyRouteSiteNameUniqueIndex(db); err != nil {
return err
}
if err := ctx.BackfillProxyRouteCertificateFields(db); err != nil {
return err
}
return ctx.BackfillProxyRouteDomainCertificateFields(db)
}
func validateV8(ctx Context, db *gorm.DB, backend string) error {
return ctx.ValidateDatabaseSchemaVersion(db, backend, 8)
}
+29
View File
@@ -0,0 +1,29 @@
// v9 升级内容:为 proxy_routes 增加 PoW 防护配置字段。
// 背景说明:反向代理站点需要支持 Proof-of-Work 抗机器人能力,因此在路由配置中持久化 PoW 开关与策略,并沿用 v8 的证书与站点字段回填。
package migrate
import "gorm.io/gorm"
func init() {
Register(V9())
}
func V9() Migration {
return Migration{
FromVersion: 8,
ToVersion: 9,
Migrate: migrateV9,
Validate: validateV9,
}
}
func migrateV9(ctx Context, db *gorm.DB, backend string) error {
if err := migrateV8(ctx, db, backend); err != nil {
return err
}
return nil
}
func validateV9(ctx Context, db *gorm.DB, backend string) error {
return ctx.ValidateDatabaseSchemaVersion(db, backend, 9)
}