- Store struct wrapping pgxpool.Pool with NewStore/Close/Pool - RunMigrations creates conversations + messages tables idempotently - DSN never logged to avoid credential exposure (T-06-01-02) - All queries parameterized (T-06-01-01)
43 lines
1.1 KiB
Go
43 lines
1.1 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// Store holds the PostgreSQL connection pool and exposes methods for
|
|
// chat persistence. All operations are parameterized (T-06-01-01).
|
|
type Store struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
// NewStore creates a new Store by connecting to PostgreSQL via pgxpool.
|
|
// It pings the pool to verify connectivity before returning.
|
|
// The DSN is sourced from the HWLAB_DATABASE_URL env var — it is never
|
|
// logged here to avoid leaking credentials (T-06-01-02).
|
|
func NewStore(ctx context.Context, dsn string) (*Store, error) {
|
|
pool, err := pgxpool.New(ctx, dsn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("store: open pool: %w", err)
|
|
}
|
|
|
|
if err := pool.Ping(ctx); err != nil {
|
|
pool.Close()
|
|
return nil, fmt.Errorf("store: ping: %w", err)
|
|
}
|
|
|
|
return &Store{pool: pool}, nil
|
|
}
|
|
|
|
// Pool returns the underlying pgxpool.Pool for direct use in migrations
|
|
// and tests.
|
|
func (s *Store) Pool() *pgxpool.Pool {
|
|
return s.pool
|
|
}
|
|
|
|
// Close releases all connections in the pool. Safe to call more than once.
|
|
func (s *Store) Close() {
|
|
s.pool.Close()
|
|
}
|