aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-01-10 15:39:51 +0100
committerChocobozzz <me@florianbigard.com>2019-01-10 15:39:51 +0100
commit5abb9fbbd12e7097e348d6a38622d364b1fa47ed (patch)
tree8ef483ad15bd76c75876e0e2f34fd3c99b786103
parent93f85e90ffa27453e2c909406b62bef65963b8ad (diff)
downloadPeerTube-5abb9fbbd12e7097e348d6a38622d364b1fa47ed.tar.gz
PeerTube-5abb9fbbd12e7097e348d6a38622d364b1fa47ed.tar.zst
PeerTube-5abb9fbbd12e7097e348d6a38622d364b1fa47ed.zip
Add ability to unfederate a local video (on blacklist)
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html6
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts6
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html3
-rw-r--r--client/src/app/shared/video-blacklist/video-blacklist.service.ts7
-rw-r--r--client/src/app/videos/+video-watch/modal/video-blacklist.component.html7
-rw-r--r--client/src/app/videos/+video-watch/modal/video-blacklist.component.ts10
-rw-r--r--server/controllers/api/videos/blacklist.ts17
-rw-r--r--server/controllers/api/videos/index.ts6
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0320-blacklist-unfederate.ts27
-rw-r--r--server/lib/activitypub/share.ts16
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts15
-rw-r--r--server/models/video/video-blacklist.ts21
-rw-r--r--server/tests/api/check-params/video-blacklist.ts118
-rw-r--r--server/tests/api/videos/index.ts1
-rw-r--r--server/tests/api/videos/video-blacklist-management.ts192
-rw-r--r--server/tests/api/videos/video-blacklist.ts299
-rw-r--r--shared/models/videos/blacklist/video-blacklist-create.model.ts1
-rw-r--r--shared/models/videos/blacklist/video-blacklist.model.ts1
-rw-r--r--shared/utils/server/servers.ts4
-rw-r--r--shared/utils/videos/video-blacklist.ts11
21 files changed, 454 insertions, 316 deletions
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
index 7cef787d2..6398af218 100644
--- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
+++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
@@ -7,6 +7,7 @@
7 <th style="width: 40px"></th> 7 <th style="width: 40px"></th>
8 <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th> 8 <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th>
9 <th i18n>Sensitive</th> 9 <th i18n>Sensitive</th>
10 <th i18n>Unfederated</th>
10 <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th> 11 <th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
11 <th style="width: 120px;"></th> 12 <th style="width: 120px;"></th>
12 </tr> 13 </tr>
@@ -26,7 +27,8 @@
26 </a> 27 </a>
27 </td> 28 </td>
28 29
29 <td>{{ videoBlacklist.video.nsfw }}</td> 30 <td>{{ booleanToText(videoBlacklist.video.nsfw) }}</td>
31 <td>{{ booleanToText(videoBlacklist.unfederated) }}</td>
30 <td>{{ videoBlacklist.createdAt }}</td> 32 <td>{{ videoBlacklist.createdAt }}</td>
31 33
32 <td class="action-cell"> 34 <td class="action-cell">
@@ -37,7 +39,7 @@
37 39
38 <ng-template pTemplate="rowexpansion" let-videoBlacklist> 40 <ng-template pTemplate="rowexpansion" let-videoBlacklist>
39 <tr> 41 <tr>
40 <td class="moderation-expanded" colspan="5"> 42 <td class="moderation-expanded" colspan="6">
41 <span i18n class="moderation-expanded-label">Blacklist reason:</span> 43 <span i18n class="moderation-expanded-label">Blacklist reason:</span>
42 <span class="moderation-expanded-text">{{ videoBlacklist.reason }}</span> 44 <span class="moderation-expanded-text">{{ videoBlacklist.reason }}</span>
43 </td> 45 </td>
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
index a02e84f67..6c6f17f0c 100644
--- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
+++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
@@ -46,6 +46,12 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
46 return Video.buildClientUrl(videoBlacklist.video.uuid) 46 return Video.buildClientUrl(videoBlacklist.video.uuid)
47 } 47 }
48 48
49 booleanToText (value: boolean) {
50 if (value === true) return this.i18n('yes')
51
52 return this.i18n('no')
53 }
54
49 async removeVideoFromBlacklist (entry: VideoBlacklist) { 55 async removeVideoFromBlacklist (entry: VideoBlacklist) {
50 const confirmMessage = this.i18n( 56 const confirmMessage = this.i18n(
51 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.' 57 'Do you really want to remove this video from the blacklist? It will be available again in the videos list.'
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index 556ab3c5d..8c03a924b 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -65,7 +65,9 @@
65 <span i18n *ngIf="user.blocked" class="banned-info">(banned)</span> 65 <span i18n *ngIf="user.blocked" class="banned-info">(banned)</span>
66 </a> 66 </a>
67 </td> 67 </td>
68
68 <td *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus">{{ user.email }}</td> 69 <td *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus">{{ user.email }}</td>
70
69 <ng-template #emailWithVerificationStatus> 71 <ng-template #emailWithVerificationStatus>
70 <td *ngIf="user.emailVerified === false; else emailVerifiedNotFalse" i18n-title title="User's email must be verified to login"> 72 <td *ngIf="user.emailVerified === false; else emailVerifiedNotFalse" i18n-title title="User's email must be verified to login">
71 <em>? {{ user.email }}</em> 73 <em>? {{ user.email }}</em>
@@ -76,6 +78,7 @@
76 </td> 78 </td>
77 </ng-template> 79 </ng-template>
78 </ng-template> 80 </ng-template>
81
79 <td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td> 82 <td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td>
80 <td>{{ user.roleLabel }}</td> 83 <td>{{ user.roleLabel }}</td>
81 <td>{{ user.createdAt }}</td> 84 <td>{{ user.createdAt }}</td>
diff --git a/client/src/app/shared/video-blacklist/video-blacklist.service.ts b/client/src/app/shared/video-blacklist/video-blacklist.service.ts
index 7d39fd4f2..94e46d7c2 100644
--- a/client/src/app/shared/video-blacklist/video-blacklist.service.ts
+++ b/client/src/app/shared/video-blacklist/video-blacklist.service.ts
@@ -36,8 +36,11 @@ export class VideoBlacklistService {
36 ) 36 )
37 } 37 }
38 38
39 blacklistVideo (videoId: number, reason?: string) { 39 blacklistVideo (videoId: number, reason: string, unfederate: boolean) {
40 const body = reason ? { reason } : {} 40 const body = {
41 unfederate,
42 reason
43 }
41 44
42 return this.authHttp.post(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist', body) 45 return this.authHttp.post(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist', body)
43 .pipe( 46 .pipe(
diff --git a/client/src/app/videos/+video-watch/modal/video-blacklist.component.html b/client/src/app/videos/+video-watch/modal/video-blacklist.component.html
index c436501b4..83600fa81 100644
--- a/client/src/app/videos/+video-watch/modal/video-blacklist.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-blacklist.component.html
@@ -15,6 +15,13 @@
15 </div> 15 </div>
16 </div> 16 </div>
17 17
18 <div class="form-group" *ngIf="video.isLocal">
19 <my-peertube-checkbox
20 inputName="unfederate" formControlName="unfederate"
21 i18n-labelText labelText="Unfederate the video (ask for its deletion from the remote instances)"
22 ></my-peertube-checkbox>
23 </div>
24
18 <div class="form-group inputs"> 25 <div class="form-group inputs">
19 <span i18n class="action-button action-button-cancel" (click)="hide()"> 26 <span i18n class="action-button action-button-cancel" (click)="hide()">
20 Cancel 27 Cancel
diff --git a/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts b/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts
index 357ce39ce..50a7cadd1 100644
--- a/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts
+++ b/client/src/app/videos/+video-watch/modal/video-blacklist.component.ts
@@ -34,9 +34,12 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit {
34 } 34 }
35 35
36 ngOnInit () { 36 ngOnInit () {
37 const defaultValues = { unfederate: 'true' }
38
37 this.buildForm({ 39 this.buildForm({
38 reason: this.videoBlacklistValidatorsService.VIDEO_BLACKLIST_REASON 40 reason: this.videoBlacklistValidatorsService.VIDEO_BLACKLIST_REASON,
39 }) 41 unfederate: null
42 }, defaultValues)
40 } 43 }
41 44
42 show () { 45 show () {
@@ -50,8 +53,9 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit {
50 53
51 blacklist () { 54 blacklist () {
52 const reason = this.form.value[ 'reason' ] || undefined 55 const reason = this.form.value[ 'reason' ] || undefined
56 const unfederate = this.video.isLocal ? this.form.value[ 'unfederate' ] : undefined
53 57
54 this.videoBlacklistService.blacklistVideo(this.video.id, reason) 58 this.videoBlacklistService.blacklistVideo(this.video.id, reason, unfederate)
55 .subscribe( 59 .subscribe(
56 () => { 60 () => {
57 this.notifier.success(this.i18n('Video blacklisted.')) 61 this.notifier.success(this.i18n('Video blacklisted.'))
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index 9ef08812b..43b0516e7 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -18,6 +18,8 @@ import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
18import { sequelizeTypescript } from '../../../initializers' 18import { sequelizeTypescript } from '../../../initializers'
19import { Notifier } from '../../../lib/notifier' 19import { Notifier } from '../../../lib/notifier'
20import { VideoModel } from '../../../models/video/video' 20import { VideoModel } from '../../../models/video/video'
21import { sendCreateVideo, sendDeleteVideo, sendUpdateVideo } from '../../../lib/activitypub/send'
22import { federateVideoIfNeeded } from '../../../lib/activitypub'
21 23
22const blacklistRouter = express.Router() 24const blacklistRouter = express.Router()
23 25
@@ -66,12 +68,17 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
66 68
67 const toCreate = { 69 const toCreate = {
68 videoId: videoInstance.id, 70 videoId: videoInstance.id,
71 unfederated: body.unfederate === true,
69 reason: body.reason 72 reason: body.reason
70 } 73 }
71 74
72 const blacklist = await VideoBlacklistModel.create(toCreate) 75 const blacklist = await VideoBlacklistModel.create(toCreate)
73 blacklist.Video = videoInstance 76 blacklist.Video = videoInstance
74 77
78 if (body.unfederate === true) {
79 await sendDeleteVideo(videoInstance, undefined)
80 }
81
75 Notifier.Instance.notifyOnVideoBlacklist(blacklist) 82 Notifier.Instance.notifyOnVideoBlacklist(blacklist)
76 83
77 logger.info('Video %s blacklisted.', res.locals.video.uuid) 84 logger.info('Video %s blacklisted.', res.locals.video.uuid)
@@ -101,8 +108,14 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex
101 const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel 108 const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel
102 const video: VideoModel = res.locals.video 109 const video: VideoModel = res.locals.video
103 110
104 await sequelizeTypescript.transaction(t => { 111 await sequelizeTypescript.transaction(async t => {
105 return videoBlacklist.destroy({ transaction: t }) 112 const unfederated = videoBlacklist.unfederated
113 await videoBlacklist.destroy({ transaction: t })
114
115 // Re federate the video
116 if (unfederated === true) {
117 await federateVideoIfNeeded(video, true, t)
118 }
106 }) 119 })
107 120
108 Notifier.Instance.notifyOnVideoUnblacklist(video) 121 Notifier.Instance.notifyOnVideoUnblacklist(video)
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 33521a8c1..28ac26598 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -364,7 +364,11 @@ async function updateVideo (req: express.Request, res: express.Response) {
364 } 364 }
365 365
366 const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE 366 const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE
367 await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) 367
368 // Don't send update if the video was unfederated
369 if (!videoInstanceUpdated.VideoBlacklist || videoInstanceUpdated.VideoBlacklist.unfederated === false) {
370 await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t)
371 }
368 372
369 auditLogger.update( 373 auditLogger.update(
370 getAuditIdFromRes(res), 374 getAuditIdFromRes(res),
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 4a88aef87..b18884eeb 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -16,7 +16,7 @@ let config: IConfig = require('config')
16 16
17// --------------------------------------------------------------------------- 17// ---------------------------------------------------------------------------
18 18
19const LAST_MIGRATION_VERSION = 315 19const LAST_MIGRATION_VERSION = 320
20 20
21// --------------------------------------------------------------------------- 21// ---------------------------------------------------------------------------
22 22
diff --git a/server/initializers/migrations/0320-blacklist-unfederate.ts b/server/initializers/migrations/0320-blacklist-unfederate.ts
new file mode 100644
index 000000000..6fb7bbb90
--- /dev/null
+++ b/server/initializers/migrations/0320-blacklist-unfederate.ts
@@ -0,0 +1,27 @@
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 {
10 const data = {
11 type: Sequelize.BOOLEAN,
12 allowNull: false,
13 defaultValue: false
14 }
15
16 await utils.queryInterface.addColumn('videoBlacklist', 'unfederated', data)
17 }
18}
19
20function down (options) {
21 throw new Error('Not implemented.')
22}
23
24export {
25 up,
26 down
27}
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index 5dcba778c..170e49238 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -78,7 +78,7 @@ async function shareByServer (video: VideoModel, t: Transaction) {
78 const serverActor = await getServerActor() 78 const serverActor = await getServerActor()
79 79
80 const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) 80 const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
81 return VideoShareModel.findOrCreate({ 81 const [ serverShare ] = await VideoShareModel.findOrCreate({
82 defaults: { 82 defaults: {
83 actorId: serverActor.id, 83 actorId: serverActor.id,
84 videoId: video.id, 84 videoId: video.id,
@@ -88,16 +88,14 @@ async function shareByServer (video: VideoModel, t: Transaction) {
88 url: serverShareUrl 88 url: serverShareUrl
89 }, 89 },
90 transaction: t 90 transaction: t
91 }).then(([ serverShare, created ]) => {
92 if (created) return sendVideoAnnounce(serverActor, serverShare, video, t)
93
94 return undefined
95 }) 91 })
92
93 return sendVideoAnnounce(serverActor, serverShare, video, t)
96} 94}
97 95
98async function shareByVideoChannel (video: VideoModel, t: Transaction) { 96async function shareByVideoChannel (video: VideoModel, t: Transaction) {
99 const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) 97 const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video)
100 return VideoShareModel.findOrCreate({ 98 const [ videoChannelShare ] = await VideoShareModel.findOrCreate({
101 defaults: { 99 defaults: {
102 actorId: video.VideoChannel.actorId, 100 actorId: video.VideoChannel.actorId,
103 videoId: video.id, 101 videoId: video.id,
@@ -107,11 +105,9 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) {
107 url: videoChannelShareUrl 105 url: videoChannelShareUrl
108 }, 106 },
109 transaction: t 107 transaction: t
110 }).then(([ videoChannelShare, created ]) => {
111 if (created) return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t)
112
113 return undefined
114 }) 108 })
109
110 return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t)
115} 111}
116 112
117async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { 113async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) {
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index 13da7acff..2688f63ae 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -1,10 +1,11 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator/check' 2import { body, param } from 'express-validator/check'
3import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' 3import { isBooleanValid, isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
4import { isVideoExist } from '../../../helpers/custom-validators/videos' 4import { isVideoExist } from '../../../helpers/custom-validators/videos'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { areValidationErrors } from '../utils' 6import { areValidationErrors } from '../utils'
7import { isVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist' 7import { isVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist'
8import { VideoModel } from '../../../models/video/video'
8 9
9const videosBlacklistRemoveValidator = [ 10const videosBlacklistRemoveValidator = [
10 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 11 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
@@ -22,6 +23,10 @@ const videosBlacklistRemoveValidator = [
22 23
23const videosBlacklistAddValidator = [ 24const videosBlacklistAddValidator = [
24 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 25 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
26 body('unfederate')
27 .optional()
28 .toBoolean()
29 .custom(isBooleanValid).withMessage('Should have a valid unfederate boolean'),
25 body('reason') 30 body('reason')
26 .optional() 31 .optional()
27 .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), 32 .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'),
@@ -32,6 +37,14 @@ const videosBlacklistAddValidator = [
32 if (areValidationErrors(req, res)) return 37 if (areValidationErrors(req, res)) return
33 if (!await isVideoExist(req.params.videoId, res)) return 38 if (!await isVideoExist(req.params.videoId, res)) return
34 39
40 const video: VideoModel = res.locals.video
41 if (req.body.unfederate === true && video.remote === true) {
42 return res
43 .status(409)
44 .send({ error: 'You cannot unfederate a remote video.' })
45 .end()
46 }
47
35 return next() 48 return next()
36 } 49 }
37] 50]
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index 23e992685..3b567e488 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -1,21 +1,7 @@
1import { 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2 AfterCreate,
3 AfterDestroy,
4 AllowNull,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 DataType,
9 ForeignKey,
10 Is,
11 Model,
12 Table,
13 UpdatedAt
14} from 'sequelize-typescript'
15import { getSortOnModel, SortType, throwIfNotValid } from '../utils' 2import { getSortOnModel, SortType, throwIfNotValid } from '../utils'
16import { VideoModel } from './video' 3import { VideoModel } from './video'
17import { isVideoBlacklistReasonValid } from '../../helpers/custom-validators/video-blacklist' 4import { isVideoBlacklistReasonValid } from '../../helpers/custom-validators/video-blacklist'
18import { Emailer } from '../../lib/emailer'
19import { VideoBlacklist } from '../../../shared/models/videos' 5import { VideoBlacklist } from '../../../shared/models/videos'
20import { CONSTRAINTS_FIELDS } from '../../initializers' 6import { CONSTRAINTS_FIELDS } from '../../initializers'
21 7
@@ -35,6 +21,10 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
35 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_BLACKLIST.REASON.max)) 21 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_BLACKLIST.REASON.max))
36 reason: string 22 reason: string
37 23
24 @AllowNull(false)
25 @Column
26 unfederated: boolean
27
38 @CreatedAt 28 @CreatedAt
39 createdAt: Date 29 createdAt: Date
40 30
@@ -93,6 +83,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
93 createdAt: this.createdAt, 83 createdAt: this.createdAt,
94 updatedAt: this.updatedAt, 84 updatedAt: this.updatedAt,
95 reason: this.reason, 85 reason: this.reason,
86 unfederated: this.unfederated,
96 87
97 video: { 88 video: {
98 id: video.id, 89 id: video.id,
diff --git a/server/tests/api/check-params/video-blacklist.ts b/server/tests/api/check-params/video-blacklist.ts
index 8e1206db3..6b82643f4 100644
--- a/server/tests/api/check-params/video-blacklist.ts
+++ b/server/tests/api/check-params/video-blacklist.ts
@@ -4,17 +4,20 @@ import 'mocha'
4 4
5import { 5import {
6 createUser, 6 createUser,
7 doubleFollow,
8 flushAndRunMultipleServers,
7 flushTests, 9 flushTests,
8 getBlacklistedVideosList, getVideo, getVideoWithToken, 10 getBlacklistedVideosList,
11 getVideo,
12 getVideoWithToken,
9 killallServers, 13 killallServers,
10 makePostBodyRequest, 14 makePostBodyRequest,
11 makePutBodyRequest, 15 makePutBodyRequest,
12 removeVideoFromBlacklist, 16 removeVideoFromBlacklist,
13 runServer,
14 ServerInfo, 17 ServerInfo,
15 setAccessTokensToServers, 18 setAccessTokensToServers,
16 uploadVideo, 19 uploadVideo,
17 userLogin 20 userLogin, waitJobs
18} from '../../../../shared/utils' 21} from '../../../../shared/utils'
19import { 22import {
20 checkBadCountPagination, 23 checkBadCountPagination,
@@ -25,8 +28,9 @@ import { VideoDetails } from '../../../../shared/models/videos'
25import { expect } from 'chai' 28import { expect } from 'chai'
26 29
27describe('Test video blacklist API validators', function () { 30describe('Test video blacklist API validators', function () {
28 let server: ServerInfo 31 let servers: ServerInfo[]
29 let notBlacklistedVideoId: number 32 let notBlacklistedVideoId: number
33 let remoteVideoUUID: string
30 let userAccessToken1 = '' 34 let userAccessToken1 = ''
31 let userAccessToken2 = '' 35 let userAccessToken2 = ''
32 36
@@ -36,75 +40,89 @@ describe('Test video blacklist API validators', function () {
36 this.timeout(120000) 40 this.timeout(120000)
37 41
38 await flushTests() 42 await flushTests()
43 servers = await flushAndRunMultipleServers(2)
39 44
40 server = await runServer(1) 45 await setAccessTokensToServers(servers)
41 46 await doubleFollow(servers[0], servers[1])
42 await setAccessTokensToServers([ server ])
43 47
44 { 48 {
45 const username = 'user1' 49 const username = 'user1'
46 const password = 'my super password' 50 const password = 'my super password'
47 await createUser(server.url, server.accessToken, username, password) 51 await createUser(servers[0].url, servers[0].accessToken, username, password)
48 userAccessToken1 = await userLogin(server, { username, password }) 52 userAccessToken1 = await userLogin(servers[0], { username, password })
49 } 53 }
50 54
51 { 55 {
52 const username = 'user2' 56 const username = 'user2'
53 const password = 'my super password' 57 const password = 'my super password'
54 await createUser(server.url, server.accessToken, username, password) 58 await createUser(servers[0].url, servers[0].accessToken, username, password)
55 userAccessToken2 = await userLogin(server, { username, password }) 59 userAccessToken2 = await userLogin(servers[0], { username, password })
56 } 60 }
57 61
58 { 62 {
59 const res = await uploadVideo(server.url, userAccessToken1, {}) 63 const res = await uploadVideo(servers[0].url, userAccessToken1, {})
60 server.video = res.body.video 64 servers[0].video = res.body.video
61 } 65 }
62 66
63 { 67 {
64 const res = await uploadVideo(server.url, server.accessToken, {}) 68 const res = await uploadVideo(servers[0].url, servers[0].accessToken, {})
65 notBlacklistedVideoId = res.body.video.uuid 69 notBlacklistedVideoId = res.body.video.uuid
66 } 70 }
71
72 {
73 const res = await uploadVideo(servers[1].url, servers[1].accessToken, {})
74 remoteVideoUUID = res.body.video.uuid
75 }
76
77 await waitJobs(servers)
67 }) 78 })
68 79
69 describe('When adding a video in blacklist', function () { 80 describe('When adding a video in blacklist', function () {
70 const basePath = '/api/v1/videos/' 81 const basePath = '/api/v1/videos/'
71 82
72 it('Should fail with nothing', async function () { 83 it('Should fail with nothing', async function () {
73 const path = basePath + server.video + '/blacklist' 84 const path = basePath + servers[0].video + '/blacklist'
74 const fields = {} 85 const fields = {}
75 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 86 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
76 }) 87 })
77 88
78 it('Should fail with a wrong video', async function () { 89 it('Should fail with a wrong video', async function () {
79 const wrongPath = '/api/v1/videos/blabla/blacklist' 90 const wrongPath = '/api/v1/videos/blabla/blacklist'
80 const fields = {} 91 const fields = {}
81 await makePostBodyRequest({ url: server.url, path: wrongPath, token: server.accessToken, fields }) 92 await makePostBodyRequest({ url: servers[0].url, path: wrongPath, token: servers[0].accessToken, fields })
82 }) 93 })
83 94
84 it('Should fail with a non authenticated user', async function () { 95 it('Should fail with a non authenticated user', async function () {
85 const path = basePath + server.video + '/blacklist' 96 const path = basePath + servers[0].video + '/blacklist'
86 const fields = {} 97 const fields = {}
87 await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: 401 }) 98 await makePostBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: 401 })
88 }) 99 })
89 100
90 it('Should fail with a non admin user', async function () { 101 it('Should fail with a non admin user', async function () {
91 const path = basePath + server.video + '/blacklist' 102 const path = basePath + servers[0].video + '/blacklist'
92 const fields = {} 103 const fields = {}
93 await makePostBodyRequest({ url: server.url, path, token: userAccessToken2, fields, statusCodeExpected: 403 }) 104 await makePostBodyRequest({ url: servers[0].url, path, token: userAccessToken2, fields, statusCodeExpected: 403 })
94 }) 105 })
95 106
96 it('Should fail with an invalid reason', async function () { 107 it('Should fail with an invalid reason', async function () {
97 const path = basePath + server.video.uuid + '/blacklist' 108 const path = basePath + servers[0].video.uuid + '/blacklist'
98 const fields = { reason: 'a'.repeat(305) } 109 const fields = { reason: 'a'.repeat(305) }
99 110
100 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 111 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
112 })
113
114 it('Should fail to unfederate a remote video', async function () {
115 const path = basePath + remoteVideoUUID + '/blacklist'
116 const fields = { unfederate: true }
117
118 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 409 })
101 }) 119 })
102 120
103 it('Should succeed with the correct params', async function () { 121 it('Should succeed with the correct params', async function () {
104 const path = basePath + server.video.uuid + '/blacklist' 122 const path = basePath + servers[0].video.uuid + '/blacklist'
105 const fields = { } 123 const fields = { }
106 124
107 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 204 }) 125 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 204 })
108 }) 126 })
109 }) 127 })
110 128
@@ -114,61 +132,61 @@ describe('Test video blacklist API validators', function () {
114 it('Should fail with a wrong video', async function () { 132 it('Should fail with a wrong video', async function () {
115 const wrongPath = '/api/v1/videos/blabla/blacklist' 133 const wrongPath = '/api/v1/videos/blabla/blacklist'
116 const fields = {} 134 const fields = {}
117 await makePutBodyRequest({ url: server.url, path: wrongPath, token: server.accessToken, fields }) 135 await makePutBodyRequest({ url: servers[0].url, path: wrongPath, token: servers[0].accessToken, fields })
118 }) 136 })
119 137
120 it('Should fail with a video not blacklisted', async function () { 138 it('Should fail with a video not blacklisted', async function () {
121 const path = '/api/v1/videos/' + notBlacklistedVideoId + '/blacklist' 139 const path = '/api/v1/videos/' + notBlacklistedVideoId + '/blacklist'
122 const fields = {} 140 const fields = {}
123 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 404 }) 141 await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 404 })
124 }) 142 })
125 143
126 it('Should fail with a non authenticated user', async function () { 144 it('Should fail with a non authenticated user', async function () {
127 const path = basePath + server.video + '/blacklist' 145 const path = basePath + servers[0].video + '/blacklist'
128 const fields = {} 146 const fields = {}
129 await makePutBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: 401 }) 147 await makePutBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: 401 })
130 }) 148 })
131 149
132 it('Should fail with a non admin user', async function () { 150 it('Should fail with a non admin user', async function () {
133 const path = basePath + server.video + '/blacklist' 151 const path = basePath + servers[0].video + '/blacklist'
134 const fields = {} 152 const fields = {}
135 await makePutBodyRequest({ url: server.url, path, token: userAccessToken2, fields, statusCodeExpected: 403 }) 153 await makePutBodyRequest({ url: servers[0].url, path, token: userAccessToken2, fields, statusCodeExpected: 403 })
136 }) 154 })
137 155
138 it('Should fail with an invalid reason', async function () { 156 it('Should fail with an invalid reason', async function () {
139 const path = basePath + server.video.uuid + '/blacklist' 157 const path = basePath + servers[0].video.uuid + '/blacklist'
140 const fields = { reason: 'a'.repeat(305) } 158 const fields = { reason: 'a'.repeat(305) }
141 159
142 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 160 await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
143 }) 161 })
144 162
145 it('Should succeed with the correct params', async function () { 163 it('Should succeed with the correct params', async function () {
146 const path = basePath + server.video.uuid + '/blacklist' 164 const path = basePath + servers[0].video.uuid + '/blacklist'
147 const fields = { reason: 'hello' } 165 const fields = { reason: 'hello' }
148 166
149 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 204 }) 167 await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields, statusCodeExpected: 204 })
150 }) 168 })
151 }) 169 })
152 170
153 describe('When getting blacklisted video', function () { 171 describe('When getting blacklisted video', function () {
154 172
155 it('Should fail with a non authenticated user', async function () { 173 it('Should fail with a non authenticated user', async function () {
156 await getVideo(server.url, server.video.uuid, 401) 174 await getVideo(servers[0].url, servers[0].video.uuid, 401)
157 }) 175 })
158 176
159 it('Should fail with another user', async function () { 177 it('Should fail with another user', async function () {
160 await getVideoWithToken(server.url, userAccessToken2, server.video.uuid, 403) 178 await getVideoWithToken(servers[0].url, userAccessToken2, servers[0].video.uuid, 403)
161 }) 179 })
162 180
163 it('Should succeed with the owner authenticated user', async function () { 181 it('Should succeed with the owner authenticated user', async function () {
164 const res = await getVideoWithToken(server.url, userAccessToken1, server.video.uuid, 200) 182 const res = await getVideoWithToken(servers[0].url, userAccessToken1, servers[0].video.uuid, 200)
165 const video: VideoDetails = res.body 183 const video: VideoDetails = res.body
166 184
167 expect(video.blacklisted).to.be.true 185 expect(video.blacklisted).to.be.true
168 }) 186 })
169 187
170 it('Should succeed with an admin', async function () { 188 it('Should succeed with an admin', async function () {
171 const res = await getVideoWithToken(server.url, server.accessToken, server.video.uuid, 200) 189 const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, servers[0].video.uuid, 200)
172 const video: VideoDetails = res.body 190 const video: VideoDetails = res.body
173 191
174 expect(video.blacklisted).to.be.true 192 expect(video.blacklisted).to.be.true
@@ -177,24 +195,24 @@ describe('Test video blacklist API validators', function () {
177 195
178 describe('When removing a video in blacklist', function () { 196 describe('When removing a video in blacklist', function () {
179 it('Should fail with a non authenticated user', async function () { 197 it('Should fail with a non authenticated user', async function () {
180 await removeVideoFromBlacklist(server.url, 'fake token', server.video.uuid, 401) 198 await removeVideoFromBlacklist(servers[0].url, 'fake token', servers[0].video.uuid, 401)
181 }) 199 })
182 200
183 it('Should fail with a non admin user', async function () { 201 it('Should fail with a non admin user', async function () {
184 await removeVideoFromBlacklist(server.url, userAccessToken2, server.video.uuid, 403) 202 await removeVideoFromBlacklist(servers[0].url, userAccessToken2, servers[0].video.uuid, 403)
185 }) 203 })
186 204
187 it('Should fail with an incorrect id', async function () { 205 it('Should fail with an incorrect id', async function () {
188 await removeVideoFromBlacklist(server.url, server.accessToken, 'hello', 400) 206 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, 'hello', 400)
189 }) 207 })
190 208
191 it('Should fail with a not blacklisted video', async function () { 209 it('Should fail with a not blacklisted video', async function () {
192 // The video was not added to the blacklist so it should fail 210 // The video was not added to the blacklist so it should fail
193 await removeVideoFromBlacklist(server.url, server.accessToken, notBlacklistedVideoId, 404) 211 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, notBlacklistedVideoId, 404)
194 }) 212 })
195 213
196 it('Should succeed with the correct params', async function () { 214 it('Should succeed with the correct params', async function () {
197 await removeVideoFromBlacklist(server.url, server.accessToken, server.video.uuid, 204) 215 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, servers[0].video.uuid, 204)
198 }) 216 })
199 }) 217 })
200 218
@@ -202,28 +220,28 @@ describe('Test video blacklist API validators', function () {
202 const basePath = '/api/v1/videos/blacklist/' 220 const basePath = '/api/v1/videos/blacklist/'
203 221
204 it('Should fail with a non authenticated user', async function () { 222 it('Should fail with a non authenticated user', async function () {
205 await getBlacklistedVideosList(server.url, 'fake token', 401) 223 await getBlacklistedVideosList(servers[0].url, 'fake token', 401)
206 }) 224 })
207 225
208 it('Should fail with a non admin user', async function () { 226 it('Should fail with a non admin user', async function () {
209 await getBlacklistedVideosList(server.url, userAccessToken2, 403) 227 await getBlacklistedVideosList(servers[0].url, userAccessToken2, 403)
210 }) 228 })
211 229
212 it('Should fail with a bad start pagination', async function () { 230 it('Should fail with a bad start pagination', async function () {
213 await checkBadStartPagination(server.url, basePath, server.accessToken) 231 await checkBadStartPagination(servers[0].url, basePath, servers[0].accessToken)
214 }) 232 })
215 233
216 it('Should fail with a bad count pagination', async function () { 234 it('Should fail with a bad count pagination', async function () {
217 await checkBadCountPagination(server.url, basePath, server.accessToken) 235 await checkBadCountPagination(servers[0].url, basePath, servers[0].accessToken)
218 }) 236 })
219 237
220 it('Should fail with an incorrect sort', async function () { 238 it('Should fail with an incorrect sort', async function () {
221 await checkBadSortPagination(server.url, basePath, server.accessToken) 239 await checkBadSortPagination(servers[0].url, basePath, servers[0].accessToken)
222 }) 240 })
223 }) 241 })
224 242
225 after(async function () { 243 after(async function () {
226 killallServers([ server ]) 244 killallServers(servers)
227 245
228 // Keep the logs if the test failed 246 // Keep the logs if the test failed
229 if (this['ok']) { 247 if (this['ok']) {
diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts
index 9bdb78491..97f467aae 100644
--- a/server/tests/api/videos/index.ts
+++ b/server/tests/api/videos/index.ts
@@ -3,7 +3,6 @@ import './services'
3import './single-server' 3import './single-server'
4import './video-abuse' 4import './video-abuse'
5import './video-blacklist' 5import './video-blacklist'
6import './video-blacklist-management'
7import './video-captions' 6import './video-captions'
8import './video-change-ownership' 7import './video-change-ownership'
9import './video-channels' 8import './video-channels'
diff --git a/server/tests/api/videos/video-blacklist-management.ts b/server/tests/api/videos/video-blacklist-management.ts
deleted file mode 100644
index 61411e30d..000000000
--- a/server/tests/api/videos/video-blacklist-management.ts
+++ /dev/null
@@ -1,192 +0,0 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import { orderBy } from 'lodash'
5import 'mocha'
6import {
7 addVideoToBlacklist,
8 flushAndRunMultipleServers,
9 getBlacklistedVideosList,
10 getMyVideos,
11 getSortedBlacklistedVideosList,
12 getVideosList,
13 killallServers,
14 removeVideoFromBlacklist,
15 ServerInfo,
16 setAccessTokensToServers,
17 updateVideoBlacklist,
18 uploadVideo
19} from '../../../../shared/utils/index'
20import { doubleFollow } from '../../../../shared/utils/server/follows'
21import { waitJobs } from '../../../../shared/utils/server/jobs'
22import { VideoAbuse } from '../../../../shared/models/videos'
23
24const expect = chai.expect
25
26describe('Test video blacklist management', function () {
27 let servers: ServerInfo[] = []
28 let videoId: number
29
30 async function blacklistVideosOnServer (server: ServerInfo) {
31 const res = await getVideosList(server.url)
32
33 const videos = res.body.data
34 for (let video of videos) {
35 await addVideoToBlacklist(server.url, server.accessToken, video.id, 'super reason')
36 }
37 }
38
39 before(async function () {
40 this.timeout(50000)
41
42 // Run servers
43 servers = await flushAndRunMultipleServers(2)
44
45 // Get the access tokens
46 await setAccessTokensToServers(servers)
47
48 // Server 1 and server 2 follow each other
49 await doubleFollow(servers[0], servers[1])
50
51 // Upload 2 videos on server 2
52 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' })
53 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' })
54
55 // Wait videos propagation, server 2 has transcoding enabled
56 await waitJobs(servers)
57
58 // Blacklist the two videos on server 1
59 await blacklistVideosOnServer(servers[0])
60 })
61
62 describe('When listing blacklisted videos', function () {
63 it('Should display all the blacklisted videos', async function () {
64 const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken)
65
66 expect(res.body.total).to.equal(2)
67
68 const blacklistedVideos = res.body.data
69 expect(blacklistedVideos).to.be.an('array')
70 expect(blacklistedVideos.length).to.equal(2)
71
72 for (const blacklistedVideo of blacklistedVideos) {
73 expect(blacklistedVideo.reason).to.equal('super reason')
74 videoId = blacklistedVideo.video.id
75 }
76 })
77
78 it('Should get the correct sort when sorting by descending id', async function () {
79 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id')
80 expect(res.body.total).to.equal(2)
81
82 const blacklistedVideos = res.body.data
83 expect(blacklistedVideos).to.be.an('array')
84 expect(blacklistedVideos.length).to.equal(2)
85
86 const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ])
87
88 expect(blacklistedVideos).to.deep.equal(result)
89 })
90
91 it('Should get the correct sort when sorting by descending video name', async function () {
92 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
93 expect(res.body.total).to.equal(2)
94
95 const blacklistedVideos = res.body.data
96 expect(blacklistedVideos).to.be.an('array')
97 expect(blacklistedVideos.length).to.equal(2)
98
99 const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ])
100
101 expect(blacklistedVideos).to.deep.equal(result)
102 })
103
104 it('Should get the correct sort when sorting by ascending creation date', async function () {
105 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt')
106 expect(res.body.total).to.equal(2)
107
108 const blacklistedVideos = res.body.data
109 expect(blacklistedVideos).to.be.an('array')
110 expect(blacklistedVideos.length).to.equal(2)
111
112 const result = orderBy(res.body.data, [ 'createdAt' ])
113
114 expect(blacklistedVideos).to.deep.equal(result)
115 })
116 })
117
118 describe('When updating blacklisted videos', function () {
119 it('Should change the reason', async function () {
120 await updateVideoBlacklist(servers[0].url, servers[0].accessToken, videoId, 'my super reason updated')
121
122 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
123 const video = res.body.data.find(b => b.video.id === videoId)
124
125 expect(video.reason).to.equal('my super reason updated')
126 })
127 })
128
129 describe('When listing my videos', function () {
130 it('Should display blacklisted videos', async function () {
131 await blacklistVideosOnServer(servers[1])
132
133 const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 5)
134
135 expect(res.body.total).to.equal(2)
136 expect(res.body.data).to.have.lengthOf(2)
137
138 for (const video of res.body.data) {
139 expect(video.blacklisted).to.be.true
140 expect(video.blacklistedReason).to.equal('super reason')
141 }
142 })
143 })
144
145 describe('When removing a blacklisted video', function () {
146 let videoToRemove: VideoAbuse
147 let blacklist = []
148
149 it('Should not have any video in videos list on server 1', async function () {
150 const res = await getVideosList(servers[0].url)
151 expect(res.body.total).to.equal(0)
152 expect(res.body.data).to.be.an('array')
153 expect(res.body.data.length).to.equal(0)
154 })
155
156 it('Should remove a video from the blacklist on server 1', async function () {
157 // Get one video in the blacklist
158 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
159 videoToRemove = res.body.data[0]
160 blacklist = res.body.data.slice(1)
161
162 // Remove it
163 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.video.id)
164 })
165
166 it('Should have the ex-blacklisted video in videos list on server 1', async function () {
167 const res = await getVideosList(servers[0].url)
168 expect(res.body.total).to.equal(1)
169
170 const videos = res.body.data
171 expect(videos).to.be.an('array')
172 expect(videos.length).to.equal(1)
173
174 expect(videos[0].name).to.equal(videoToRemove.video.name)
175 expect(videos[0].id).to.equal(videoToRemove.video.id)
176 })
177
178 it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () {
179 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
180 expect(res.body.total).to.equal(1)
181
182 const videos = res.body.data
183 expect(videos).to.be.an('array')
184 expect(videos.length).to.equal(1)
185 expect(videos).to.deep.equal(blacklist)
186 })
187 })
188
189 after(async function () {
190 killallServers(servers)
191 })
192})
diff --git a/server/tests/api/videos/video-blacklist.ts b/server/tests/api/videos/video-blacklist.ts
index 1cce82d2a..d39ad63b4 100644
--- a/server/tests/api/videos/video-blacklist.ts
+++ b/server/tests/api/videos/video-blacklist.ts
@@ -1,24 +1,43 @@
1/* tslint:disable:no-unused-expression */ 1/* tslint:disable:no-unused-expression */
2 2
3import * as chai from 'chai' 3import * as chai from 'chai'
4import { orderBy } from 'lodash'
4import 'mocha' 5import 'mocha'
5import { 6import {
6 addVideoToBlacklist, 7 addVideoToBlacklist,
7 flushAndRunMultipleServers, 8 flushAndRunMultipleServers,
9 getBlacklistedVideosList,
10 getMyVideos,
11 getSortedBlacklistedVideosList,
8 getVideosList, 12 getVideosList,
9 killallServers, 13 killallServers,
14 removeVideoFromBlacklist,
10 searchVideo, 15 searchVideo,
11 ServerInfo, 16 ServerInfo,
12 setAccessTokensToServers, 17 setAccessTokensToServers,
13 uploadVideo 18 updateVideo,
19 updateVideoBlacklist,
20 uploadVideo,
21 viewVideo
14} from '../../../../shared/utils/index' 22} from '../../../../shared/utils/index'
15import { doubleFollow } from '../../../../shared/utils/server/follows' 23import { doubleFollow } from '../../../../shared/utils/server/follows'
16import { waitJobs } from '../../../../shared/utils/server/jobs' 24import { waitJobs } from '../../../../shared/utils/server/jobs'
25import { VideoBlacklist } from '../../../../shared/models/videos'
17 26
18const expect = chai.expect 27const expect = chai.expect
19 28
20describe('Test video blacklists', function () { 29describe('Test video blacklist management', function () {
21 let servers: ServerInfo[] = [] 30 let servers: ServerInfo[] = []
31 let videoId: number
32
33 async function blacklistVideosOnServer (server: ServerInfo) {
34 const res = await getVideosList(server.url)
35
36 const videos = res.body.data
37 for (let video of videos) {
38 await addVideoToBlacklist(server.url, server.accessToken, video.id, 'super reason')
39 }
40 }
22 41
23 before(async function () { 42 before(async function () {
24 this.timeout(50000) 43 this.timeout(50000)
@@ -32,58 +51,270 @@ describe('Test video blacklists', function () {
32 // Server 1 and server 2 follow each other 51 // Server 1 and server 2 follow each other
33 await doubleFollow(servers[0], servers[1]) 52 await doubleFollow(servers[0], servers[1])
34 53
35 // Upload a video on server 2 54 // Upload 2 videos on server 2
36 const videoAttributes = { 55 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' })
37 name: 'my super name for server 2', 56 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' })
38 description: 'my super description for server 2'
39 }
40 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes)
41 57
42 // Wait videos propagation, server 2 has transcoding enabled 58 // Wait videos propagation, server 2 has transcoding enabled
43 await waitJobs(servers) 59 await waitJobs(servers)
44 60
45 const res = await getVideosList(servers[0].url) 61 // Blacklist the two videos on server 1
46 const videos = res.body.data 62 await blacklistVideosOnServer(servers[0])
63 })
64
65 describe('When listing/searching videos', function () {
47 66
48 expect(videos.length).to.equal(1) 67 it('Should not have the video blacklisted in videos list/search on server 1', async function () {
68 {
69 const res = await getVideosList(servers[ 0 ].url)
49 70
50 servers[0].remoteVideo = videos.find(video => video.name === 'my super name for server 2') 71 expect(res.body.total).to.equal(0)
72 expect(res.body.data).to.be.an('array')
73 expect(res.body.data.length).to.equal(0)
74 }
75
76 {
77 const res = await searchVideo(servers[ 0 ].url, 'name')
78
79 expect(res.body.total).to.equal(0)
80 expect(res.body.data).to.be.an('array')
81 expect(res.body.data.length).to.equal(0)
82 }
83 })
84
85 it('Should have the blacklisted video in videos list/search on server 2', async function () {
86 {
87 const res = await getVideosList(servers[ 1 ].url)
88
89 expect(res.body.total).to.equal(2)
90 expect(res.body.data).to.be.an('array')
91 expect(res.body.data.length).to.equal(2)
92 }
93
94 {
95 const res = await searchVideo(servers[ 1 ].url, 'video')
96
97 expect(res.body.total).to.equal(2)
98 expect(res.body.data).to.be.an('array')
99 expect(res.body.data.length).to.equal(2)
100 }
101 })
51 }) 102 })
52 103
53 it('Should blacklist a remote video on server 1', async function () { 104 describe('When listing blacklisted videos', function () {
54 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, servers[0].remoteVideo.id) 105 it('Should display all the blacklisted videos', async function () {
106 const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken)
107
108 expect(res.body.total).to.equal(2)
109
110 const blacklistedVideos = res.body.data
111 expect(blacklistedVideos).to.be.an('array')
112 expect(blacklistedVideos.length).to.equal(2)
113
114 for (const blacklistedVideo of blacklistedVideos) {
115 expect(blacklistedVideo.reason).to.equal('super reason')
116 videoId = blacklistedVideo.video.id
117 }
118 })
119
120 it('Should get the correct sort when sorting by descending id', async function () {
121 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id')
122 expect(res.body.total).to.equal(2)
123
124 const blacklistedVideos = res.body.data
125 expect(blacklistedVideos).to.be.an('array')
126 expect(blacklistedVideos.length).to.equal(2)
127
128 const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ])
129
130 expect(blacklistedVideos).to.deep.equal(result)
131 })
132
133 it('Should get the correct sort when sorting by descending video name', async function () {
134 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
135 expect(res.body.total).to.equal(2)
136
137 const blacklistedVideos = res.body.data
138 expect(blacklistedVideos).to.be.an('array')
139 expect(blacklistedVideos.length).to.equal(2)
140
141 const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ])
142
143 expect(blacklistedVideos).to.deep.equal(result)
144 })
145
146 it('Should get the correct sort when sorting by ascending creation date', async function () {
147 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt')
148 expect(res.body.total).to.equal(2)
149
150 const blacklistedVideos = res.body.data
151 expect(blacklistedVideos).to.be.an('array')
152 expect(blacklistedVideos.length).to.equal(2)
153
154 const result = orderBy(res.body.data, [ 'createdAt' ])
155
156 expect(blacklistedVideos).to.deep.equal(result)
157 })
55 }) 158 })
56 159
57 it('Should not have the video blacklisted in videos list on server 1', async function () { 160 describe('When updating blacklisted videos', function () {
58 const res = await getVideosList(servers[0].url) 161 it('Should change the reason', async function () {
162 await updateVideoBlacklist(servers[0].url, servers[0].accessToken, videoId, 'my super reason updated')
163
164 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
165 const video = res.body.data.find(b => b.video.id === videoId)
59 166
60 expect(res.body.total).to.equal(0) 167 expect(video.reason).to.equal('my super reason updated')
61 expect(res.body.data).to.be.an('array') 168 })
62 expect(res.body.data.length).to.equal(0)
63 }) 169 })
64 170
65 it('Should not have the video blacklisted in videos search on server 1', async function () { 171 describe('When listing my videos', function () {
66 const res = await searchVideo(servers[0].url, 'name') 172 it('Should display blacklisted videos', async function () {
173 await blacklistVideosOnServer(servers[1])
174
175 const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 5)
67 176
68 expect(res.body.total).to.equal(0) 177 expect(res.body.total).to.equal(2)
69 expect(res.body.data).to.be.an('array') 178 expect(res.body.data).to.have.lengthOf(2)
70 expect(res.body.data.length).to.equal(0) 179
180 for (const video of res.body.data) {
181 expect(video.blacklisted).to.be.true
182 expect(video.blacklistedReason).to.equal('super reason')
183 }
184 })
71 }) 185 })
72 186
73 it('Should have the blacklisted video in videos list on server 2', async function () { 187 describe('When removing a blacklisted video', function () {
74 const res = await getVideosList(servers[1].url) 188 let videoToRemove: VideoBlacklist
189 let blacklist = []
190
191 it('Should not have any video in videos list on server 1', async function () {
192 const res = await getVideosList(servers[0].url)
193 expect(res.body.total).to.equal(0)
194 expect(res.body.data).to.be.an('array')
195 expect(res.body.data.length).to.equal(0)
196 })
197
198 it('Should remove a video from the blacklist on server 1', async function () {
199 // Get one video in the blacklist
200 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
201 videoToRemove = res.body.data[0]
202 blacklist = res.body.data.slice(1)
203
204 // Remove it
205 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.video.id)
206 })
207
208 it('Should have the ex-blacklisted video in videos list on server 1', async function () {
209 const res = await getVideosList(servers[0].url)
210 expect(res.body.total).to.equal(1)
211
212 const videos = res.body.data
213 expect(videos).to.be.an('array')
214 expect(videos.length).to.equal(1)
215
216 expect(videos[0].name).to.equal(videoToRemove.video.name)
217 expect(videos[0].id).to.equal(videoToRemove.video.id)
218 })
219
220 it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () {
221 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
222 expect(res.body.total).to.equal(1)
75 223
76 expect(res.body.total).to.equal(1) 224 const videos = res.body.data
77 expect(res.body.data).to.be.an('array') 225 expect(videos).to.be.an('array')
78 expect(res.body.data.length).to.equal(1) 226 expect(videos.length).to.equal(1)
227 expect(videos).to.deep.equal(blacklist)
228 })
79 }) 229 })
80 230
81 it('Should have the video blacklisted in videos search on server 2', async function () { 231 describe('When blacklisting local videos', function () {
82 const res = await searchVideo(servers[1].url, 'name') 232 let video3UUID: string
233 let video4UUID: string
234
235 before(async function () {
236 this.timeout(10000)
237
238 {
239 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'Video 3' })
240 video3UUID = res.body.video.uuid
241 }
242 {
243 const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'Video 4' })
244 video4UUID = res.body.video.uuid
245 }
246
247 await waitJobs(servers)
248 })
249
250 it('Should blacklist video 3 and keep it federated', async function () {
251 this.timeout(10000)
252
253 await addVideoToBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video3UUID, 'super reason', false)
254
255 await waitJobs(servers)
256
257 {
258 const res = await getVideosList(servers[ 0 ].url)
259 expect(res.body.data.find(v => v.uuid === video3UUID)).to.be.undefined
260 }
261
262 {
263 const res = await getVideosList(servers[ 1 ].url)
264 expect(res.body.data.find(v => v.uuid === video3UUID)).to.not.be.undefined
265 }
266 })
267
268 it('Should unfederate the video', async function () {
269 this.timeout(10000)
270
271 await addVideoToBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video4UUID, 'super reason', true)
272
273 await waitJobs(servers)
274
275 for (const server of servers) {
276 const res = await getVideosList(server.url)
277 expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined
278 }
279 })
280
281 it('Should have the video unfederated even after an Update AP message', async function () {
282 this.timeout(10000)
283
284 await updateVideo(servers[ 0 ].url, servers[ 0 ].accessToken, video4UUID, { description: 'super description' })
285
286 await waitJobs(servers)
287
288 for (const server of servers) {
289 const res = await getVideosList(server.url)
290 expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined
291 }
292 })
293
294 it('Should have the correct video blacklist unfederate attribute', async function () {
295 const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt')
296
297 const blacklistedVideos: VideoBlacklist[] = res.body.data
298 const video3Blacklisted = blacklistedVideos.find(b => b.video.uuid === video3UUID)
299 const video4Blacklisted = blacklistedVideos.find(b => b.video.uuid === video4UUID)
300
301 expect(video3Blacklisted.unfederated).to.be.false
302 expect(video4Blacklisted.unfederated).to.be.true
303 })
304
305 it('Should remove the video from blacklist and refederate the video', async function () {
306 this.timeout(10000)
307
308 await removeVideoFromBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video4UUID)
309
310 await waitJobs(servers)
311
312 for (const server of servers) {
313 const res = await getVideosList(server.url)
314 expect(res.body.data.find(v => v.uuid === video4UUID)).to.not.be.undefined
315 }
316 })
83 317
84 expect(res.body.total).to.equal(1)
85 expect(res.body.data).to.be.an('array')
86 expect(res.body.data.length).to.equal(1)
87 }) 318 })
88 319
89 after(async function () { 320 after(async function () {
diff --git a/shared/models/videos/blacklist/video-blacklist-create.model.ts b/shared/models/videos/blacklist/video-blacklist-create.model.ts
index 89c69cb56..6e7d36421 100644
--- a/shared/models/videos/blacklist/video-blacklist-create.model.ts
+++ b/shared/models/videos/blacklist/video-blacklist-create.model.ts
@@ -1,3 +1,4 @@
1export interface VideoBlacklistCreate { 1export interface VideoBlacklistCreate {
2 reason?: string 2 reason?: string
3 unfederate?: boolean
3} 4}
diff --git a/shared/models/videos/blacklist/video-blacklist.model.ts b/shared/models/videos/blacklist/video-blacklist.model.ts
index ef4e5e3a2..4bd976190 100644
--- a/shared/models/videos/blacklist/video-blacklist.model.ts
+++ b/shared/models/videos/blacklist/video-blacklist.model.ts
@@ -2,6 +2,7 @@ export interface VideoBlacklist {
2 id: number 2 id: number
3 createdAt: Date 3 createdAt: Date
4 updatedAt: Date 4 updatedAt: Date
5 unfederated: boolean
5 reason?: string 6 reason?: string
6 7
7 video: { 8 video: {
diff --git a/shared/utils/server/servers.ts b/shared/utils/server/servers.ts
index 568385a41..1e9c83c72 100644
--- a/shared/utils/server/servers.ts
+++ b/shared/utils/server/servers.ts
@@ -145,8 +145,12 @@ function runServer (serverNumber: number, configOverride?: Object, args = []) {
145 if (dontContinue === true) return 145 if (dontContinue === true) return
146 146
147 server.app.stdout.removeListener('data', onStdout) 147 server.app.stdout.removeListener('data', onStdout)
148
149 process.on('exit', () => process.kill(server.app.pid))
150
148 res(server) 151 res(server)
149 }) 152 })
153
150 }) 154 })
151} 155}
152 156
diff --git a/shared/utils/videos/video-blacklist.ts b/shared/utils/videos/video-blacklist.ts
index 2c176fde0..f2ae0ed26 100644
--- a/shared/utils/videos/video-blacklist.ts
+++ b/shared/utils/videos/video-blacklist.ts
@@ -1,11 +1,18 @@
1import * as request from 'supertest' 1import * as request from 'supertest'
2 2
3function addVideoToBlacklist (url: string, token: string, videoId: number | string, reason?: string, specialStatus = 204) { 3function addVideoToBlacklist (
4 url: string,
5 token: string,
6 videoId: number | string,
7 reason?: string,
8 unfederate?: boolean,
9 specialStatus = 204
10) {
4 const path = '/api/v1/videos/' + videoId + '/blacklist' 11 const path = '/api/v1/videos/' + videoId + '/blacklist'
5 12
6 return request(url) 13 return request(url)
7 .post(path) 14 .post(path)
8 .send({ reason }) 15 .send({ reason, unfederate })
9 .set('Accept', 'application/json') 16 .set('Accept', 'application/json')
10 .set('Authorization', 'Bearer ' + token) 17 .set('Authorization', 'Bearer ' + token)
11 .expect(specialStatus) 18 .expect(specialStatus)