Files
Kazier/backend/db/db.go
T
m 484498d989 feat(backend): migrations Postgres testées (users, devices, resources, operations, ocr_jobs) + migrate au boot
- golang-migrate embarqué via embed (db/migrations/*.sql), MigrateDatabase(url) au boot
- ids TEXT 32-hex avec CHECK ~ '^[0-9a-f]{32}$' ; operations idempotentes UNIQUE(device_id, operation_id)
- harness db/migrations_test.go (up → assertions schéma → down, skip si PG indisponible)
- DB dev fraîche vaultdrop_dev (la DB vaultdrop héberge un prototype V2 abandonné)
2026-09-10 07:35:36 +02:00

81 lines
1.9 KiB
Go

package db
import (
"database/sql"
"embed"
"errors"
"fmt"
"github.com/golang-migrate/migrate/v4"
migratepostgres "github.com/golang-migrate/migrate/v4/database/postgres"
"github.com/golang-migrate/migrate/v4/source/iofs"
_ "github.com/lib/pq"
)
//go:embed migrations/*.sql
var migrationsFS embed.FS
// Open opens a raw *sql.DB (Postgres). Callers must Close it.
func Open(databaseURL string) (*sql.DB, error) {
conn, err := sql.Open("postgres", databaseURL)
if err != nil {
return nil, fmt.Errorf("open database: %w", err)
}
if err := conn.Ping(); err != nil {
conn.Close()
return nil, fmt.Errorf("ping database: %w", err)
}
return conn, nil
}
func newMigrator(conn *sql.DB) (*migrate.Migrate, error) {
source, err := iofs.New(migrationsFS, "migrations")
if err != nil {
return nil, fmt.Errorf("load embedded migrations: %w", err)
}
driver, err := migratepostgres.WithInstance(conn, &migratepostgres.Config{})
if err != nil {
return nil, fmt.Errorf("init postgres migrate driver: %w", err)
}
m, err := migrate.NewWithInstance("iofs", source, "postgres", driver)
if err != nil {
return nil, fmt.Errorf("new migrate: %w", err)
}
return m, nil
}
// MigrateDatabase applies all pending migrations up to the latest version.
// migrate.ErrNoChange (already up-to-date) is not an error.
func MigrateDatabase(databaseURL string) error {
conn, err := Open(databaseURL)
if err != nil {
return err
}
defer conn.Close()
m, err := newMigrator(conn)
if err != nil {
return err
}
if err := m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
return err
}
return nil
}
// MigrateDownDatabase rolls back every migration (used by tests).
func MigrateDownDatabase(databaseURL string) error {
conn, err := Open(databaseURL)
if err != nil {
return err
}
defer conn.Close()
m, err := newMigrator(conn)
if err != nil {
return err
}
if err := m.Down(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
return err
}
return nil
}