aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-06-15 09:17:19 +0200
committerChocobozzz <me@florianbigard.com>2021-06-15 10:40:06 +0200
commiteae0365b5c5468e51e9795b0e894815ebda86b4a (patch)
tree5da54b28c94d43d1715a0c69e35eb1cf3a1a959d /server
parent51f636ad0f928bb94069c9143e38df0518f6e4a7 (diff)
downloadPeerTube-eae0365b5c5468e51e9795b0e894815ebda86b4a.tar.gz
PeerTube-eae0365b5c5468e51e9795b0e894815ebda86b4a.tar.zst
PeerTube-eae0365b5c5468e51e9795b0e894815ebda86b4a.zip
Fix missing transactions
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/server/follows.ts2
-rw-r--r--server/controllers/api/videos/ownership.ts4
-rw-r--r--server/lib/activitypub/follow.ts13
-rw-r--r--server/lib/activitypub/process/process-delete.ts7
-rw-r--r--server/lib/activitypub/process/process-follow.ts7
-rw-r--r--server/lib/activitypub/process/process-undo.ts4
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts50
-rw-r--r--server/lib/moderation.ts2
-rw-r--r--server/lib/user.ts8
-rw-r--r--server/lib/video-comment.ts13
-rw-r--r--server/models/server/server.ts6
-rw-r--r--server/models/video/video-caption.ts10
-rw-r--r--server/models/video/video-channel.ts4
-rw-r--r--server/models/video/video-comment.ts6
14 files changed, 66 insertions, 70 deletions
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
176async function acceptFollower (req: express.Request, res: express.Response) { 176async function acceptFollower (req: express.Request, res: express.Response) {
177 const follow = res.locals.follow 177 const follow = res.locals.follow
178 178
179 await sendAccept(follow) 179 sendAccept(follow)
180 180
181 follow.state = 'accepted' 181 follow.state = 'accepted'
182 await follow.save() 182 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) {
105 const channel = res.locals.videoChannel 105 const channel = res.locals.videoChannel
106 106
107 // We need more attributes for federation 107 // We need more attributes for federation
108 const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id) 108 const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id, t)
109 109
110 const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId) 110 const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId, t)
111 111
112 targetVideo.channelId = channel.id 112 targetVideo.channelId = channel.id
113 113
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 @@
1import { MActorFollowActors } from '../../types/models' 1import { Transaction } from 'sequelize'
2import { getServerActor } from '@server/models/application/application'
3import { logger } from '../../helpers/logger'
2import { CONFIG } from '../../initializers/config' 4import { CONFIG } from '../../initializers/config'
3import { SERVER_ACTOR_NAME } from '../../initializers/constants' 5import { SERVER_ACTOR_NAME } from '../../initializers/constants'
4import { JobQueue } from '../job-queue'
5import { logger } from '../../helpers/logger'
6import { ServerModel } from '../../models/server/server' 6import { ServerModel } from '../../models/server/server'
7import { getServerActor } from '@server/models/application/application' 7import { MActorFollowActors } from '../../types/models'
8import { JobQueue } from '../job-queue'
8 9
9async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) { 10async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors, transaction?: Transaction) {
10 if (!CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED) return 11 if (!CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED) return
11 12
12 const follower = actorFollow.ActorFollower 13 const follower = actorFollow.ActorFollower
@@ -16,7 +17,7 @@ async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) {
16 17
17 const me = await getServerActor() 18 const me = await getServerActor()
18 19
19 const server = await ServerModel.load(follower.serverId) 20 const server = await ServerModel.load(follower.serverId, transaction)
20 const host = server.host 21 const host = server.host
21 22
22 const payload = { 23 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 {
16 MChannelActor, 16 MChannelActor,
17 MCommentOwnerVideo 17 MCommentOwnerVideo
18} from '../../../types/models' 18} from '../../../types/models'
19import { markCommentAsDeleted } from '../../video-comment'
20import { forwardVideoRelatedActivity } from '../send/utils' 19import { forwardVideoRelatedActivity } from '../send/utils'
21 20
22async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { 21async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
@@ -139,11 +138,9 @@ function processDeleteVideoComment (byActor: MActorSignature, videoComment: MCom
139 throw new Error(`Account ${byActor.url} does not own video comment ${videoComment.url} or video ${videoComment.Video.url}`) 138 throw new Error(`Account ${byActor.url} does not own video comment ${videoComment.url} or video ${videoComment.Video.url}`)
140 } 139 }
141 140
142 await sequelizeTypescript.transaction(async t => { 141 videoComment.markAsDeleted()
143 markCommentAsDeleted(videoComment)
144 142
145 await videoComment.save() 143 await videoComment.save({ transaction: t })
146 })
147 144
148 if (videoComment.Video.isOwned()) { 145 if (videoComment.Video.isOwned()) {
149 // Don't resend the activity to the sender 146 // 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
43 if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) { 43 if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
44 logger.info('Rejecting %s because instance followers are disabled.', targetActor.url) 44 logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
45 45
46 await sendReject(activityId, byActor, targetActor) 46 sendReject(activityId, byActor, targetActor)
47 47
48 return { actorFollow: undefined as MActorFollowActors } 48 return { actorFollow: undefined as MActorFollowActors }
49 } 49 }
@@ -84,8 +84,9 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
84 84
85 // Target sends to actor he accepted the follow request 85 // Target sends to actor he accepted the follow request
86 if (actorFollow.state === 'accepted') { 86 if (actorFollow.state === 'accepted') {
87 await sendAccept(actorFollow) 87 sendAccept(actorFollow)
88 await autoFollowBackIfNeeded(actorFollow) 88
89 await autoFollowBackIfNeeded(actorFollow, t)
89 } 90 }
90 91
91 return { actorFollow, created, isFollowingInstance, targetActor } 92 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
106 const { video } = await getOrCreateAPVideo({ videoObject: cacheFileObject.object }) 106 const { video } = await getOrCreateAPVideo({ videoObject: cacheFileObject.object })
107 107
108 return sequelizeTypescript.transaction(async t => { 108 return sequelizeTypescript.transaction(async t => {
109 const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id) 109 const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t)
110 if (!cacheFile) { 110 if (!cacheFile) {
111 logger.debug('Cannot undo unknown video cache %s.', cacheFileObject.id) 111 logger.debug('Cannot undo unknown video cache %s.', cacheFileObject.id)
112 return 112 return
@@ -114,7 +114,7 @@ async function processUndoCacheFile (byActor: MActorSignature, activity: Activit
114 114
115 if (cacheFile.actorId !== byActor.id) throw new Error('Cannot delete redundancy ' + cacheFile.url + ' of another actor.') 115 if (cacheFile.actorId !== byActor.id) throw new Error('Cannot delete redundancy ' + cacheFile.url + ' of another actor.')
116 116
117 await cacheFile.destroy() 117 await cacheFile.destroy({ transaction: t })
118 118
119 if (video.isOwned()) { 119 if (video.isOwned()) {
120 // Don't resend the activity to the sender 120 // 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 (
151 // Outside the transaction (IO on disk) 151 // Outside the transaction (IO on disk)
152 const { videoFileResolution, isPortraitMode } = await videoArg.getMaxQualityResolution() 152 const { videoFileResolution, isPortraitMode } = await videoArg.getMaxQualityResolution()
153 153
154 const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { 154 // Maybe the video changed in database, refresh it
155 // Maybe the video changed in database, refresh it 155 const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid)
156 const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t) 156 // Video does not exist anymore
157 // Video does not exist anymore 157 if (!videoDatabase) return undefined
158 if (!videoDatabase) return undefined 158
159 159 let videoPublished = false
160 let videoPublished = false 160
161 161 // Generate HLS version of the original file
162 // Generate HLS version of the original file 162 const originalFileHLSPayload = Object.assign({}, payload, {
163 const originalFileHLSPayload = Object.assign({}, payload, { 163 isPortraitMode,
164 isPortraitMode, 164 resolution: videoDatabase.getMaxQualityFile().resolution,
165 resolution: videoDatabase.getMaxQualityFile().resolution, 165 // If we quick transcoded original file, force transcoding for HLS to avoid some weird playback issues
166 // If we quick transcoded original file, force transcoding for HLS to avoid some weird playback issues 166 copyCodecs: transcodeType !== 'quick-transcode',
167 copyCodecs: transcodeType !== 'quick-transcode', 167 isMaxQuality: true
168 isMaxQuality: true 168 })
169 }) 169 const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload)
170 const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload)
171
172 const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent')
173
174 if (!hasHls && !hasNewResolutions) {
175 // No transcoding to do, it's now published
176 videoPublished = await videoDatabase.publishIfNeededAndSave(t)
177 }
178 170
179 await federateVideoIfNeeded(videoDatabase, payload.isNewVideo, t) 171 const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent')
180 172
181 return { videoDatabase, videoPublished } 173 if (!hasHls && !hasNewResolutions) {
182 }) 174 // No transcoding to do, it's now published
175 videoPublished = await videoDatabase.publishIfNeededAndSave(undefined)
176 }
177
178 await federateVideoIfNeeded(videoDatabase, payload.isNewVideo)
183 179
184 if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoDatabase) 180 if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoDatabase)
185 if (videoPublished) Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase) 181 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: {
221 const { isOwned } = await associateFun(abuseInstance) 221 const { isOwned } = await associateFun(abuseInstance)
222 222
223 if (isOwned === false) { 223 if (isOwned === false) {
224 await sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction) 224 sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
225 } 225 }
226 226
227 const abuseJSON = abuseInstance.toFormattedAdminJSON() 227 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: {
44 displayName: userDisplayName, 44 displayName: userDisplayName,
45 userId: userCreated.id, 45 userId: userCreated.id,
46 applicationId: null, 46 applicationId: null,
47 t: t 47 t
48 }) 48 })
49 userCreated.Account = accountCreated 49 userCreated.Account = accountCreated
50 50
51 const channelAttributes = await buildChannelAttributes(userCreated, channelNames) 51 const channelAttributes = await buildChannelAttributes(userCreated, t, channelNames)
52 const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t) 52 const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t)
53 53
54 const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) 54 const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t)
@@ -203,13 +203,13 @@ function createDefaultUserNotificationSettings (user: MUserId, t: Transaction |
203 return UserNotificationSettingModel.create(values, { transaction: t }) 203 return UserNotificationSettingModel.create(values, { transaction: t })
204} 204}
205 205
206async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) { 206async function buildChannelAttributes (user: MUser, transaction?: Transaction, channelNames?: ChannelNames) {
207 if (channelNames) return channelNames 207 if (channelNames) return channelNames
208 208
209 let channelName = user.username + '_channel' 209 let channelName = user.username + '_channel'
210 210
211 // Conflict, generate uuid instead 211 // Conflict, generate uuid instead
212 const actor = await ActorModel.loadLocalByName(channelName) 212 const actor = await ActorModel.loadLocalByName(channelName, transaction)
213 if (actor) channelName = uuidv4() 213 if (actor) channelName = uuidv4()
214 214
215 const videoChannelDisplayName = `Main ${user.username} channel` 215 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) {
18 await sendDeleteVideoComment(videoCommentInstance, t) 18 await sendDeleteVideoComment(videoCommentInstance, t)
19 } 19 }
20 20
21 markCommentAsDeleted(videoCommentInstance) 21 videoCommentInstance.markAsDeleted()
22 22
23 await videoCommentInstance.save() 23 await videoCommentInstance.save({ transaction: t })
24 }) 24 })
25 25
26 logger.info('Video comment %d deleted.', videoCommentInstance.id) 26 logger.info('Video comment %d deleted.', videoCommentInstance.id)
@@ -95,17 +95,10 @@ function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>):
95 return thread 95 return thread
96} 96}
97 97
98function markCommentAsDeleted (comment: MComment): void {
99 comment.text = ''
100 comment.deletedAt = new Date()
101 comment.accountId = null
102}
103
104// --------------------------------------------------------------------------- 98// ---------------------------------------------------------------------------
105 99
106export { 100export {
107 removeComment, 101 removeComment,
108 createVideoComment, 102 createVideoComment,
109 buildFormattedCommentTree, 103 buildFormattedCommentTree
110 markCommentAsDeleted
111} 104}
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 @@
1import { Transaction } from 'sequelize'
1import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { MServer, MServerFormattable } from '@server/types/models/server' 3import { MServer, MServerFormattable } from '@server/types/models/server'
3import { AttributesOnly } from '@shared/core-utils' 4import { AttributesOnly } from '@shared/core-utils'
@@ -51,11 +52,12 @@ export class ServerModel extends Model<Partial<AttributesOnly<ServerModel>>> {
51 }) 52 })
52 BlockedByAccounts: ServerBlocklistModel[] 53 BlockedByAccounts: ServerBlocklistModel[]
53 54
54 static load (id: number): Promise<MServer> { 55 static load (id: number, transaction?: Transaction): Promise<MServer> {
55 const query = { 56 const query = {
56 where: { 57 where: {
57 id 58 id
58 } 59 },
60 transaction
59 } 61 }
60 62
61 return ServerModel.findOne(query) 63 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 Model<Partial<AttributesOnly<VideoCaption
91 Video: VideoModel 91 Video: VideoModel
92 92
93 @BeforeDestroy 93 @BeforeDestroy
94 static async removeFiles (instance: VideoCaptionModel) { 94 static async removeFiles (instance: VideoCaptionModel, options) {
95 if (!instance.Video) { 95 if (!instance.Video) {
96 instance.Video = await instance.$get('Video') 96 instance.Video = await instance.$get('Video', { transaction: options.transaction })
97 } 97 }
98 98
99 if (instance.isOwned()) { 99 if (instance.isOwned()) {
@@ -113,8 +113,7 @@ export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaption
113 const videoInclude = { 113 const videoInclude = {
114 model: VideoModel.unscoped(), 114 model: VideoModel.unscoped(),
115 attributes: [ 'id', 'remote', 'uuid' ], 115 attributes: [ 'id', 'remote', 'uuid' ],
116 where: buildWhereIdOrUUID(videoId), 116 where: buildWhereIdOrUUID(videoId)
117 transaction
118 } 117 }
119 118
120 const query = { 119 const query = {
@@ -123,7 +122,8 @@ export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaption
123 }, 122 },
124 include: [ 123 include: [
125 videoInclude 124 videoInclude
126 ] 125 ],
126 transaction
127 } 127 }
128 128
129 return VideoCaptionModel.findOne(query) 129 return VideoCaptionModel.findOne(query)
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 8c4357009..33749ea70 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -522,10 +522,10 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
522 }) 522 })
523 } 523 }
524 524
525 static loadAndPopulateAccount (id: number): Promise<MChannelBannerAccountDefault> { 525 static loadAndPopulateAccount (id: number, transaction?: Transaction): Promise<MChannelBannerAccountDefault> {
526 return VideoChannelModel.unscoped() 526 return VideoChannelModel.unscoped()
527 .scope([ ScopeNames.WITH_ACTOR_BANNER, ScopeNames.WITH_ACCOUNT ]) 527 .scope([ ScopeNames.WITH_ACTOR_BANNER, ScopeNames.WITH_ACCOUNT ])
528 .findByPk(id) 528 .findByPk(id, { transaction })
529 } 529 }
530 530
531 static loadByUrlAndPopulateAccount (url: string): Promise<MChannelBannerAccountDefault> { 531 static loadByUrlAndPopulateAccount (url: string): Promise<MChannelBannerAccountDefault> {
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<Partial<AttributesOnly<VideoComment
739 return this.Account.isOwned() 739 return this.Account.isOwned()
740 } 740 }
741 741
742 markAsDeleted () {
743 this.text = ''
744 this.deletedAt = new Date()
745 this.accountId = null
746 }
747
742 isDeleted () { 748 isDeleted () {
743 return this.deletedAt !== null 749 return this.deletedAt !== null
744 } 750 }