Feat: use jwt auth middleware

pull/21/head
zijiren233 2 years ago
parent 05c9e14674
commit de073b3217

@ -9,6 +9,7 @@ import (
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/public"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/middlewares"
"github.com/synctv-org/synctv/utils"
rtmps "github.com/zijiren233/livelib/server"
)
@ -54,6 +55,9 @@ func Init(e *gin.Engine, s *rtmps.Server, r *room.Rooms) {
{
api := e.Group("/api")
needAuthApi := api.Group("")
needAuthApi.Use(middlewares.AuthRoom)
{
public := api.Group("/public")
@ -62,6 +66,7 @@ func Init(e *gin.Engine, s *rtmps.Server, r *room.Rooms) {
{
room := api.Group("/room")
needAuthRoom := needAuthApi.Group("/room")
room.GET("/ws", NewWebSocketHandler(utils.NewWebSocketServer()))
@ -75,42 +80,43 @@ func Init(e *gin.Engine, s *rtmps.Server, r *room.Rooms) {
room.POST("/login", LoginRoom)
room.POST("/delete", DeleteRoom)
needAuthRoom.POST("/delete", DeleteRoom)
room.POST("/pwd", SetPassword)
needAuthRoom.POST("/pwd", SetPassword)
room.PUT("/admin", AddAdmin)
needAuthRoom.PUT("/admin", AddAdmin)
room.DELETE("/admin", DelAdmin)
needAuthRoom.DELETE("/admin", DelAdmin)
}
{
movie := api.Group("/movie")
needAuthMovie := needAuthApi.Group("/movie")
movie.GET("/list", MovieList)
needAuthMovie.GET("/list", MovieList)
movie.GET("/movies", Movies)
needAuthMovie.GET("/current", CurrentMovie)
movie.GET("/current", CurrentMovie)
needAuthMovie.GET("/movies", Movies)
movie.POST("/current", ChangeCurrentMovie)
needAuthMovie.POST("/current", ChangeCurrentMovie)
movie.POST("/push", PushMovie)
needAuthMovie.POST("/push", PushMovie)
movie.POST("/edit", EditMovie)
needAuthMovie.POST("/edit", EditMovie)
movie.POST("/swap", SwapMovie)
needAuthMovie.POST("/swap", SwapMovie)
movie.POST("/delete", DelMovie)
needAuthMovie.POST("/delete", DelMovie)
movie.POST("/clear", ClearMovies)
needAuthMovie.POST("/clear", ClearMovies)
movie.HEAD("/proxy/:roomId/:pullKey", ProxyMovie)
movie.GET("/proxy/:roomId/:pullKey", ProxyMovie)
{
live := movie.Group("/live")
live := needAuthMovie.Group("/live")
live.POST("/publishKey", NewPublishKey)
@ -119,11 +125,12 @@ func Init(e *gin.Engine, s *rtmps.Server, r *room.Rooms) {
}
{
user := api.Group("/user")
// user := api.Group("/user")
needAuthUser := needAuthApi.Group("/user")
user.GET("/me", Me)
needAuthUser.GET("/me", Me)
user.POST("/pwd", SetUserPassword)
needAuthUser.POST("/pwd", SetUserPassword)
}
}

@ -20,6 +20,7 @@ import (
pb "github.com/synctv-org/synctv/proto"
"github.com/synctv-org/synctv/proxy"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/livelib/av"
"github.com/zijiren233/livelib/container/flv"
@ -45,22 +46,17 @@ func GetPageItems[T any](ctx *gin.Context, items []T) ([]T, error) {
}
func MovieList(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
ml := user.Movies()
movies, err := GetPageItems(ctx, ml)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"current": user.Room().Current(),
"total": len(ml),
"movies": movies,
@ -68,35 +64,25 @@ func MovieList(ctx *gin.Context) {
}
func CurrentMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"current": user.Room().Current(),
}))
}
func Movies(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
ml := user.Movies()
movies, err := GetPageItems(ctx, ml)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"total": len(ml),
"movies": movies,
}))
@ -105,52 +91,47 @@ func Movies(ctx *gin.Context) {
type PushMovieReq = room.BaseMovieInfo
func PushMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
req := new(PushMovieReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
movie, err := user.NewMovieWithBaseMovie(*req)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
switch {
case movie.RtmpSource && movie.Proxy:
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("rtmp source and proxy can not be true at the same time"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("rtmp source and proxy can not be true at the same time"))
return
case movie.Live && movie.RtmpSource:
if !conf.Conf.Rtmp.Enable {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("rtmp source is not enabled"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("rtmp source is not enabled"))
return
} else if movie.Type == "m3u8" && !conf.Conf.Rtmp.HlsPlayer {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("hls player is not enabled"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("hls player is not enabled"))
return
}
movie.PullKey = uuid.New().String()
c, err := user.Room().NewLiveChannel(movie.PullKey)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
movie.SetChannel(c)
case movie.Live && movie.Proxy:
if !conf.Conf.Proxy.LiveProxy {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("live proxy is not enabled"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("live proxy is not enabled"))
return
}
u, err := url.Parse(movie.Url)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
switch u.Scheme {
@ -158,7 +139,7 @@ func PushMovie(ctx *gin.Context) {
PullKey := uuid.New().String()
c, err := user.Room().NewLiveChannel(PullKey)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
movie.PullKey = PullKey
@ -184,7 +165,7 @@ func PushMovie(ctx *gin.Context) {
PullKey := uuid.New().String()
c, err := user.Room().NewLiveChannel(PullKey)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
movie.PullKey = PullKey
@ -209,15 +190,15 @@ func PushMovie(ctx *gin.Context) {
}
}()
default:
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("only support rtmp temporarily"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("only support rtmp temporarily"))
return
}
case !movie.Live && movie.RtmpSource:
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("rtmp source must be live"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("rtmp source must be live"))
return
case !movie.Live && movie.Proxy:
if !conf.Conf.Proxy.MovieProxy {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("movie proxy is not enabled"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("movie proxy is not enabled"))
return
}
movie.PullKey = uuid.New().String()
@ -225,15 +206,15 @@ func PushMovie(ctx *gin.Context) {
case !movie.Live && !movie.Proxy, movie.Live && !movie.Proxy && !movie.RtmpSource:
u, err := url.Parse(movie.Url)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if u.Scheme != "http" && u.Scheme != "https" {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("only support http or https"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("only support http or https"))
return
}
default:
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("unknown error"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("unknown error"))
return
}
@ -247,7 +228,7 @@ func PushMovie(ctx *gin.Context) {
err = FormatErrNotSupportPosition(s)
}
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
@ -257,32 +238,27 @@ func PushMovie(ctx *gin.Context) {
Sender: user.Name(),
},
}, room.WithSendToSelf()); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusCreated, NewApiDataResp(gin.H{
ctx.JSON(http.StatusCreated, model.NewApiDataResp(gin.H{
"id": movie.Id(),
}))
}
func NewPublishKey(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
user := ctx.Value("user").(*room.User)
req := new(IdReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
movie, err := user.Room().GetMovie(req.Id)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
@ -292,18 +268,18 @@ func NewPublishKey(ctx *gin.Context) {
}
if !movie.RtmpSource {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("only live movie can get publish key"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("only live movie can get publish key"))
return
}
if movie.PullKey == "" {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorStringResp("pull key is empty"))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("pull key is empty"))
return
}
token, err := NewRtmpAuthorization(movie.PullKey)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -312,7 +288,7 @@ func NewPublishKey(ctx *gin.Context) {
host = ctx.Request.Host
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"host": host,
"app": user.Room().ID(),
"token": token,
@ -325,21 +301,16 @@ type EditMovieReq struct {
}
func EditMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
req := new(EditMovieReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.EditMovie(req.Id, req.BaseMovieInfo); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
@ -349,7 +320,7 @@ func EditMovie(ctx *gin.Context) {
Sender: user.Name(),
},
}, room.WithSendToSelf()); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -361,21 +332,16 @@ type IdsReq struct {
}
func DelMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
req := new(IdsReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.Room().DelMovie(req.Ids...); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
@ -385,7 +351,7 @@ func DelMovie(ctx *gin.Context) {
Sender: user.Name(),
},
}, room.WithSendToSelf()); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -393,15 +359,10 @@ func DelMovie(ctx *gin.Context) {
}
func ClearMovies(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
if err := user.Room().ClearMovies(); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
@ -411,7 +372,7 @@ func ClearMovies(ctx *gin.Context) {
Sender: user.Name(),
},
}, room.WithSendToSelf()); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -424,21 +385,16 @@ type SwapMovieReq struct {
}
func SwapMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
req := new(SwapMovieReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.Room().SwapMovie(req.Id1, req.Id2); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
@ -448,7 +404,7 @@ func SwapMovie(ctx *gin.Context) {
Sender: user.Name(),
},
}, room.WithSendToSelf()); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -460,21 +416,16 @@ type IdReq struct {
}
func ChangeCurrentMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
req := new(IdReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.Room().ChangeCurrentMovie(req.Id); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.Broadcast(&room.ElementMessage{
@ -484,7 +435,7 @@ func ChangeCurrentMovie(ctx *gin.Context) {
Current: user.Room().Current().Proto(),
},
}, room.WithSendToSelf()); err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -532,23 +483,23 @@ func ProxyMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
roomId := ctx.Param("roomId")
if roomId == "" {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("roomId is empty"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("roomId is empty"))
return
}
room, err := rooms.GetRoom(roomId)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
m, err := room.GetMovieWithPullKey(ctx.Param("pullKey"))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if !m.Proxy || m.Live || m.RtmpSource {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("not support proxy"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support proxy"))
return
}
@ -559,13 +510,13 @@ func ProxyMovie(ctx *gin.Context) {
}
resp, err := r.Head(m.Url)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
defer resp.RawBody().Close()
if _, ok := allowedProxyMovieContentType[resp.Header().Get("Content-Type")]; !ok {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(fmt.Errorf("this movie type support proxy: %s", resp.Header().Get("Content-Type"))))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(fmt.Errorf("this movie type support proxy: %s", resp.Header().Get("Content-Type"))))
return
}
ctx.Status(resp.StatusCode())
@ -576,7 +527,7 @@ func ProxyMovie(ctx *gin.Context) {
length, err := strconv.ParseInt(l, 10, 64)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -602,16 +553,11 @@ func (e FormatErrNotSupportFileType) Error() string {
}
func JoinLive(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
if !conf.Conf.Proxy.LiveProxy && !conf.Conf.Rtmp.Enable {
ctx.AbortWithStatusJSON(http.StatusForbidden, NewApiErrorStringResp("live proxy and rtmp source is not enabled"))
return
}
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("live proxy and rtmp source is not enabled"))
return
}
user := ctx.Value("user").(*room.User)
pullKey := strings.Trim(ctx.Param("pullKey"), "/")
pullKeySplitd := strings.Split(pullKey, "/")
@ -620,7 +566,7 @@ func JoinLive(ctx *gin.Context) {
channelName := strings.TrimSuffix(fileName, fileExt)
m, err := user.Room().GetMovieWithPullKey(channelName)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
channel := m.Channel()
@ -635,20 +581,20 @@ func JoinLive(ctx *gin.Context) {
ctx.Header("Cache-Control", "no-store")
b, err := channel.GenM3U8PlayList(fmt.Sprintf("/api/movie/live/%s", channelName))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
ctx.Data(http.StatusOK, hls.M3U8ContentType, b.Bytes())
case ".ts":
b, err := channel.GetTsFile(pullKeySplitd[1])
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
ctx.Header("Cache-Control", "public, max-age=90")
ctx.Data(http.StatusOK, hls.TSContentType, b)
default:
ctx.Header("Cache-Control", "no-store")
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(FormatErrNotSupportFileType(fileExt)))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(FormatErrNotSupportFileType(fileExt)))
}
}

