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(`
`)) 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 }