mirror of
https://github.com/datarhei/core.git
synced 2025-09-26 20:11:29 +08:00
657 lines
16 KiB
Go
657 lines
16 KiB
Go
// @title datarhei Core API
|
|
// @version 3.0
|
|
// @description Expose REST API for the datarhei Core
|
|
|
|
// @contact.name datarhei Core Support
|
|
// @contact.url https://www.datarhei.com
|
|
// @contact.email hello@datarhei.com
|
|
|
|
// @license.name Apache 2.0
|
|
// @license.url https://github.com/datarhei/core/v16/blob/main/LICENSE
|
|
|
|
// @BasePath /
|
|
|
|
// @securityDefinitions.apikey ApiKeyAuth
|
|
// @in header
|
|
// @name Authorization
|
|
// @Param Authorization header string true "Insert your access token" default(Bearer <Add access token here>)
|
|
|
|
// @securityDefinitions.apikey ApiRefreshKeyAuth
|
|
// @in header
|
|
// @name Authorization
|
|
|
|
// @securityDefinitions.apikey Auth0KeyAuth
|
|
// @in header
|
|
// @name Authorization
|
|
|
|
// @securityDefinitions.basic BasicAuth
|
|
|
|
package http
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
cfgstore "github.com/datarhei/core/v16/config/store"
|
|
"github.com/datarhei/core/v16/http/cache"
|
|
"github.com/datarhei/core/v16/http/errorhandler"
|
|
"github.com/datarhei/core/v16/http/graph/resolver"
|
|
"github.com/datarhei/core/v16/http/handler"
|
|
api "github.com/datarhei/core/v16/http/handler/api"
|
|
"github.com/datarhei/core/v16/http/jwt"
|
|
"github.com/datarhei/core/v16/http/router"
|
|
"github.com/datarhei/core/v16/http/validator"
|
|
"github.com/datarhei/core/v16/io/fs"
|
|
"github.com/datarhei/core/v16/log"
|
|
"github.com/datarhei/core/v16/monitor"
|
|
"github.com/datarhei/core/v16/net"
|
|
"github.com/datarhei/core/v16/prometheus"
|
|
"github.com/datarhei/core/v16/restream"
|
|
"github.com/datarhei/core/v16/rtmp"
|
|
"github.com/datarhei/core/v16/session"
|
|
"github.com/datarhei/core/v16/srt"
|
|
|
|
mwcache "github.com/datarhei/core/v16/http/middleware/cache"
|
|
mwcors "github.com/datarhei/core/v16/http/middleware/cors"
|
|
mwgzip "github.com/datarhei/core/v16/http/middleware/gzip"
|
|
mwhlsrewrite "github.com/datarhei/core/v16/http/middleware/hlsrewrite"
|
|
mwiplimit "github.com/datarhei/core/v16/http/middleware/iplimit"
|
|
mwlog "github.com/datarhei/core/v16/http/middleware/log"
|
|
mwmime "github.com/datarhei/core/v16/http/middleware/mime"
|
|
mwredirect "github.com/datarhei/core/v16/http/middleware/redirect"
|
|
mwsession "github.com/datarhei/core/v16/http/middleware/session"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/labstack/echo/v4/middleware"
|
|
|
|
echoSwagger "github.com/swaggo/echo-swagger" // echo-swagger middleware
|
|
|
|
// Expose the API docs
|
|
_ "github.com/datarhei/core/v16/docs"
|
|
)
|
|
|
|
var ListenAndServe = http.ListenAndServe
|
|
|
|
type Config struct {
|
|
Logger log.Logger
|
|
LogBuffer log.BufferWriter
|
|
Restream restream.Restreamer
|
|
Metrics monitor.HistoryReader
|
|
Prometheus prometheus.Reader
|
|
MimeTypesFile string
|
|
DiskFS fs.Filesystem
|
|
MemFS MemFSConfig
|
|
IPLimiter net.IPLimiter
|
|
Profiling bool
|
|
Cors CorsConfig
|
|
RTMP rtmp.Server
|
|
SRT srt.Server
|
|
JWT jwt.JWT
|
|
Config cfgstore.Store
|
|
Cache cache.Cacher
|
|
Sessions session.RegistryReader
|
|
Router router.Router
|
|
ReadOnly bool
|
|
}
|
|
|
|
type MemFSConfig struct {
|
|
EnableAuth bool
|
|
Username string
|
|
Password string
|
|
Filesystem fs.Filesystem
|
|
}
|
|
|
|
type CorsConfig struct {
|
|
Origins []string
|
|
}
|
|
|
|
type Server interface {
|
|
ServeHTTP(w http.ResponseWriter, r *http.Request)
|
|
}
|
|
|
|
type server struct {
|
|
logger log.Logger
|
|
|
|
handler struct {
|
|
about *api.AboutHandler
|
|
memfs *handler.MemFSHandler
|
|
diskfs *handler.DiskFSHandler
|
|
prometheus *handler.PrometheusHandler
|
|
profiling *handler.ProfilingHandler
|
|
ping *handler.PingHandler
|
|
graph *api.GraphHandler
|
|
jwt jwt.JWT
|
|
}
|
|
|
|
v3handler struct {
|
|
log *api.LogHandler
|
|
restream *api.RestreamHandler
|
|
playout *api.PlayoutHandler
|
|
memfs *api.MemFSHandler
|
|
diskfs *api.DiskFSHandler
|
|
rtmp *api.RTMPHandler
|
|
srt *api.SRTHandler
|
|
config *api.ConfigHandler
|
|
session *api.SessionHandler
|
|
widget *api.WidgetHandler
|
|
resources *api.MetricsHandler
|
|
}
|
|
|
|
middleware struct {
|
|
iplimit echo.MiddlewareFunc
|
|
log echo.MiddlewareFunc
|
|
accessJWT echo.MiddlewareFunc
|
|
refreshJWT echo.MiddlewareFunc
|
|
cors echo.MiddlewareFunc
|
|
cache echo.MiddlewareFunc
|
|
session echo.MiddlewareFunc
|
|
hlsrewrite echo.MiddlewareFunc
|
|
}
|
|
|
|
memfs struct {
|
|
enableAuth bool
|
|
username string
|
|
password string
|
|
}
|
|
|
|
diskfs fs.Filesystem
|
|
|
|
gzip struct {
|
|
mimetypes []string
|
|
}
|
|
|
|
router *echo.Echo
|
|
mimeTypesFile string
|
|
profiling bool
|
|
|
|
readOnly bool
|
|
}
|
|
|
|
func NewServer(config Config) (Server, error) {
|
|
s := &server{
|
|
logger: config.Logger,
|
|
mimeTypesFile: config.MimeTypesFile,
|
|
profiling: config.Profiling,
|
|
diskfs: config.DiskFS,
|
|
readOnly: config.ReadOnly,
|
|
}
|
|
|
|
s.v3handler.diskfs = api.NewDiskFS(
|
|
config.DiskFS,
|
|
config.Cache,
|
|
)
|
|
|
|
s.handler.diskfs = handler.NewDiskFS(
|
|
config.DiskFS,
|
|
config.Cache,
|
|
)
|
|
|
|
s.middleware.hlsrewrite = mwhlsrewrite.NewHLSRewriteWithConfig(mwhlsrewrite.HLSRewriteConfig{
|
|
PathPrefix: config.DiskFS.Base(),
|
|
})
|
|
|
|
s.memfs.enableAuth = config.MemFS.EnableAuth
|
|
s.memfs.username = config.MemFS.Username
|
|
s.memfs.password = config.MemFS.Password
|
|
|
|
if config.Logger == nil {
|
|
s.logger = log.New("HTTP")
|
|
}
|
|
|
|
if config.JWT == nil {
|
|
s.handler.about = api.NewAbout(
|
|
config.Restream,
|
|
[]string{},
|
|
)
|
|
} else {
|
|
s.handler.about = api.NewAbout(
|
|
config.Restream,
|
|
config.JWT.Validators(),
|
|
)
|
|
}
|
|
|
|
s.v3handler.log = api.NewLog(
|
|
config.LogBuffer,
|
|
)
|
|
|
|
if config.Restream != nil {
|
|
s.v3handler.restream = api.NewRestream(
|
|
config.Restream,
|
|
)
|
|
|
|
s.v3handler.playout = api.NewPlayout(
|
|
config.Restream,
|
|
)
|
|
}
|
|
|
|
if config.MemFS.Filesystem != nil {
|
|
s.v3handler.memfs = api.NewMemFS(
|
|
config.MemFS.Filesystem,
|
|
)
|
|
|
|
s.handler.memfs = handler.NewMemFS(
|
|
config.MemFS.Filesystem,
|
|
)
|
|
}
|
|
|
|
if config.Prometheus != nil {
|
|
s.handler.prometheus = handler.NewPrometheus(
|
|
config.Prometheus.HTTPHandler(),
|
|
)
|
|
}
|
|
|
|
if config.Profiling {
|
|
s.handler.profiling = handler.NewProfiling()
|
|
}
|
|
|
|
if config.IPLimiter != nil {
|
|
s.middleware.iplimit = mwiplimit.NewWithConfig(mwiplimit.Config{
|
|
Limiter: config.IPLimiter,
|
|
})
|
|
}
|
|
|
|
s.handler.ping = handler.NewPing()
|
|
|
|
if config.RTMP != nil {
|
|
s.v3handler.rtmp = api.NewRTMP(
|
|
config.RTMP,
|
|
)
|
|
}
|
|
|
|
if config.SRT != nil {
|
|
s.v3handler.srt = api.NewSRT(
|
|
config.SRT,
|
|
)
|
|
}
|
|
|
|
if config.Config != nil {
|
|
s.v3handler.config = api.NewConfig(
|
|
config.Config,
|
|
)
|
|
}
|
|
|
|
if config.JWT != nil {
|
|
s.handler.jwt = config.JWT
|
|
s.middleware.accessJWT = config.JWT.AccessMiddleware()
|
|
s.middleware.refreshJWT = config.JWT.RefreshMiddleware()
|
|
}
|
|
|
|
if config.Sessions == nil {
|
|
config.Sessions, _ = session.New(session.Config{})
|
|
}
|
|
|
|
s.v3handler.session = api.NewSession(
|
|
config.Sessions,
|
|
)
|
|
s.middleware.session = mwsession.NewHLSWithConfig(mwsession.HLSConfig{
|
|
EgressCollector: config.Sessions.Collector("hls"),
|
|
IngressCollector: config.Sessions.Collector("hlsingress"),
|
|
})
|
|
|
|
s.middleware.log = mwlog.NewWithConfig(mwlog.Config{
|
|
Logger: s.logger,
|
|
})
|
|
|
|
if config.Cache != nil {
|
|
s.middleware.cache = mwcache.NewWithConfig(mwcache.Config{
|
|
Cache: config.Cache,
|
|
})
|
|
}
|
|
|
|
s.v3handler.widget = api.NewWidget(api.WidgetConfig{
|
|
Restream: config.Restream,
|
|
Registry: config.Sessions,
|
|
})
|
|
|
|
s.v3handler.resources = api.NewMetrics(api.MetricsConfig{
|
|
Metrics: config.Metrics,
|
|
})
|
|
|
|
if middleware, err := mwcors.NewWithConfig(mwcors.Config{
|
|
Prefixes: map[string][]string{
|
|
"/": config.Cors.Origins,
|
|
"/api": {"*"},
|
|
"/memfs": config.Cors.Origins,
|
|
},
|
|
}); err != nil {
|
|
return nil, err
|
|
} else {
|
|
s.middleware.cors = middleware
|
|
}
|
|
|
|
s.handler.graph = api.NewGraph(resolver.Resolver{
|
|
Restream: config.Restream,
|
|
Monitor: config.Metrics,
|
|
LogBuffer: config.LogBuffer,
|
|
}, "/api/graph/query")
|
|
|
|
s.gzip.mimetypes = []string{
|
|
"text/plain",
|
|
"text/html",
|
|
"text/javascript",
|
|
"application/json",
|
|
"application/x-mpegurl",
|
|
"application/vnd.apple.mpegurl",
|
|
"image/svg+xml",
|
|
}
|
|
|
|
s.router = echo.New()
|
|
s.router.HTTPErrorHandler = errorhandler.HTTPErrorHandler
|
|
s.router.Validator = validator.New()
|
|
s.router.Use(s.middleware.log)
|
|
s.router.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{
|
|
LogErrorFunc: func(c echo.Context, err error, stack []byte) error {
|
|
rows := strings.Split(string(stack), "\n")
|
|
s.logger.Error().WithField("stack", rows).Log("recovered from a panic")
|
|
return nil
|
|
},
|
|
}))
|
|
s.router.Use(mwsession.NewHTTPWithConfig(mwsession.HTTPConfig{
|
|
Collector: config.Sessions.Collector("http"),
|
|
}))
|
|
|
|
s.router.HideBanner = true
|
|
s.router.HidePort = true
|
|
|
|
s.router.Logger.SetOutput(newLogwrapper(s.logger))
|
|
|
|
if s.middleware.cors != nil {
|
|
s.router.Use(s.middleware.cors)
|
|
}
|
|
|
|
// Add static routes
|
|
if path, target := config.Router.StaticRoute(); len(target) != 0 {
|
|
group := s.router.Group(path)
|
|
group.Use(middleware.AddTrailingSlashWithConfig(middleware.TrailingSlashConfig{
|
|
Skipper: func(c echo.Context) bool {
|
|
return path != c.Request().URL.Path
|
|
},
|
|
RedirectCode: 301,
|
|
}))
|
|
group.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
|
Skipper: middleware.DefaultSkipper,
|
|
Root: target,
|
|
Index: "index.html",
|
|
IgnoreBase: true,
|
|
}))
|
|
}
|
|
|
|
s.router.Use(mwredirect.NewWithConfig(mwredirect.Config{
|
|
Redirects: config.Router.FileRoutes(),
|
|
}))
|
|
|
|
for prefix, target := range config.Router.DirRoutes() {
|
|
group := s.router.Group(prefix)
|
|
group.Use(middleware.AddTrailingSlashWithConfig(middleware.TrailingSlashConfig{
|
|
Skipper: func(prefix string) func(c echo.Context) bool {
|
|
return func(c echo.Context) bool {
|
|
return prefix != c.Request().URL.Path
|
|
}
|
|
}(prefix),
|
|
RedirectCode: 301,
|
|
}))
|
|
group.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
|
Skipper: middleware.DefaultSkipper,
|
|
Root: target,
|
|
Index: "index.html",
|
|
IgnoreBase: true,
|
|
}))
|
|
}
|
|
|
|
s.setRoutes()
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
s.router.ServeHTTP(w, r)
|
|
}
|
|
|
|
func (s *server) setRoutes() {
|
|
gzipMiddleware := mwgzip.NewWithConfig(mwgzip.Config{
|
|
Level: mwgzip.BestSpeed,
|
|
MinLength: 1000,
|
|
Skipper: mwgzip.ContentTypeSkipper(nil),
|
|
})
|
|
|
|
// API router grouo
|
|
api := s.router.Group("/api")
|
|
|
|
if s.middleware.iplimit != nil {
|
|
api.Use(s.middleware.iplimit)
|
|
}
|
|
|
|
if s.middleware.accessJWT != nil {
|
|
// Enable JWT auth
|
|
api.Use(s.middleware.accessJWT)
|
|
|
|
// The login endpoint should not be blocked by auth
|
|
s.router.POST("/api/login", s.handler.jwt.LoginHandler)
|
|
s.router.GET("/api/login/refresh", s.handler.jwt.RefreshHandler, s.middleware.refreshJWT)
|
|
}
|
|
|
|
api.GET("", s.handler.about.About)
|
|
|
|
// Swagger API documentation router group
|
|
doc := s.router.Group("/api/swagger/*")
|
|
doc.Use(gzipMiddleware)
|
|
doc.GET("", echoSwagger.WrapHandler)
|
|
|
|
// Serve static data
|
|
fs := s.router.Group("/*")
|
|
fs.Use(mwmime.NewWithConfig(mwmime.Config{
|
|
MimeTypesFile: s.mimeTypesFile,
|
|
DefaultContentType: "text/html",
|
|
}))
|
|
fs.Use(mwgzip.NewWithConfig(mwgzip.Config{
|
|
Level: mwgzip.BestSpeed,
|
|
MinLength: 1000,
|
|
Skipper: mwgzip.ContentTypeSkipper(s.gzip.mimetypes),
|
|
}))
|
|
if s.middleware.cache != nil {
|
|
fs.Use(s.middleware.cache)
|
|
}
|
|
fs.Use(s.middleware.hlsrewrite)
|
|
if s.middleware.session != nil {
|
|
fs.Use(s.middleware.session)
|
|
}
|
|
|
|
fs.GET("", s.handler.diskfs.GetFile)
|
|
fs.HEAD("", s.handler.diskfs.GetFile)
|
|
|
|
// Memory FS
|
|
if s.handler.memfs != nil {
|
|
memfs := s.router.Group("/memfs/*")
|
|
memfs.Use(mwmime.NewWithConfig(mwmime.Config{
|
|
MimeTypesFile: s.mimeTypesFile,
|
|
DefaultContentType: "application/data",
|
|
}))
|
|
memfs.Use(mwgzip.NewWithConfig(mwgzip.Config{
|
|
Level: mwgzip.BestSpeed,
|
|
MinLength: 1000,
|
|
Skipper: mwgzip.ContentTypeSkipper(s.gzip.mimetypes),
|
|
}))
|
|
if s.middleware.session != nil {
|
|
memfs.Use(s.middleware.session)
|
|
}
|
|
|
|
memfs.HEAD("", s.handler.memfs.GetFile)
|
|
memfs.GET("", s.handler.memfs.GetFile)
|
|
|
|
var authmw echo.MiddlewareFunc
|
|
|
|
if s.memfs.enableAuth {
|
|
authmw = middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
|
|
if username == s.memfs.username && password == s.memfs.password {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
})
|
|
|
|
memfs.POST("", s.handler.memfs.PutFile, authmw)
|
|
memfs.PUT("", s.handler.memfs.PutFile, authmw)
|
|
memfs.DELETE("", s.handler.memfs.DeleteFile, authmw)
|
|
} else {
|
|
memfs.POST("", s.handler.memfs.PutFile)
|
|
memfs.PUT("", s.handler.memfs.PutFile)
|
|
memfs.DELETE("", s.handler.memfs.DeleteFile)
|
|
}
|
|
}
|
|
|
|
// Prometheus metrics
|
|
if s.handler.prometheus != nil {
|
|
metrics := s.router.Group("/metrics")
|
|
|
|
if s.middleware.iplimit != nil {
|
|
metrics.Use(s.middleware.iplimit)
|
|
}
|
|
|
|
metrics.GET("", s.handler.prometheus.Metrics)
|
|
}
|
|
|
|
// Health check
|
|
s.router.GET("/ping", s.handler.ping.Ping)
|
|
|
|
// Profiling routes
|
|
if s.profiling {
|
|
prof := s.router.Group("/profiling")
|
|
|
|
if s.middleware.iplimit != nil {
|
|
prof.Use(s.middleware.iplimit)
|
|
}
|
|
|
|
s.handler.profiling.Register(prof)
|
|
}
|
|
|
|
// GraphQL
|
|
graphql := api.Group("/graph")
|
|
graphql.Use(gzipMiddleware)
|
|
|
|
graphql.GET("", s.handler.graph.Playground)
|
|
graphql.POST("/query", s.handler.graph.Query)
|
|
|
|
// APIv3 router group
|
|
v3 := api.Group("/v3")
|
|
|
|
if s.handler.jwt != nil {
|
|
v3.Use(s.middleware.accessJWT)
|
|
}
|
|
|
|
v3.Use(gzipMiddleware)
|
|
|
|
s.setRoutesV3(v3)
|
|
}
|
|
|
|
func (s *server) setRoutesV3(v3 *echo.Group) {
|
|
if s.v3handler.widget != nil {
|
|
// The widget endpoint should not be blocked by auth
|
|
s.router.GET("/api/v3/widget/process/:id", s.v3handler.widget.Get)
|
|
}
|
|
|
|
// v3 Restreamer
|
|
if s.v3handler.restream != nil {
|
|
v3.GET("/skills", s.v3handler.restream.Skills)
|
|
v3.GET("/skills/reload", s.v3handler.restream.ReloadSkills)
|
|
|
|
v3.GET("/process", s.v3handler.restream.GetAll)
|
|
v3.GET("/process/:id", s.v3handler.restream.Get)
|
|
|
|
v3.GET("/process/:id/config", s.v3handler.restream.GetConfig)
|
|
v3.GET("/process/:id/state", s.v3handler.restream.GetState)
|
|
v3.GET("/process/:id/report", s.v3handler.restream.GetReport)
|
|
v3.GET("/process/:id/probe", s.v3handler.restream.Probe)
|
|
|
|
v3.GET("/process/:id/metadata", s.v3handler.restream.GetProcessMetadata)
|
|
v3.GET("/process/:id/metadata/:key", s.v3handler.restream.GetProcessMetadata)
|
|
|
|
v3.GET("/metadata", s.v3handler.restream.GetMetadata)
|
|
v3.GET("/metadata/:key", s.v3handler.restream.GetMetadata)
|
|
|
|
if !s.readOnly {
|
|
v3.POST("/process", s.v3handler.restream.Add)
|
|
v3.PUT("/process/:id", s.v3handler.restream.Update)
|
|
v3.DELETE("/process/:id", s.v3handler.restream.Delete)
|
|
v3.PUT("/process/:id/command", s.v3handler.restream.Command)
|
|
v3.PUT("/process/:id/metadata/:key", s.v3handler.restream.SetProcessMetadata)
|
|
v3.PUT("/metadata/:key", s.v3handler.restream.SetMetadata)
|
|
}
|
|
|
|
// v3 Playout
|
|
if s.v3handler.playout != nil {
|
|
v3.GET("/process/:id/playout/:inputid/status", s.v3handler.playout.Status)
|
|
v3.GET("/process/:id/playout/:inputid/reopen", s.v3handler.playout.ReopenInput)
|
|
v3.GET("/process/:id/playout/:inputid/keyframe/*", s.v3handler.playout.Keyframe)
|
|
v3.GET("/process/:id/playout/:inputid/errorframe/encode", s.v3handler.playout.EncodeErrorframe)
|
|
|
|
if !s.readOnly {
|
|
v3.PUT("/process/:id/playout/:inputid/errorframe/*", s.v3handler.playout.SetErrorframe)
|
|
v3.POST("/process/:id/playout/:inputid/errorframe/*", s.v3handler.playout.SetErrorframe)
|
|
|
|
v3.PUT("/process/:id/playout/:inputid/stream", s.v3handler.playout.SetStream)
|
|
}
|
|
}
|
|
}
|
|
|
|
// v3 Memory FS
|
|
if s.v3handler.memfs != nil {
|
|
v3.GET("/fs/mem", s.v3handler.memfs.ListFiles)
|
|
v3.GET("/fs/mem/*", s.v3handler.memfs.GetFile)
|
|
|
|
if !s.readOnly {
|
|
v3.DELETE("/fs/mem/*", s.v3handler.memfs.DeleteFile)
|
|
v3.PUT("/fs/mem/*", s.v3handler.memfs.PutFile)
|
|
v3.PATCH("/fs/mem/*", s.v3handler.memfs.PatchFile)
|
|
}
|
|
}
|
|
|
|
// v3 Disk FS
|
|
v3.GET("/fs/disk", s.v3handler.diskfs.ListFiles)
|
|
v3.GET("/fs/disk/*", s.v3handler.diskfs.GetFile, mwmime.NewWithConfig(mwmime.Config{
|
|
MimeTypesFile: s.mimeTypesFile,
|
|
DefaultContentType: "application/data",
|
|
}))
|
|
v3.HEAD("/fs/disk/*", s.v3handler.diskfs.GetFile, mwmime.NewWithConfig(mwmime.Config{
|
|
MimeTypesFile: s.mimeTypesFile,
|
|
DefaultContentType: "application/data",
|
|
}))
|
|
|
|
if !s.readOnly {
|
|
v3.PUT("/fs/disk/*", s.v3handler.diskfs.PutFile)
|
|
v3.DELETE("/fs/disk/*", s.v3handler.diskfs.DeleteFile)
|
|
}
|
|
|
|
// v3 RTMP
|
|
if s.v3handler.rtmp != nil {
|
|
v3.GET("/rtmp", s.v3handler.rtmp.ListChannels)
|
|
}
|
|
|
|
// v3 SRT
|
|
if s.v3handler.srt != nil {
|
|
v3.GET("/srt", s.v3handler.srt.ListChannels)
|
|
}
|
|
|
|
// v3 Config
|
|
if s.v3handler.config != nil {
|
|
v3.GET("/config", s.v3handler.config.Get)
|
|
|
|
if !s.readOnly {
|
|
v3.PUT("/config", s.v3handler.config.Set)
|
|
v3.GET("/config/reload", s.v3handler.config.Reload)
|
|
}
|
|
}
|
|
|
|
// v3 Session
|
|
if s.v3handler.session != nil {
|
|
v3.GET("/session", s.v3handler.session.Summary)
|
|
v3.GET("/session/active", s.v3handler.session.Active)
|
|
}
|
|
|
|
// v3 Log
|
|
v3.GET("/log", s.v3handler.log.Log)
|
|
|
|
// v3 Metrics
|
|
v3.GET("/metrics", s.v3handler.resources.Describe)
|
|
v3.POST("/metrics", s.v3handler.resources.Metrics)
|
|
}
|