@ -3,10 +3,11 @@ package handlers
import (
"github.com/gin-gonic/gin"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/server/model"
)
func Settings(ctx *gin.Context) {
ctx.JSON(200, NewApiDataResp(gin.H{
ctx.JSON(200, model.NewApiDataResp(gin.H{
"rtmp": gin.H{
"enable": conf.Conf.Rtmp.Enable,
"rtmpPlayer": conf.Conf.Rtmp.RtmpPlayer,

@ -5,21 +5,19 @@ import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
json "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/maruel/natural"
"github.com/synctv-org/synctv/internal/conf"
pb "github.com/synctv-org/synctv/proto"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/middlewares"
"github.com/synctv-org/synctv/server/model"
"github.com/zijiren233/gencontainer/vec"
rtmps "github.com/zijiren233/livelib/server"
"github.com/zijiren233/stream"
)
var (
@ -34,94 +32,6 @@ func (e FormatErrNotSupportPosition) Error() string {
return fmt.Sprintf("not support position %s", string(e))
}
type AuthClaims struct {
RoomID string `json:"id"`
Version uint64 `json:"v"`
Username string `json:"un"`
UserVersion uint64 `json:"uv"`
jwt.RegisteredClaims
}
func AuthRoom(Authorization string, rooms *room.Rooms) (*room.User, error) {
t, err := jwt.ParseWithClaims(strings.TrimPrefix(Authorization, `Bearer `), &AuthClaims{}, func(token *jwt.Token) (any, error) {
return stream.StringToBytes(conf.Conf.Jwt.Secret), nil
})
if err != nil {
return nil, ErrAuthFailed
}
claims := t.Claims.(*AuthClaims)
r, err := rooms.GetRoom(claims.RoomID)
if err != nil {
return nil, err
}
if !r.CheckVersion(claims.Version) {
return nil, ErrAuthExpired
}
user, err := r.GetUser(claims.Username)
if err != nil {
return nil, err
}
if !user.CheckVersion(claims.UserVersion) {
return nil, ErrAuthExpired
}
return user, nil
}
func authWithPassword(rooms *room.Rooms, roomid, password, username, userPassword string) (*room.User, error) {
r, err := rooms.GetRoom(roomid)
if err != nil {
return nil, err
}
if !r.CheckPassword(password) {
return nil, ErrAuthFailed
}
user, err := r.GetUser(username)
if err != nil {
return nil, err
}
if !user.CheckPassword(userPassword) {
return nil, ErrAuthFailed
}
return user, nil
}
func authOrNewWithPassword(rooms *room.Rooms, roomid, password, username, userPassword string, conf ...room.UserConf) (*room.User, error) {
r, err := rooms.GetRoom(roomid)
if err != nil {
return nil, err
}
if !r.CheckPassword(password) {
return nil, ErrAuthFailed
}
user, err := r.GetOrNewUser(username, userPassword, conf...)
if err != nil {
return nil, err
}
if !user.CheckPassword(userPassword) {
return nil, ErrAuthFailed
}
return user, nil
}
func newAuthorization(user *room.User) (string, error) {
claims := &AuthClaims{
RoomID: user.Room().ID(),
Version: user.Room().Version(),
Username: user.Name(),
UserVersion: user.Version(),
RegisteredClaims: jwt.RegisteredClaims{
NotBefore: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(conf.Conf.Jwt.Expire))),
},
}
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret))
}
type CreateRoomReq struct {
RoomID string `json:"roomId"`
Password string `json:"password"`
@ -135,13 +45,13 @@ func NewCreateRoomHandler(s *rtmps.Server) gin.HandlerFunc {
rooms := ctx.Value("rooms").(*room.Rooms)
req := new(CreateRoomReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
user, err := room.NewUser(req.Username, req.UserPassword, nil, room.WithUserAdmin(true))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
@ -150,13 +60,13 @@ func NewCreateRoomHandler(s *rtmps.Server) gin.HandlerFunc {
room.WithRootUser(user),
)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
token, err := newAuthorization(user)
token, err := middlewares.NewAuthToken(user)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -193,7 +103,7 @@ func NewCreateRoomHandler(s *rtmps.Server) gin.HandlerFunc {
}
}()
ctx.JSON(http.StatusCreated, NewApiDataResp(gin.H{
ctx.JSON(http.StatusCreated, model.NewApiDataResp(gin.H{
"token": token,
}))
}
@ -253,7 +163,7 @@ func RoomList(ctx *gin.Context) {
return t1.NeedPassword == t2.NeedPassword
})
default:
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("sort must be peoplenum or roomid"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("sort must be peoplenum or roomid"))
return
}
@ -263,17 +173,17 @@ func RoomList(ctx *gin.Context) {
case "desc":
resp.Reverse()
default:
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("order must be asc or desc"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("order must be asc or desc"))
return
}
list, err := GetPageItems(ctx, resp.Slice())
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"total": resp.Len(),
"list": list,
}))
@ -283,11 +193,11 @@ func CheckRoom(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
r, err := rooms.GetRoom(ctx.Query("roomId"))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"peopleNum": r.ClientNum(),
"needPassword": r.NeedPassword(),
}))
@ -297,17 +207,17 @@ func CheckUser(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
r, err := rooms.GetRoom(ctx.Query("roomId"))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
u, err := r.GetUser(ctx.Query("username"))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"idRoot": u.IsRoot(),
"idAdmin": u.IsAdmin(),
"lastAct": u.LastAct(),
@ -325,13 +235,13 @@ func LoginRoom(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
req := new(LoginRoomReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
autoNew, err := strconv.ParseBool(ctx.DefaultQuery("autoNew", "false"))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("autoNew must be bool"))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("autoNew must be bool"))
return
}
@ -339,46 +249,42 @@ func LoginRoom(ctx *gin.Context) {
user *room.User
)
if autoNew {
user, err = authOrNewWithPassword(rooms, req.RoomID, req.Password, req.Username, req.UserPassword)
user, err = middlewares.AuthOrNewWithPassword(req.RoomID, req.Password, req.Username, req.UserPassword, rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err))
return
}
} else {
user, err = authWithPassword(rooms, req.RoomID, req.Password, req.Username, req.UserPassword)
user, err = middlewares.AuthWithPassword(req.RoomID, req.Password, req.Username, req.UserPassword, rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err))
return
}
}
token, err := newAuthorization(user)
token, err := middlewares.NewAuthToken(user)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"token": token,
}))
}
func DeleteRoom(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
if !user.IsRoot() {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorStringResp("only root can close room"))
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorStringResp("only root can close room"))
return
}
err = rooms.DelRoom(user.Room().ID())
err := rooms.DelRoom(user.Room().ID())
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
@ -390,33 +296,28 @@ type SetPasswordReq struct {
}
func SetPassword(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
if !user.IsRoot() || !user.IsAdmin() {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorStringResp("only root or admin can set password"))
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorStringResp("only root or admin can set password"))
return
}
req := new(SetPasswordReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
user.Room().SetPassword(req.Password)
token, err := newAuthorization(user)
token, err := middlewares.NewAuthToken(user)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"token": token,
}))
}
@ -426,27 +327,22 @@ type UsernameReq struct {
}
func AddAdmin(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
if !user.IsRoot() && !user.IsAdmin() {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorStringResp("only root or admin can add admin"))
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorStringResp("only root or admin can add admin"))
return
}
req := new(UsernameReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
u, err := user.Room().GetUser(req.Username)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
@ -456,27 +352,22 @@ func AddAdmin(ctx *gin.Context) {
}
func DelAdmin(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
if !user.IsRoot() {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorStringResp("only root can del admin"))
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorStringResp("only root can del admin"))
return
}
req := new(UsernameReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
u, err := user.Room().GetUser(req.Username)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return
}
@ -484,25 +375,3 @@ func DelAdmin(ctx *gin.Context) {
ctx.Status(http.StatusNoContent)
}
func CloseRoom(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
if !user.IsRoot() {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorStringResp("only root can close room"))
return
}
err = rooms.DelRoom(user.Room().ID())
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
return
}
ctx.Status(http.StatusNoContent)
}

