From 68d19a0ace01cb7a3550da420d27663e2db1b98d Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Thu, 16 Apr 2020 14:22:27 +0200 Subject: Make sure a report doesn't get deleted upon the deletion of its video --- server/helpers/middlewares/video-abuses.ts | 12 +++++-- server/initializers/constants.ts | 2 +- server/initializers/migrations/0490-abuse-video.ts | 28 ++++++++++++++++ .../middlewares/validators/videos/video-abuses.ts | 6 ++-- server/models/video/video-abuse.ts | 37 +++++++++++++++------- server/models/video/video.ts | 33 +++++++++++++++++-- server/typings/models/video/video-abuse.ts | 2 +- 7 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 server/initializers/migrations/0490-abuse-video.ts (limited to 'server') 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 @@ import { Response } from 'express' import { VideoAbuseModel } from '../../models/video/video-abuse' +import { fetchVideo } from '../video' -async function doesVideoAbuseExist (abuseIdArg: number | string, videoId: number, res: Response) { +async function doesVideoAbuseExist (abuseIdArg: number | string, videoUUID: string, res: Response) { const abuseId = parseInt(abuseIdArg + '', 10) - const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId) + let videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, null, videoUUID) + + if (!videoAbuse) { + const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined + const video = await fetchVideo(videoUUID, 'all', userId) + + if (video) videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, video.id) + } if (videoAbuse === null) { 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' // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 485 +const LAST_MIGRATION_VERSION = 490 // --------------------------------------------------------------------------- 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 @@ +import * as Sequelize from 'sequelize' + +async function up (utils: { + transaction: Sequelize.Transaction + queryInterface: Sequelize.QueryInterface + sequelize: Sequelize.Sequelize +}): Promise { + + const deletedVideo = { + type: Sequelize.JSONB, + allowNull: true + } + await utils.queryInterface.addColumn('videoAbuse', 'deletedVideo', deletedVideo) + await utils.sequelize.query(`ALTER TABLE "videoAbsue" ALTER COLUMN "videoId" DROP NOT NULL;`) + await utils.sequelize.query(`ALTER TABLE "videoAbuse" DROP CONSTRAINT IF EXISTS "videoAbuse_videoId_fkey";`) + await utils.sequelize.query(`ALTER TABLE "videoAbuse" ADD CONSTRAINT "videoAbuse_videoId_fkey" + FOREIGN KEY ("videoId") REFERENCES video(id) ON UPDATE CASCADE ON DELETE SET NULL;`) + +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + up, + down +} 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 = [ logger.debug('Checking videoAbuseGetValidator parameters', { parameters: req.body }) if (areValidationErrors(req, res)) return - if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return + if (!await doesVideoAbuseExist(req.params.id, req.params.videoId, res)) return return next() } @@ -53,8 +52,7 @@ const videoAbuseUpdateValidator = [ logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body }) if (areValidationErrors(req, res)) return - if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return + if (!await doesVideoAbuseExist(req.params.id, req.params.videoId, res)) return return next() } 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 { import { AccountModel } from '../account/account' import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils' import { VideoModel } from './video' -import { VideoAbuseState } from '../../../shared' +import { VideoAbuseState, Video } from '../../../shared' import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models' import * as Bluebird from 'bluebird' @@ -46,6 +46,11 @@ export class VideoAbuseModel extends Model { @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max)) moderationComment: string + @AllowNull(true) + @Default(null) + @Column(DataType.JSONB) + deletedVideo: Video + @CreatedAt createdAt: Date @@ -58,9 +63,9 @@ export class VideoAbuseModel extends Model { @BelongsTo(() => AccountModel, { foreignKey: { - allowNull: false + allowNull: true }, - onDelete: 'cascade' + onDelete: 'set null' }) Account: AccountModel @@ -70,17 +75,21 @@ export class VideoAbuseModel extends Model { @BelongsTo(() => VideoModel, { foreignKey: { - allowNull: false + allowNull: true }, - onDelete: 'cascade' + onDelete: 'set null' }) Video: VideoModel - static loadByIdAndVideoId (id: number, videoId: number): Bluebird { + static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird { + const videoAttributes = {} + if (videoId) videoAttributes['videoId'] = videoId + if (uuid) videoAttributes['deletedVideo'] = { uuid } + const query = { where: { id, - videoId + ...videoAttributes } } return VideoAbuseModel.findOne(query) @@ -112,7 +121,7 @@ export class VideoAbuseModel extends Model { }, { model: VideoModel, - required: true + required: false } ] } @@ -124,6 +133,10 @@ export class VideoAbuseModel extends Model { } toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse { + const video = this.Video + ? this.Video + : this.deletedVideo + return { id: this.id, reason: this.reason, @@ -134,9 +147,11 @@ export class VideoAbuseModel extends Model { }, moderationComment: this.moderationComment, video: { - id: this.Video.id, - uuid: this.Video.uuid, - name: this.Video.name + id: video.id, + uuid: video.uuid, + name: video.name, + nsfw: video.nsfw, + deleted: !this.Video }, createdAt: this.createdAt } 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 { @HasMany(() => VideoAbuseModel, { foreignKey: { name: 'videoId', - allowNull: false + allowNull: true }, - onDelete: 'cascade' + onDelete: 'set null' }) VideoAbuses: VideoAbuseModel[] @@ -798,6 +798,35 @@ export class VideoModel extends Model { ModelCache.Instance.invalidateCache('video', instance.id) } + @BeforeDestroy + static async saveEssentialDataToAbuses (instance: VideoModel, options) { + const tasks: Promise[] = [] + + logger.info('Saving video abuses details of video %s.', instance.url) + + if (!Array.isArray(instance.VideoAbuses)) { + instance.VideoAbuses = await instance.$get('VideoAbuses') + + if (instance.VideoAbuses.length === 0) return undefined + } + + const details = instance.toFormattedJSON() + + for (const abuse of instance.VideoAbuses) { + tasks.push((_ => { + abuse.deletedVideo = details + return abuse.save({ transaction: options.transaction }) + })()) + } + + Promise.all(tasks) + .catch(err => { + logger.error('Some errors when saving details of video %s in its abuses before destroy hook.', instance.uuid, { err }) + }) + + return undefined + } + static listLocal (): Bluebird { const query = { 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 = export type MVideoAbuseFormattable = MVideoAbuse & Use<'Account', MAccountFormattable> & - Use<'Video', Pick> + Use<'Video', Pick> -- cgit v1.2.3