fix: refactor migration system to be incremental and fix error 500 on articles

This commit is contained in:
Michael committed 2026-02-09 10:15:31 +01:00
1 parent 5b219cf0e7
commit 187b6c8b86
2 files changed
+64 -16

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ services:
environment: environment:
- PORT=8080 - PORT=8080
- DATABASE_URL=postgres://flowreader:flowreader@db:5432/flowreader?sslmode=disable - DATABASE_URL=postgres://flowreader:flowreader@db:5432/flowreader?sslmode=disable
- OPENROUTER_API_KEY=votre_clef_ici
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
+63 -16
View File
@@ -42,27 +42,54 @@ func Connect(ctx context.Context, databaseURL string) (*pgxpool.Pool, error) {
// RunMigrations executes pending database migrations. // RunMigrations executes pending database migrations.
func RunMigrations(ctx context.Context, pool *pgxpool.Pool) error { func RunMigrations(ctx context.Context, pool *pgxpool.Pool) error {
// Check if users table exists // 1. Create schema_migrations table if it doesn't exist
var exists bool _, err := pool.Exec(ctx, `
err := pool.QueryRow(ctx, ` CREATE TABLE IF NOT EXISTS schema_migrations (
SELECT EXISTS ( version VARCHAR(255) PRIMARY KEY,
SELECT FROM information_schema.tables applied_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
WHERE table_schema = 'public'
AND table_name = 'users'
) )
`).Scan(&exists) `)
if err != nil { if err != nil {
return fmt.Errorf("checking users table: %w", err) return fmt.Errorf("creating schema_migrations table: %w", err)
} }
if exists { // 2. Get applied migrations
log.Println("Database already initialized") rows, err := pool.Query(ctx, "SELECT version FROM schema_migrations")
return nil if err != nil {
return fmt.Errorf("querying applied migrations: %w", err)
}
defer rows.Close()
applied := make(map[string]bool)
for rows.Next() {
var version string
if err := rows.Scan(&version); err != nil {
return fmt.Errorf("scanning migration version: %w", err)
}
applied[version] = true
} }
log.Println("Initializing database and applying migrations...") // 3. Special case for legacy system:
// If 'users' table exists but 'schema_migrations' is empty,
// mark all migrations up to 005 as applied to avoid duplicate creation errors.
if len(applied) == 0 {
var usersExists bool
pool.QueryRow(ctx, "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'users')").Scan(&usersExists)
if usersExists {
log.Println("Legacy database detected, bootstrapping migration tracking...")
// We assume 001 to 005 are already there if users table exists in the old system
legacyVersions := []string{"001_create_users.up.sql", "002_create_sessions.up.sql", "003_create_feeds.up.sql", "004_add_user_roles.up.sql", "005_search_articles.up.sql"}
for _, v := range legacyVersions {
_, err := pool.Exec(ctx, "INSERT INTO schema_migrations (version) VALUES ($1)", v)
if err != nil {
log.Printf("Warning: failed to bootstrap version %s: %v", v, err)
}
applied[v] = true
}
}
}
// Read migration files // 4. Read migration files
entries, err := os.ReadDir("./migrations") entries, err := os.ReadDir("./migrations")
if err != nil { if err != nil {
return fmt.Errorf("reading migrations dir: %w", err) return fmt.Errorf("reading migrations dir: %w", err)
@@ -76,18 +103,38 @@ func RunMigrations(ctx context.Context, pool *pgxpool.Pool) error {
} }
sort.Strings(upFiles) sort.Strings(upFiles)
// 5. Apply pending migrations
for _, file := range upFiles { for _, file := range upFiles {
if applied[file] {
continue
}
log.Printf("Applying migration: %s", file) log.Printf("Applying migration: %s", file)
content, err := os.ReadFile(filepath.Join("./migrations", file)) content, err := os.ReadFile(filepath.Join("./migrations", file))
if err != nil { if err != nil {
return fmt.Errorf("reading migration %s: %w", file, err) return fmt.Errorf("reading migration %s: %w", file, err)
} }
if _, err := pool.Exec(ctx, string(content)); err != nil { // Execute in transaction
tx, err := pool.Begin(ctx)
if err != nil {
return fmt.Errorf("starting transaction for %s: %w", file, err)
}
defer tx.Rollback(ctx)
if _, err := tx.Exec(ctx, string(content)); err != nil {
return fmt.Errorf("executing migration %s: %w", file, err) return fmt.Errorf("executing migration %s: %w", file, err)
} }
if _, err := tx.Exec(ctx, "INSERT INTO schema_migrations (version) VALUES ($1)", file); err != nil {
return fmt.Errorf("recording migration %s: %w", file, err)
}
if err := tx.Commit(ctx); err != nil {
return fmt.Errorf("committing migration %s: %w", file, err)
}
} }
log.Println("All migrations applied successfully") log.Println("Database schema is up to date")
return nil return nil
} }