74 lines
1.7 KiB
Go
74 lines
1.7 KiB
Go
// FILE: internal/service/service.go
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"chess/internal/game"
|
|
"chess/internal/storage"
|
|
)
|
|
|
|
// Service is a pure state manager for chess games with optional persistence
|
|
type Service struct {
|
|
games map[string]*game.Game
|
|
mu sync.RWMutex
|
|
store *storage.Store // nil if persistence disabled
|
|
jwtSecret []byte
|
|
waiter *WaitRegistry // Long-polling notification registry
|
|
}
|
|
|
|
// New creates a new service instance with optional storage
|
|
func New(store *storage.Store, jwtSecret []byte) *Service {
|
|
return &Service{
|
|
games: make(map[string]*game.Game),
|
|
store: store,
|
|
jwtSecret: jwtSecret,
|
|
waiter: NewWaitRegistry(),
|
|
}
|
|
}
|
|
|
|
// GetStorageHealth returns the storage component status
|
|
func (s *Service) GetStorageHealth() string {
|
|
if s.store == nil {
|
|
return "disabled"
|
|
}
|
|
if s.store.IsHealthy() {
|
|
return "ok"
|
|
}
|
|
return "degraded"
|
|
}
|
|
|
|
// RegisterWait registers a client to wait for game state changes
|
|
func (s *Service) RegisterWait(gameID string, moveCount int, ctx context.Context) <-chan struct{} {
|
|
return s.waiter.RegisterWait(gameID, moveCount, ctx)
|
|
}
|
|
|
|
// Shutdown gracefully shuts down the service
|
|
func (s *Service) Shutdown(timeout time.Duration) error {
|
|
// Collect all errors
|
|
var errs []error
|
|
|
|
// Shutdown wait registry
|
|
if err := s.waiter.Shutdown(timeout); err != nil {
|
|
errs = append(errs, fmt.Errorf("wait registry: %w", err))
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// Clear all games
|
|
s.games = make(map[string]*game.Game)
|
|
|
|
// Close storage if enabled
|
|
if s.store != nil {
|
|
if err := s.store.Close(); err != nil {
|
|
errs = append(errs, fmt.Errorf("storage: %w", err))
|
|
}
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
} |