aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorRigel Kent <sendmemail@rigelk.eu>2020-04-16 14:22:27 +0200
committerRigel Kent <par@rigelk.eu>2020-05-01 16:41:02 +0200
commit68d19a0ace01cb7a3550da420d27663e2db1b98d (patch)
treefcdac5341001b9e6d15ddd0ca8239372ec2b3053 /server
parent165ee2929bc76fc7f9985ae81cc33736820c7865 (diff)
downloadPeerTube-68d19a0ace01cb7a3550da420d27663e2db1b98d.tar.gz
PeerTube-68d19a0ace01cb7a3550da420d27663e2db1b98d.tar.zst
PeerTube-68d19a0ace01cb7a3550da420d27663e2db1b98d.zip
Make sure a report doesn't get deleted upon the deletion of its video
Diffstat (limited to 'server')
-rw-r--r--server/helpers/middlewares/video-abuses.ts12
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0490-abuse-video.ts28
-rw-r--r--server/middlewares/validators/videos/video-abuses.ts6
-rw-r--r--server/models/video/video-abuse.ts37
-rw-r--r--server/models/video/video.ts33
-rw-r--r--server/typings/models/video/video-abuse.ts2
7 files changed, 99 insertions, 21 deletions
diff --git a/server/helpers/middlewares/video-abuses.ts b/server/helpers/middlewares/video-abuses.ts
index 8a1d3d618..7553a5eb3 100644
--- a/server/helpers/middlewares/video-abuses.ts
+++ b/server/helpers/middlewares/video-abuses.ts
@@ -1,9 +1,17 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { VideoAbuseModel } from '../../models/video/video-abuse' 2import { VideoAbuseModel } from '../../models/video/video-abuse'
3import { fetchVideo } from '../video'
3 4
4async function doesVideoAbuseExist (abuseIdArg: number | string, videoId: number, res: Response) { 5async function doesVideoAbuseExist (abuseIdArg: number | string, videoUUID: string, res: Response) {
5 const abuseId = parseInt(abuseIdArg + '', 10) 6 const abuseId = parseInt(abuseIdArg + '', 10)
6 const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId) 7 let videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, null, videoUUID)
8
9 if (!videoAbuse) {
10 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
11 const video = await fetchVideo(videoUUID, 'all', userId)
12
13 if (video) videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, video.id)
14 }
7 15
8 if (videoAbuse === null) { 16 if (videoAbuse === null) {
9 res.status(404) 17 res.status(404)
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index bc6c58b06..c8623a5d4 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -14,7 +14,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
14 14
15// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
16 16
17const LAST_MIGRATION_VERSION = 485 17const LAST_MIGRATION_VERSION = 490
18 18
19// --------------------------------------------------------------------------- 19// ---------------------------------------------------------------------------
20 20
diff --git a/server/initializers/migrations/0490-abuse-video.ts b/server/initializers/migrations/0490-abuse-video.ts
new file mode 100644
index 000000000..26333feb5
--- /dev/null
+++ b/server/initializers/migrations/0490-abuse-video.ts
@@ -0,0 +1,28 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7}): Promise<void> {
8
9 const deletedVideo = {
10 type: Sequelize.JSONB,
11 allowNull: true
12 }
13 await utils.queryInterface.addColumn('videoAbuse', 'deletedVideo', deletedVideo)
14 await utils.sequelize.query(`ALTER TABLE "videoAbsue" ALTER COLUMN "videoId" DROP NOT NULL;`)
15 await utils.sequelize.query(`ALTER TABLE "videoAbuse" DROP CONSTRAINT IF EXISTS "videoAbuse_videoId_fkey";`)
16 await utils.sequelize.query(`ALTER TABLE "videoAbuse" ADD CONSTRAINT "videoAbuse_videoId_fkey"
17 FOREIGN KEY ("videoId") REFERENCES video(id) ON UPDATE CASCADE ON DELETE SET NULL;`)
18
19}
20
21function down (options) {
22 throw new Error('Not implemented.')
23}
24
25export {
26 up,
27 down
28}
diff --git a/server/middlewares/validators/videos/video-abuses.ts b/server/middlewares/validators/videos/video-abuses.ts
index a4aef4024..7c316fe13 100644
--- a/server/middlewares/validators/videos/video-abuses.ts
+++ b/server/middlewares/validators/videos/video-abuses.ts
@@ -32,8 +32,7 @@ const videoAbuseGetValidator = [
32 logger.debug('Checking videoAbuseGetValidator parameters', { parameters: req.body }) 32 logger.debug('Checking videoAbuseGetValidator parameters', { parameters: req.body })
33 33
34 if (areValidationErrors(req, res)) return 34 if (areValidationErrors(req, res)) return
35 if (!await doesVideoExist(req.params.videoId, res)) return 35 if (!await doesVideoAbuseExist(req.params.id, req.params.videoId, res)) return
36 if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
37 36
38 return next() 37 return next()
39 } 38 }
@@ -53,8 +52,7 @@ const videoAbuseUpdateValidator = [
53 logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body }) 52 logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body })
54 53
55 if (areValidationErrors(req, res)) return 54 if (areValidationErrors(req, res)) return
56 if (!await doesVideoExist(req.params.videoId, res)) return 55 if (!await doesVideoAbuseExist(req.params.id, req.params.videoId, res)) return
57 if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
58 56
59 return next() 57 return next()
60 } 58 }
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index da8c1577c..ea9856213 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -9,7 +9,7 @@ import {
9import { AccountModel } from '../account/account' 9import { AccountModel } from '../account/account'
10import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils' 10import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
11import { VideoModel } from './video' 11import { VideoModel } from './video'
12import { VideoAbuseState } from '../../../shared' 12import { VideoAbuseState, Video } from '../../../shared'
13import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' 13import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
14import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models' 14import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models'
15import * as Bluebird from 'bluebird' 15import * as Bluebird from 'bluebird'
@@ -46,6 +46,11 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
46 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max)) 46 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max))
47 moderationComment: string 47 moderationComment: string
48 48
49 @AllowNull(true)
50 @Default(null)
51 @Column(DataType.JSONB)
52 deletedVideo: Video
53
49 @CreatedAt 54 @CreatedAt
50 createdAt: Date 55 createdAt: Date
51 56
@@ -58,9 +63,9 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
58 63
59 @BelongsTo(() => AccountModel, { 64 @BelongsTo(() => AccountModel, {
60 foreignKey: { 65 foreignKey: {
61 allowNull: false 66 allowNull: true
62 }, 67 },
63 onDelete: 'cascade' 68 onDelete: 'set null'
64 }) 69 })
65 Account: AccountModel 70 Account: AccountModel
66 71
@@ -70,17 +75,21 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
70 75
71 @BelongsTo(() => VideoModel, { 76 @BelongsTo(() => VideoModel, {
72 foreignKey: { 77 foreignKey: {
73 allowNull: false 78 allowNull: true
74 }, 79 },
75 onDelete: 'cascade' 80 onDelete: 'set null'
76 }) 81 })
77 Video: VideoModel 82 Video: VideoModel
78 83
79 static loadByIdAndVideoId (id: number, videoId: number): Bluebird<MVideoAbuse> { 84 static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MVideoAbuse> {
85 const videoAttributes = {}
86 if (videoId) videoAttributes['videoId'] = videoId
87 if (uuid) videoAttributes['deletedVideo'] = { uuid }
88
80 const query = { 89 const query = {
81 where: { 90 where: {
82 id, 91 id,
83 videoId 92 ...videoAttributes
84 } 93 }
85 } 94 }
86 return VideoAbuseModel.findOne(query) 95 return VideoAbuseModel.findOne(query)
@@ -112,7 +121,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
112 }, 121 },
113 { 122 {
114 model: VideoModel, 123 model: VideoModel,
115 required: true 124 required: false
116 } 125 }
117 ] 126 ]
118 } 127 }
@@ -124,6 +133,10 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
124 } 133 }
125 134
126 toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse { 135 toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
136 const video = this.Video
137 ? this.Video
138 : this.deletedVideo
139
127 return { 140 return {
128 id: this.id, 141 id: this.id,
129 reason: this.reason, 142 reason: this.reason,
@@ -134,9 +147,11 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
134 }, 147 },
135 moderationComment: this.moderationComment, 148 moderationComment: this.moderationComment,
136 video: { 149 video: {
137 id: this.Video.id, 150 id: video.id,
138 uuid: this.Video.uuid, 151 uuid: video.uuid,
139 name: this.Video.name 152 name: video.name,
153 nsfw: video.nsfw,
154 deleted: !this.Video
140 }, 155 },
141 createdAt: this.createdAt 156 createdAt: this.createdAt
142 } 157 }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 0e7505af5..2636ebd8e 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -628,9 +628,9 @@ export class VideoModel extends Model<VideoModel> {
628 @HasMany(() => VideoAbuseModel, { 628 @HasMany(() => VideoAbuseModel, {
629 foreignKey: { 629 foreignKey: {
630 name: 'videoId', 630 name: 'videoId',
631 allowNull: false 631 allowNull: true
632 }, 632 },
633 onDelete: 'cascade' 633 onDelete: 'set null'
634 }) 634 })
635 VideoAbuses: VideoAbuseModel[] 635 VideoAbuses: VideoAbuseModel[]
636 636
@@ -798,6 +798,35 @@ export class VideoModel extends Model<VideoModel> {
798 ModelCache.Instance.invalidateCache('video', instance.id) 798 ModelCache.Instance.invalidateCache('video', instance.id)
799 } 799 }
800 800
801 @BeforeDestroy
802 static async saveEssentialDataToAbuses (instance: VideoModel, options) {
803 const tasks: Promise<any>[] = []
804
805 logger.info('Saving video abuses details of video %s.', instance.url)
806
807 if (!Array.isArray(instance.VideoAbuses)) {
808 instance.VideoAbuses = await instance.$get('VideoAbuses')
809
810 if (instance.VideoAbuses.length === 0) return undefined
811 }
812
813 const details = instance.toFormattedJSON()
814
815 for (const abuse of instance.VideoAbuses) {
816 tasks.push((_ => {
817 abuse.deletedVideo = details
818 return abuse.save({ transaction: options.transaction })
819 })())
820 }
821
822 Promise.all(tasks)
823 .catch(err => {
824 logger.error('Some errors when saving details of video %s in its abuses before destroy hook.', instance.uuid, { err })
825 })
826
827 return undefined
828 }
829
801 static listLocal (): Bluebird<MVideoWithAllFiles[]> { 830 static listLocal (): Bluebird<MVideoWithAllFiles[]> {
802 const query = { 831 const query = {
803 where: { 832 where: {
diff --git a/server/typings/models/video/video-abuse.ts b/server/typings/models/video/video-abuse.ts
index 955ec4780..49bd1ff2e 100644
--- a/server/typings/models/video/video-abuse.ts
+++ b/server/typings/models/video/video-abuse.ts
@@ -31,4 +31,4 @@ export type MVideoAbuseAccountVideo =
31export type MVideoAbuseFormattable = 31export type MVideoAbuseFormattable =
32 MVideoAbuse & 32 MVideoAbuse &
33 Use<'Account', MAccountFormattable> & 33 Use<'Account', MAccountFormattable> &
34 Use<'Video', Pick<MVideo, 'id' | 'uuid' | 'name'>> 34 Use<'Video', Pick<MVideo, 'id' | 'uuid' | 'name' | 'nsfw'>>