Files
HoneyandClaude Opus 5 35ebf609b7 fix(config): Datenbank-Passwort mit Sonderzeichen bricht den Start
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>
2026-08-24 11:20:01 +02:00

47 lines
1.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}