Optimize SQL requests of videos AP endpoints

This commit is contained in:
Chocobozzz 2018-09-19 10:16:44 +02:00
parent ad76628b17
commit 96f29c0f6d
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
7 changed files with 56 additions and 44 deletions

View File

@ -6,7 +6,13 @@ import { CONFIG, ROUTE_CACHE_LIFETIME } from '../../initializers'
import { buildAnnounceWithVideoAudience } from '../../lib/activitypub/send' import { buildAnnounceWithVideoAudience } from '../../lib/activitypub/send'
import { audiencify, getAudience } from '../../lib/activitypub/audience' import { audiencify, getAudience } from '../../lib/activitypub/audience'
import { buildCreateActivity } from '../../lib/activitypub/send/send-create' import { buildCreateActivity } from '../../lib/activitypub/send/send-create'
import { asyncMiddleware, executeIfActivityPub, localAccountValidator, localVideoChannelValidator } from '../../middlewares' import {
asyncMiddleware,
executeIfActivityPub,
localAccountValidator,
localVideoChannelValidator,
videosCustomGetValidator
} from '../../middlewares'
import { videosGetValidator, videosShareValidator } from '../../middlewares/validators' import { videosGetValidator, videosShareValidator } from '../../middlewares/validators'
import { videoCommentGetValidator } from '../../middlewares/validators/video-comments' import { videoCommentGetValidator } from '../../middlewares/validators/video-comments'
import { AccountModel } from '../../models/account/account' import { AccountModel } from '../../models/account/account'
@ -54,7 +60,7 @@ activityPubClientRouter.get('/videos/watch/:id/activity',
executeIfActivityPub(asyncMiddleware(videoController)) executeIfActivityPub(asyncMiddleware(videoController))
) )
activityPubClientRouter.get('/videos/watch/:id/announces', activityPubClientRouter.get('/videos/watch/:id/announces',
executeIfActivityPub(asyncMiddleware(videosGetValidator)), executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))),
executeIfActivityPub(asyncMiddleware(videoAnnouncesController)) executeIfActivityPub(asyncMiddleware(videoAnnouncesController))
) )
activityPubClientRouter.get('/videos/watch/:id/announces/:accountId', activityPubClientRouter.get('/videos/watch/:id/announces/:accountId',
@ -62,15 +68,15 @@ activityPubClientRouter.get('/videos/watch/:id/announces/:accountId',
executeIfActivityPub(asyncMiddleware(videoAnnounceController)) executeIfActivityPub(asyncMiddleware(videoAnnounceController))
) )
activityPubClientRouter.get('/videos/watch/:id/likes', activityPubClientRouter.get('/videos/watch/:id/likes',
executeIfActivityPub(asyncMiddleware(videosGetValidator)), executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))),
executeIfActivityPub(asyncMiddleware(videoLikesController)) executeIfActivityPub(asyncMiddleware(videoLikesController))
) )
activityPubClientRouter.get('/videos/watch/:id/dislikes', activityPubClientRouter.get('/videos/watch/:id/dislikes',
executeIfActivityPub(asyncMiddleware(videosGetValidator)), executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))),
executeIfActivityPub(asyncMiddleware(videoDislikesController)) executeIfActivityPub(asyncMiddleware(videoDislikesController))
) )
activityPubClientRouter.get('/videos/watch/:id/comments', activityPubClientRouter.get('/videos/watch/:id/comments',
executeIfActivityPub(asyncMiddleware(videosGetValidator)), executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))),
executeIfActivityPub(asyncMiddleware(videoCommentsController)) executeIfActivityPub(asyncMiddleware(videoCommentsController))
) )
activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId', activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId',

View File

