Files
67/services/internal/service/db.go
2026-06-22 22:39:08 +03:00

166 lines
4.5 KiB
Go

package service
import (
"context"
"errors"
"fmt"
"os"
"strings"
"sync"
"time"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var (
backendStore Store = unavailableStore{err: errors.New("backend store not initialized")}
defaultStoreOnce sync.Once
defaultStoreErr error
)
func setBackendStore(store Store) {
if store == nil {
backendStore = unavailableStore{err: errors.New("backend store not initialized")}
return
}
backendStore = store
}
func bootstrapDefaultStore() (Store, error) {
defaultStoreOnce.Do(func() {
db, err := openDBFromEnv()
if err != nil {
defaultStoreErr = err
setBackendStore(unavailableStore{err: err})
return
}
store, err := newGORMStore(db)
if err != nil {
defaultStoreErr = err
setBackendStore(unavailableStore{err: err})
return
}
setBackendStore(store)
})
return backendStore, defaultStoreErr
}
func openDBFromEnv() (*gorm.DB, error) {
dsn := strings.TrimSpace(os.Getenv("DATABASE_URL"))
if dsn == "" {
return nil, errors.New("DATABASE_URL is required")
}
return openDB(dsn)
}
func openDB(dsn string) (*gorm.DB, error) {
config := &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}
var (
db *gorm.DB
err error
)
if strings.HasPrefix(dsn, "sqlite:") || strings.HasPrefix(dsn, "file:") || dsn == ":memory:" {
db, err = gorm.Open(sqlite.Open(dsn), config)
} else {
db, err = gorm.Open(postgres.Open(dsn), config)
}
if err != nil {
return nil, fmt.Errorf("open database: %w", err)
}
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("database handle: %w", err)
}
sqlDB.SetConnMaxLifetime(5 * time.Minute)
sqlDB.SetMaxIdleConns(5)
sqlDB.SetMaxOpenConns(10)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := sqlDB.PingContext(ctx); err != nil {
return nil, fmt.Errorf("ping database: %w", err)
}
return db, nil
}
type unavailableStore struct {
err error
}
func (s unavailableStore) Ping(context.Context) error { return s.err }
func (s unavailableStore) UserByLogin(context.Context, string) (UserProfile, bool, error) {
return UserProfile{}, false, s.err
}
func (s unavailableStore) UserByID(context.Context, string) (UserProfile, bool, error) {
return UserProfile{}, false, s.err
}
func (s unavailableStore) AddUser(context.Context, string, string, string) (UserProfile, error) {
return UserProfile{}, s.err
}
func (s unavailableStore) UpdatePassword(context.Context, string, string) error { return s.err }
func (s unavailableStore) ListUsers(context.Context) ([]UserProfile, error) { return nil, s.err }
func (s unavailableStore) ListContent(context.Context, ContentFilter) ([]ContentItem, error) {
return nil, s.err
}
func (s unavailableStore) ContentByID(context.Context, string) (ContentItem, bool, error) {
return ContentItem{}, false, s.err
}
func (s unavailableStore) AddContent(context.Context, ContentItem) (ContentItem, error) {
return ContentItem{}, s.err
}
func (s unavailableStore) PatchContent(context.Context, string, ContentItem) (ContentItem, bool, error) {
return ContentItem{}, false, s.err
}
func (s unavailableStore) DeleteContent(context.Context, string) (bool, error) { return false, s.err }
func (s unavailableStore) ListCategories(context.Context) ([]string, error) { return nil, s.err }
func (s unavailableStore) ListTags(context.Context) ([]string, error) { return nil, s.err }
func (s unavailableStore) ListSpeakers(context.Context) ([]Speaker, error) { return nil, s.err }
func (s unavailableStore) AddFile(context.Context, StoredFile) (StoredFile, error) {
return StoredFile{}, s.err
}
func (s unavailableStore) FileByID(context.Context, string) (StoredFile, bool, error) {
return StoredFile{}, false, s.err
}
func (s unavailableStore) UpsertSubscription(context.Context, string, string) ([]string, error) {
return nil, s.err
}
func (s unavailableStore) ListNotifications(context.Context) ([]NotificationItem, error) {
return nil, s.err
}
func (s unavailableStore) MarkNotificationRead(context.Context, string) (*NotificationItem, error) {
return nil, s.err
}
func (s unavailableStore) ListComments(context.Context, string) ([]CommentItem, error) {
return nil, s.err
}
func (s unavailableStore) AddComment(context.Context, string, UserProfile, string) (CommentItem, error) {
return CommentItem{}, s.err
}
func (s unavailableStore) CountComments(context.Context) (int, error) { return 0, s.err }
func (s unavailableStore) ListAudit(context.Context) ([]AuditItem, error) { return nil, s.err }