Documentation
¶
Overview ¶
Package api: handlers that mutate the artist row outside the field-level edit flow. The directory-rename handler in this file is intentionally separate from handleFieldUpdate ("name") because renaming an artist's on-disk directory has filesystem and platform-mapping consequences that must be opt-in, not a side-effect of saving a name change. See issue #1077: editing the Name field used to indirectly trigger an on-disk rename via the directory_name_mismatch rule fixer, which broke Emby/Jellyfin item-to-path mappings. The fix decouples the two: name edits go through PATCH /api/v1/artists/{id}/fields/name and stay in the DB+NFO; the directory rename below is a deliberate, user-driven action.
Package api implements the HTTP router, handlers, middleware, and static asset management for the REST API and HTMX web interface.
Index ¶
- Constants
- Variables
- func DecodeJSON(w http.ResponseWriter, req *http.Request, target any) bool
- func RequirePathParam(w http.ResponseWriter, req *http.Request, name string) (string, bool)
- type BulkActionProgress
- type ClobberCheckResponse
- type ClobberRisk
- type FileRemover
- type FixAllProgress
- type IdentifyCandidate
- type IdentifyProgress
- type LibraryOpResult
- type Router
- type RouterDeps
- type SSEClient
- type SSEEvent
- type SSEHub
- type ScoredCandidate
- type SharedFilesystemEntry
- type SharedFilesystemStatus
- type StaticAssets
Constants ¶
const ( BulkActionRunRules = "run_rules" BulkActionReIdentify = "re_identify" // legacy alias for re_identify_auto BulkActionReIdentifyAuto = "re_identify_auto" // silent auto-link + queue path BulkActionScan = "scan" BulkActionFetchImages = "fetch_images" )
Allowed bulk action types.
BulkActionReIdentify is the legacy alias retained so existing callers keep working; it is normalized to BulkActionReIdentifyAuto on entry. The review variant (re_identify_review string in the UI) is dispatched separately through the wizard endpoints in handlers_reidentify_wizard.go and never flows through this handler, so no Go constant is defined for it here.
const ( PrefTheme = "theme" PrefSidebarState = "sidebar_state" PrefContentWidth = "content_width" PrefFontFamily = "font_family" PrefFontSize = "font_size" PrefLetterSpacing = "letter_spacing" PrefThumbnailSize = "thumbnail_size" PrefReducedMotion = "reduced_motion" PrefLiteMode = "lite_mode" PrefLanguage = "language" PrefMetadataLanguages = "metadata_languages" PrefNotificationEnabled = "notification_enabled" PrefAutoFetchImages = "auto_fetch_images" PrefBgOpacity = "bg_opacity" PrefPageSize = "page_size" // PrefSuppressConfirmPrefix is the prefix for per-action confirm suppression // preferences. Keys have the form "suppress_confirm_{action}" and accept // "true" or "false". These are not listed in preferenceDefaults because they // are created dynamically by the UI as the user opts out of specific dialogs. PrefSuppressConfirmPrefix = "suppress_confirm_" // PageSizeDefault is the default number of items per page. // PageSizeMin and PageSizeMax define the allowed range for the page_size preference. PageSizeDefault = 50 PageSizeMin = 10 PageSizeMax = 500 )
Preference key constants. Use these instead of raw strings when referencing preference keys in Go code to catch typos at compile time.
const ( BgOpacityDefault = 65 BgOpacityMin = 20 BgOpacityMax = 100 )
BgOpacityDefault is the default background opacity percentage. BgOpacityMin and BgOpacityMax define the allowed range for the bg_opacity preference.
const MaxBulkActionIDs = artist.MaxListIDs
MaxBulkActionIDs caps the number of artist IDs accepted in a single bulk action request. This bounds both memory and run time so a single request cannot monopolize the singleton bulk-action slot indefinitely. Sourced from artist.MaxListIDs so the API request cap and the domain-layer IDs-filter cap stay in lockstep without a comment-only contract.
const MetadataLanguagesDefault = langpref.DefaultJSON
MetadataLanguagesDefault is the default value for the metadata_languages preference when no user preference is stored.
Deprecated: use langpref.DefaultJSON in new code.
const MetadataLanguagesMaxEntries = langpref.MaxEntries
MetadataLanguagesMaxEntries limits how many language tags a user can store.
Deprecated: use langpref.MaxEntries in new code.
Variables ¶
var ( ErrConflictPeerRejected = errors.New("peer rejected stillwater-managed change") ErrConflictLocalPersist = errors.New("persisting stillwater-managed state failed") )
Sentinel errors classify failures from applyStillwaterManaged / clearStillwaterManaged so the HTTP handler can map them to the right status code. ErrConflictPeerRejected => 502 (peer-side: snapshot read, disable, restore). ErrConflictLocalPersist => 500 (Stillwater-side: SetPreStillwaterConfig, SetManageServerFiles). Local persistence failures returned a 502 in the original implementation, which sent callers toward the wrong remediation path.
Functions ¶
func DecodeJSON ¶ added in v0.9.5
DecodeJSON decodes the JSON request body into target. If decoding fails, it writes a 400 error and returns false. Callers must return immediately when the return value is false.
func RequirePathParam ¶ added in v0.9.5
RequirePathParam extracts a named path parameter from the request. If the value is empty, it writes a 400 error and returns ("", false). Callers must return immediately when the second return value is false.
Types ¶
type BulkActionProgress ¶ added in v0.9.6
type BulkActionProgress struct {
Action string `json:"action"`
Status string `json:"status"` // "running", "completed", "failed", "canceled"
Total int `json:"total"`
Processed int `json:"processed"`
Succeeded int `json:"succeeded"`
Skipped int `json:"skipped"`
Failed int `json:"failed"`
CurrentName string `json:"current_name"`
// Re-identify-specific breakdown. These remain zero for other actions,
// so the bulk-completion toast can detect the re_identify_auto path by
// checking whether any of them are non-zero. Populated in addition to
// the generic succeeded/skipped/failed counters so existing consumers
// keep working unchanged.
AutoLinked int `json:"auto_linked"`
Queued int `json:"queued"`
NoMatch int `json:"no_match"`
StartedAt time.Time
CompletedAt time.Time
// contains filtered or unexported fields
}
BulkActionProgress tracks the state of an in-flight bulk action. It is mutex-protected and shared between the request handler and the background goroutine processing the IDs.
type ClobberCheckResponse ¶ added in v0.9.5
type ClobberCheckResponse struct {
HasRisk bool `json:"has_risk"`
Risks []ClobberRisk `json:"risks"`
}
ClobberCheckResponse is the response for GET /api/v1/connections/clobber-check.
type ClobberRisk ¶ added in v0.9.5
type ClobberRisk struct {
ConnectionID string `json:"connection_id"`
ConnectionName string `json:"connection_name"`
ConnectionType string `json:"connection_type"`
NFOWriter bool `json:"nfo_writer"`
LibraryName string `json:"library_name,omitempty"`
Error string `json:"error,omitempty"`
}
ClobberRisk describes whether a specific connection may overwrite NFO/image files.
type FileRemover ¶ added in v0.9.5
FileRemover abstracts file removal for testability. Production code uses osRemover (the default); tests can inject a stub that returns errors on demand to exercise error paths that are otherwise unreachable as root or on Windows. Implementations must return an error wrapping os.ErrNotExist when the target file does not exist, since callers use errors.Is to distinguish missing files from genuine failures.
type FixAllProgress ¶ added in v0.9.5
type FixAllProgress struct {
Status string `json:"status"`
Total int `json:"total"`
Processed int `json:"processed"`
Fixed int `json:"fixed"`
Skipped int `json:"skipped"`
Failed int `json:"failed"`
// contains filtered or unexported fields
}
FixAllProgress tracks the state of an async fix-all operation.
type IdentifyCandidate ¶ added in v0.9.5
type IdentifyCandidate struct {
ArtistID string `json:"artist_id"`
ArtistName string `json:"artist_name"`
ArtistPath string `json:"artist_path"`
Tier string `json:"tier"` // "connection", "album", "name"
Candidates []ScoredCandidate `json:"candidates"`
}
IdentifyCandidate represents an artist that needs manual review for linking.
type IdentifyProgress ¶ added in v0.9.5
type IdentifyProgress struct {
Status string `json:"status"` // "running", "completed", "canceled"
Total int `json:"total"`
Processed int `json:"processed"`
AutoLinked int `json:"auto_linked"`
Queued int `json:"queued"`
Unmatched int `json:"unmatched"`
Failed int `json:"failed"`
CurrentName string `json:"current_name"`
ReviewQueue []IdentifyCandidate `json:"review_queue,omitempty"`
// contains filtered or unexported fields
}
IdentifyProgress tracks the state of a bulk-identify operation.
type LibraryOpResult ¶ added in v0.9.5
type LibraryOpResult struct {
LibraryID string `json:"library_id"`
LibraryName string `json:"library_name"`
Operation string `json:"operation"`
Status string `json:"status"`
Message string `json:"message,omitempty"`
StartedAt time.Time `json:"started_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
}
LibraryOpResult tracks the state of an async library operation.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router sets up all HTTP routes for the application.
func NewRouter ¶
func NewRouter(deps RouterDeps) *Router
NewRouter creates a new Router with all routes configured.
func (*Router) Handler ¶
Handler returns the fully configured HTTP handler with middleware applied. The provided context controls the lifecycle of background goroutines (e.g. rate limiter cleanup).
func (*Router) InvalidateHealthCache ¶ added in v0.9.5
func (r *Router) InvalidateHealthCache()
InvalidateHealthCache is a no-op retained for API compatibility with callers added by PR #700. Health scores are now read from stored per-artist values (updated via the event bus), so there is no in-memory cache to invalidate.
func (*Router) ServeError500 ¶ added in v0.9.5
func (r *Router) ServeError500(w http.ResponseWriter, req *http.Request)
ServeError500 renders the custom 500 error page. Other handlers can call this helper instead of http.Error when an unexpected internal error occurs and the request was a browser navigation (not a JSON API call). JSON API clients (Accept: application/json or /api/ path prefix) and HTMX partial requests (HX-Request: true) receive a JSON error body.
type RouterDeps ¶ added in v0.9.5
type RouterDeps struct {
AuthService *auth.Service
AuthRegistry *auth.Registry
ArtistService *artist.Service
HistoryService *artist.HistoryService
ScannerService *scanner.Service
PlatformService *platform.Service
ProviderSettings *provider.SettingsService
ProviderRegistry *provider.Registry
WebSearchRegistry *provider.WebSearchRegistry
RateLimiters *provider.RateLimiterMap
Orchestrator *provider.Orchestrator
RuleService *rule.Service
RuleEngine *rule.Engine
Pipeline rule.PipelineRunner
BulkService *rule.BulkService
BulkExecutor *rule.BulkExecutor
RuleScheduler *rule.Scheduler
NFOSnapshotService *nfo.SnapshotService
NFOSettingsService *nfo.NFOSettingsService
ConnectionService *connection.Service
ScraperService *scraper.Service
LibraryService *library.Service
WebhookService *webhook.Service
WebhookDispatcher *webhook.Dispatcher
BackupService *backup.Service
LogManager *logging.Manager
MaintenanceService *maintenance.Service
SettingsIOService *settingsio.Service
UpdaterService *updater.Service
ProbeCache *watcher.ProbeCache
ExpectedWrites *watcher.ExpectedWrites
EventBus *event.Bus
DB *sql.DB
Logger *slog.Logger
BasePath string
BasePathFromEnv bool
StaticFS fs.FS
ImageCacheDir string
Publisher *publish.Publisher
SSEHub *SSEHub
I18nBundle *i18n.Bundle
}
RouterDeps bundles all dependencies needed by the HTTP router.
type SSEClient ¶ added in v0.9.5
type SSEClient struct {
// contains filtered or unexported fields
}
SSEClient represents a single connected SSE browser client.
type SSEEvent ¶ added in v0.9.5
type SSEEvent struct {
// Type is the SSE event name (e.g. "scan.completed", "rule.violation").
Type string `json:"type"`
// Title is a short human-readable summary for toast/notification display.
Title string `json:"title"`
// Message is the full notification body text.
Message string `json:"message"`
// Timestamp is when the event occurred.
Timestamp time.Time `json:"timestamp"`
// Data carries optional structured metadata about the event.
Data map[string]any `json:"data,omitempty"`
}
SSEEvent is a single server-sent event payload delivered to browser clients.
type SSEHub ¶ added in v0.9.5
type SSEHub struct {
// contains filtered or unexported fields
}
SSEHub manages connected SSE clients and fans out events from the event bus. It is safe for concurrent access from multiple goroutines.
func (*SSEHub) Broadcast ¶ added in v0.9.5
Broadcast sends an event to all connected clients. If a client's buffer is full the event is dropped for that client (non-blocking).
func (*SSEHub) ClientCount ¶ added in v0.9.5
ClientCount returns the number of connected SSE clients.
func (*SSEHub) Register ¶ added in v0.9.5
Register adds a client to the hub and returns it. The caller must call Unregister when the client disconnects.
func (*SSEHub) SubscribeToEventBus ¶ added in v0.9.5
SubscribeToEventBus registers event bus handlers that convert internal events into SSE events and broadcast them to all connected clients.
func (*SSEHub) Unregister ¶ added in v0.9.5
Unregister removes a client from the hub and closes its channel.
type ScoredCandidate ¶ added in v0.9.5
type ScoredCandidate struct {
provider.ArtistSearchResult
AlbumComparison *artist.AlbumComparison `json:"album_comparison,omitempty"`
Confidence float64 `json:"confidence"`
Reason string `json:"reason"`
}
ScoredCandidate wraps a provider search result with confidence scoring.
type SharedFilesystemEntry ¶ added in v0.9.5
type SharedFilesystemEntry struct {
}
SharedFilesystemEntry describes one library with a shared-filesystem overlap.
type SharedFilesystemStatus ¶ added in v0.9.5
type SharedFilesystemStatus struct {
}
SharedFilesystemStatus holds the current shared-filesystem detection state returned by the status endpoint and consumed by the notification bar template.
type StaticAssets ¶
type StaticAssets struct {
// contains filtered or unexported fields
}
StaticAssets manages static file serving with content-hash cache busting. Files are served with a version query parameter (e.g., /static/css/styles.css?v=abc123). When the hash matches, responses include immutable cache headers for aggressive browser caching. When files change, the hash changes, forcing browsers to fetch the new version.
func NewStaticAssets ¶
func NewStaticAssets(fsys fs.FS, logger *slog.Logger) *StaticAssets
NewStaticAssets creates a StaticAssets manager that scans the given filesystem.
func (*StaticAssets) Handler ¶
func (sa *StaticAssets) Handler(basePath string) http.Handler
Handler returns an HTTP handler that serves static files with appropriate cache headers.
func (*StaticAssets) Path ¶
func (sa *StaticAssets) Path(filePath string) string
Path returns a cache-busted URL for a static file. Example: Path("/css/styles.css") returns "/static/css/styles.css?v=a1b2c3d4"
func (*StaticAssets) Rescan ¶
func (sa *StaticAssets) Rescan(logger *slog.Logger)
Rescan rescans the static filesystem and updates hashes. Useful if files change at runtime (e.g., during development with os.DirFS).
Source Files
¶
- artist_update.go
- doc.go
- fs.go
- handlers.go
- handlers_alias.go
- handlers_apitoken.go
- handlers_artist.go
- handlers_artist_lock.go
- handlers_backdrop.go
- handlers_backup.go
- handlers_bulk_actions.go
- handlers_conflict.go
- handlers_connection.go
- handlers_connection_library.go
- handlers_contribution.go
- handlers_dashboard.go
- handlers_discography.go
- handlers_docs.go
- handlers_field.go
- handlers_filesystem_browse.go
- handlers_fix.go
- handlers_foreign_files.go
- handlers_guide.go
- handlers_history.go
- handlers_identify.go
- handlers_image.go
- handlers_inbound_webhook.go
- handlers_library.go
- handlers_logging.go
- handlers_logs.go
- handlers_maintenance.go
- handlers_nfo.go
- handlers_notifications.go
- handlers_oidc.go
- handlers_onboarding_conflict.go
- handlers_platform.go
- handlers_platform_ids.go
- handlers_platform_state.go
- handlers_preferences.go
- handlers_provider.go
- handlers_push.go
- handlers_refresh.go
- handlers_reidentify_wizard.go
- handlers_report.go
- handlers_rule.go
- handlers_scanner.go
- handlers_scraper.go
- handlers_settings.go
- handlers_settings_cache.go
- handlers_settings_io.go
- handlers_settings_nfo.go
- handlers_shared_filesystem.go
- handlers_sse.go
- handlers_updater.go
- handlers_user.go
- handlers_webhook.go
- params.go
- pprof.go
- router.go
- static.go