diff --git a/server/handlers/admin.go b/server/handlers/admin.go index df625b36..f2059837 100644 --- a/server/handlers/admin.go +++ b/server/handlers/admin.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" "github.com/maruel/natural" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" @@ -25,9 +26,11 @@ import ( func EditAdminSettings(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) req := model.AdminSettingsReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -35,6 +38,7 @@ func EditAdminSettings(ctx *gin.Context) { for k, v := range req { err := settings.SetValue(k, v) if err != nil { + log.WithError(err).Error("set value error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -45,6 +49,8 @@ func EditAdminSettings(ctx *gin.Context) { func AdminSettings(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) + group := ctx.Param("group") switch group { case "oauth2": @@ -58,6 +64,7 @@ func AdminSettings(ctx *gin.Context) { } s, ok := f.Interface().(settings.Setting) if !ok { + log.Error("type error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("type error")) return } @@ -81,6 +88,7 @@ func AdminSettings(ctx *gin.Context) { default: s, ok := settings.GroupSettings[dbModel.SettingGroup(group)] if !ok { + log.Error("group not found") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("group not found")) return } @@ -98,8 +106,11 @@ func AdminSettings(ctx *gin.Context) { func Users(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) + page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.WithError(err).Error("get page and max error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -135,6 +146,7 @@ func Users(ctx *gin.Context) { scopes = append(scopes, db.OrderByAsc("username")) } default: + log.Error("not support sort") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support sort")) return } @@ -171,14 +183,18 @@ func genUserListResp(us []*dbModel.User) []*model.UserInfoResp { } func GetRoomUsers(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + id := ctx.Query("id") if len(id) != 32 { + log.Error("room id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("room id error")) return } page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.WithError(err).Error("get page and max error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -203,6 +219,7 @@ func GetRoomUsers(ctx *gin.Context) { scopes = append(scopes, db.OrderByAsc("username")) } default: + log.Error("not support sort") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support sort")) return } @@ -242,25 +259,31 @@ func genRoomUserListResp(us []*dbModel.User) []*model.RoomUsersResp { } func ApprovePendingUser(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } user, err := db.GetUserByID(req.ID) if err != nil { + log.WithError(err).Error("get user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if !user.IsPending() { + log.Error("user is not pending") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("user is not pending")) return } err = db.SetRoleByID(req.ID, dbModel.RoleUser) if err != nil { + log.WithError(err).Error("set role by id error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -270,30 +293,37 @@ func ApprovePendingUser(ctx *gin.Context) { func BanUser(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) + req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } u, err := op.LoadOrInitUserByID(req.ID) if err != nil { + log.WithError(err).Error("load or init user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if u.Value().IsRoot() { + log.Error("cannot ban root") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot ban root")) return } if u.Value().IsAdmin() && !user.IsRoot() { + log.Error("cannot ban admin") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot ban admin")) return } err = u.Value().SetRole(dbModel.RoleBanned) if err != nil { + log.WithError(err).Error("set role error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -303,25 +333,31 @@ func BanUser(ctx *gin.Context) { func UnBanUser(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) + req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } u, err := op.LoadOrInitUserByID(req.ID) if err != nil { + log.WithError(err).Error("load or init user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if !u.Value().IsBanned() { + log.Error("user is not banned") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("user is not banned")) return } err = u.Value().SetRole(dbModel.RoleUser) if err != nil { + log.WithError(err).Error("set role error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -331,9 +367,11 @@ func UnBanUser(ctx *gin.Context) { func Rooms(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.WithError(err).Error("get page and max error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -365,6 +403,7 @@ func Rooms(ctx *gin.Context) { scopes = append(scopes, db.OrderByAsc("name")) } default: + log.Error("not support sort") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support sort")) return } @@ -392,13 +431,17 @@ func Rooms(ctx *gin.Context) { } func GetUserRooms(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + id := ctx.Query("id") if len(id) != 32 { + log.Error("user id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("user id error")) return } page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.WithError(err).Error("get page and max error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -432,6 +475,7 @@ func GetUserRooms(ctx *gin.Context) { scopes = append(scopes, db.OrderByAsc("name")) } default: + log.Error("not support sort") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support sort")) return } @@ -455,25 +499,31 @@ func GetUserRooms(ctx *gin.Context) { } func ApprovePendingRoom(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + req := model.RoomIDReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } room, err := db.GetRoomByID(req.Id) if err != nil { + log.WithError(err).Error("get room by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if !room.IsPending() { + log.Error("room is not pending") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("room is not pending")) return } err = db.SetRoomStatus(req.Id, dbModel.RoomStatusActive) if err != nil { + log.WithError(err).Error("set room status error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -483,36 +533,44 @@ func ApprovePendingRoom(ctx *gin.Context) { func BanRoom(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) + req := model.RoomIDReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } r, err := db.GetRoomByID(req.Id) if err != nil { + log.WithError(err).Error("get room by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } creator, err := db.GetUserByID(r.CreatorID) if err != nil { + log.WithError(err).Error("get user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if creator.IsRoot() { + log.Error("cannot ban root") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot ban root")) return } if creator.IsAdmin() && !user.IsRoot() { + log.Error("cannot ban admin") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("cannot ban admin")) return } err = op.SetRoomStatusByID(req.Id, dbModel.RoomStatusBanned) if err != nil { + log.WithError(err).Error("set room status error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -522,25 +580,31 @@ func BanRoom(ctx *gin.Context) { func UnBanRoom(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) + req := model.RoomIDReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } r, err := db.GetRoomByID(req.Id) if err != nil { + log.WithError(err).Error("get room by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if !r.IsBanned() { + log.Error("room is not banned") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("room is not banned")) return } err = op.SetRoomStatusByID(req.Id, dbModel.RoomStatusActive) if err != nil { + log.WithError(err).Error("set room status error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -550,20 +614,24 @@ func UnBanRoom(ctx *gin.Context) { func AddUser(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.AddUserReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if req.Role == dbModel.RoleRoot && !user.IsRoot() { + log.Error("cannot add root user") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("you cannot add root user")) return } _, err := op.CreateUser(req.Username, req.Password, db.WithRole(req.Role)) if err != nil { + log.WithError(err).Error("create user error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -573,30 +641,36 @@ func AddUser(ctx *gin.Context) { func DeleteUser(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } u, err := op.LoadOrInitUserByID(req.ID) if err != nil { + log.WithError(err).Error("load or init user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if u.Value().IsRoot() { + log.Error("cannot delete root") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot delete root")) return } if u.Value().IsAdmin() && !user.IsRoot() { + log.Error("cannot delete admin") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("cannot delete admin")) return } if err := op.DeleteUserByID(req.ID); err != nil { + log.WithError(err).Error("delete user by id error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -606,30 +680,36 @@ func DeleteUser(ctx *gin.Context) { func AdminUserPassword(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.AdminUserPasswordReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp(err.Error())) return } u, err := op.LoadOrInitUserByID(req.ID) if err != nil { + log.WithError(err).Error("load or init user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("user not found")) return } if u.Value().IsRoot() { + log.Error("cannot change root password") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot change root password")) return } if u.Value().IsAdmin() && !user.IsRoot() { + log.Error("cannot change admin password") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("cannot change admin password")) return } if err := u.Value().SetPassword(req.Password); err != nil { + log.WithError(err).Error("set password error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp(err.Error())) return } @@ -639,30 +719,36 @@ func AdminUserPassword(ctx *gin.Context) { func AdminUsername(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.AdminUsernameReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp(err.Error())) return } u, err := op.LoadOrInitUserByID(req.ID) if err != nil { + log.WithError(err).Error("load or init user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("user not found")) return } if u.Value().IsRoot() { + log.Error("cannot change root username") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot change root username")) return } if u.Value().IsAdmin() && !user.IsRoot() { + log.Error("cannot change admin username") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("cannot change admin username")) return } if err := u.Value().SetUsername(req.Username); err != nil { + log.WithError(err).Error("set username error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp(err.Error())) return } @@ -672,36 +758,43 @@ func AdminUsername(ctx *gin.Context) { func AdminRoomPassword(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.AdminRoomPasswordReq{} if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp(err.Error())) return } r, err := op.LoadOrInitRoomByID(req.ID) if err != nil { + log.WithError(err).Error("load or init room by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("room not found")) return } creator, err := op.LoadOrInitUserByID(r.Value().CreatorID) if err != nil { + log.WithError(err).Error("load or init user by id error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("room creator not found")) return } if creator.Value().IsRoot() { + log.Error("cannot change root room password") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot change root room password")) return } if creator.Value().IsAdmin() && !user.IsRoot() { + log.Error("cannot change admin room password") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("cannot change admin room password")) return } if err := r.Value().SetPassword(req.Password); err != nil { + log.WithError(err).Error("set password error") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp(err.Error())) return } @@ -711,10 +804,12 @@ func AdminRoomPassword(ctx *gin.Context) { func AdminGetVendorBackends(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) conns := vendor.LoadConns() page, size, err := utils.GetPageAndMax(ctx) if err != nil { + log.WithError(err).Error("get page and max error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -753,14 +848,17 @@ func AdminGetVendorBackends(ctx *gin.Context) { func AdminAddVendorBackend(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) var req model.AddVendorBackendReq if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := vendor.AddVendorBackend(ctx, (*dbModel.VendorBackend)(&req)); err != nil { + log.WithError(err).Error("add vendor backend error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -770,14 +868,17 @@ func AdminAddVendorBackend(ctx *gin.Context) { func AdminDeleteVendorBackends(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := vendor.DeleteVendorBackends(ctx, req.Endpoints); err != nil { + log.WithError(err).Error("delete vendor backends error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -787,14 +888,17 @@ func AdminDeleteVendorBackends(ctx *gin.Context) { func AdminUpdateVendorBackends(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) var req model.AddVendorBackendReq if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := vendor.UpdateVendorBackend(ctx, (*dbModel.VendorBackend)(&req)); err != nil { + log.WithError(err).Error("update vendor backend error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -804,9 +908,11 @@ func AdminUpdateVendorBackends(ctx *gin.Context) { func AdminReconnectVendorBackends(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -824,6 +930,7 @@ func AdminReconnectVendorBackends(ctx *gin.Context) { } } } else { + log.WithField("endpoint", v).Error("endpoint not found") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp(fmt.Sprintf("endpoint %s not found", v))) return } @@ -834,14 +941,17 @@ func AdminReconnectVendorBackends(ctx *gin.Context) { func AdminEnableVendorBackends(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := vendor.EnableVendorBackends(ctx, req.Endpoints); err != nil { + log.WithError(err).Error("enable vendor backends error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -851,14 +961,17 @@ func AdminEnableVendorBackends(ctx *gin.Context) { func AdminDisableVendorBackends(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) var req model.VendorBackendEndpointsReq if err := model.Decode(ctx, &req); err != nil { + log.WithError(err).Error("decode error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := vendor.DisableVendorBackends(ctx, req.Endpoints); err != nil { + log.WithError(err).Error("disable vendor backends error") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } diff --git a/server/handlers/movie.go b/server/handlers/movie.go index 6b95c37e..45e83d83 100644 --- a/server/handlers/movie.go +++ b/server/handlers/movie.go @@ -17,6 +17,7 @@ import ( "strings" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/conf" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" @@ -42,23 +43,26 @@ func GetPageItems[T any](ctx *gin.Context, items []T) ([]T, error) { func MovieList(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) page, max, err := utils.GetPageAndMax(ctx) if err != nil { + log.Errorf("get page and max error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } currentResp, err := genCurrentResp(ctx, user, room) if err != nil { + log.Errorf("gen current resp error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } m := room.GetMoviesWithPage(page, max) - mresp := make([]model.MoviesResp, len(m)) + mresp := make([]model.MovieResp, len(m)) for i, v := range m { - mresp[i] = model.MoviesResp{ + mresp[i] = model.MovieResp{ Id: v.Movie.ID, Base: v.Movie.Base, Creator: op.GetUserName(v.Movie.CreatorID), @@ -81,13 +85,9 @@ func genCurrentResp(ctx context.Context, user *op.User, room *op.Room) (*model.C return genCurrentRespWithCurrent(ctx, user, room, room.Current()) } -func genCurrentRespWithCurrent(ctx context.Context, user *op.User, room *op.Room, current *op.Current) (*model.CurrentMovieResp, error) { - if current.Movie.ID == "" { - return &model.CurrentMovieResp{}, nil - } - opMovie, err := room.GetMovieByID(current.Movie.ID) - if err != nil { - return nil, fmt.Errorf("get current movie error: %w", err) +func genCurrentMovieInfo(ctx context.Context, user *op.User, room *op.Room, opMovie *op.Movie) (*model.MovieResp, error) { + if opMovie == nil || opMovie.ID == "" { + return &model.MovieResp{}, nil } var movie = opMovie.Movie if movie.Base.VendorInfo.Vendor != "" { @@ -113,15 +113,33 @@ func genCurrentRespWithCurrent(ctx context.Context, user *op.User, room *op.Room if movie.Base.Type == "" && movie.Base.Url != "" { movie.Base.Type = utils.GetUrlExtension(movie.Base.Url) } + resp := &model.MovieResp{ + Id: movie.ID, + CreatedAt: movie.CreatedAt.UnixMilli(), + Base: movie.Base, + Creator: op.GetUserName(movie.CreatorID), + CreatorId: movie.CreatorID, + } + return resp, nil +} + +func genCurrentRespWithCurrent(ctx context.Context, user *op.User, room *op.Room, current *op.Current) (*model.CurrentMovieResp, error) { + if current.Movie.ID == "" { + return &model.CurrentMovieResp{ + Movie: &model.MovieResp{}, + }, nil + } + opMovie, err := room.GetMovieByID(current.Movie.ID) + if err != nil { + return nil, fmt.Errorf("get current movie error: %w", err) + } + mr, err := genCurrentMovieInfo(ctx, user, room, opMovie) + if err != nil { + return nil, fmt.Errorf("gen current movie info error: %w", err) + } resp := &model.CurrentMovieResp{ - Status: current.UpdateStatus(), - Movie: model.MoviesResp{ - Id: movie.ID, - CreatedAt: movie.CreatedAt.UnixMilli(), - Base: movie.Base, - Creator: op.GetUserName(movie.CreatorID), - CreatorId: movie.CreatorID, - }, + Status: current.UpdateStatus(), + Movie: mr, ExpireId: opMovie.ExpireId(), } return resp, nil @@ -130,9 +148,11 @@ func genCurrentRespWithCurrent(ctx context.Context, user *op.User, room *op.Room func CurrentMovie(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) currentResp, err := genCurrentResp(ctx, user, room) if err != nil { + log.Errorf("gen current resp error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -143,18 +163,20 @@ func CurrentMovie(ctx *gin.Context) { func Movies(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) page, max, err := utils.GetPageAndMax(ctx) if err != nil { + log.Errorf("get page and max error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } m := room.GetMoviesWithPage(int(page), int(max)) - mresp := make([]*model.MoviesResp, len(m)) + mresp := make([]*model.MovieResp, len(m)) for i, v := range m { - mresp[i] = &model.MoviesResp{ + mresp[i] = &model.MovieResp{ Id: v.Movie.ID, Base: v.Movie.Base, Creator: op.GetUserName(v.Movie.CreatorID), @@ -175,15 +197,18 @@ func Movies(ctx *gin.Context) { func PushMovie(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.PushMovieReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("push movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } err := user.AddMovieToRoom(room, (*dbModel.BaseMovie)(&req)) if err != nil { + log.Errorf("push movie error: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -199,6 +224,7 @@ func PushMovie(ctx *gin.Context) { Userid: user.ID, }, }); err != nil { + log.Errorf("push movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -209,6 +235,7 @@ func PushMovie(ctx *gin.Context) { func PushMovies(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.PushMoviesReq{} if err := model.Decode(ctx, &req); err != nil { @@ -225,6 +252,7 @@ func PushMovies(ctx *gin.Context) { err := user.AddMoviesToRoom(room, ms) if err != nil { + log.Errorf("push movies error: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -240,6 +268,7 @@ func PushMovies(ctx *gin.Context) { Userid: user.ID, }, }); err != nil { + log.Errorf("push movies error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -248,7 +277,10 @@ func PushMovies(ctx *gin.Context) { } func NewPublishKey(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + if !conf.Conf.Server.Rtmp.Enable { + log.Errorf("rtmp is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("rtmp is not enabled")) return } @@ -258,27 +290,32 @@ func NewPublishKey(ctx *gin.Context) { req := model.IdReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("new publish key error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } movie, err := room.GetMovieByID(req.Id) if err != nil { + log.Errorf("new publish key error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if movie.Movie.CreatorID != user.ID && !user.HasRoomPermission(room, dbModel.PermissionEditUser) { + log.Errorf("new publish key error: %v", dbModel.ErrNoPermission) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(dbModel.ErrNoPermission)) return } if !movie.Movie.Base.RtmpSource { + log.Errorf("new publish key error: %v", "only rtmp source movie can get publish key") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("only live movie can get publish key")) return } token, err := rtmp.NewRtmpAuthorization(movie.Movie.ID) if err != nil { + log.Errorf("new publish key error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -298,14 +335,17 @@ func NewPublishKey(ctx *gin.Context) { func EditMovie(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.EditMovieReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("edit movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := user.UpdateMovie(room, req.Id, (*dbModel.BaseMovie)(&req.PushMovieReq)); err != nil { + log.Errorf("edit movie error: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -321,6 +361,7 @@ func EditMovie(ctx *gin.Context) { Userid: user.ID, }, }); err != nil { + log.Errorf("edit movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -331,15 +372,18 @@ func EditMovie(ctx *gin.Context) { func DelMovie(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.IdsReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("del movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } err := user.DeleteMoviesByID(room, req.Ids) if err != nil { + log.Errorf("del movie error: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -421,6 +465,7 @@ func SwapMovie(ctx *gin.Context) { func ChangeCurrentMovie(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.IdCanEmptyReq{} err := model.Decode(ctx, &req) @@ -432,14 +477,29 @@ func ChangeCurrentMovie(ctx *gin.Context) { if req.Id == "" { err = user.SetCurrentMovie(room, nil, false) } else { - err = user.SetCurrentMovieByID(room, req.Id, true) + var movie *op.Movie + movie, err = room.GetMovieByID(req.Id) + if err != nil { + log.Errorf("change current movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return + } + _, err = genCurrentMovieInfo(ctx, user, room, movie) + if err != nil { + log.Errorf("change current movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return + } + err = user.SetCurrentMovie(room, &movie.Movie, true) } if err != nil { + log.Errorf("change current movie error: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return } ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return } if err := room.Broadcast(&pb.ElementMessage{ @@ -449,6 +509,7 @@ func ChangeCurrentMovie(ctx *gin.Context) { Userid: user.ID, }, }); err != nil { + log.Errorf("change current movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -457,24 +518,30 @@ func ChangeCurrentMovie(ctx *gin.Context) { } func ProxyMovie(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + if !settings.MovieProxy.Get() { + log.Errorf("movie proxy is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("movie proxy is not enabled")) return } roomId := ctx.Param("roomId") if roomId == "" { + log.Errorf("room id is empty") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("roomId is empty")) return } room, err := op.LoadOrInitRoomByID(roomId) if err != nil { + log.Errorf("load or init room by id error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } m, err := room.Value().GetMovieByID(ctx.Param("movieId")) if err != nil { + log.Errorf("get movie by id error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -496,7 +563,7 @@ func ProxyMovie(ctx *gin.Context) { default: err = proxyURL(ctx, m.Movie.Base.Url, m.Movie.Base.Headers) if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + log.Errorf("proxy movie error: %v", err) return } } @@ -588,23 +655,29 @@ func (e FormatErrNotSupportFileType) Error() string { } func JoinLive(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + ctx.Header("Cache-Control", "no-store") room := ctx.MustGet("room").(*op.RoomEntry).Value() movieId := strings.Trim(ctx.Param("movieId"), "/") m, err := room.GetMovieByID(movieId) if err != nil { + log.Errorf("join live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } if m.Movie.Base.RtmpSource && !conf.Conf.Server.Rtmp.Enable { + log.Errorf("join live error: %v", "rtmp is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("rtmp is not enabled")) return } else if m.Movie.Base.Live && !settings.LiveProxy.Get() { + log.Errorf("join live error: %v", "live proxy is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("live proxy is not enabled")) return } channel, err := m.Channel() if err != nil { + log.Errorf("join live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -619,6 +692,7 @@ func JoinLive(ctx *gin.Context) { defer w.Close() err = channel.AddPlayer(w) if err != nil { + log.Errorf("join live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -632,34 +706,42 @@ func JoinLive(ctx *gin.Context) { return fmt.Sprintf("/api/movie/live/hls/data/%s/%s/%s.%s", room.ID, movieId, tsName, ext) }) if err != nil { + log.Errorf("join live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } ctx.Data(http.StatusOK, hls.M3U8ContentType, b) default: + log.Errorf("join live error: %v", FormatErrNotSupportFileType(joinType)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp(fmt.Sprintf("not support join type: %s", joinType))) return } } func JoinFlvLive(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + ctx.Header("Cache-Control", "no-store") room := ctx.MustGet("room").(*op.RoomEntry).Value() movieId := strings.TrimSuffix(strings.Trim(ctx.Param("movieId"), "/"), ".flv") m, err := room.GetMovieByID(movieId) if err != nil { + log.Errorf("join flv live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } if m.Movie.Base.RtmpSource && !conf.Conf.Server.Rtmp.Enable { + log.Errorf("join flv live error: %v", "rtmp is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("rtmp is not enabled")) return } else if m.Movie.Base.Live && !settings.LiveProxy.Get() { + log.Errorf("join flv live error: %v", "live proxy is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("live proxy is not enabled")) return } channel, err := m.Channel() if err != nil { + log.Errorf("join flv live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -668,6 +750,7 @@ func JoinFlvLive(ctx *gin.Context) { defer w.Close() err = channel.AddPlayer(w) if err != nil { + log.Errorf("join flv live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -675,23 +758,29 @@ func JoinFlvLive(ctx *gin.Context) { } func JoinHlsLive(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + ctx.Header("Cache-Control", "no-store") room := ctx.MustGet("room").(*op.RoomEntry).Value() movieId := strings.TrimSuffix(strings.Trim(ctx.Param("movieId"), "/"), ".m3u8") m, err := room.GetMovieByID(movieId) if err != nil { + log.Errorf("join hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } if m.Movie.Base.RtmpSource && !conf.Conf.Server.Rtmp.Enable { + log.Errorf("join hls live error: %v", "rtmp is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("rtmp is not enabled")) return } else if m.Movie.Base.Live && !settings.LiveProxy.Get() { + log.Errorf("join hls live error: %v", "live proxy is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("live proxy is not enabled")) return } channel, err := m.Channel() if err != nil { + log.Errorf("join hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -704,6 +793,7 @@ func JoinHlsLive(ctx *gin.Context) { return fmt.Sprintf("/api/movie/live/hls/data/%s/%s/%s.%s", room.ID, movieId, tsName, ext) }) if err != nil { + log.Errorf("join hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -711,10 +801,13 @@ func JoinHlsLive(ctx *gin.Context) { } func ServeHlsLive(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + ctx.Header("Cache-Control", "no-store") roomId := ctx.Param("roomId") roomE, err := op.LoadOrInitRoomByID(roomId) if err != nil { + log.Errorf("serve hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -722,18 +815,22 @@ func ServeHlsLive(ctx *gin.Context) { movieId := ctx.Param("movieId") m, err := room.GetMovieByID(movieId) if err != nil { + log.Errorf("serve hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } if m.Movie.Base.RtmpSource && !conf.Conf.Server.Rtmp.Enable { + log.Errorf("serve hls live error: %v", "rtmp is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("rtmp is not enabled")) return } else if m.Movie.Base.Live && !settings.LiveProxy.Get() { + log.Errorf("serve hls live error: %v", "live proxy is not enabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("live proxy is not enabled")) return } channel, err := m.Channel() if err != nil { + log.Errorf("serve hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -742,11 +839,13 @@ func ServeHlsLive(ctx *gin.Context) { switch fileExt := filepath.Ext(dataId); fileExt { case ".ts": if settings.TsDisguisedAsPng.Get() { + log.Errorf("serve hls live error: %v", FormatErrNotSupportFileType(fileExt)) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(FormatErrNotSupportFileType(fileExt))) return } b, err := channel.GetTsFile(strings.TrimSuffix(dataId, fileExt)) if err != nil { + log.Errorf("serve hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -754,11 +853,13 @@ func ServeHlsLive(ctx *gin.Context) { ctx.Data(http.StatusOK, hls.TSContentType, b) case ".png": if !settings.TsDisguisedAsPng.Get() { + log.Errorf("serve hls live error: %v", FormatErrNotSupportFileType(fileExt)) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(FormatErrNotSupportFileType(fileExt))) return } b, err := channel.GetTsFile(strings.TrimSuffix(dataId, fileExt)) if err != nil { + log.Errorf("serve hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -768,33 +869,40 @@ func ServeHlsLive(ctx *gin.Context) { cache := bytes.NewBuffer(make([]byte, 0, 71)) err = png.Encode(cache, img) if err != nil { + log.Errorf("serve hls live error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } ctx.Data(http.StatusOK, "image/png", append(cache.Bytes(), b...)) default: ctx.Header("Cache-Control", "no-store") + log.Errorf("serve hls live error: %v", FormatErrNotSupportFileType(fileExt)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(FormatErrNotSupportFileType(fileExt))) } } func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) { + log := ctx.MustGet("log").(*logrus.Entry) + switch movie.Movie.Base.VendorInfo.Vendor { case dbModel.VendorBilibili: t := ctx.Query("t") switch t { case "", "hevc": if !movie.Movie.Base.Proxy { + log.Errorf("proxy vendor movie error: %v", "not support movie proxy") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support movie proxy")) return } u, err := op.LoadOrInitUserByID(movie.Movie.CreatorID) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } mpdC, err := movie.BilibiliCache().SharedMpd.Get(ctx, u.Value().BilibiliCache()) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -808,10 +916,12 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) { } else { streamId, err := strconv.Atoi(id) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if streamId >= len(mpdC.Urls) { + log.Errorf("proxy vendor movie error: %v", "stream id out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("stream id out of range")) return } @@ -825,34 +935,42 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) { headers["Referer"] = "https://www.bilibili.com" headers["User-Agent"] = utils.UA } - proxyURL(ctx, mpdC.Urls[streamId], headers) + err = proxyURL(ctx, mpdC.Urls[streamId], headers) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + } return } case "subtitle": id := ctx.Query("n") if id == "" { + log.Errorf("proxy vendor movie error: %v", "n is empty") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("n is empty")) return } u, err := op.LoadOrInitUserByID(movie.Movie.CreatorID) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } srtI, err := movie.BilibiliCache().Subtitle.Get(ctx, u.Value().BilibiliCache()) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } if s, ok := srtI[id]; ok { srtData, err := s.Srt.Get(ctx) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } ctx.Data(http.StatusOK, "text/plain; charset=utf-8", srtData) return } else { + log.Errorf("proxy vendor movie error: %v", "subtitle not found") ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorStringResp("subtitle not found")) return } @@ -861,11 +979,13 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) { case dbModel.VendorAlist: u, err := op.LoadOrInitUserByID(movie.Movie.CreatorID) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } alistC, err := movie.AlistCache().Get(ctx, u.Value().AlistCache()) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -878,30 +998,38 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) { case "subtitle": idS := ctx.Query("id") if idS == "" { + log.Errorf("proxy vendor movie error: %v", "id is empty") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("id is empty")) return } id, err := strconv.Atoi(idS) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if id >= len(alistC.Ali.Subtitles) { + log.Errorf("proxy vendor movie error: %v", "id out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("id out of range")) return } data, err := alistC.Ali.Subtitles[id].Cache.Get(ctx) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } ctx.Data(http.StatusOK, "text/plain; charset=utf-8", data) } } else if !movie.Movie.Base.Proxy { + log.Errorf("proxy vendor movie error: %v", "not support movie proxy") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support movie proxy")) return } else { - proxyURL(ctx, alistC.URL, nil) + err = proxyURL(ctx, alistC.URL, nil) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + } } return @@ -911,71 +1039,88 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) { switch t { case "": if !movie.Movie.Base.Proxy { + log.Errorf("proxy vendor movie error: %v", "not support movie proxy") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support movie proxy")) return } u, err := op.LoadOrInitUserByID(movie.Movie.CreatorID) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } embyC, err := movie.EmbyCache().Get(ctx, u.Value().EmbyCache()) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } source, err := strconv.Atoi(ctx.Query("source")) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if source >= len(embyC.Sources) { + log.Errorf("proxy vendor movie error: %v", "source out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("source out of range")) return } id, err := strconv.Atoi(ctx.Query("id")) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if id >= len(embyC.Sources[source].URLs) { + log.Errorf("proxy vendor movie error: %v", "id out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("id out of range")) return } - proxyURL(ctx, embyC.Sources[source].URLs[id].URL, nil) + err = proxyURL(ctx, embyC.Sources[source].URLs[id].URL, nil) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + } return case "subtitle": u, err := op.LoadOrInitUserByID(movie.Movie.CreatorID) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } embyC, err := movie.EmbyCache().Get(ctx, u.Value().EmbyCache()) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } source, err := strconv.Atoi(ctx.Query("source")) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if source >= len(embyC.Sources) { + log.Errorf("proxy vendor movie error: %v", "source out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("source out of range")) return } id, err := strconv.Atoi(ctx.Query("id")) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if id >= len(embyC.Sources[source].Subtitles) { + log.Errorf("proxy vendor movie error: %v", "id out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("id out of range")) return } data, err := embyC.Sources[source].Subtitles[id].Cache.Get(ctx) if err != nil { + log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -984,6 +1129,7 @@ func proxyVendorMovie(ctx *gin.Context, movie *op.Movie) { } default: + log.Errorf("proxy vendor movie error: %v", "vendor not support proxy") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("vendor not support proxy")) return } diff --git a/server/handlers/room.go b/server/handlers/room.go index 9ffebcbf..37ab58e1 100644 --- a/server/handlers/room.go +++ b/server/handlers/room.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" "github.com/maruel/natural" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" @@ -36,26 +37,31 @@ func (e FormatErrNotSupportPosition) Error() string { func CreateRoom(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) if settings.DisableCreateRoom.Get() && !user.IsAdmin() { + log.Warn("create room is disabled") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("create room is disabled")) return } req := model.CreateRoomReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("create room failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } room, err := user.CreateRoom(req.RoomName, req.Password, db.WithSetting(req.Setting)) if err != nil { + log.Errorf("create room failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } token, err := middlewares.NewAuthRoomToken(user, room.Value()) if err != nil { + log.Errorf("create room failed: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -104,14 +110,18 @@ var roomHotCache = refreshcache.NewRefreshCache[[]*model.RoomListResp](func(cont }, time.Second*3) func RoomHotList(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.Errorf("get room hot list failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } r, err := roomHotCache.Get(ctx) if err != nil { + log.Errorf("get room hot list failed: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -123,8 +133,11 @@ func RoomHotList(ctx *gin.Context) { } func RoomList(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.Errorf("get room list failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -150,6 +163,7 @@ func RoomList(ctx *gin.Context) { scopes = append(scopes, db.OrderByAsc("name")) } default: + log.Errorf("get room list failed: not support sort") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support sort")) return } @@ -193,8 +207,11 @@ func genRoomListResp(scopes ...func(db *gorm.DB) *gorm.DB) []*model.RoomListResp } func CheckRoom(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + r, err := db.GetRoomByID(ctx.Query("roomId")) if err != nil { + log.Errorf("check room failed: %v", err) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } @@ -208,15 +225,18 @@ func CheckRoom(ctx *gin.Context) { func LoginRoom(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.LoginRoomReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("login room failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } room, err := op.LoadOrInitRoomByID(req.RoomId) if err != nil { + log.Errorf("login room failed: %v", err) if err == op.ErrRoomBanned || err == op.ErrRoomPending { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -226,12 +246,14 @@ func LoginRoom(ctx *gin.Context) { } if room.Value().CreatorID != user.ID && !room.Value().CheckPassword(req.Password) { + log.Warn("login room failed: password error") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("password error")) return } token, err := middlewares.NewAuthRoomToken(user, room.Value()) if err != nil { + log.Errorf("login room failed: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -245,8 +267,10 @@ func LoginRoom(ctx *gin.Context) { func DeleteRoom(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry) user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) if err := user.DeleteRoom(room); err != nil { + log.Errorf("delete room failed: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -261,14 +285,17 @@ func DeleteRoom(ctx *gin.Context) { func SetRoomPassword(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.SetRoomPasswordReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("set room password failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := user.SetRoomPassword(room, req.Password); err != nil { + log.Errorf("set room password failed: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -279,6 +306,7 @@ func SetRoomPassword(ctx *gin.Context) { token, err := middlewares.NewAuthRoomToken(user, room) if err != nil { + log.Errorf("set room password failed: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -299,14 +327,17 @@ func RoomSetting(ctx *gin.Context) { func SetRoomSetting(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.SetRoomSettingReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("set room setting failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if err := user.SetRoomSetting(room, dbModel.RoomSettings(req)); err != nil { + log.Errorf("set room setting failed: %v", err) if errors.Is(err, dbModel.ErrNoPermission) { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -320,8 +351,11 @@ func SetRoomSetting(ctx *gin.Context) { func RoomUsers(ctx *gin.Context) { room := ctx.MustGet("room").(*op.RoomEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) + page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.Errorf("get room users failed: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -354,6 +388,7 @@ func RoomUsers(ctx *gin.Context) { scopes = append(scopes, db.OrderByAsc("username")) } default: + log.Errorf("get room users failed: not support sort") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support sort")) return } diff --git a/server/handlers/root.go b/server/handlers/root.go index d4b1b777..a47d9026 100644 --- a/server/handlers/root.go +++ b/server/handlers/root.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/server/model" @@ -11,29 +12,35 @@ import ( func AddAdmin(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) req := model.IdReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if req.Id == user.ID { + log.Errorf("cannot add yourself") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot add yourself")) return } u, err := op.LoadOrInitUserByID(req.Id) if err != nil { + log.Errorf("failed to load user: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("user not found")) return } if u.Value().IsAdmin() { + log.Errorf("user is already admin") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("user is already admin")) return } if err := u.Value().SetRole(dbModel.RoleAdmin); err != nil { - ctx.AbortWithError(http.StatusInternalServerError, err) + log.Errorf("failed to set role: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -42,29 +49,35 @@ func AddAdmin(ctx *gin.Context) { func DeleteAdmin(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) req := model.IdReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if req.Id == user.Value().ID { + log.Errorf("cannot remove yourself") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot remove yourself")) return } u, err := op.LoadOrInitUserByID(req.Id) if err != nil { + log.Errorf("failed to load user: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("user not found")) return } if u.Value().IsRoot() { + log.Errorf("cannot remove root") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot remove root")) return } if err := u.Value().SetRole(dbModel.RoleUser); err != nil { - ctx.AbortWithError(http.StatusInternalServerError, err) + log.Errorf("failed to set role: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } diff --git a/server/handlers/user.go b/server/handlers/user.go index 7f39bef6..20574dcf 100644 --- a/server/handlers/user.go +++ b/server/handlers/user.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" @@ -27,14 +28,18 @@ func Me(ctx *gin.Context) { } func LoginUser(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + req := model.LoginUserReq{} if err := model.Decode(ctx, &req); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } user, err := op.LoadUserByUsername(req.Username) if err != nil { + log.Errorf("failed to load user: %v", err) if err == op.ErrUserBanned || err == op.ErrUserPending { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) return @@ -44,12 +49,14 @@ func LoginUser(ctx *gin.Context) { } if ok := user.Value().CheckPassword(req.Password); !ok { + log.Errorf("password incorrect") ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("password incorrect")) return } token, err := middlewares.NewAuthUserToken(user.Value()) if err != nil { + log.Errorf("failed to generate token: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -61,9 +68,11 @@ func LoginUser(ctx *gin.Context) { func LogoutUser(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry) + log := ctx.MustGet("log").(*logrus.Entry) err := op.CompareAndDeleteUser(user) if err != nil { + log.Errorf("failed to logout: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -73,9 +82,11 @@ func LogoutUser(ctx *gin.Context) { func UserRooms(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) page, pageSize, err := utils.GetPageAndMax(ctx) if err != nil { + log.Errorf("failed to get page and max: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -109,6 +120,7 @@ func UserRooms(ctx *gin.Context) { scopes = append(scopes, db.OrderByAsc("name")) } default: + log.Errorf("not support sort") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support sort")) return } @@ -133,15 +145,18 @@ func UserRooms(ctx *gin.Context) { func SetUsername(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) var req model.SetUsernameReq if err := model.Decode(ctx, &req); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } err := user.SetUsername(req.Username) if err != nil { + log.Errorf("failed to set username: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -151,21 +166,25 @@ func SetUsername(ctx *gin.Context) { func SetUserPassword(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) var req model.SetUserPasswordReq if err := model.Decode(ctx, &req); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } err := user.SetPassword(req.Password) if err != nil { + log.Errorf("failed to set password: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } token, err := middlewares.NewAuthUserToken(user) if err != nil { + log.Errorf("failed to generate token: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } @@ -177,9 +196,11 @@ func SetUserPassword(ctx *gin.Context) { func UserBindProviders(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) up, err := db.GetBindProviders(user.ID) if err != nil { + log.Errorf("failed to get bind providers: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return } diff --git a/server/handlers/websocket.go b/server/handlers/websocket.go index 4e5fdf0e..b89a3a47 100644 --- a/server/handlers/websocket.go +++ b/server/handlers/websocket.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/gorilla/websocket" + "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/op" pb "github.com/synctv-org/synctv/proto/message" @@ -21,20 +22,27 @@ const maxInterval = 10 func NewWebSocketHandler(wss *utils.WebSocket) gin.HandlerFunc { return func(ctx *gin.Context) { token := ctx.GetHeader("Sec-WebSocket-Protocol") - user, room, err := middlewares.AuthRoom(token) + userE, roomE, err := middlewares.AuthRoom(token) if err != nil { ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err)) return } + user := userE.Value() + room := roomE.Value() + entry := log.WithFields(log.Fields{ + "rid": room.ID, + "rnm": room.Name, + "uid": user.ID, + "unm": user.Username, + "uro": user.Role.String(), + }) - wss.Server(ctx.Writer, ctx.Request, []string{token}, NewWSMessageHandler(user, room)) + _ = wss.Server(ctx.Writer, ctx.Request, []string{token}, NewWSMessageHandler(user, room, entry)) } } -func NewWSMessageHandler(uE *op.UserEntry, rE *op.RoomEntry) func(c *websocket.Conn) error { +func NewWSMessageHandler(u *op.User, r *op.Room, l *logrus.Entry) func(c *websocket.Conn) error { return func(c *websocket.Conn) error { - r := rE.Value() - u := uE.Value() client, err := r.NewClient(u, c) if err != nil { log.Errorf("ws: register client error: %v", err) @@ -49,92 +57,92 @@ func NewWSMessageHandler(uE *op.UserEntry, rE *op.RoomEntry) func(c *websocket.C } return em.Encode(wc) } - log.Infof("ws: room %s user %s connected", r.Name, u.Username) + l.Info("ws: connected") defer func() { - r.UnregisterClient(client) + _ = r.UnregisterClient(client) client.Close() - log.Infof("ws: room %s user %s disconnected", r.Name, u.Username) + l.Info("ws: disconnected") }() - go handleReaderMessage(client) - return handleWriterMessage(client) + go handleReaderMessage(client, l) + return handleWriterMessage(client, l) } } -func handleWriterMessage(c *op.Client) error { +func handleWriterMessage(c *op.Client, l *logrus.Entry) error { for v := range c.GetReadChan() { wc, err := c.NextWriter(v.MessageType()) if err != nil { - log.Debugf("ws: room %s user %s get next writer error: %v", c.Room().Name, c.User().Username, err) + l.Errorf("ws: get next writer error: %v", err) return err } if err = v.Encode(wc); err != nil { - log.Debugf("ws: room %s user %s encode message error: %v", c.Room().Name, c.User().Username, err) + l.Errorf("ws: encode message error: %v", err) return err } if err = wc.Close(); err != nil { + l.Errorf("ws: close writer error: %v", err) return err } } return nil } -func handleReaderMessage(c *op.Client) error { - defer c.Close() +func handleReaderMessage(c *op.Client, l *logrus.Entry) error { + defer func() { + c.Close() + if r := recover(); r != nil { + l.Errorf("ws: panic: %v", r) + } + }() for { t, rd, err := c.NextReader() if err != nil { - log.Debugf("ws: room %s user %s get next reader error: %v", c.Room().Name, c.User().Username, err) + l.Errorf("ws: get next reader error: %v", err) return err } - log.Debugf("ws: room %s user %s receive message type: %d", c.Room().Name, c.User().Username, t) - switch t { - case websocket.CloseMessage: - log.Debugf("ws: room %s user %s receive close message", c.Room().Name, c.User().Username) - return nil - case websocket.BinaryMessage: - var data []byte - if data, err = io.ReadAll(rd); err != nil { - log.Errorf("ws: room %s user %s read message error: %v", c.Room().Name, c.User().Username, err) - if err := c.Send(&pb.ElementMessage{ - Type: pb.ElementMessageType_ERROR, - Error: err.Error(), - }); err != nil { - log.Errorf("ws: room %s user %s send error message error: %v", c.Room().Name, c.User().Username, err) - return err - } - continue - } - var msg pb.ElementMessage - if err := proto.Unmarshal(data, &msg); err != nil { - log.Errorf("ws: room %s user %s decode message error: %v", c.Room().Name, c.User().Username, err) - if err := c.Send(&pb.ElementMessage{ - Type: pb.ElementMessageType_ERROR, - Error: err.Error(), - }); err != nil { - log.Errorf("ws: room %s user %s send error message error: %v", c.Room().Name, c.User().Username, err) - return err - } - continue + l.Debugf("ws: receive message type: %d", t) + if t != websocket.BinaryMessage { + l.Errorf("ws: receive unknown message type: %d", t) + continue + } + var data []byte + if data, err = io.ReadAll(rd); err != nil { + l.Errorf("ws: read message error: %v", err) + if err := c.Send(&pb.ElementMessage{ + Type: pb.ElementMessageType_ERROR, + Error: err.Error(), + }); err != nil { + l.Errorf("ws: send error message error: %v", err) + return err } - - log.Debugf("ws: receive room %s user %s message: %+v", c.Room().Name, c.User().Username, msg.String()) - if err = handleElementMsg(c, &msg); err != nil { - log.Errorf("ws: room %s user %s handle message error: %v", c.Room().Name, c.User().Username, err) + continue + } + var msg pb.ElementMessage + if err := proto.Unmarshal(data, &msg); err != nil { + l.Errorf("ws: unmarshal message error: %v", err) + if err := c.Send(&pb.ElementMessage{ + Type: pb.ElementMessageType_ERROR, + Error: err.Error(), + }); err != nil { + l.Errorf("ws: send error message error: %v", err) return err } - - default: - log.Errorf("ws: room %s user %s receive unknown message type: %d", c.Room().Name, c.User().Username, t) continue } + + l.Debugf("ws: receive message: %v", msg.String()) + if err = handleElementMsg(c, &msg, l); err != nil { + l.Errorf("ws: handle message error: %v", err) + return err + } } } const MaxChatMessageLength = 4096 -func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error { +func handleElementMsg(cli *op.Client, msg *pb.ElementMessage, l *logrus.Entry) error { var timeDiff float64 if msg.Time != 0 { timeDiff = time.Since(time.UnixMilli(msg.Time)).Seconds() @@ -150,13 +158,12 @@ func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error { case pb.ElementMessageType_CHAT_MESSAGE: message := msg.GetChatReq() if len(message) > MaxChatMessageLength { - cli.Send(&pb.ElementMessage{ + return cli.Send(&pb.ElementMessage{ Type: pb.ElementMessageType_ERROR, Error: "message too long", }) - return nil } - cli.Broadcast(&pb.ElementMessage{ + return cli.Broadcast(&pb.ElementMessage{ Type: pb.ElementMessageType_CHAT_MESSAGE, ChatResp: &pb.ChatResp{ Sender: &pb.Sender{ @@ -170,7 +177,7 @@ func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error { pb.ElementMessageType_PAUSE, pb.ElementMessageType_CHANGE_RATE: status := cli.Room().SetStatus(msg.ChangeMovieStatusReq.Playing, msg.ChangeMovieStatusReq.Seek, msg.ChangeMovieStatusReq.Rate, timeDiff) - cli.Broadcast(&pb.ElementMessage{ + return cli.Broadcast(&pb.ElementMessage{ Type: msg.Type, MovieStatusChanged: &pb.MovieStatusChanged{ Sender: &pb.Sender{ @@ -186,7 +193,7 @@ func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error { }, op.WithIgnoreClient(cli)) case pb.ElementMessageType_CHANGE_SEEK: status := cli.Room().SetSeekRate(msg.ChangeMovieStatusReq.Seek, msg.ChangeMovieStatusReq.Rate, timeDiff) - cli.Broadcast(&pb.ElementMessage{ + return cli.Broadcast(&pb.ElementMessage{ Type: msg.Type, MovieStatusChanged: &pb.MovieStatusChanged{ Sender: &pb.Sender{ @@ -202,7 +209,7 @@ func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error { }, op.WithIgnoreClient(cli)) case pb.ElementMessageType_SYNC_MOVIE_STATUS: status := cli.Room().Current().Status - cli.Send(&pb.ElementMessage{ + return cli.Send(&pb.ElementMessage{ Type: pb.ElementMessageType_SYNC_MOVIE_STATUS, MovieStatusChanged: &pb.MovieStatusChanged{ Sender: &pb.Sender{ @@ -237,7 +244,7 @@ func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error { status := current.Status cliStatus := msg.CheckReq.Status if status.Seek+maxInterval < cliStatus.Seek+timeDiff { - cli.Send(&pb.ElementMessage{ + return cli.Send(&pb.ElementMessage{ Type: pb.ElementMessageType_TOO_FAST, MovieStatusChanged: &pb.MovieStatusChanged{ Status: &pb.MovieStatus{ @@ -248,7 +255,7 @@ func handleElementMsg(cli *op.Client, msg *pb.ElementMessage) error { }, }) } else if status.Seek-maxInterval > cliStatus.Seek+timeDiff { - cli.Send(&pb.ElementMessage{ + return cli.Send(&pb.ElementMessage{ Type: pb.ElementMessageType_TOO_SLOW, MovieStatusChanged: &pb.MovieStatusChanged{ Status: &pb.MovieStatus{ diff --git a/server/middlewares/auth.go b/server/middlewares/auth.go index 994c9814..e11deb6c 100644 --- a/server/middlewares/auth.go +++ b/server/middlewares/auth.go @@ -8,6 +8,7 @@ import ( "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/conf" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/server/model" @@ -213,6 +214,15 @@ func AuthRoomMiddleware(ctx *gin.Context) { ctx.Set("user", userE) ctx.Set("room", roomE) + log := ctx.MustGet("log").(*logrus.Entry) + if log.Data == nil { + log.Data = make(logrus.Fields, 5) + } + log.Data["rid"] = room.ID + log.Data["rnm"] = room.Name + log.Data["uid"] = user.ID + log.Data["unm"] = user.Username + log.Data["uro"] = user.Role.String() ctx.Next() } @@ -222,21 +232,29 @@ func AuthUserMiddleware(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err)) return } - user, err := AuthUser(token) + userE, err := AuthUser(token) if err != nil { ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err)) return } - if user.Value().IsBanned() { + user := userE.Value() + if user.IsBanned() { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user banned")) return } - if user.Value().IsPending() { + if user.IsPending() { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is pending, need admin to approve")) return } - ctx.Set("user", user) + ctx.Set("user", userE) + log := ctx.MustGet("log").(*logrus.Entry) + if log.Data == nil { + log.Data = make(logrus.Fields, 3) + } + log.Data["uid"] = user.ID + log.Data["unm"] = user.Username + log.Data["uro"] = user.Role.String() ctx.Next() } @@ -246,17 +264,25 @@ func AuthAdminMiddleware(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err)) return } - user, err := AuthUser(token) + userE, err := AuthUser(token) if err != nil { ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err)) return } - if !user.Value().IsAdmin() { + user := userE.Value() + if !user.IsAdmin() { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not admin")) return } - ctx.Set("user", user) + ctx.Set("user", userE) + log := ctx.MustGet("log").(*logrus.Entry) + if log.Data == nil { + log.Data = make(logrus.Fields, 3) + } + log.Data["uid"] = user.ID + log.Data["unm"] = user.Username + log.Data["uro"] = user.Role.String() ctx.Next() } @@ -266,17 +292,25 @@ func AuthRootMiddleware(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err)) return } - user, err := AuthUser(token) + userE, err := AuthUser(token) if err != nil { ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(err)) return } - if !user.Value().IsRoot() { + user := userE.Value() + if !user.IsRoot() { ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not root")) return } - ctx.Set("user", user) + ctx.Set("user", userE) + log := ctx.MustGet("log").(*logrus.Entry) + if log.Data == nil { + log.Data = make(logrus.Fields, 3) + } + log.Data["uid"] = user.ID + log.Data["unm"] = user.Username + log.Data["uro"] = user.Role.String() ctx.Next() } diff --git a/server/middlewares/init.go b/server/middlewares/init.go index b4aa9e5c..8fe627a1 100644 --- a/server/middlewares/init.go +++ b/server/middlewares/init.go @@ -13,7 +13,8 @@ func Init(e *gin.Engine) { w := log.StandardLogger().Writer() e. Use(gin.LoggerWithWriter(w), gin.RecoveryWithWriter(w)). - Use(NewCors()) + Use(NewCors()). + Use(NewLog(log.StandardLogger())) if conf.Conf.RateLimit.Enable { d, err := time.ParseDuration(conf.Conf.RateLimit.Period) if err != nil { diff --git a/server/middlewares/log.go b/server/middlewares/log.go new file mode 100644 index 00000000..2133ceab --- /dev/null +++ b/server/middlewares/log.go @@ -0,0 +1,14 @@ +package middlewares + +import ( + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" +) + +func NewLog(l *logrus.Logger) gin.HandlerFunc { + return func(ctx *gin.Context) { + ctx.Set("log", &logrus.Entry{ + Logger: l, + }) + } +} diff --git a/server/model/movie.go b/server/model/movie.go index 31f47c08..410c6c69 100644 --- a/server/model/movie.go +++ b/server/model/movie.go @@ -145,7 +145,7 @@ func (s *SwapMovieReq) Validate() error { return nil } -type MoviesResp struct { +type MovieResp struct { Id string `json:"id"` CreatedAt int64 `json:"createAt"` Base model.BaseMovie `json:"base"` @@ -155,6 +155,6 @@ type MoviesResp struct { type CurrentMovieResp struct { Status op.Status `json:"status"` - Movie MoviesResp `json:"movie"` + Movie *MovieResp `json:"movie"` ExpireId uint64 `json:"expireId"` } diff --git a/server/oauth2/auth.go b/server/oauth2/auth.go index cc8bec5e..e8c411cf 100644 --- a/server/oauth2/auth.go +++ b/server/oauth2/auth.go @@ -6,6 +6,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" @@ -21,8 +22,11 @@ import ( // GET // /oauth2/login/:type func OAuth2(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + pi, err := providers.GetProvider(provider.OAuth2Provider(ctx.Param("type"))) if err != nil { + log.Errorf("failed to get provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -30,18 +34,25 @@ func OAuth2(ctx *gin.Context) { state := utils.RandString(16) states.Store(state, newAuthFunc(ctx.Query("redirect")), time.Minute*5) - RenderRedirect(ctx, pi.NewAuthURL(state)) + err = RenderRedirect(ctx, pi.NewAuthURL(state)) + if err != nil { + log.Errorf("failed to render redirect: %v", err) + } } // POST func OAuth2Api(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + pi, err := providers.GetProvider(provider.OAuth2Provider(ctx.Param("type"))) if err != nil { + log.Errorf("failed to get provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) } meta := model.OAuth2Req{} if err := model.Decode(ctx, &meta); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -57,20 +68,25 @@ func OAuth2Api(ctx *gin.Context) { // GET // /oauth2/callback/:type func OAuth2Callback(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + code := ctx.Query("code") if code == "" { + log.Errorf("invalid oauth2 code") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("invalid oauth2 code")) return } pi, err := providers.GetProvider(provider.OAuth2Provider(ctx.Param("type"))) if err != nil { + log.Errorf("failed to get provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } meta, loaded := states.LoadAndDelete(ctx.Query("state")) if !loaded { + log.Errorf("invalid oauth2 state") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("invalid oauth2 state")) return } @@ -78,6 +94,7 @@ func OAuth2Callback(ctx *gin.Context) { if meta.Value() != nil { meta.Value()(ctx, pi, code) } else { + log.Errorf("invalid oauth2 handler") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("invalid oauth2 handler")) } } @@ -85,19 +102,24 @@ func OAuth2Callback(ctx *gin.Context) { // POST // /oauth2/callback/:type func OAuth2CallbackApi(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + req := model.OAuth2CallbackReq{} if err := req.Decode(ctx); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } pi, err := providers.GetProvider(provider.OAuth2Provider(ctx.Param("type"))) if err != nil { + log.Errorf("failed to get provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) } meta, loaded := states.LoadAndDelete(req.State) if !loaded { + log.Errorf("invalid oauth2 state") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("invalid oauth2 state")) return } @@ -105,26 +127,32 @@ func OAuth2CallbackApi(ctx *gin.Context) { if meta.Value() != nil { meta.Value()(ctx, pi, req.Code) } else { + log.Errorf("invalid oauth2 handler") ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorStringResp("invalid oauth2 handler")) } } func newAuthFunc(redirect string) stateHandler { return func(ctx *gin.Context, pi provider.ProviderInterface, code string) { + log := ctx.MustGet("log").(*logrus.Entry) + t, err := pi.GetToken(ctx, code) if err != nil { + log.Errorf("failed to get token: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } ui, err := pi.GetUserInfo(ctx, t) if err != nil { + log.Errorf("failed to get user info: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } pgs, loaded := bootstrap.ProviderGroupSettings[dbModel.SettingGroup(fmt.Sprintf("%s_%s", dbModel.SettingGroupOauth2, pi.Provider()))] if !loaded { + log.Errorf("invalid oauth2 provider") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("invalid oauth2 provider")) return } @@ -140,18 +168,23 @@ func newAuthFunc(redirect string) stateHandler { } } if err != nil { + log.Errorf("failed to create or load user: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } token, err := middlewares.NewAuthUserToken(user.Value()) if err != nil { + log.Errorf("failed to generate token: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } if ctx.Request.Method == http.MethodGet { - RenderToken(ctx, redirect, token) + err = RenderToken(ctx, redirect, token) + if err != nil { + log.Errorf("failed to render token: %v", err) + } } else if ctx.Request.Method == http.MethodPost { ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ "token": token, diff --git a/server/oauth2/bind.go b/server/oauth2/bind.go index 53dcec61..c2b43db0 100644 --- a/server/oauth2/bind.go +++ b/server/oauth2/bind.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/provider" @@ -16,14 +17,17 @@ import ( func BindApi(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) pi, err := providers.GetProvider(provider.OAuth2Provider(ctx.Param("type"))) if err != nil { + log.Errorf("failed to get provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) } meta := model.OAuth2Req{} if err := model.Decode(ctx, &meta); err != nil { + log.Errorf("failed to decode request: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -38,15 +42,18 @@ func BindApi(ctx *gin.Context) { func UnBindApi(ctx *gin.Context) { user := ctx.MustGet("user").(*op.UserEntry).Value() + log := ctx.MustGet("log").(*logrus.Entry) pi, err := providers.GetProvider(provider.OAuth2Provider(ctx.Param("type"))) if err != nil { + log.Errorf("failed to get provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } err = db.UnBindProvider(user.ID, pi.Provider()) if err != nil { + log.Errorf("failed to unbind provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } @@ -56,32 +63,39 @@ func UnBindApi(ctx *gin.Context) { func newBindFunc(userID, redirect string) stateHandler { return func(ctx *gin.Context, pi provider.ProviderInterface, code string) { + log := ctx.MustGet("log").(*logrus.Entry) + t, err := pi.GetToken(ctx, code) if err != nil { + log.Errorf("failed to get token: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } ui, err := pi.GetUserInfo(ctx, t) if err != nil { + log.Errorf("failed to get user info: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } user, err := op.LoadOrInitUserByID(userID) if err != nil { + log.Errorf("failed to load user: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } err = user.Value().BindProvider(pi.Provider(), ui.ProviderUserID) if err != nil { + log.Errorf("failed to bind provider: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } token, err := middlewares.NewAuthUserToken(user.Value()) if err != nil { + log.Errorf("failed to generate token: %v", err) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } diff --git a/server/oauth2/oauth2.go b/server/oauth2/oauth2.go index f74e5ccf..0ddd7a04 100644 --- a/server/oauth2/oauth2.go +++ b/server/oauth2/oauth2.go @@ -4,13 +4,17 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/bootstrap" "github.com/synctv-org/synctv/server/model" ) func OAuth2EnabledApi(ctx *gin.Context) { + log := ctx.MustGet("log").(*logrus.Entry) + data, err := bootstrap.Oauth2EnabledCache.Get(ctx) if err != nil { + log.Errorf("failed to get oauth2 enabled: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) return }