Compare commits

..

2 Commits

Author SHA1 Message Date
sagit e7b777890e fix(backend): rename legacy postgres unique constraints before AutoMigrate (#138)
Old schema.sql created tables with inline UNIQUE column constraints,
which PostgreSQL auto-names as <table>_<column>_key. GORM expects
uni_<table>_<column> (its NamingStrategy convention). On upgrade,
AutoMigrate issued DROP CONSTRAINT uni_... against a name that did not
exist, crashing startup with SQLSTATE 42704.

Add preparePostgresLegacySchema() that runs before autoMigrateAll and
renames all five mismatched constraints:
- vite_config_name_key -> uni_vite_config_name
- peer_share_token_key -> uni_peer_share_token
- peer_share_runtime_reservation_id_key -> uni_peer_share_runtime_reservation_id
- peer_share_runtime_resource_key_key -> uni_peer_share_runtime_resource_key
- federation_tunnel_binding_resource_key_key -> uni_federation_tunnel_binding_resource_key

The function is idempotent: it checks information_schema before each
rename so re-runs on already-migrated databases are no-ops.

Co-authored-by: Antigravity <antigravity@google.com>
2026-02-18 18:44:37 +08:00
Misaka Master 5b03ce87ff feat(tunnel): 支持 0~1 浮点倍率输入 (#137)
Co-authored-by: ZJU-Inno-WMX <wumingxuan@zju.edu.cn>
2026-02-18 18:01:18 +08:00
2 changed files with 54 additions and 3 deletions
@@ -127,6 +127,11 @@ func OpenPostgres(dsn string) (*Repository, error) {
return nil, err
}
if err := preparePostgresLegacySchema(db); err != nil {
_ = sqlDB.Close()
return nil, fmt.Errorf("prepare postgres legacy schema: %w", err)
}
if err := autoMigrateAll(db); err != nil {
_ = sqlDB.Close()
return nil, fmt.Errorf("auto migrate: %w", err)
@@ -205,6 +210,49 @@ func autoMigrateAll(db *gorm.DB) error {
return nil
}
// preparePostgresLegacySchema renames unique constraints that were created by
// the old schema.sql (which used inline UNIQUE column syntax) to the names
// expected by GORM's NamingStrategy ("uni_<table>_<column>"). Without this,
// GORM's AutoMigrate emits "DROP CONSTRAINT uni_..." against constraints that
// don't exist under that name, crashing startup on upgraded PostgreSQL installs.
func preparePostgresLegacySchema(db *gorm.DB) error {
if db == nil || db.Dialector.Name() != "postgres" {
return nil
}
type rename struct{ table, oldName, newName string }
renames := []rename{
{"vite_config", "vite_config_name_key", "uni_vite_config_name"},
{"peer_share", "peer_share_token_key", "uni_peer_share_token"},
{"peer_share_runtime", "peer_share_runtime_reservation_id_key", "uni_peer_share_runtime_reservation_id"},
{"peer_share_runtime", "peer_share_runtime_resource_key_key", "uni_peer_share_runtime_resource_key"},
{"federation_tunnel_binding", "federation_tunnel_binding_resource_key_key", "uni_federation_tunnel_binding_resource_key"},
}
for _, r := range renames {
var count int64
if err := db.Raw(
`SELECT COUNT(*) FROM information_schema.table_constraints
WHERE constraint_schema = current_schema()
AND table_name = ?
AND constraint_name = ?
AND constraint_type = 'UNIQUE'`,
r.table, r.oldName,
).Scan(&count).Error; err != nil {
return fmt.Errorf("check constraint %s.%s: %w", r.table, r.oldName, err)
}
if count == 0 {
continue
}
if err := db.Exec(
fmt.Sprintf(`ALTER TABLE %q RENAME CONSTRAINT %q TO %q`, r.table, r.oldName, r.newName),
).Error; err != nil {
return fmt.Errorf("rename constraint %s.%s→%s: %w", r.table, r.oldName, r.newName, err)
}
}
return nil
}
func prepareSQLiteLegacyColumns(db *gorm.DB) error {
if db == nil || db.Dialector.Name() != "sqlite" {
return nil
+6 -3
View File
@@ -256,8 +256,8 @@ export default function TunnelPage() {
}
}
if (form.trafficRatio < 0.0 || form.trafficRatio > 100.0) {
newErrors.trafficRatio = "流量倍率必须在0.0-100.0之间";
if (form.trafficRatio <= 0 || form.trafficRatio > 100.0) {
newErrors.trafficRatio = "流量倍率须大于0,支持小数(如 0.5)";
}
// 隧道转发时的验证
@@ -1280,8 +1280,11 @@ export default function TunnelPage() {
errorMessage={errors.trafficRatio}
isInvalid={!!errors.trafficRatio}
label="流量倍率"
placeholder="请输入流量倍率"
placeholder="例如:0.5 或 1 或 2"
type="number"
step="any"
min={0.01}
max={100}
value={form.trafficRatio.toString()}
variant="bordered"
onChange={(e) =>