Documentation
¶
Overview ¶
Package handler contains HTTP request handlers for the API server.
Index ¶
- type ConfigHandler
- func (h *ConfigHandler) GetConfig(w http.ResponseWriter, _ *http.Request)
- func (h *ConfigHandler) GetConfigYAML(w http.ResponseWriter, r *http.Request)
- func (h *ConfigHandler) ReplaceConfigYAML(w http.ResponseWriter, r *http.Request)
- func (h *ConfigHandler) SetRuntimeManager(m RuntimeConfigManager)
- func (h *ConfigHandler) UpdateConfig(w http.ResponseWriter, r *http.Request)
- type HookHandler
- func (h *HookHandler) Create(w http.ResponseWriter, r *http.Request)
- func (h *HookHandler) Delete(w http.ResponseWriter, r *http.Request)
- func (h *HookHandler) Get(w http.ResponseWriter, r *http.Request)
- func (h *HookHandler) List(w http.ResponseWriter, r *http.Request)
- func (h *HookHandler) Test(w http.ResponseWriter, r *http.Request)
- func (h *HookHandler) Update(w http.ResponseWriter, r *http.Request)
- type RecordingHandler
- func (h *RecordingHandler) Get(w http.ResponseWriter, r *http.Request)
- func (h *RecordingHandler) Info(w http.ResponseWriter, r *http.Request)
- func (h *RecordingHandler) ListByStream(w http.ResponseWriter, r *http.Request)
- func (h *RecordingHandler) Playlist(w http.ResponseWriter, r *http.Request)
- func (h *RecordingHandler) ServeSegment(w http.ResponseWriter, r *http.Request)
- func (h *RecordingHandler) Timeshift(w http.ResponseWriter, r *http.Request)
- type RuntimeConfigManager
- type StreamHandler
- func (h *StreamHandler) Delete(w http.ResponseWriter, r *http.Request)
- func (h *StreamHandler) Get(w http.ResponseWriter, r *http.Request)
- func (h *StreamHandler) List(w http.ResponseWriter, r *http.Request)
- func (h *StreamHandler) Put(w http.ResponseWriter, r *http.Request)
- func (h *StreamHandler) Restart(w http.ResponseWriter, r *http.Request)
- func (h *StreamHandler) SwitchInput(w http.ResponseWriter, r *http.Request)
- type VODHandler
- func (h *VODHandler) Create(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) Delete(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) DeleteFile(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) Get(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) List(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) ListFiles(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) Raw(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) Update(w http.ResponseWriter, r *http.Request)
- func (h *VODHandler) UploadFile(w http.ResponseWriter, r *http.Request)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ConfigHandler ¶
type ConfigHandler struct {
// contains filtered or unexported fields
}
ConfigHandler serves the GET/POST /config and GET/PUT /config/yaml endpoints.
The /config/yaml endpoints expose every editable resource — GlobalConfig, streams, and hooks — as a single YAML document, so the frontend editor can round-trip the entire system configuration in one screen.
func NewConfigHandler ¶
func NewConfigHandler(i do.Injector) (*ConfigHandler, error)
NewConfigHandler creates a ConfigHandler from the DI injector. The RuntimeConfigManager is injected later via SetRuntimeManager because it depends on services that themselves depend on this handler (circular DI).
func (*ConfigHandler) GetConfig ¶
func (h *ConfigHandler) GetConfig(w http.ResponseWriter, _ *http.Request)
GetConfig returns static enum values, host-detected hardware capabilities, current GlobalConfig, and derived publisher ports.
@Summary Get server configuration. @Description Returns available hardware accelerators (OS-detected), static enum lists, publisher listener ports, and the current runtime GlobalConfig. @Tags system @Produce json @Success 200 {object} apidocs.ConfigData @Router /config [get].
func (*ConfigHandler) GetConfigYAML ¶
func (h *ConfigHandler) GetConfigYAML(w http.ResponseWriter, r *http.Request)
GetConfigYAML returns the entire system state — GlobalConfig, all streams, all hooks — as one YAML document. The frontend editor uses this to populate its initial buffer.
@Summary Get full system configuration as YAML @Description Returns global_config, streams, and hooks bundled in a single YAML document. Pair with PUT /config/yaml to round-trip an editor. @Tags system @Produce application/yaml @Success 200 {string} string "YAML document" @Failure 500 {object} map[string]any @Router /config/yaml [get].
func (*ConfigHandler) ReplaceConfigYAML ¶
func (h *ConfigHandler) ReplaceConfigYAML(w http.ResponseWriter, r *http.Request)
ReplaceConfigYAML replaces the entire system configuration with the YAML body. Performs a full replace, not a merge:
- GlobalConfig sections omitted from the body are treated as absent and the corresponding service is stopped.
- Streams omitted from the body are stopped and deleted.
- Hooks omitted from the body are deleted.
All field errors are collected and returned together so the editor can highlight every issue in one round-trip.
@Summary Replace full system configuration from YAML @Description Replaces global_config, streams, and hooks with the request body. Validation errors are returned as a list. On success, the runtime manager + coordinator diff against the previous state and start/stop/restart services accordingly. @Tags system @Accept application/yaml @Produce json @Param body body string true "Full system configuration YAML document" @Success 200 {object} map[string]any @Failure 400 {object} map[string]any @Failure 422 {object} map[string]any @Failure 500 {object} map[string]any @Router /config/yaml [put].
func (*ConfigHandler) SetRuntimeManager ¶
func (h *ConfigHandler) SetRuntimeManager(m RuntimeConfigManager)
SetRuntimeManager injects the RuntimeManager after construction (breaks the circular DI dependency).
func (*ConfigHandler) UpdateConfig ¶
func (h *ConfigHandler) UpdateConfig(w http.ResponseWriter, r *http.Request)
UpdateConfig partially updates the runtime GlobalConfig, persists it to the store, and hot-reloads services (start/stop) to match the new configuration.
Only the fields present in the request body are updated; omitted fields keep their current values. Sending a section as JSON null disables that service.
@Summary Partially update server configuration. @Description Merges the request body into the current GlobalConfig and applies it — services are started or stopped to match. @Tags system @Accept json @Produce json @Param body body domain.GlobalConfig true "Partial configuration to merge" @Success 200 {object} apidocs.ConfigUpdateResponse @Failure 400 {object} apidocs.ErrorResponse @Failure 500 {object} apidocs.ErrorResponse @Router /config [post].
type HookHandler ¶
type HookHandler struct {
// contains filtered or unexported fields
}
HookHandler handles webhook and hook management REST endpoints.
func NewHookHandler ¶
func NewHookHandler(i do.Injector) (*HookHandler, error)
NewHookHandler creates a HookHandler and registers it with the DI injector.
func (*HookHandler) Create ¶
func (h *HookHandler) Create(w http.ResponseWriter, r *http.Request)
Create registers a new hook. @Summary Create hook @Tags hooks @Accept json @Produce json @Param body body domain.Hook true "Hook configuration" @Success 201 {object} apidocs.HookData @Failure 400 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /hooks [post].
func (*HookHandler) Delete ¶
func (h *HookHandler) Delete(w http.ResponseWriter, r *http.Request)
Delete removes a hook. @Summary Delete hook @Tags hooks @Param hid path string true "Hook ID" @Success 204 "No Content" @Failure 500 {object} apidocs.ErrorBody @Router /hooks/{hid} [delete].
func (*HookHandler) Get ¶
func (h *HookHandler) Get(w http.ResponseWriter, r *http.Request)
Get returns one hook. @Summary Get hook @Tags hooks @Produce json @Param hid path string true "Hook ID" @Success 200 {object} apidocs.HookData @Failure 404 {object} apidocs.ErrorBody @Router /hooks/{hid} [get].
func (*HookHandler) List ¶
func (h *HookHandler) List(w http.ResponseWriter, r *http.Request)
List registered hooks. @Summary List hooks @Tags hooks @Produce json @Success 200 {object} apidocs.HookList @Failure 500 {object} apidocs.ErrorBody @Router /hooks [get].
func (*HookHandler) Test ¶
func (h *HookHandler) Test(w http.ResponseWriter, r *http.Request)
Test delivers a synthetic event to an HTTP hook (same signing/delivery path as production). @Summary Test hook delivery @Tags hooks @Produce json @Param hid path string true "Hook ID" @Success 200 {object} apidocs.HookTestData @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 502 {object} apidocs.ErrorBody @Router /hooks/{hid}/test [post].
func (*HookHandler) Update ¶
func (h *HookHandler) Update(w http.ResponseWriter, r *http.Request)
Update replaces hook configuration. @Summary Update hook @Tags hooks @Accept json @Produce json @Param hid path string true "Hook ID" @Param body body domain.Hook true "Hook configuration" @Success 200 {object} apidocs.HookData @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /hooks/{hid} [put].
type RecordingHandler ¶
type RecordingHandler struct {
// contains filtered or unexported fields
}
RecordingHandler handles DVR recording and playback REST endpoints.
func NewRecordingHandler ¶
func NewRecordingHandler(i do.Injector) (*RecordingHandler, error)
NewRecordingHandler creates a RecordingHandler and registers it with the DI injector.
func (*RecordingHandler) Get ¶
func (h *RecordingHandler) Get(w http.ResponseWriter, r *http.Request)
Get returns one recording's lifecycle metadata by id. @Summary Get recording @Tags recordings @Produce json @Param rid path string true "Recording ID (= stream code)" @Success 200 {object} apidocs.RecordingData @Failure 404 {object} apidocs.ErrorBody @Router /recordings/{rid} [get].
func (*RecordingHandler) Info ¶
func (h *RecordingHandler) Info(w http.ResponseWriter, r *http.Request)
Info returns full DVR metadata for a recording: dvr_range, gaps, segment count, total size. Data is read from index.json on disk, so it is always up-to-date. @Summary DVR recording info @Tags recordings @Produce json @Param rid path string true "Recording ID (= stream code)" @Success 200 {object} map[string]any @Failure 404 {object} apidocs.ErrorBody @Router /recordings/{rid}/info [get].
func (*RecordingHandler) ListByStream ¶
func (h *RecordingHandler) ListByStream(w http.ResponseWriter, r *http.Request)
ListByStream lists recording metadata for a stream. @Summary List recordings by stream @Tags recordings @Produce json @Param code path string true "Stream code" @Success 200 {object} apidocs.RecordingList @Failure 500 {object} apidocs.ErrorBody @Router /streams/{code}/recordings [get].
func (*RecordingHandler) Playlist ¶
func (h *RecordingHandler) Playlist(w http.ResponseWriter, r *http.Request)
Playlist serves the M3U8 playlist written to disk by the DVR worker. Reads playlist.m3u8 directly for an always-current response. @Summary Recording M3U8 playlist @Tags recordings @Produce plain @Param rid path string true "Recording ID (= stream code)" @Success 200 {string} string "M3U8 body" @Failure 404 {object} apidocs.ErrorBody @Router /recordings/{rid}/playlist.m3u8 [get].
func (*RecordingHandler) ServeSegment ¶
func (h *RecordingHandler) ServeSegment(w http.ResponseWriter, r *http.Request)
ServeSegment serves a DVR TS segment file from the recording's SegmentDir. @Summary Serve DVR segment @Tags recordings @Produce octet-stream @Param rid path string true "Recording ID (= stream code)" @Param file path string true "Segment filename (e.g. 000000.ts)" @Success 200 {file} binary @Failure 404 {object} apidocs.ErrorBody @Router /recordings/{rid}/{file} [get].
func (*RecordingHandler) Timeshift ¶
func (h *RecordingHandler) Timeshift(w http.ResponseWriter, r *http.Request)
Timeshift builds and returns a dynamic VOD M3U8 for a time window.
Query parameters (mutually exclusive for start point):
- from=<RFC3339> absolute start time, e.g. 2026-04-06T14:30:00Z
- offset_sec=<N> seconds from recording start (relative)
Optional:
- duration=<N> window length in seconds (default: all remaining)
Example:
GET /recordings/test/timeshift.m3u8?from=2026-04-06T14:30:00Z&duration=3600 GET /recordings/test/timeshift.m3u8?offset_sec=1800&duration=3600
@Summary Timeshift VOD playlist @Tags recordings @Produce plain @Param rid path string true "Recording ID (= stream code)" @Param from query string false "Absolute start time (RFC3339)" @Param offset_sec query int false "Relative start offset in seconds from recording start" @Param duration query int false "Window duration in seconds (default: all remaining)" @Success 200 {string} string "M3U8 body" @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Router /recordings/{rid}/timeshift.m3u8 [get].
type RuntimeConfigManager ¶
type RuntimeConfigManager interface {
CurrentConfig() *domain.GlobalConfig
Apply(ctx context.Context, newCfg *domain.GlobalConfig) error
}
RuntimeConfigManager is the subset of runtime.Manager that the config handler needs.
type StreamHandler ¶
type StreamHandler struct {
// contains filtered or unexported fields
}
StreamHandler handles stream lifecycle REST endpoints.
func NewStreamHandler ¶
func NewStreamHandler(i do.Injector) (*StreamHandler, error)
NewStreamHandler creates a StreamHandler and registers it with the DI injector.
func (*StreamHandler) Delete ¶
func (h *StreamHandler) Delete(w http.ResponseWriter, r *http.Request)
Delete removes a stream and stops its pipeline. @Summary Delete stream @Tags streams @Param code path string true "Stream code" @Success 204 "No Content" @Failure 500 {object} apidocs.ErrorBody @Router /streams/{code} [delete].
func (*StreamHandler) Get ¶
func (h *StreamHandler) Get(w http.ResponseWriter, r *http.Request)
Get returns one stream by code. @Summary Get stream @Tags streams @Produce json @Param code path string true "Stream code (a-zA-Z0-9_)" @Success 200 {object} apidocs.StreamData @Failure 404 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /streams/{code} [get].
func (*StreamHandler) List ¶
func (h *StreamHandler) List(w http.ResponseWriter, r *http.Request)
List streams; optional ?status=idle|active|degraded|stopped. @Summary List streams @Tags streams @Produce json @Param status query string false "Filter by status" @Success 200 {object} apidocs.StreamList @Failure 400 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /streams [get].
func (*StreamHandler) Put ¶
func (h *StreamHandler) Put(w http.ResponseWriter, r *http.Request)
Put creates a new stream or partially updates an existing one. Only fields present in the request body are applied; omitted fields keep their current values. @Summary Create or partial-update stream @Tags streams @Accept json @Produce json @Param code path string true "Stream code" @Param body body apidocs.StreamPutRequest true "Partial or full stream document" @Success 200 {object} apidocs.StreamData @Success 201 {object} apidocs.StreamData @Failure 400 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /streams/{code} [post].
func (*StreamHandler) Restart ¶
func (h *StreamHandler) Restart(w http.ResponseWriter, r *http.Request)
Restart stops the running pipeline (if any), waits for full cleanup, then starts a fresh pipeline. This guarantees all goroutines have exited and on-disk segments have been removed before the new pipeline begins. @Summary Restart stream pipeline @Tags streams @Produce json @Param code path string true "Stream code" @Success 200 {object} apidocs.StreamActionData @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /streams/{code}/restart [post].
func (*StreamHandler) SwitchInput ¶
func (h *StreamHandler) SwitchInput(w http.ResponseWriter, r *http.Request)
SwitchInput forces the active ingest source to the given input priority at runtime. The switch is temporary — it reverts automatically when the selected input degrades permanently. Switching to a different priority replaces any previous manual override.
@Summary Manual input switch @Tags streams @Produce json @Param code path string true "Stream code" @Param body body apidocs.InputSwitchRequest true "Target input priority" @Success 200 {object} apidocs.StreamActionData @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /streams/{code}/inputs/switch [post].
type VODHandler ¶
type VODHandler struct {
// contains filtered or unexported fields
}
VODHandler manages VOD mount registrations and serves live filesystem listings for the UI. The system never indexes file content — listings come straight from the host filesystem on every request.
func NewVODHandler ¶
func NewVODHandler(i do.Injector) (*VODHandler, error)
NewVODHandler wires the handler from DI.
func (*VODHandler) Create ¶
func (h *VODHandler) Create(w http.ResponseWriter, r *http.Request)
Create registers a new VOD mount. Name must be unique; storage must be an absolute host path. The directory is not required to exist at create time — operators may provision it later — but a missing directory will surface as an error from List/Resolve until it is fixed.
@Summary Create VOD mount @Tags vod @Accept json @Produce json @Param body body domain.VODMount true "VOD mount" @Success 201 {object} apidocs.VODMountData @Failure 400 {object} apidocs.ErrorBody @Failure 409 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /vod [post].
func (*VODHandler) Delete ¶
func (h *VODHandler) Delete(w http.ResponseWriter, r *http.Request)
Delete removes a mount. Files on disk are untouched.
@Summary Delete VOD mount @Tags vod @Param name path string true "Mount name" @Success 204 "No Content" @Failure 500 {object} apidocs.ErrorBody @Router /vod/{name} [delete].
func (*VODHandler) DeleteFile ¶
func (h *VODHandler) DeleteFile(w http.ResponseWriter, r *http.Request)
DeleteFile removes a single file from a VOD mount. The wildcard path is the file's location relative to the mount root. Directories are refused — this endpoint is intentionally narrow so an operator typo cannot wipe a tree.
@Summary Delete a file from a VOD mount @Tags vod @Param name path string true "Mount name" @Param path path string true "File path inside the mount (forward-slash separated)" @Success 204 "No Content" @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /vod/{name}/files/{path} [delete].
func (*VODHandler) Get ¶
func (h *VODHandler) Get(w http.ResponseWriter, r *http.Request)
Get returns one mount by name.
@Summary Get VOD mount @Tags vod @Produce json @Param name path string true "Mount name" @Success 200 {object} apidocs.VODMountData @Failure 404 {object} apidocs.ErrorBody @Router /vod/{name} [get].
func (*VODHandler) List ¶
func (h *VODHandler) List(w http.ResponseWriter, r *http.Request)
List returns all registered VOD mounts.
@Summary List VOD mounts @Tags vod @Produce json @Success 200 {object} apidocs.VODMountList @Failure 500 {object} apidocs.ErrorBody @Router /vod [get].
func (*VODHandler) ListFiles ¶
func (h *VODHandler) ListFiles(w http.ResponseWriter, r *http.Request)
ListFiles lists the contents of a directory inside a mount, one level deep. The optional ?path= query selects a subdirectory; defaults to the mount root. Listings are computed live from the host filesystem; the system never caches or indexes file content.
@Summary List files in a VOD mount @Tags vod @Produce json @Param name path string true "Mount name" @Param path query string false "Subdirectory (relative, no leading slash)" @Success 200 {object} apidocs.VODFileList @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /vod/{name}/files [get].
func (*VODHandler) Raw ¶
func (h *VODHandler) Raw(w http.ResponseWriter, r *http.Request)
Raw streams a single file from a VOD mount over HTTP. The browser hits this endpoint directly via the play_url returned by ListFiles. http.ServeFile handles Content-Type detection and Range requests so the browser's <video> tag can seek inside the file.
@Summary Stream a VOD file over HTTP @Description Returns the raw bytes of a file inside a VOD mount. Supports HTTP Range requests so an HTML5 video player can seek. @Tags vod @Produce octet-stream @Param name path string true "Mount name" @Param path path string true "File path inside the mount (forward-slash separated)" @Success 200 {file} file @Success 206 {file} file @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Router /vod/{name}/raw/{path} [get].
func (*VODHandler) Update ¶
func (h *VODHandler) Update(w http.ResponseWriter, r *http.Request)
Update replaces a mount's storage / comment. Name in URL wins over body.
@Summary Update VOD mount @Tags vod @Accept json @Produce json @Param name path string true "Mount name" @Param body body domain.VODMount true "VOD mount" @Success 200 {object} apidocs.VODMountData @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /vod/{name} [put].
func (*VODHandler) UploadFile ¶
func (h *VODHandler) UploadFile(w http.ResponseWriter, r *http.Request)
UploadFile accepts a multipart upload and stores it inside a VOD mount. The form field "file" holds the bytes; the optional ?path= query selects a subdirectory under the mount root (created on demand). Only files with a known video extension are accepted; the destination filename is taken from the upload header and stripped to its base via filepath.Base. Existing files are not overwritten — clients must DELETE first.
@Summary Upload a file to a VOD mount @Tags vod @Accept multipart/form-data @Produce json @Param name path string true "Mount name" @Param path query string false "Subdirectory (relative, no leading slash)" @Param file formData file true "Video file (mp4, mkv, mov, …)" @Success 201 {object} apidocs.VODFileData @Failure 400 {object} apidocs.ErrorBody @Failure 404 {object} apidocs.ErrorBody @Failure 409 {object} apidocs.ErrorBody @Failure 413 {object} apidocs.ErrorBody @Failure 500 {object} apidocs.ErrorBody @Router /vod/{name}/files [post].