@ -6,17 +6,14 @@ import (
"github.com/gin-gonic/gin"
json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/middlewares"
"github.com/synctv-org/synctv/server/model"
)
func Me(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"isRoot": user.IsRoot(),
"isAdmin": user.IsAdmin(),
"username": user.Name(),
@ -25,33 +22,28 @@ func Me(ctx *gin.Context) {
}
func SetUserPassword(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
user := ctx.Value("user").(*room.User)
req := new(SetPasswordReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.SetPassword(req.Password); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
user.CloseHub()
token, err := newAuthorization(user)
token, err := middlewares.NewAuthToken(user)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.JSON(http.StatusOK, NewApiDataResp(gin.H{
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"token": token,
}))
}

@ -11,6 +11,8 @@ import (
log "github.com/sirupsen/logrus"
pb "github.com/synctv-org/synctv/proto"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/middlewares"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
"google.golang.org/protobuf/proto"
)
@ -21,9 +23,9 @@ func NewWebSocketHandler(wss *utils.WebSocket) gin.HandlerFunc {
return func(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
token := ctx.GetHeader("Sec-WebSocket-Protocol")
user, err := AuthRoom(token, rooms)
user, err := middlewares.Auth(token, rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err))
return
}
wss.Server(ctx.Writer, ctx.Request, []string{token}, NewWSMessageHandler(user))

@ -0,0 +1,129 @@
package middlewares
import (
"errors"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/model"
"github.com/zijiren233/stream"
)
var (
ErrAuthFailed = errors.New("auth failed")
ErrAuthExpired = errors.New("auth expired")
)
type AuthClaims struct {
RoomID string `json:"id"`
Version uint64 `json:"v"`
Username string `json:"un"`
UserVersion uint64 `json:"uv"`
jwt.RegisteredClaims
}
func auth(Authorization string) (*AuthClaims, error) {
t, err := jwt.ParseWithClaims(strings.TrimPrefix(Authorization, `Bearer `), &AuthClaims{}, func(token *jwt.Token) (any, error) {
return stream.StringToBytes(conf.Conf.Jwt.Secret), nil
})
if err != nil {
return nil, ErrAuthFailed
}
claims, ok := t.Claims.(*AuthClaims)
if !ok || !t.Valid {
return nil, ErrAuthFailed
}
return claims, nil
}
func Auth(Authorization string, rooms *room.Rooms) (*room.User, error) {
claims, err := auth(Authorization)
if err != nil {
return nil, err
}
r, err := rooms.GetRoom(claims.RoomID)
if err != nil {
return nil, err
}
if !r.CheckVersion(claims.Version) {
return nil, ErrAuthExpired
}
user, err := r.GetUser(claims.Username)
if err != nil {
return nil, err
}
if !user.CheckVersion(claims.UserVersion) {
return nil, ErrAuthExpired
}
return user, nil
}
func AuthWithPassword(roomID, roomPassword, username, password string, rooms *room.Rooms) (*room.User, error) {
room, err := rooms.GetRoom(roomID)
if err != nil {
return nil, err
}
if !room.CheckPassword(roomPassword) {
return nil, ErrAuthFailed
}
user, err := room.GetUser(username)
if err != nil {
return nil, err
}
if !user.CheckPassword(password) {
return nil, ErrAuthFailed
}
return user, nil
}
func AuthOrNewWithPassword(roomID, roomPassword, username, password string, rooms *room.Rooms) (*room.User, error) {
room, err := rooms.GetRoom(roomID)
if err != nil {
return nil, err
}
if !room.CheckPassword(roomPassword) {
return nil, ErrAuthFailed
}
user, err := room.GetOrNewUser(username, password)
if err != nil {
return nil, err
}
if !user.CheckPassword(password) {
return nil, ErrAuthFailed
}
return user, nil
}
func AuthRoom(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := Auth(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(401, model.NewApiErrorResp(err))
return
}
ctx.Set("user", user)
ctx.Next()
}
func NewAuthToken(user *room.User) (string, error) {
claims := &AuthClaims{
RoomID: user.Room().ID(),
Version: user.Room().Version(),
Username: user.Name(),
UserVersion: user.Version(),
RegisteredClaims: jwt.RegisteredClaims{
NotBefore: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(conf.Conf.Jwt.Expire))),
},
}
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret))
}

@ -1,4 +1,4 @@
package handlers
package model
import (
"time"
Loading…
Cancel
Save