Smail/backend/db/migration/verification_retention.go
smail f60d3b0880
Some checks are pending
Security Checks / security (push) Waiting to run
整理前后端与测试构建目录并完善配置和安全引导
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
2026-09-05 19:19:52 +08:00

83 lines
3.6 KiB
Go

package migration
import (
"context"
"fmt"
)
func applyVerificationRetentionSchema(ctx context.Context, exec executor, dialect Dialect) error {
columns := []struct {
table string
name string
definition map[Dialect]string
}{
{table: "verification_rate_limit", name: "expires_at", definition: map[Dialect]string{
DialectSQLite: `DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00'`,
DialectMySQL: `DATETIME(6) NOT NULL DEFAULT '1970-01-01 00:00:00'`,
DialectPostgres: `TIMESTAMPTZ NOT NULL DEFAULT '1970-01-01 00:00:00+00'`,
}},
{table: "verification_event", name: "expires_at", definition: map[Dialect]string{
DialectSQLite: `DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00'`,
DialectMySQL: `DATETIME(6) NOT NULL DEFAULT '1970-01-01 00:00:00'`,
DialectPostgres: `TIMESTAMPTZ NOT NULL DEFAULT '1970-01-01 00:00:00+00'`,
}},
{table: "verification_event", name: "retention_class", definition: map[Dialect]string{
DialectSQLite: `TEXT NOT NULL DEFAULT 'detail'`,
DialectMySQL: `VARCHAR(16) NOT NULL DEFAULT 'detail'`,
DialectPostgres: `VARCHAR(16) NOT NULL DEFAULT 'detail'`,
}},
{table: "verification_event", name: "investigation_hold", definition: map[Dialect]string{
DialectSQLite: `INTEGER NOT NULL DEFAULT 0`,
DialectMySQL: `TINYINT NOT NULL DEFAULT 0`,
DialectPostgres: `BOOLEAN NOT NULL DEFAULT FALSE`,
}},
}
for _, column := range columns {
if err := ensureColumn(ctx, exec, dialect, column.table, column.name, column.definition[dialect]); err != nil {
return err
}
}
updates := map[Dialect][]string{
DialectSQLite: {
`UPDATE verification_rate_limit SET expires_at = datetime(updated_at, '+30 days') WHERE expires_at = '1970-01-01 00:00:00'`,
`UPDATE verification_event SET expires_at = datetime(created_at, '+30 days') WHERE expires_at = '1970-01-01 00:00:00'`,
},
DialectMySQL: {
`UPDATE verification_rate_limit SET expires_at = DATE_ADD(updated_at, INTERVAL 30 DAY) WHERE expires_at = '1970-01-01 00:00:00'`,
`UPDATE verification_event SET expires_at = DATE_ADD(created_at, INTERVAL 30 DAY) WHERE expires_at = '1970-01-01 00:00:00'`,
},
DialectPostgres: {
`UPDATE verification_rate_limit SET expires_at = updated_at + INTERVAL '30 days' WHERE expires_at = '1970-01-01 00:00:00+00'`,
`UPDATE verification_event SET expires_at = created_at + INTERVAL '30 days' WHERE expires_at = '1970-01-01 00:00:00+00'`,
},
}
for _, statement := range updates[dialect] {
if _, err := exec.ExecContext(ctx, statement); err != nil {
return fmt.Errorf("backfill verification retention: %w", err)
}
}
return createVerificationRetentionIndexes(ctx, exec, dialect)
}
func createVerificationRetentionIndexes(ctx context.Context, exec executor, dialect Dialect) error {
statement := `CREATE INDEX IF NOT EXISTS "idx_verification_event_expires_at" ON "verification_event"("expires_at")`
if dialect == DialectMySQL {
var count int
if err := exec.QueryRowContext(ctx, "SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = ? AND index_name = ?", "verification_event", "idx_verification_event_expires_at").Scan(&count); err != nil {
return fmt.Errorf("check verification retention index: %w", err)
}
if count == 0 {
if _, err := exec.ExecContext(ctx, "CREATE INDEX `idx_verification_event_expires_at` ON `verification_event` (`expires_at`)"); err != nil {
return fmt.Errorf("create verification retention index: %w", err)
}
}
return nil
}
if _, err := exec.ExecContext(ctx, statement); err != nil {
return fmt.Errorf("create verification retention index: %w", err)
}
return nil
}