From 91411dba928678c15a5e99d9795ae061909e397d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 20 Sep 2018 10:13:13 +0200 Subject: [PATCH] Limit associations fetch when loading token --- server/controllers/api/users/index.ts | 16 ++--------- server/controllers/api/users/me.ts | 28 ++++++++----------- server/controllers/api/video-channel.ts | 9 +++--- server/controllers/api/videos/abuse.ts | 23 +++++++-------- server/controllers/api/videos/comment.ts | 14 ++++++---- server/controllers/api/videos/ownership.ts | 13 +++++---- server/controllers/api/videos/rate.ts | 11 ++++---- server/lib/activitypub/videos.ts | 4 +-- .../schedulers/youtube-dl-update-scheduler.ts | 7 ----- server/models/account/account.ts | 4 +-- server/models/oauth/oauth-token.ts | 26 +++++++++++------ 11 files changed, 75 insertions(+), 80 deletions(-) diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index a299167e8..d1163900b 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -267,15 +267,9 @@ async function updateUser (req: express.Request, res: express.Response, next: ex const user = await userToUpdate.save() // Destroy user token to refresh rights - if (roleChanged) { - await OAuthTokenModel.deleteUserToken(userToUpdate.id) - } + if (roleChanged) await OAuthTokenModel.deleteUserToken(userToUpdate.id) - auditLogger.update( - getAuditIdFromRes(res), - new UserAuditView(user.toFormattedJSON()), - oldUserAuditView - ) + auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) // Don't need to send this update to followers, these attributes are not propagated @@ -343,9 +337,5 @@ async function changeUserBlock (res: express.Response, user: UserModel, block: b await Emailer.Instance.addUserBlockJob(user, block, reason) - auditLogger.update( - getAuditIdFromRes(res), - new UserAuditView(user.toFormattedJSON()), - oldUserAuditView - ) + auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) } diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index d4b7e3715..eba1e7edd 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -38,6 +38,7 @@ import { VideoFilter } from '../../../../shared/models/videos/video-query.type' import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { JobQueue } from '../../../lib/job-queue' import { logger } from '../../../helpers/logger' +import { AccountModel } from '../../../models/account/account' const auditLogger = auditLoggerFactory('users-me') @@ -329,19 +330,17 @@ async function updateMe (req: express.Request, res: express.Response, next: expr if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo await sequelizeTypescript.transaction(async t => { + const userAccount = await AccountModel.load(user.Account.id) + await user.save({ transaction: t }) - if (body.displayName !== undefined) user.Account.name = body.displayName - if (body.description !== undefined) user.Account.description = body.description - await user.Account.save({ transaction: t }) + if (body.displayName !== undefined) userAccount.name = body.displayName + if (body.description !== undefined) userAccount.description = body.description + await userAccount.save({ transaction: t }) - await sendUpdateActor(user.Account, t) + await sendUpdateActor(userAccount, t) - auditLogger.update( - getAuditIdFromRes(res), - new UserAuditView(user.toFormattedJSON()), - oldUserAuditView - ) + auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) }) return res.sendStatus(204) @@ -351,15 +350,12 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] const user: UserModel = res.locals.oauth.token.user const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) - const account = user.Account - const avatar = await updateActorAvatarFile(avatarPhysicalFile, account.Actor, account) + const userAccount = await AccountModel.load(user.Account.id) - auditLogger.update( - getAuditIdFromRes(res), - new UserAuditView(user.toFormattedJSON()), - oldUserAuditView - ) + const avatar = await updateActorAvatarFile(avatarPhysicalFile, userAccount.Actor, userAccount) + + auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) return res.json({ avatar: avatar.toFormattedJSON() }) } diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 50dc44f7c..8fc340224 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -29,6 +29,7 @@ import { updateAvatarValidator } from '../../middlewares/validators/avatar' import { updateActorAvatarFile } from '../../lib/avatar' import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' import { resetSequelizeInstance } from '../../helpers/database-utils' +import { UserModel } from '../../models/account/user' const auditLogger = auditLoggerFactory('channels') const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) @@ -123,19 +124,17 @@ async function updateVideoChannelAvatar (req: express.Request, res: express.Resp async function addVideoChannel (req: express.Request, res: express.Response) { const videoChannelInfo: VideoChannelCreate = req.body - const account: AccountModel = res.locals.oauth.token.User.Account const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { + const account = await AccountModel.load((res.locals.oauth.token.User as UserModel).Account.id, t) + return createVideoChannel(videoChannelInfo, account, t) }) setAsyncActorKeys(videoChannelCreated.Actor) .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) - auditLogger.create( - getAuditIdFromRes(res), - new VideoChannelAuditView(videoChannelCreated.toFormattedJSON()) - ) + auditLogger.create(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelCreated.toFormattedJSON())) logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) return res.json({ diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts index 08e11b00b..d0c81804b 100644 --- a/server/controllers/api/videos/abuse.ts +++ b/server/controllers/api/videos/abuse.ts @@ -21,6 +21,7 @@ import { AccountModel } from '../../../models/account/account' import { VideoModel } from '../../../models/video/video' import { VideoAbuseModel } from '../../../models/video/video-abuse' import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' +import { UserModel } from '../../../models/account/user' const auditLogger = auditLoggerFactory('abuse') const abuseVideoRouter = express.Router() @@ -95,17 +96,18 @@ async function deleteVideoAbuse (req: express.Request, res: express.Response) { async function reportVideoAbuse (req: express.Request, res: express.Response) { const videoInstance = res.locals.video as VideoModel - const reporterAccount = res.locals.oauth.token.User.Account as AccountModel const body: VideoAbuseCreate = req.body - const abuseToCreate = { - reporterAccountId: reporterAccount.id, - reason: body.reason, - videoId: videoInstance.id, - state: VideoAbuseState.PENDING - } - const videoAbuse: VideoAbuseModel = await sequelizeTypescript.transaction(async t => { + const reporterAccount = await AccountModel.load((res.locals.oauth.token.User as UserModel).Account.id, t) + + const abuseToCreate = { + reporterAccountId: reporterAccount.id, + reason: body.reason, + videoId: videoInstance.id, + state: VideoAbuseState.PENDING + } + const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t }) videoAbuseInstance.Video = videoInstance videoAbuseInstance.Account = reporterAccount @@ -121,7 +123,6 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) { }) logger.info('Abuse report for video %s created.', videoInstance.name) - return res.json({ - videoAbuse: videoAbuse.toFormattedJSON() - }).end() + + return res.json({ videoAbuse: videoAbuse.toFormattedJSON() }).end() } diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index 40ad54d09..dc25e1e85 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts @@ -24,6 +24,8 @@ import { import { VideoModel } from '../../../models/video/video' import { VideoCommentModel } from '../../../models/video/video-comment' import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' +import { AccountModel } from '../../../models/account/account' +import { UserModel } from '../../../models/account/user' const auditLogger = auditLoggerFactory('comments') const videoCommentRouter = express.Router() @@ -101,11 +103,13 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons const videoCommentInfo: VideoCommentCreate = req.body const comment = await sequelizeTypescript.transaction(async t => { + const account = await AccountModel.load((res.locals.oauth.token.User as UserModel).Account.id, t) + return createVideoComment({ text: videoCommentInfo.text, inReplyToComment: null, video: res.locals.video, - account: res.locals.oauth.token.User.Account + account }, t) }) @@ -120,19 +124,19 @@ async function addVideoCommentReply (req: express.Request, res: express.Response const videoCommentInfo: VideoCommentCreate = req.body const comment = await sequelizeTypescript.transaction(async t => { + const account = await AccountModel.load((res.locals.oauth.token.User as UserModel).Account.id, t) + return createVideoComment({ text: videoCommentInfo.text, inReplyToComment: res.locals.videoComment, video: res.locals.video, - account: res.locals.oauth.token.User.Account + account }, t) }) auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON())) - return res.json({ - comment: comment.toFormattedJSON() - }).end() + return res.json({ comment: comment.toFormattedJSON() }).end() } async function removeVideoComment (req: express.Request, res: express.Response) { diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index d26ed6cfc..5ea7d7c6a 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts @@ -19,6 +19,7 @@ import { VideoChannelModel } from '../../../models/video/video-channel' import { getFormattedObjects } from '../../../helpers/utils' import { changeVideoChannelShare } from '../../../lib/activitypub' import { sendUpdateVideo } from '../../../lib/activitypub/send' +import { UserModel } from '../../../models/account/user' const ownershipVideoRouter = express.Router() @@ -58,26 +59,25 @@ export { async function giveVideoOwnership (req: express.Request, res: express.Response) { const videoInstance = res.locals.video as VideoModel - const initiatorAccount = res.locals.oauth.token.User.Account as AccountModel + const initiatorAccountId = (res.locals.oauth.token.User as UserModel).Account.id const nextOwner = res.locals.nextOwner as AccountModel await sequelizeTypescript.transaction(t => { return VideoChangeOwnershipModel.findOrCreate({ where: { - initiatorAccountId: initiatorAccount.id, + initiatorAccountId, nextOwnerAccountId: nextOwner.id, videoId: videoInstance.id, status: VideoChangeOwnershipStatus.WAITING }, defaults: { - initiatorAccountId: initiatorAccount.id, + initiatorAccountId, nextOwnerAccountId: nextOwner.id, videoId: videoInstance.id, status: VideoChangeOwnershipStatus.WAITING }, transaction: t }) - }) logger.info('Ownership change for video %s created.', videoInstance.name) @@ -85,9 +85,10 @@ async function giveVideoOwnership (req: express.Request, res: express.Response) } async function listVideoOwnership (req: express.Request, res: express.Response) { - const currentAccount = res.locals.oauth.token.User.Account as AccountModel + const currentAccountId = (res.locals.oauth.token.User as UserModel).Account.id + const resultList = await VideoChangeOwnershipModel.listForApi( - currentAccount.id, + currentAccountId, req.query.start || 0, req.query.count || 10, req.query.sort || 'createdAt' diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts index b1732837d..dc322bb0c 100644 --- a/server/controllers/api/videos/rate.ts +++ b/server/controllers/api/videos/rate.ts @@ -28,10 +28,11 @@ async function rateVideo (req: express.Request, res: express.Response) { const body: UserVideoRateUpdate = req.body const rateType = body.rating const videoInstance: VideoModel = res.locals.video - const accountInstance: AccountModel = res.locals.oauth.token.User.Account await sequelizeTypescript.transaction(async t => { const sequelizeOptions = { transaction: t } + + const accountInstance = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) const previousRate = await AccountVideoRateModel.load(accountInstance.id, videoInstance.id, t) let likesToIncrement = 0 @@ -47,10 +48,10 @@ async function rateVideo (req: express.Request, res: express.Response) { else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement-- if (rateType === 'none') { // Destroy previous rate - await previousRate.destroy({ transaction: t }) + await previousRate.destroy(sequelizeOptions) } else { // Update previous rate previousRate.type = rateType - await previousRate.save({ transaction: t }) + await previousRate.save(sequelizeOptions) } } else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate const query = { @@ -70,9 +71,9 @@ async function rateVideo (req: express.Request, res: express.Response) { await videoInstance.increment(incrementQuery, sequelizeOptions) await sendVideoRateChange(accountInstance, videoInstance, likesToIncrement, dislikesToIncrement, t) - }) - logger.info('Account video rate for video %s of account %s updated.', videoInstance.name, accountInstance.name) + logger.info('Account video rate for video %s of account %s updated.', videoInstance.name, accountInstance.name) + }) return res.type('json').status(204).end() } diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 91231a187..48c0e0a5c 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts @@ -354,11 +354,11 @@ async function refreshVideoIfNeeded (options: { syncParam: SyncParam, refreshViews: boolean }): Promise { + if (!options.video.isOutdated()) return options.video + // We need more attributes if the argument video was fetched with not enough joints const video = options.fetchedType === 'all' ? options.video : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) - if (!video.isOutdated()) return video - try { const { response, videoObject } = await fetchRemoteVideo(video.url) if (response.statusCode === 404) { diff --git a/server/lib/schedulers/youtube-dl-update-scheduler.ts b/server/lib/schedulers/youtube-dl-update-scheduler.ts index 2fc8950fe..461cd045e 100644 --- a/server/lib/schedulers/youtube-dl-update-scheduler.ts +++ b/server/lib/schedulers/youtube-dl-update-scheduler.ts @@ -1,12 +1,5 @@ - - import { AbstractScheduler } from './abstract-scheduler' import { SCHEDULER_INTERVALS_MS } from '../../initializers' -import { logger } from '../../helpers/logger' -import * as request from 'request' -import { createWriteStream, ensureDir, writeFile } from 'fs-extra' -import { join } from 'path' -import { root } from '../../helpers/core-utils' import { updateYoutubeDLBinary } from '../../helpers/youtube-dl' export class YoutubeDlUpdateScheduler extends AbstractScheduler { diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 6bbfc6f4e..580d920ce 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -134,8 +134,8 @@ export class AccountModel extends Model { return undefined } - static load (id: number) { - return AccountModel.findById(id) + static load (id: number, transaction?: Sequelize.Transaction) { + return AccountModel.findById(id, { transaction }) } static loadByUUID (uuid: string) { diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts index 4c53848dc..1dd5e0289 100644 --- a/server/models/oauth/oauth-token.ts +++ b/server/models/oauth/oauth-token.ts @@ -1,9 +1,10 @@ import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { logger } from '../../helpers/logger' -import { AccountModel } from '../account/account' import { UserModel } from '../account/user' import { OAuthClientModel } from './oauth-client' import { Transaction } from 'sequelize' +import { AccountModel } from '../account/account' +import { ActorModel } from '../activitypub/actor' export type OAuthTokenInfo = { refreshToken: string @@ -17,18 +18,27 @@ export type OAuthTokenInfo = { } enum ScopeNames { - WITH_ACCOUNT = 'WITH_ACCOUNT' + WITH_USER = 'WITH_USER' } @Scopes({ - [ScopeNames.WITH_ACCOUNT]: { + [ScopeNames.WITH_USER]: { include: [ { - model: () => UserModel, + model: () => UserModel.unscoped(), + required: true, include: [ { - model: () => AccountModel, - required: true + attributes: [ 'id' ], + model: () => AccountModel.unscoped(), + required: true, + include: [ + { + attributes: [ 'id' ], + model: () => ActorModel.unscoped(), + required: true + } + ] } ] } @@ -138,7 +148,7 @@ export class OAuthTokenModel extends Model { } } - return OAuthTokenModel.scope(ScopeNames.WITH_ACCOUNT).findOne(query).then(token => { + return OAuthTokenModel.scope(ScopeNames.WITH_USER).findOne(query).then(token => { if (token) token['user'] = token.User return token @@ -152,7 +162,7 @@ export class OAuthTokenModel extends Model { } } - return OAuthTokenModel.scope(ScopeNames.WITH_ACCOUNT) + return OAuthTokenModel.scope(ScopeNames.WITH_USER) .findOne(query) .then(token => { if (token) { -- 2.25.1