@ -152,7 +152,8 @@ function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: Use
return true return true
} }
async function isVideoExist (id: string, res: Response, fetchType: 'all' | 'only-video' | 'id' | 'none' = 'all') { export type VideoFetchType = 'all' | 'only-video' | 'id' | 'none'
async function isVideoExist (id: string, res: Response, fetchType: VideoFetchType = 'all') {
let video: VideoModel | null let video: VideoModel | null
if (fetchType === 'all') { if (fetchType === 'all') {

View File

@ -61,7 +61,7 @@ function fetchRemoteVideoStaticFile (video: VideoModel, path: string, reject: Fu
async function fetchRemoteVideoDescription (video: VideoModel) { async function fetchRemoteVideoDescription (video: VideoModel) {
const host = video.VideoChannel.Account.Actor.Server.host const host = video.VideoChannel.Account.Actor.Server.host
const path = video.getDescriptionPath() const path = video.getDescriptionAPIPath()
const options = { const options = {
uri: REMOTE_SCHEME.HTTP + '://' + host + path, uri: REMOTE_SCHEME.HTTP + '://' + host + path,
json: true json: true

View File

@ -78,7 +78,7 @@ const videoCommentGetValidator = [
logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params }) logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params })
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await isVideoExist(req.params.videoId, res)) return if (!await isVideoExist(req.params.videoId, res, 'id')) return
if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return
return next() return next()

View File

@ -26,7 +26,8 @@ import {
isVideoPrivacyValid, isVideoPrivacyValid,
isVideoRatingTypeValid, isVideoRatingTypeValid,
isVideoSupportValid, isVideoSupportValid,
isVideoTagsValid isVideoTagsValid,
VideoFetchType
} from '../../helpers/custom-validators/videos' } from '../../helpers/custom-validators/videos'
import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
@ -128,47 +129,49 @@ const videosUpdateValidator = getCommonVideoAttributes().concat([
} }
]) ])
const videosGetValidator = [ const videosCustomGetValidator = (fetchType: VideoFetchType) => {
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), return [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videosGet parameters', { parameters: req.params }) logger.debug('Checking videosGet parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await isVideoExist(req.params.id, res)) return if (!await isVideoExist(req.params.id, res, fetchType)) return
const video: VideoModel = res.locals.video const video: VideoModel = res.locals.video
// Video private or blacklisted // Video private or blacklisted
if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) {
return authenticate(req, res, () => { return authenticate(req, res, () => {
const user: UserModel = res.locals.oauth.token.User const user: UserModel = res.locals.oauth.token.User
// Only the owner or a user that have blacklist rights can see the video // Only the owner or a user that have blacklist rights can see the video
if (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) { if (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) {
return res.status(403) return res.status(403)
.json({ error: 'Cannot get this private or blacklisted video.' }) .json({ error: 'Cannot get this private or blacklisted video.' })
.end() .end()
} }
return next() return next()
}) })
}
return // Video is public, anyone can access it
if (video.privacy === VideoPrivacy.PUBLIC) return next()
// Video is unlisted, check we used the uuid to fetch it
if (video.privacy === VideoPrivacy.UNLISTED) {
if (isUUIDValid(req.params.id)) return next()
// Don't leak this unlisted video
return res.status(404).end()
}
} }
]
}
// Video is public, anyone can access it const videosGetValidator = videosCustomGetValidator('all')
if (video.privacy === VideoPrivacy.PUBLIC) return next()
// Video is unlisted, check we used the uuid to fetch it
if (video.privacy === VideoPrivacy.UNLISTED) {
if (isUUIDValid(req.params.id)) return next()
// Don't leak this unlisted video
return res.status(404).end()
}
}
]
const videosRemoveValidator = [ const videosRemoveValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
@ -366,6 +369,7 @@ export {
videosAddValidator, videosAddValidator,
videosUpdateValidator, videosUpdateValidator,
videosGetValidator, videosGetValidator,
videosCustomGetValidator,
videosRemoveValidator, videosRemoveValidator,
videosShareValidator, videosShareValidator,

View File

@ -112,12 +112,13 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails {
} }
}) })
const tags = video.Tags ? video.Tags.map(t => t.name) : []
const detailsJson = { const detailsJson = {
support: video.support, support: video.support,
descriptionPath: video.getDescriptionPath(), descriptionPath: video.getDescriptionAPIPath(),
channel: video.VideoChannel.toFormattedJSON(), channel: video.VideoChannel.toFormattedJSON(),
account: video.VideoChannel.Account.toFormattedJSON(), account: video.VideoChannel.Account.toFormattedJSON(),
tags: video.Tags.map(t => t.name), tags,
commentsEnabled: video.commentsEnabled, commentsEnabled: video.commentsEnabled,
waitTranscoding: video.waitTranscoding, waitTranscoding: video.waitTranscoding,
state: { state: {

View File

@ -1406,7 +1406,7 @@ export class VideoModel extends Model<VideoModel> {
return getVideoFileResolution(originalFilePath) return getVideoFileResolution(originalFilePath)
} }
getDescriptionPath () { getDescriptionAPIPath () {
return `/api/${API_VERSION}/videos/${this.uuid}/description` return `/api/${API_VERSION}/videos/${this.uuid}/description`
} }