mediamtx/internal/core/api.go

1005 lines
20 KiB
Go

package core
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
"sort"
"strconv"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/httpserv"
)
var errAPINotFound = errors.New("not found")
func interfaceIsEmpty(i interface{}) bool {
return reflect.ValueOf(i).Kind() != reflect.Ptr || reflect.ValueOf(i).IsNil()
}
func paginate2(itemsPtr interface{}, itemsPerPage int, page int) int {
ritems := reflect.ValueOf(itemsPtr).Elem()
itemsLen := ritems.Len()
if itemsLen == 0 {
return 0
}
pageCount := (itemsLen / itemsPerPage)
if (itemsLen % itemsPerPage) != 0 {
pageCount++
}
min := page * itemsPerPage
if min >= itemsLen {
min = itemsLen - 1
}
max := (page + 1) * itemsPerPage
if max >= itemsLen {
max = itemsLen
}
ritems.Set(ritems.Slice(min, max))
return pageCount
}
func paginate(itemsPtr interface{}, itemsPerPageStr string, pageStr string) (int, error) {
itemsPerPage := 100
if itemsPerPageStr != "" {
tmp, err := strconv.ParseUint(itemsPerPageStr, 10, 31)
if err != nil {
return 0, err
}
itemsPerPage = int(tmp)
}
page := 0
if pageStr != "" {
tmp, err := strconv.ParseUint(pageStr, 10, 31)
if err != nil {
return 0, err
}
page = int(tmp)
}
return paginate2(itemsPtr, itemsPerPage, page), nil
}
func sortedKeys(paths map[string]*conf.OptionalPath) []string {
ret := make([]string, len(paths))
i := 0
for name := range paths {
ret[i] = name
i++
}
sort.Strings(ret)
return ret
}
func paramName(ctx *gin.Context) (string, bool) {
name := ctx.Param("name")
if len(name) < 2 || name[0] != '/' {
return "", false
}
return name[1:], true
}
type apiPathManager interface {
apiPathsList() (*apiPathList, error)
apiPathsGet(string) (*apiPath, error)
}
type apiHLSManager interface {
apiMuxersList() (*apiHLSMuxerList, error)
apiMuxersGet(string) (*apiHLSMuxer, error)
}
type apiRTSPServer interface {
apiConnsList() (*apiRTSPConnsList, error)
apiConnsGet(uuid.UUID) (*apiRTSPConn, error)
apiSessionsList() (*apiRTSPSessionList, error)
apiSessionsGet(uuid.UUID) (*apiRTSPSession, error)
apiSessionsKick(uuid.UUID) error
}
type apiRTMPServer interface {
apiConnsList() (*apiRTMPConnList, error)
apiConnsGet(uuid.UUID) (*apiRTMPConn, error)
apiConnsKick(uuid.UUID) error
}
type apiWebRTCManager interface {
apiSessionsList() (*apiWebRTCSessionList, error)
apiSessionsGet(uuid.UUID) (*apiWebRTCSession, error)
apiSessionsKick(uuid.UUID) error
}
type apiSRTServer interface {
apiConnsList() (*apiSRTConnList, error)
apiConnsGet(uuid.UUID) (*apiSRTConn, error)
apiConnsKick(uuid.UUID) error
}
type apiParent interface {
logger.Writer
apiConfigSet(conf *conf.Conf)
}
type api struct {
conf *conf.Conf
pathManager apiPathManager
rtspServer apiRTSPServer
rtspsServer apiRTSPServer
rtmpServer apiRTMPServer
rtmpsServer apiRTMPServer
hlsManager apiHLSManager
webRTCManager apiWebRTCManager
srtServer apiSRTServer
parent apiParent
httpServer *httpserv.WrappedServer
mutex sync.Mutex
}
func newAPI(
address string,
readTimeout conf.StringDuration,
conf *conf.Conf,
pathManager apiPathManager,
rtspServer apiRTSPServer,
rtspsServer apiRTSPServer,
rtmpServer apiRTMPServer,
rtmpsServer apiRTMPServer,
hlsManager apiHLSManager,
webRTCManager apiWebRTCManager,
srtServer apiSRTServer,
parent apiParent,
) (*api, error) {
a := &api{
conf: conf,
pathManager: pathManager,
rtspServer: rtspServer,
rtspsServer: rtspsServer,
rtmpServer: rtmpServer,
rtmpsServer: rtmpsServer,
hlsManager: hlsManager,
webRTCManager: webRTCManager,
srtServer: srtServer,
parent: parent,
}
router := gin.New()
router.SetTrustedProxies(nil) //nolint:errcheck
group := router.Group("/")
group.GET("/v3/config/global/get", a.onConfigGlobalGet)
group.PATCH("/v3/config/global/patch", a.onConfigGlobalPatch)
group.GET("/v3/config/pathdefaults/get", a.onConfigPathDefaultsGet)
group.PATCH("/v3/config/pathdefaults/patch", a.onConfigPathDefaultsPatch)
group.GET("/v3/config/paths/list", a.onConfigPathsList)
group.GET("/v3/config/paths/get/*name", a.onConfigPathsGet)
group.POST("/v3/config/paths/add/*name", a.onConfigPathsAdd)
group.PATCH("/v3/config/paths/patch/*name", a.onConfigPathsPatch)
group.POST("/v3/config/paths/replace/*name", a.onConfigPathsReplace)
group.DELETE("/v3/config/paths/delete/*name", a.onConfigPathsDelete)
group.GET("/v3/paths/list", a.onPathsList)
group.GET("/v3/paths/get/*name", a.onPathsGet)
if !interfaceIsEmpty(a.hlsManager) {
group.GET("/v3/hlsmuxers/list", a.onHLSMuxersList)
group.GET("/v3/hlsmuxers/get/*name", a.onHLSMuxersGet)
}
if !interfaceIsEmpty(a.rtspServer) {
group.GET("/v3/rtspconns/list", a.onRTSPConnsList)
group.GET("/v3/rtspconns/get/:id", a.onRTSPConnsGet)
group.GET("/v3/rtspsessions/list", a.onRTSPSessionsList)
group.GET("/v3/rtspsessions/get/:id", a.onRTSPSessionsGet)
group.POST("/v3/rtspsessions/kick/:id", a.onRTSPSessionsKick)
}
if !interfaceIsEmpty(a.rtspsServer) {
group.GET("/v3/rtspsconns/list", a.onRTSPSConnsList)
group.GET("/v3/rtspsconns/get/:id", a.onRTSPSConnsGet)
group.GET("/v3/rtspssessions/list", a.onRTSPSSessionsList)
group.GET("/v3/rtspssessions/get/:id", a.onRTSPSSessionsGet)
group.POST("/v3/rtspssessions/kick/:id", a.onRTSPSSessionsKick)
}
if !interfaceIsEmpty(a.rtmpServer) {
group.GET("/v3/rtmpconns/list", a.onRTMPConnsList)
group.GET("/v3/rtmpconns/get/:id", a.onRTMPConnsGet)
group.POST("/v3/rtmpconns/kick/:id", a.onRTMPConnsKick)
}
if !interfaceIsEmpty(a.rtmpsServer) {
group.GET("/v3/rtmpsconns/list", a.onRTMPSConnsList)
group.GET("/v3/rtmpsconns/get/:id", a.onRTMPSConnsGet)
group.POST("/v3/rtmpsconns/kick/:id", a.onRTMPSConnsKick)
}
if !interfaceIsEmpty(a.webRTCManager) {
group.GET("/v3/webrtcsessions/list", a.onWebRTCSessionsList)
group.GET("/v3/webrtcsessions/get/:id", a.onWebRTCSessionsGet)
group.POST("/v3/webrtcsessions/kick/:id", a.onWebRTCSessionsKick)
}
if !interfaceIsEmpty(a.srtServer) {
group.GET("/v3/srtconns/list", a.onSRTConnsList)
group.GET("/v3/srtconns/get/:id", a.onSRTConnsGet)
group.POST("/v3/srtconns/kick/:id", a.onSRTConnsKick)
}
network, address := restrictNetwork("tcp", address)
var err error
a.httpServer, err = httpserv.NewWrappedServer(
network,
address,
time.Duration(readTimeout),
"",
"",
router,
a,
)
if err != nil {
return nil, err
}
a.Log(logger.Info, "listener opened on "+address)
return a, nil
}
func (a *api) close() {
a.Log(logger.Info, "listener is closing")
a.httpServer.Close()
}
func (a *api) Log(level logger.Level, format string, args ...interface{}) {
a.parent.Log(level, "[API] "+format, args...)
}
// error coming from something the user inserted into the request.
func (a *api) writeUserError(ctx *gin.Context, err error) {
a.Log(logger.Error, err.Error())
ctx.AbortWithStatus(http.StatusBadRequest)
}
// error coming from the server.
func (a *api) writeServerError(ctx *gin.Context, err error) {
a.Log(logger.Error, err.Error())
ctx.AbortWithStatus(http.StatusInternalServerError)
}
func (a *api) writeNotFound(ctx *gin.Context) {
ctx.AbortWithStatus(http.StatusNotFound)
}
func (a *api) writeServerErrorOrNotFound(ctx *gin.Context, err error) {
if err == errAPINotFound {
a.writeNotFound(ctx)
} else {
a.writeServerError(ctx, err)
}
}
func (a *api) onConfigGlobalGet(ctx *gin.Context) {
a.mutex.Lock()
c := a.conf
a.mutex.Unlock()
ctx.JSON(http.StatusOK, c.Global())
}
func (a *api) onConfigGlobalPatch(ctx *gin.Context) {
var c conf.OptionalGlobal
err := json.NewDecoder(ctx.Request.Body).Decode(&c)
if err != nil {
a.writeUserError(ctx, err)
return
}
a.mutex.Lock()
defer a.mutex.Unlock()
newConf := a.conf.Clone()
newConf.PatchGlobal(&c)
err = newConf.Check()
if err != nil {
a.writeUserError(ctx, err)
return
}
a.conf = newConf
// since reloading the configuration can cause the shutdown of the API,
// call it in a goroutine
go a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK)
}
func (a *api) onConfigPathDefaultsGet(ctx *gin.Context) {
a.mutex.Lock()
c := a.conf
a.mutex.Unlock()
ctx.JSON(http.StatusOK, c.PathDefaults)
}
func (a *api) onConfigPathDefaultsPatch(ctx *gin.Context) {
var p conf.OptionalPath
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
if err != nil {
a.writeUserError(ctx, err)
return
}
a.mutex.Lock()
defer a.mutex.Unlock()
newConf := a.conf.Clone()
newConf.PatchPathDefaults(&p)
err = newConf.Check()
if err != nil {
a.writeUserError(ctx, err)
return
}
a.conf = newConf
a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK)
}
func (a *api) onConfigPathsList(ctx *gin.Context) {
a.mutex.Lock()
c := a.conf
a.mutex.Unlock()
data := &apiPathConfList{
Items: make([]*conf.OptionalPath, len(c.OptionalPaths)),
}
for i, key := range sortedKeys(c.OptionalPaths) {
data.Items[i] = c.OptionalPaths[key]
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onConfigPathsGet(ctx *gin.Context) {
name, ok := paramName(ctx)
if !ok {
a.writeUserError(ctx, fmt.Errorf("invalid name"))
return
}
a.mutex.Lock()
c := a.conf
a.mutex.Unlock()
p, ok := c.OptionalPaths[name]
if !ok {
a.writeNotFound(ctx)
return
}
ctx.JSON(http.StatusOK, p)
}
func (a *api) onConfigPathsAdd(ctx *gin.Context) { //nolint:dupl
name, ok := paramName(ctx)
if !ok {
a.writeUserError(ctx, fmt.Errorf("invalid name"))
return
}
var p conf.OptionalPath
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
if err != nil {
a.writeUserError(ctx, err)
return
}
a.mutex.Lock()
defer a.mutex.Unlock()
newConf := a.conf.Clone()
err = newConf.AddPath(name, &p)
if err != nil {
a.writeUserError(ctx, err)
return
}
err = newConf.Check()
if err != nil {
a.writeUserError(ctx, err)
return
}
a.conf = newConf
a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK)
}
func (a *api) onConfigPathsPatch(ctx *gin.Context) { //nolint:dupl
name, ok := paramName(ctx)
if !ok {
a.writeUserError(ctx, fmt.Errorf("invalid name"))
return
}
var p conf.OptionalPath
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
if err != nil {
a.writeUserError(ctx, err)
return
}
a.mutex.Lock()
defer a.mutex.Unlock()
newConf := a.conf.Clone()
err = newConf.PatchPath(name, &p)
if err != nil {
a.writeUserError(ctx, err)
return
}
err = newConf.Check()
if err != nil {
a.writeUserError(ctx, err)
return
}
a.conf = newConf
a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK)
}
func (a *api) onConfigPathsReplace(ctx *gin.Context) { //nolint:dupl
name, ok := paramName(ctx)
if !ok {
a.writeUserError(ctx, fmt.Errorf("invalid name"))
return
}
var p conf.OptionalPath
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
if err != nil {
a.writeUserError(ctx, err)
return
}
a.mutex.Lock()
defer a.mutex.Unlock()
newConf := a.conf.Clone()
err = newConf.ReplacePath(name, &p)
if err != nil {
a.writeUserError(ctx, err)
return
}
err = newConf.Check()
if err != nil {
a.writeUserError(ctx, err)
return
}
a.conf = newConf
a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK)
}
func (a *api) onConfigPathsDelete(ctx *gin.Context) {
name, ok := paramName(ctx)
if !ok {
a.writeUserError(ctx, fmt.Errorf("invalid name"))
return
}
a.mutex.Lock()
defer a.mutex.Unlock()
newConf := a.conf.Clone()
err := newConf.RemovePath(name)
if err != nil {
a.writeUserError(ctx, err)
return
}
err = newConf.Check()
if err != nil {
a.writeUserError(ctx, err)
return
}
a.conf = newConf
a.parent.apiConfigSet(newConf)
ctx.Status(http.StatusOK)
}
func (a *api) onPathsList(ctx *gin.Context) {
data, err := a.pathManager.apiPathsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onPathsGet(ctx *gin.Context) {
name, ok := paramName(ctx)
if !ok {
a.writeUserError(ctx, fmt.Errorf("invalid name"))
return
}
data, err := a.pathManager.apiPathsGet(name)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPConnsList(ctx *gin.Context) {
data, err := a.rtspServer.apiConnsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPConnsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.rtspServer.apiConnsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPSessionsList(ctx *gin.Context) {
data, err := a.rtspServer.apiSessionsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPSessionsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.rtspServer.apiSessionsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPSessionsKick(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
err = a.rtspServer.apiSessionsKick(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.Status(http.StatusOK)
}
func (a *api) onRTSPSConnsList(ctx *gin.Context) {
data, err := a.rtspsServer.apiConnsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPSConnsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.rtspsServer.apiConnsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPSSessionsList(ctx *gin.Context) {
data, err := a.rtspsServer.apiSessionsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPSSessionsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.rtspsServer.apiSessionsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTSPSSessionsKick(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
err = a.rtspsServer.apiSessionsKick(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.Status(http.StatusOK)
}
func (a *api) onRTMPConnsList(ctx *gin.Context) {
data, err := a.rtmpServer.apiConnsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTMPConnsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.rtmpServer.apiConnsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTMPConnsKick(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
err = a.rtmpServer.apiConnsKick(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.Status(http.StatusOK)
}
func (a *api) onRTMPSConnsList(ctx *gin.Context) {
data, err := a.rtmpsServer.apiConnsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTMPSConnsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.rtmpsServer.apiConnsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onRTMPSConnsKick(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
err = a.rtmpsServer.apiConnsKick(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.Status(http.StatusOK)
}
func (a *api) onHLSMuxersList(ctx *gin.Context) {
data, err := a.hlsManager.apiMuxersList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onHLSMuxersGet(ctx *gin.Context) {
name, ok := paramName(ctx)
if !ok {
a.writeUserError(ctx, fmt.Errorf("invalid name"))
return
}
data, err := a.hlsManager.apiMuxersGet(name)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onWebRTCSessionsList(ctx *gin.Context) {
data, err := a.webRTCManager.apiSessionsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onWebRTCSessionsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.webRTCManager.apiSessionsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onWebRTCSessionsKick(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
err = a.webRTCManager.apiSessionsKick(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.Status(http.StatusOK)
}
func (a *api) onSRTConnsList(ctx *gin.Context) {
data, err := a.srtServer.apiConnsList()
if err != nil {
a.writeServerError(ctx, err)
return
}
data.ItemCount = len(data.Items)
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data.PageCount = pageCount
ctx.JSON(http.StatusOK, data)
}
func (a *api) onSRTConnsGet(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
data, err := a.srtServer.apiConnsGet(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.JSON(http.StatusOK, data)
}
func (a *api) onSRTConnsKick(ctx *gin.Context) {
uuid, err := uuid.Parse(ctx.Param("id"))
if err != nil {
a.writeUserError(ctx, err)
return
}
err = a.srtServer.apiConnsKick(uuid)
if err != nil {
a.writeServerErrorOrNotFound(ctx, err)
return
}
ctx.Status(http.StatusOK)
}
// confReload is called by core.
func (a *api) confReload(conf *conf.Conf) {
a.mutex.Lock()
defer a.mutex.Unlock()
a.conf = conf
}