package library import ( "context" "errors" "log/slog" "pipu/api/internal/auth" "pipu/api/internal/platform/apperr" ) // Коды ошибок библиотеки. const ( CodeSourceNotFound = "library.source_not_found" CodeScanRunning = "library.scan_running" CodeSourceOffline = "library.source_offline" CodePatternInvalid = "validation.pattern_invalid" CodeNFSInvalid = "validation.nfs_invalid" CodeLocalPathInvalid = "validation.local_path_invalid" CodeNameInvalid = "validation.name_invalid" CodeIntervalInvalid = "validation.scan_interval_invalid" CodeNotFound = "catalog.not_found" CodeTemplateInvalid = "validation.template_invalid" CodeTemplateNone = "library.template_unavailable" ) // Config — пути к хранилищам в файловой системе api. type Config struct { LibraryRoot string // точки монтирования NFS: / HostRoot string // LIBRARY_HOST внутри контейнера, для local-источников } // Service preserves the API while each use case owns only its dependencies. type Service struct { *SourceService *TemplateService *ScanService *Scheduler } func NewService(store *Store, scanner *Scanner, board *ProgressBoard, agent Agent, prober Prober, cfg Config, log *slog.Logger) *Service { sources := &SourceService{store: store, board: board, agent: agent, cfg: cfg, log: log} scans := &ScanService{store: store, scanner: scanner, board: board, root: sources.Root} return &Service{ SourceService: sources, TemplateService: &TemplateService{store: store, prober: prober, root: sources.Root}, ScanService: scans, Scheduler: &Scheduler{store: store, sources: sources, scans: scans, log: log}, } } func forbidden() error { return apperr.Forbidden(auth.CodeForbidden, "not allowed") } func requirePerm(ctx context.Context, perm auth.Permission) error { p, ok := auth.PrincipalFrom(ctx) if !ok { return apperr.Unauthorized(auth.CodeUnauthorized, "not authenticated") } if !auth.Can(p.Role, perm) { return forbidden() } return nil } func notFound(err error) error { if errors.Is(err, ErrNotFound) { return apperr.NotFound(CodeSourceNotFound, "source not found") } return err }