package db import ( "context" "database/sql" "errors" "fmt" ) type querier interface { ExecContext(context.Context, string, ...any) (sql.Result, error) QueryContext(context.Context, string, ...any) (*sql.Rows, error) QueryRowContext(context.Context, string, ...any) *sql.Row } type Store struct { DB *sql.DB Users *UserRepository Projects *ProjectRepository Tags *TagRepository Releases *ReleaseRepository APIKeys *APIKeyRepository Sessions *SessionRepository AuditLogs *AuditLogRepository } type TxStore struct { Tx *sql.Tx Users *UserRepository Projects *ProjectRepository Tags *TagRepository Releases *ReleaseRepository APIKeys *APIKeyRepository Sessions *SessionRepository AuditLogs *AuditLogRepository } type UserRepository struct { q querier } type ProjectRepository struct { q querier } type TagRepository struct { q querier } type ReleaseRepository struct { q querier } type APIKeyRepository struct { q querier } type SessionRepository struct { q querier } type AuditLogRepository struct { q querier } func NewStore(database *sql.DB) *Store { return &Store{ DB: database, Users: &UserRepository{q: database}, Projects: &ProjectRepository{q: database}, Tags: &TagRepository{q: database}, Releases: &ReleaseRepository{q: database}, APIKeys: &APIKeyRepository{q: database}, Sessions: &SessionRepository{q: database}, AuditLogs: &AuditLogRepository{q: database}, } } func (s *Store) Close() error { if s == nil || s.DB == nil { return nil } return s.DB.Close() } func (s *Store) HealthCheck(ctx context.Context) error { if s == nil || s.DB == nil { return fmt.Errorf("database store is not initialized") } return s.DB.PingContext(ctx) } func (s *Store) WithTx(ctx context.Context, fn func(*TxStore) error) error { if s == nil || s.DB == nil { return fmt.Errorf("database store is not initialized") } tx, err := s.DB.BeginTx(ctx, nil) if err != nil { return fmt.Errorf("begin transaction: %w", err) } if err := fn(newTxStore(tx)); err != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil && !errors.Is(rollbackErr, sql.ErrTxDone) { return errors.Join(err, fmt.Errorf("rollback transaction: %w", rollbackErr)) } return err } if err := tx.Commit(); err != nil { return fmt.Errorf("commit transaction: %w", err) } return nil } func newTxStore(tx *sql.Tx) *TxStore { return &TxStore{ Tx: tx, Users: &UserRepository{q: tx}, Projects: &ProjectRepository{q: tx}, Tags: &TagRepository{q: tx}, Releases: &ReleaseRepository{q: tx}, APIKeys: &APIKeyRepository{q: tx}, Sessions: &SessionRepository{q: tx}, AuditLogs: &AuditLogRepository{q: tx}, } }