diff --git a/internal/migrate/migrate.go b/internal/migrate/migrate.go index 1c95561..99c117e 100644 --- a/internal/migrate/migrate.go +++ b/internal/migrate/migrate.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/dushixiang/pika/internal/migrate/v0_1_1" + "github.com/dushixiang/pika/internal/migrate/v0_1_2" "github.com/dushixiang/pika/internal/service" "github.com/dushixiang/pika/pkg/version" "go.uber.org/zap" @@ -23,6 +24,12 @@ func AutoMigrate(logger *zap.Logger, db *gorm.DB, propertyService *service.Prope return err } } + // 升级到 v0.1.2 版本 + if strings.Compare(localVersion, "v0.1.2") < 0 { + if err := v0_1_2.Migrate(logger, db); err != nil { + return err + } + } return propertyService.SetSystemVersion(ctx, version.Version) } diff --git a/internal/migrate/v0_1_2/migrate.go b/internal/migrate/v0_1_2/migrate.go new file mode 100644 index 0000000..d6817ac --- /dev/null +++ b/internal/migrate/v0_1_2/migrate.go @@ -0,0 +1,110 @@ +package v0_1_2 + +import ( + "strings" + + "github.com/dushixiang/pika/internal/models" + "go.uber.org/zap" + "gorm.io/gorm" +) + +func Migrate(logger *zap.Logger, db *gorm.DB) error { + // 移除 ddns_configs 表的 enable_ipv4,enable_ipv6 字段的默认值 + + logger.Info("开始执行 v0.1.2 版本数据迁移") + + migrator := db.Migrator() + if migrator == nil { + logger.Warn("无法获取数据库 migrator,跳过迁移") + return nil + } + + if !migrator.HasTable("ddns_configs") { + logger.Info("未检测到 ddns_configs 表,跳过迁移") + return nil + } + + switch db.Dialector.Name() { + case "postgres": + if err := dropPostgresDefaults(logger, db, migrator); err != nil { + return err + } + case "sqlite": + if err := rebuildSQLiteDDNSConfigs(logger, db, migrator); err != nil { + return err + } + default: + logger.Warn("未知数据库类型,跳过移除默认值") + } + + logger.Info("v0.1.2 版本数据迁移完成") + return nil +} + +func dropPostgresDefaults(logger *zap.Logger, db *gorm.DB, migrator gorm.Migrator) error { + columns := []string{"enable_ipv4", "enable_ipv6"} + for _, column := range columns { + if !migrator.HasColumn("ddns_configs", column) { + logger.Info("未检测到字段,跳过处理", zap.String("column", column)) + continue + } + sql := "ALTER TABLE ddns_configs ALTER COLUMN " + column + " DROP DEFAULT" + if err := db.Exec(sql).Error; err != nil { + logger.Error("移除字段默认值失败", zap.String("column", column), zap.Error(err)) + return err + } + logger.Info("已移除字段默认值", zap.String("column", column)) + } + return nil +} + +func rebuildSQLiteDDNSConfigs(logger *zap.Logger, db *gorm.DB, migrator gorm.Migrator) error { + tempTable := "ddns_configs_backup_v0_1_2" + if migrator.HasTable(tempTable) { + logger.Warn("检测到残留备份表,跳过重建以避免覆盖", zap.String("table", tempTable)) + return nil + } + + logger.Info("SQLite 不支持直接删除默认值,开始重建 ddns_configs 表") + if err := db.Exec("ALTER TABLE ddns_configs RENAME TO " + tempTable).Error; err != nil { + logger.Error("重命名 ddns_configs 表失败", zap.Error(err)) + return err + } + + if err := db.AutoMigrate(&models.DDNSConfig{}); err != nil { + logger.Error("重建 ddns_configs 表失败", zap.Error(err)) + return err + } + + columns := []string{ + "id", + "agent_id", + "name", + "enabled", + "provider", + "domains_ipv4", + "domains_ipv6", + "enable_ipv4", + "enable_ipv6", + "ipv4_get_method", + "ipv6_get_method", + "ipv4_get_value", + "ipv6_get_value", + "created_at", + "updated_at", + } + columnList := strings.Join(columns, ", ") + copySQL := "INSERT INTO ddns_configs (" + columnList + ") SELECT " + columnList + " FROM " + tempTable + if err := db.Exec(copySQL).Error; err != nil { + logger.Error("迁移 ddns_configs 数据失败", zap.Error(err)) + return err + } + + if err := migrator.DropTable(tempTable); err != nil { + logger.Error("删除备份表失败", zap.Error(err)) + return err + } + + logger.Info("ddns_configs 表重建完成") + return nil +} diff --git a/internal/models/ddns.go b/internal/models/ddns.go index 88050cc..9988856 100644 --- a/internal/models/ddns.go +++ b/internal/models/ddns.go @@ -4,23 +4,23 @@ import "gorm.io/datatypes" // DDNSConfig DDNS 配置 type DDNSConfig struct { - ID string `gorm:"primaryKey" json:"id"` // 配置ID (UUID) - AgentID string `gorm:"index" json:"agentId"` // 探针ID - Name string `json:"name"` // 配置名称 - Enabled bool `gorm:"default:true" json:"enabled"` // 是否启用 - Provider string `gorm:"index" json:"provider"` // DNS服务商类型: aliyun, tencentcloud, cloudflare, huaweicloud + ID string `gorm:"primaryKey" json:"id"` // 配置ID (UUID) + AgentID string `gorm:"index" json:"agentId"` // 探针ID + Name string `json:"name"` // 配置名称 + Enabled bool `json:"enabled"` // 是否启用 + Provider string `gorm:"index" json:"provider"` // DNS服务商类型: aliyun, tencentcloud, cloudflare, huaweicloud // 域名配置(IPv4 和 IPv6 分开) DomainsIPv4 datatypes.JSONSlice[string] `json:"domainsIpv4"` // IPv4 域名列表 DomainsIPv6 datatypes.JSONSlice[string] `json:"domainsIpv6"` // IPv6 域名列表 // IP 获取配置 - EnableIPv4 bool `gorm:"default:true" json:"enableIpv4"` // 是否启用 IPv4 - EnableIPv6 bool `gorm:"default:false" json:"enableIpv6"` // 是否启用 IPv6 - IPv4GetMethod string `json:"ipv4GetMethod"` // IPv4 获取方式: api, interface - IPv6GetMethod string `json:"ipv6GetMethod"` // IPv6 获取方式: api, interface - IPv4GetValue string `json:"ipv4GetValue,omitempty"` // IPv4 获取配置值(接口名/API URL) - IPv6GetValue string `json:"ipv6GetValue,omitempty"` // IPv6 获取配置值(接口名/API URL) + EnableIPv4 bool `json:"enableIpv4"` // 是否启用 IPv4 + EnableIPv6 bool `json:"enableIpv6"` // 是否启用 IPv6 + IPv4GetMethod string `json:"ipv4GetMethod"` // IPv4 获取方式: api, interface + IPv6GetMethod string `json:"ipv6GetMethod"` // IPv6 获取方式: api, interface + IPv4GetValue string `json:"ipv4GetValue,omitempty"` // IPv4 获取配置值(接口名/API URL) + IPv6GetValue string `json:"ipv6GetValue,omitempty"` // IPv6 获取配置值(接口名/API URL) CreatedAt int64 `json:"createdAt"` // 创建时间(时间戳毫秒) UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime:milli"` // 更新时间(时间戳毫秒)