diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index daeef22de..12357a2ca 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts @@ -176,7 +176,7 @@ async function removeOrRejectFollower (req: express.Request, res: express.Respon async function acceptFollower (req: express.Request, res: express.Response) { const follow = res.locals.follow - await sendAccept(follow) + sendAccept(follow) follow.state = 'accepted' await follow.save() diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index 2d6ca60a8..1bb96e046 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts @@ -105,9 +105,9 @@ function acceptOwnership (req: express.Request, res: express.Response) { const channel = res.locals.videoChannel // We need more attributes for federation - const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id) + const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id, t) - const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId) + const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId, t) targetVideo.channelId = channel.id diff --git a/server/lib/activitypub/follow.ts b/server/lib/activitypub/follow.ts index 351499bd1..c1bd667e0 100644 --- a/server/lib/activitypub/follow.ts +++ b/server/lib/activitypub/follow.ts @@ -1,12 +1,13 @@ -import { MActorFollowActors } from '../../types/models' +import { Transaction } from 'sequelize' +import { getServerActor } from '@server/models/application/application' +import { logger } from '../../helpers/logger' import { CONFIG } from '../../initializers/config' import { SERVER_ACTOR_NAME } from '../../initializers/constants' -import { JobQueue } from '../job-queue' -import { logger } from '../../helpers/logger' import { ServerModel } from '../../models/server/server' -import { getServerActor } from '@server/models/application/application' +import { MActorFollowActors } from '../../types/models' +import { JobQueue } from '../job-queue' -async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) { +async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors, transaction?: Transaction) { if (!CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED) return const follower = actorFollow.ActorFollower @@ -16,7 +17,7 @@ async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) { const me = await getServerActor() - const server = await ServerModel.load(follower.serverId) + const server = await ServerModel.load(follower.serverId, transaction) const host = server.host const payload = { diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 44f6a87b0..1d2279df5 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts @@ -16,7 +16,6 @@ import { MChannelActor, MCommentOwnerVideo } from '../../../types/models' -import { markCommentAsDeleted } from '../../video-comment' import { forwardVideoRelatedActivity } from '../send/utils' async function processDeleteActivity (options: APProcessorOptions) { @@ -139,11 +138,9 @@ function processDeleteVideoComment (byActor: MActorSignature, videoComment: MCom throw new Error(`Account ${byActor.url} does not own video comment ${videoComment.url} or video ${videoComment.Video.url}`) } - await sequelizeTypescript.transaction(async t => { - markCommentAsDeleted(videoComment) + videoComment.markAsDeleted() - await videoComment.save() - }) + await videoComment.save({ transaction: t }) if (videoComment.Video.isOwned()) { // Don't resend the activity to the sender diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 9009c6469..f85238f8e 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts @@ -43,7 +43,7 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) { logger.info('Rejecting %s because instance followers are disabled.', targetActor.url) - await sendReject(activityId, byActor, targetActor) + sendReject(activityId, byActor, targetActor) return { actorFollow: undefined as MActorFollowActors } } @@ -84,8 +84,9 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ // Target sends to actor he accepted the follow request if (actorFollow.state === 'accepted') { - await sendAccept(actorFollow) - await autoFollowBackIfNeeded(actorFollow) + sendAccept(actorFollow) + + await autoFollowBackIfNeeded(actorFollow, t) } return { actorFollow, created, isFollowingInstance, targetActor } diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index fdb8dac24..d4b2a795f 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts @@ -106,7 +106,7 @@ async function processUndoCacheFile (byActor: MActorSignature, activity: Activit const { video } = await getOrCreateAPVideo({ videoObject: cacheFileObject.object }) return sequelizeTypescript.transaction(async t => { - const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id) + const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t) if (!cacheFile) { logger.debug('Cannot undo unknown video cache %s.', cacheFileObject.id) return @@ -114,7 +114,7 @@ async function processUndoCacheFile (byActor: MActorSignature, activity: Activit if (cacheFile.actorId !== byActor.id) throw new Error('Cannot delete redundancy ' + cacheFile.url + ' of another actor.') - await cacheFile.destroy() + await cacheFile.destroy({ transaction: t }) if (video.isOwned()) { // Don't resend the activity to the sender diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 8d659daa6..8f218ca3a 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts @@ -151,35 +151,31 @@ async function onVideoFileOptimizer ( // Outside the transaction (IO on disk) const { videoFileResolution, isPortraitMode } = await videoArg.getMaxQualityResolution() - const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { - // Maybe the video changed in database, refresh it - const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t) - // Video does not exist anymore - if (!videoDatabase) return undefined + // Maybe the video changed in database, refresh it + const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid) + // Video does not exist anymore + if (!videoDatabase) return undefined - let videoPublished = false + let videoPublished = false - // Generate HLS version of the original file - const originalFileHLSPayload = Object.assign({}, payload, { - isPortraitMode, - resolution: videoDatabase.getMaxQualityFile().resolution, - // If we quick transcoded original file, force transcoding for HLS to avoid some weird playback issues - copyCodecs: transcodeType !== 'quick-transcode', - isMaxQuality: true - }) - const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload) - - const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent') - - if (!hasHls && !hasNewResolutions) { - // No transcoding to do, it's now published - videoPublished = await videoDatabase.publishIfNeededAndSave(t) - } - - await federateVideoIfNeeded(videoDatabase, payload.isNewVideo, t) - - return { videoDatabase, videoPublished } + // Generate HLS version of the original file + const originalFileHLSPayload = Object.assign({}, payload, { + isPortraitMode, + resolution: videoDatabase.getMaxQualityFile().resolution, + // If we quick transcoded original file, force transcoding for HLS to avoid some weird playback issues + copyCodecs: transcodeType !== 'quick-transcode', + isMaxQuality: true }) + const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload) + + const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent') + + if (!hasHls && !hasNewResolutions) { + // No transcoding to do, it's now published + videoPublished = await videoDatabase.publishIfNeededAndSave(undefined) + } + + await federateVideoIfNeeded(videoDatabase, payload.isNewVideo) if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoDatabase) if (videoPublished) Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase) diff --git a/server/lib/moderation.ts b/server/lib/moderation.ts index 0cefe1648..14e00518e 100644 --- a/server/lib/moderation.ts +++ b/server/lib/moderation.ts @@ -221,7 +221,7 @@ async function createAbuse (options: { const { isOwned } = await associateFun(abuseInstance) if (isOwned === false) { - await sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction) + sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction) } const abuseJSON = abuseInstance.toFormattedAdminJSON() diff --git a/server/lib/user.ts b/server/lib/user.ts index a2163abb1..91a682a7e 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -44,11 +44,11 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { displayName: userDisplayName, userId: userCreated.id, applicationId: null, - t: t + t }) userCreated.Account = accountCreated - const channelAttributes = await buildChannelAttributes(userCreated, channelNames) + const channelAttributes = await buildChannelAttributes(userCreated, t, channelNames) const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t) const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) @@ -203,13 +203,13 @@ function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | return UserNotificationSettingModel.create(values, { transaction: t }) } -async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) { +async function buildChannelAttributes (user: MUser, transaction?: Transaction, channelNames?: ChannelNames) { if (channelNames) return channelNames let channelName = user.username + '_channel' // Conflict, generate uuid instead - const actor = await ActorModel.loadLocalByName(channelName) + const actor = await ActorModel.loadLocalByName(channelName, transaction) if (actor) channelName = uuidv4() const videoChannelDisplayName = `Main ${user.username} channel` diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts index 51a9c747e..c76570a5d 100644 --- a/server/lib/video-comment.ts +++ b/server/lib/video-comment.ts @@ -18,9 +18,9 @@ async function removeComment (videoCommentInstance: MCommentOwnerVideo) { await sendDeleteVideoComment(videoCommentInstance, t) } - markCommentAsDeleted(videoCommentInstance) + videoCommentInstance.markAsDeleted() - await videoCommentInstance.save() + await videoCommentInstance.save({ transaction: t }) }) logger.info('Video comment %d deleted.', videoCommentInstance.id) @@ -95,17 +95,10 @@ function buildFormattedCommentTree (resultList: ResultList): return thread } -function markCommentAsDeleted (comment: MComment): void { - comment.text = '' - comment.deletedAt = new Date() - comment.accountId = null -} - // --------------------------------------------------------------------------- export { removeComment, createVideoComment, - buildFormattedCommentTree, - markCommentAsDeleted + buildFormattedCommentTree } diff --git a/server/models/server/server.ts b/server/models/server/server.ts index 25d9924fb..0d3c092e0 100644 --- a/server/models/server/server.ts +++ b/server/models/server/server.ts @@ -1,3 +1,4 @@ +import { Transaction } from 'sequelize' import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { MServer, MServerFormattable } from '@server/types/models/server' import { AttributesOnly } from '@shared/core-utils' @@ -51,11 +52,12 @@ export class ServerModel extends Model>> { }) BlockedByAccounts: ServerBlocklistModel[] - static load (id: number): Promise { + static load (id: number, transaction?: Transaction): Promise { const query = { where: { id - } + }, + transaction } return ServerModel.findOne(query) diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index bbda0f3ee..5ec944b9e 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts @@ -91,9 +91,9 @@ export class VideoCaptionModel extends ModelActor"."serverId" = "Account->Actor->Server"."id"` }) } - static loadAndPopulateAccount (id: number): Promise { + static loadAndPopulateAccount (id: number, transaction?: Transaction): Promise { return VideoChannelModel.unscoped() .scope([ ScopeNames.WITH_ACTOR_BANNER, ScopeNames.WITH_ACCOUNT ]) - .findByPk(id) + .findByPk(id, { transaction }) } static loadByUrlAndPopulateAccount (url: string): Promise { diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index e1faf2b9a..e933989ae 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts @@ -739,6 +739,12 @@ export class VideoCommentModel extends Model