131 lines
2.7 KiB
Go
131 lines
2.7 KiB
Go
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},
|
|
}
|
|
}
|