diff --git a/internal/database/fixes.go b/internal/database/fixes.go index 17f1b0f..4f73590 100644 --- a/internal/database/fixes.go +++ b/internal/database/fixes.go @@ -5,27 +5,16 @@ import ( "database/sql" "fmt" "log" - "sort" "time" + + dbfixes "mal/internal/database/fixes" ) -type dataFix struct { - id string - apply func(ctx context.Context, sqlDB *sql.DB) error -} - -var registeredDataFixes []dataFix - -func registerDataFix(fix dataFix) { - registeredDataFixes = append(registeredDataFixes, fix) -} - func RunDataFixes(sqlDB *sql.DB) error { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() - fixes := append([]dataFix(nil), registeredDataFixes...) - sort.Slice(fixes, func(i, j int) bool { return fixes[i].id < fixes[j].id }) + fixes := dbfixes.All() if len(fixes) == 0 { return nil @@ -41,15 +30,15 @@ func RunDataFixes(sqlDB *sql.DB) error { } for _, fix := range fixes { - if applied[fix.id] { + if applied[fix.ID] { continue } - log.Printf("Running data fix id=%s", fix.id) - if err := fix.apply(ctx, sqlDB); err != nil { - return fmt.Errorf("data fix %s failed: %w", fix.id, err) + log.Printf("Running data fix id=%s", fix.ID) + if err := fix.Apply(ctx, sqlDB); err != nil { + return fmt.Errorf("data fix %s failed: %w", fix.ID, err) } - if err := markFixApplied(ctx, sqlDB, fix.id); err != nil { + if err := markFixApplied(ctx, sqlDB, fix.ID); err != nil { return err } } diff --git a/internal/database/fix_20260526_episode_availability_backfill_next_refresh_at.go b/internal/database/fixes/20260526_episode_availability_backfill_next_refresh_at.go similarity index 77% rename from internal/database/fix_20260526_episode_availability_backfill_next_refresh_at.go rename to internal/database/fixes/20260526_episode_availability_backfill_next_refresh_at.go index 3c9000f..5c46481 100644 --- a/internal/database/fix_20260526_episode_availability_backfill_next_refresh_at.go +++ b/internal/database/fixes/20260526_episode_availability_backfill_next_refresh_at.go @@ -1,4 +1,4 @@ -package database +package fixes import ( "context" @@ -7,9 +7,9 @@ import ( ) func init() { - registerDataFix(dataFix{ - id: "20260526_episode_availability_backfill_next_refresh_at", - apply: func(ctx context.Context, sqlDB *sql.DB) error { + Register(Fix{ + ID: "20260526_episode_availability_backfill_next_refresh_at", + Apply: func(ctx context.Context, sqlDB *sql.DB) error { // Old caches could have next_refresh_at NULL (especially for airing shows with missing broadcast metadata), // which can result in "never refresh again" behavior on the server. _, err := sqlDB.ExecContext(ctx, ` @@ -25,3 +25,4 @@ WHERE next_refresh_at IS NULL; }, }) } + diff --git a/internal/database/fixes/registry.go b/internal/database/fixes/registry.go new file mode 100644 index 0000000..27f8300 --- /dev/null +++ b/internal/database/fixes/registry.go @@ -0,0 +1,25 @@ +package fixes + +import ( + "context" + "database/sql" + "sort" +) + +type Fix struct { + ID string + Apply func(ctx context.Context, sqlDB *sql.DB) error +} + +var registered []Fix + +func Register(fix Fix) { + registered = append(registered, fix) +} + +func All() []Fix { + out := append([]Fix(nil), registered...) + sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) + return out +} + diff --git a/scripts/new-data-fix.ts b/scripts/new-data-fix.ts index 04fab9b..8d65d07 100644 --- a/scripts/new-data-fix.ts +++ b/scripts/new-data-fix.ts @@ -32,8 +32,8 @@ async function main(): Promise { } const id = `${formatYYYYMMDD(new Date())}_${slug}`; - const dir = path.join(process.cwd(), 'internal', 'database'); - const filePath = path.join(dir, `fix_${id}.go`); + const dir = path.join(process.cwd(), 'internal', 'database', 'fixes'); + const filePath = path.join(dir, `${id}.go`); await mkdir(dir, { recursive: true }); @@ -41,7 +41,7 @@ async function main(): Promise { throw new Error(`data fix already exists: ${filePath}`); } - const contents = `package database + const contents = `package fixes import ( "context" @@ -50,9 +50,9 @@ import ( ) func init() { - registerDataFix(dataFix{ - id: "${id}", - apply: func(ctx context.Context, sqlDB *sql.DB) error { + Register(Fix{ + ID: "${id}", + Apply: func(ctx context.Context, sqlDB *sql.DB) error { // TODO: implement fix // _, err := sqlDB.ExecContext(ctx, \`UPDATE ...\`) // if err != nil { return fmt.Errorf("...: %w", err) }