mirror of
https://github.com/R0m1k3/FlowReader.git
synced 2026-10-11 17:28:05 +02:00
fix: refactor migration system to be incremental and fix error 500 on articles
This commit is contained in:
1 parent
5b219cf0e7
commit
187b6c8b86
2 files changed
+64
-16
No files matched your search
@@ -11,6 +11,7 @@ services:
|
||||
environment:
|
||||
- PORT=8080
|
||||
- DATABASE_URL=postgres://flowreader:flowreader@db:5432/flowreader?sslmode=disable
|
||||
- OPENROUTER_API_KEY=votre_clef_ici
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -42,27 +42,54 @@ func Connect(ctx context.Context, databaseURL string) (*pgxpool.Pool, error) {
|
||||
|
||||
// RunMigrations executes pending database migrations.
|
||||
func RunMigrations(ctx context.Context, pool *pgxpool.Pool) error {
|
||||
// Check if users table exists
|
||||
var exists bool
|
||||
err := pool.QueryRow(ctx, `
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'users'
|
||||
// 1. Create schema_migrations table if it doesn't exist
|
||||
_, err := pool.Exec(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version VARCHAR(255) PRIMARY KEY,
|
||||
applied_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
)
|
||||
`).Scan(&exists)
|
||||
`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking users table: %w", err)
|
||||
return fmt.Errorf("creating schema_migrations table: %w", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
log.Println("Database already initialized")
|
||||
return nil
|
||||
// 2. Get applied migrations
|
||||
rows, err := pool.Query(ctx, "SELECT version FROM schema_migrations")
|
||||
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")
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading migrations dir: %w", err)
|
||||
@@ -76,18 +103,38 @@ func RunMigrations(ctx context.Context, pool *pgxpool.Pool) error {
|
||||
}
|
||||
sort.Strings(upFiles)
|
||||
|
||||
// 5. Apply pending migrations
|
||||
for _, file := range upFiles {
|
||||
if applied[file] {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("Applying migration: %s", file)
|
||||
content, err := os.ReadFile(filepath.Join("./migrations", file))
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user