135 lines
4.4 KiB
Go
135 lines
4.4 KiB
Go
package liveview
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"html/template"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-admin-team/go-admin-core/sdk/api"
|
|
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
|
|
coreService "github.com/go-admin-team/go-admin-core/sdk/service"
|
|
)
|
|
|
|
type API struct{ api.Api }
|
|
|
|
func (e *API) service(c *gin.Context) (*Service, error) {
|
|
base := coreService.Service{}
|
|
if err := e.MakeContext(c).MakeOrm().MakeService(&base).Errors; err != nil {
|
|
return nil, err
|
|
}
|
|
return NewService(base.Orm, nil), nil
|
|
}
|
|
|
|
func (e *API) List(c *gin.Context) {
|
|
service, err := e.service(c)
|
|
if err != nil {
|
|
e.Error(http.StatusInternalServerError, err, "实时监看服务初始化失败")
|
|
return
|
|
}
|
|
pageIndex, _ := strconv.Atoi(c.DefaultQuery("pageIndex", "1"))
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
|
items, total, err := service.List(c.Request.Context(), PageRequest{Keyword: c.Query("keyword"), PageIndex: pageIndex, PageSize: pageSize})
|
|
if err != nil {
|
|
e.writeError(err)
|
|
return
|
|
}
|
|
e.PageOK(items, int(total), pageIndex, pageSize, "查询成功")
|
|
}
|
|
|
|
func (e *API) Create(c *gin.Context) {
|
|
service, err := e.service(c)
|
|
if err != nil {
|
|
e.Error(http.StatusInternalServerError, err, "实时监看服务初始化失败")
|
|
return
|
|
}
|
|
var request struct {
|
|
RouteID string `json:"routeId"`
|
|
}
|
|
if err = decodeJSON(c, &request); err != nil {
|
|
e.Error(http.StatusBadRequest, err, "请求内容格式不正确")
|
|
return
|
|
}
|
|
session, err := service.Create(c.Request.Context(), user.GetUserId(c), request.RouteID, c.Request)
|
|
if err != nil {
|
|
e.writeError(err)
|
|
return
|
|
}
|
|
e.OK(session, "播放会话已创建")
|
|
}
|
|
|
|
func (e *API) Get(c *gin.Context) {
|
|
service, err := e.service(c)
|
|
if err != nil {
|
|
e.Error(http.StatusInternalServerError, err, "实时监看服务初始化失败")
|
|
return
|
|
}
|
|
session, err := service.Get(c.Request.Context(), user.GetUserId(c), c.Param("id"))
|
|
if err != nil {
|
|
e.writeError(err)
|
|
return
|
|
}
|
|
e.OK(session, "查询成功")
|
|
}
|
|
|
|
var playerTemplate = template.Must(template.New("liveview-player").Parse(`<!doctype html>
|
|
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<style>html,body,iframe{width:100%;height:100%;margin:0;border:0;background:#101419;overflow:hidden}</style></head>
|
|
<body><iframe src="{{.}}" title="Sense 实时视频" allow="autoplay; fullscreen" referrerpolicy="no-referrer"></iframe></body></html>`))
|
|
|
|
func (e *API) Player(c *gin.Context) {
|
|
target, err := NewService(nil, nil).PlayerTarget(c.Param("id"))
|
|
if err != nil {
|
|
c.Header("Cache-Control", "no-store")
|
|
c.String(http.StatusGone, "播放会话已过期,请重新连接")
|
|
return
|
|
}
|
|
parsed := template.URL(target)
|
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
|
c.Header("Cache-Control", "no-store")
|
|
c.Header("Referrer-Policy", "no-referrer")
|
|
c.Header("X-Content-Type-Options", "nosniff")
|
|
c.Header("X-Frame-Options", "SAMEORIGIN")
|
|
c.Header("Content-Security-Policy", "default-src 'none'; frame-ancestors 'self'; frame-src http: https:; style-src 'unsafe-inline'")
|
|
if err = playerTemplate.Execute(c.Writer, parsed); err != nil {
|
|
c.AbortWithStatus(http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func (e *API) writeError(err error) {
|
|
switch {
|
|
case errors.Is(err, ErrInvalidRequest):
|
|
e.Error(http.StatusBadRequest, err, err.Error())
|
|
case errors.Is(err, ErrRouteNotFound):
|
|
e.Error(http.StatusNotFound, err, err.Error())
|
|
case errors.Is(err, ErrSessionExpired):
|
|
e.Error(http.StatusGone, err, "播放会话已过期,请重新连接")
|
|
case strings.Contains(err.Error(), "WEBRTC_PUBLIC_BASE"), strings.Contains(err.Error(), "浏览器可访问"):
|
|
e.Error(http.StatusServiceUnavailable, err, "浏览器播放地址未正确配置")
|
|
default:
|
|
e.Error(http.StatusInternalServerError, err, "实时监看操作失败")
|
|
}
|
|
}
|
|
|
|
func decodeJSON(c *gin.Context, target any) error {
|
|
if !strings.HasPrefix(strings.ToLower(strings.TrimSpace(c.GetHeader("Content-Type"))), "application/json") {
|
|
return errors.New("content type must be application/json")
|
|
}
|
|
decoder := json.NewDecoder(http.MaxBytesReader(c.Writer, c.Request.Body, 16<<10))
|
|
decoder.DisallowUnknownFields()
|
|
if err := decoder.Decode(target); err != nil {
|
|
return err
|
|
}
|
|
if err := decoder.Decode(&struct{}{}); !errors.Is(err, io.EOF) {
|
|
if err == nil {
|
|
return errors.New("request body must contain one JSON object")
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|