Die Verbindungszeichenkette wurde mit fmt.Sprintf zusammengeklebt. Ein Passwort mit % und @ ergab eine kaputte URL: parse "postgres://aether:8UoNj%CoWJ7ze@qy@localhost:5432/aether": invalid URL escape "%Co" Der Server kam nicht über die Migrationen hinaus. Die key=value-Form hätte dasselbe Problem mit einem Leerzeichen gehabt, nur stiller. config.DSN() liefert jetzt eine postgres://-URL, gebaut mit net/url und net.JoinHostPort (klammert IPv6). pgxpool und golang-migrate nehmen sie beide unverändert – dsnToURL, splitDSN, indexByte und orDefault in pkg/db sind ersatzlos entfallen. Test deckt %, @, :, /, Leerzeichen und IPv6 ab. Dazu: DB_HOST und DB_PORT fehlten in der schlanken .env.example, weshalb localhost statt postgres gegriffen hat. APP_ENV ergänzt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
1.2 KiB
Go
47 lines
1.2 KiB
Go
package db
|
||
|
||
import (
|
||
"context"
|
||
"embed"
|
||
"fmt"
|
||
|
||
"github.com/golang-migrate/migrate/v4"
|
||
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
||
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||
"github.com/jackc/pgx/v5/pgxpool"
|
||
)
|
||
|
||
//go:embed migrations/*.sql
|
||
var migrationsFS embed.FS
|
||
|
||
func Connect(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
|
||
pool, err := pgxpool.New(ctx, dsn)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("create pool: %w", err)
|
||
}
|
||
if err := pool.Ping(ctx); err != nil {
|
||
return nil, fmt.Errorf("ping db: %w", err)
|
||
}
|
||
return pool, nil
|
||
}
|
||
|
||
// Migrate führt die eingebetteten Migrationen aus.
|
||
//
|
||
// `dsn` ist die postgres://-URL aus config.DSN(). Hier stand früher eine
|
||
// eigene Umrechnung von key=value in eine URL, die Nutzer und Passwort
|
||
// unmaskiert hineinschrieb – siehe die Begründung bei config.DSN().
|
||
func Migrate(dsn string) error {
|
||
src, err := iofs.New(migrationsFS, "migrations")
|
||
if err != nil {
|
||
return fmt.Errorf("load migrations: %w", err)
|
||
}
|
||
m, err := migrate.NewWithSourceInstance("iofs", src, dsn)
|
||
if err != nil {
|
||
return fmt.Errorf("init migrate: %w", err)
|
||
}
|
||
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
||
return fmt.Errorf("run migrations: %w", err)
|
||
}
|
||
return nil
|
||
}
|