From 418d092afa81e2c8fe8ac6838fc4b5eb0af6a782 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 26 Feb 2019 10:55:40 +0100 Subject: Playlist server API --- .../validators/videos/video-playlists.ts | 302 +++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 server/middlewares/validators/videos/video-playlists.ts (limited to 'server/middlewares/validators/videos/video-playlists.ts') diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts new file mode 100644 index 000000000..ef8d0b851 --- /dev/null +++ b/server/middlewares/validators/videos/video-playlists.ts @@ -0,0 +1,302 @@ +import * as express from 'express' +import { body, param, ValidationChain } from 'express-validator/check' +import { UserRight, VideoPrivacy } from '../../../../shared' +import { logger } from '../../../helpers/logger' +import { UserModel } from '../../../models/account/user' +import { areValidationErrors } from '../utils' +import { isVideoExist, isVideoImage } from '../../../helpers/custom-validators/videos' +import { CONSTRAINTS_FIELDS } from '../../../initializers' +import { isIdOrUUIDValid, toValueOrNull } from '../../../helpers/custom-validators/misc' +import { + isVideoPlaylistDescriptionValid, + isVideoPlaylistExist, + isVideoPlaylistNameValid, + isVideoPlaylistPrivacyValid +} from '../../../helpers/custom-validators/video-playlists' +import { VideoPlaylistModel } from '../../../models/video/video-playlist' +import { cleanUpReqFiles } from '../../../helpers/express-utils' +import { isVideoChannelIdExist } from '../../../helpers/custom-validators/video-channels' +import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' +import { VideoModel } from '../../../models/video/video' +import { authenticatePromiseIfNeeded } from '../../oauth' +import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' + +const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsAddValidator parameters', { parameters: req.body }) + + if (areValidationErrors(req, res)) return cleanUpReqFiles(req) + + if (req.body.videoChannelId && !await isVideoChannelIdExist(req.body.videoChannelId, res)) return cleanUpReqFiles(req) + + return next() + } +]) + +const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsUpdateValidator parameters', { parameters: req.body }) + + if (areValidationErrors(req, res)) return cleanUpReqFiles(req) + + if (!await isVideoPlaylistExist(req.params.playlistId, res)) return cleanUpReqFiles(req) + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { + return cleanUpReqFiles(req) + } + + if (req.body.videoChannelId && !await isVideoChannelIdExist(req.body.videoChannelId, res)) return cleanUpReqFiles(req) + + return next() + } +]) + +const videoPlaylistsDeleteValidator = [ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsDeleteValidator parameters', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + + if (!await isVideoPlaylistExist(req.params.playlistId, res)) return + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { + return + } + + return next() + } +] + +const videoPlaylistsGetValidator = [ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + + if (!await isVideoPlaylistExist(req.params.playlistId, res)) return + + const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist + if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { + await authenticatePromiseIfNeeded(req, res) + + const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : null + + if ( + !user || + (videoPlaylist.OwnerAccount.userId !== user.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) + ) { + return res.status(403) + .json({ error: 'Cannot get this private video playlist.' }) + } + + return next() + } + + return next() + } +] + +const videoPlaylistsAddVideoValidator = [ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + body('videoId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid video id/uuid'), + body('startTimestamp') + .optional() + .isInt({ min: 0 }).withMessage('Should have a valid start timestamp'), + body('stopTimestamp') + .optional() + .isInt({ min: 0 }).withMessage('Should have a valid stop timestamp'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsAddVideoValidator parameters', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + + if (!await isVideoPlaylistExist(req.params.playlistId, res)) return + if (!await isVideoExist(req.body.videoId, res, 'id')) return + + const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist + const video: VideoModel = res.locals.video + + const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id) + if (videoPlaylistElement) { + res.status(409) + .json({ error: 'This video in this playlist already exists' }) + .end() + + return + } + + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { + return + } + + return next() + } +] + +const videoPlaylistsUpdateOrRemoveVideoValidator = [ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + param('videoId') + .custom(isIdOrUUIDValid).withMessage('Should have an video id/uuid'), + body('startTimestamp') + .optional() + .isInt({ min: 0 }).withMessage('Should have a valid start timestamp'), + body('stopTimestamp') + .optional() + .isInt({ min: 0 }).withMessage('Should have a valid stop timestamp'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsRemoveVideoValidator parameters', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + + if (!await isVideoPlaylistExist(req.params.playlistId, res)) return + if (!await isVideoExist(req.params.playlistId, res, 'id')) return + + const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist + const video: VideoModel = res.locals.video + + const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id) + if (!videoPlaylistElement) { + res.status(404) + .json({ error: 'Video playlist element not found' }) + .end() + + return + } + res.locals.videoPlaylistElement = videoPlaylistElement + + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return + + return next() + } +] + +const videoPlaylistElementAPGetValidator = [ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + param('videoId') + .custom(isIdOrUUIDValid).withMessage('Should have an video id/uuid'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistElementAPGetValidator parameters', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + + const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideoForAP(req.params.playlistId, req.params.videoId) + if (!videoPlaylistElement) { + res.status(404) + .json({ error: 'Video playlist element not found' }) + .end() + + return + } + + if (videoPlaylistElement.VideoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { + return res.status(403).end() + } + + res.locals.videoPlaylistElement = videoPlaylistElement + + return next() + } +] + +const videoPlaylistsReorderVideosValidator = [ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + body('startPosition') + .isInt({ min: 1 }).withMessage('Should have a valid start position'), + body('insertAfterPosition') + .isInt({ min: 0 }).withMessage('Should have a valid insert after position'), + body('reorderLength') + .optional() + .isInt({ min: 1 }).withMessage('Should have a valid range length'), + + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsReorderVideosValidator parameters', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + + if (!await isVideoPlaylistExist(req.params.playlistId, res)) return + + const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return + + return next() + } +] + +// --------------------------------------------------------------------------- + +export { + videoPlaylistsAddValidator, + videoPlaylistsUpdateValidator, + videoPlaylistsDeleteValidator, + videoPlaylistsGetValidator, + + videoPlaylistsAddVideoValidator, + videoPlaylistsUpdateOrRemoveVideoValidator, + videoPlaylistsReorderVideosValidator, + + videoPlaylistElementAPGetValidator +} + +// --------------------------------------------------------------------------- + +function getCommonPlaylistEditAttributes () { + return [ + body('thumbnailfile') + .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( + 'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' + + CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.IMAGE.EXTNAME.join(', ') + ), + + body('displayName') + .custom(isVideoPlaylistNameValid).withMessage('Should have a valid display name'), + body('description') + .optional() + .customSanitizer(toValueOrNull) + .custom(isVideoPlaylistDescriptionValid).withMessage('Should have a valid description'), + body('privacy') + .optional() + .toInt() + .custom(isVideoPlaylistPrivacyValid).withMessage('Should have correct playlist privacy'), + body('videoChannelId') + .optional() + .toInt() + ] as (ValidationChain | express.Handler)[] +} + +function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoPlaylistModel, right: UserRight, res: express.Response) { + if (videoPlaylist.isOwned() === false) { + res.status(403) + .json({ error: 'Cannot manage video playlist of another server.' }) + .end() + + return false + } + + // Check if the user can manage the video playlist + // The user can delete it if s/he is an admin + // Or if s/he is the video playlist's owner + if (user.hasRight(right) === false && videoPlaylist.ownerAccountId !== user.Account.id) { + res.status(403) + .json({ error: 'Cannot manage video playlist of another user' }) + .end() + + return false + } + + return true +} -- cgit v1.2.3