aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-10-12 15:26:04 +0200
committerChocobozzz <me@florianbigard.com>2018-10-16 16:41:36 +0200
commit7ad9b9846c44d198a736183fb186c2039f5236b5 (patch)
tree9c8456882a261c0522efb507f20e323c2741a0f8 /server/models/video
parentdffd5d127f49eb63d2b2b3133aec75ec1d7e4dcb (diff)
downloadPeerTube-7ad9b9846c44d198a736183fb186c2039f5236b5.tar.gz
PeerTube-7ad9b9846c44d198a736183fb186c2039f5236b5.tar.zst
PeerTube-7ad9b9846c44d198a736183fb186c2039f5236b5.zip
Add ability for users to block an account/instance on server side
Diffstat (limited to 'server/models/video')
-rw-r--r--server/models/video/video-comment.ts95
-rw-r--r--server/models/video/video.ts40
2 files changed, 104 insertions, 31 deletions
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index f84c1880c..08c6b3ff0 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,6 +1,17 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, 3 AllowNull,
4 BeforeDestroy,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 DataType,
9 ForeignKey,
10 IFindOptions,
11 Is,
12 Model,
13 Scopes,
14 Table,
4 UpdatedAt 15 UpdatedAt
5} from 'sequelize-typescript' 16} from 'sequelize-typescript'
6import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' 17import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
@@ -13,9 +24,11 @@ import { AccountModel } from '../account/account'
13import { ActorModel } from '../activitypub/actor' 24import { ActorModel } from '../activitypub/actor'
14import { AvatarModel } from '../avatar/avatar' 25import { AvatarModel } from '../avatar/avatar'
15import { ServerModel } from '../server/server' 26import { ServerModel } from '../server/server'
16import { getSort, throwIfNotValid } from '../utils' 27import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
17import { VideoModel } from './video' 28import { VideoModel } from './video'
18import { VideoChannelModel } from './video-channel' 29import { VideoChannelModel } from './video-channel'
30import { getServerActor } from '../../helpers/utils'
31import { UserModel } from '../account/user'
19 32
20enum ScopeNames { 33enum ScopeNames {
21 WITH_ACCOUNT = 'WITH_ACCOUNT', 34 WITH_ACCOUNT = 'WITH_ACCOUNT',
@@ -25,18 +38,29 @@ enum ScopeNames {
25} 38}
26 39
27@Scopes({ 40@Scopes({
28 [ScopeNames.ATTRIBUTES_FOR_API]: { 41 [ScopeNames.ATTRIBUTES_FOR_API]: (serverAccountId: number, userAccountId?: number) => {
29 attributes: { 42 return {
30 include: [ 43 attributes: {
31 [ 44 include: [
32 Sequelize.literal( 45 [
33 '(SELECT COUNT("replies"."id") ' + 46 Sequelize.literal(
34 'FROM "videoComment" AS "replies" ' + 47 '(' +
35 'WHERE "replies"."originCommentId" = "VideoCommentModel"."id")' 48 'WITH "blocklist" AS (' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' +
36 ), 49 'SELECT COUNT("replies"."id") - (' +
37 'totalReplies' 50 'SELECT COUNT("replies"."id") ' +
51 'FROM "videoComment" AS "replies" ' +
52 'WHERE "replies"."originCommentId" = "VideoCommentModel"."id" ' +
53 'AND "accountId" IN (SELECT "id" FROM "blocklist")' +
54 ')' +
55 'FROM "videoComment" AS "replies" ' +
56 'WHERE "replies"."originCommentId" = "VideoCommentModel"."id" ' +
57 'AND "accountId" NOT IN (SELECT "id" FROM "blocklist")' +
58 ')'
59 ),
60 'totalReplies'
61 ]
38 ] 62 ]
39 ] 63 }
40 } 64 }
41 }, 65 },
42 [ScopeNames.WITH_ACCOUNT]: { 66 [ScopeNames.WITH_ACCOUNT]: {
@@ -267,26 +291,47 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
267 return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_VIDEO ]).findOne(query) 291 return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_VIDEO ]).findOne(query)
268 } 292 }
269 293
270 static listThreadsForApi (videoId: number, start: number, count: number, sort: string) { 294 static async listThreadsForApi (videoId: number, start: number, count: number, sort: string, user?: UserModel) {
295 const serverActor = await getServerActor()
296 const serverAccountId = serverActor.Account.id
297 const userAccountId = user.Account.id
298
271 const query = { 299 const query = {
272 offset: start, 300 offset: start,
273 limit: count, 301 limit: count,
274 order: getSort(sort), 302 order: getSort(sort),
275 where: { 303 where: {
276 videoId, 304 videoId,
277 inReplyToCommentId: null 305 inReplyToCommentId: null,
306 accountId: {
307 [Sequelize.Op.notIn]: Sequelize.literal(
308 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
309 )
310 }
278 } 311 }
279 } 312 }
280 313
314 // FIXME: typings
315 const scopes: any[] = [
316 ScopeNames.WITH_ACCOUNT,
317 {
318 method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
319 }
320 ]
321
281 return VideoCommentModel 322 return VideoCommentModel
282 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ]) 323 .scope(scopes)
283 .findAndCountAll(query) 324 .findAndCountAll(query)
284 .then(({ rows, count }) => { 325 .then(({ rows, count }) => {
285 return { total: count, data: rows } 326 return { total: count, data: rows }
286 }) 327 })
287 } 328 }
288 329
289 static listThreadCommentsForApi (videoId: number, threadId: number) { 330 static async listThreadCommentsForApi (videoId: number, threadId: number, user?: UserModel) {
331 const serverActor = await getServerActor()
332 const serverAccountId = serverActor.Account.id
333 const userAccountId = user.Account.id
334
290 const query = { 335 const query = {
291 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ], 336 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ],
292 where: { 337 where: {
@@ -294,12 +339,24 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
294 [ Sequelize.Op.or ]: [ 339 [ Sequelize.Op.or ]: [
295 { id: threadId }, 340 { id: threadId },
296 { originCommentId: threadId } 341 { originCommentId: threadId }
297 ] 342 ],
343 accountId: {
344 [Sequelize.Op.notIn]: Sequelize.literal(
345 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
346 )
347 }
298 } 348 }
299 } 349 }
300 350
351 const scopes: any[] = [
352 ScopeNames.WITH_ACCOUNT,
353 {
354 method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
355 }
356 ]
357
301 return VideoCommentModel 358 return VideoCommentModel
302 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ]) 359 .scope(scopes)
303 .findAndCountAll(query) 360 .findAndCountAll(query)
304 .then(({ rows, count }) => { 361 .then(({ rows, count }) => {
305 return { total: count, data: rows } 362 return { total: count, data: rows }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 4f3f75613..eab99cba7 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -27,7 +27,7 @@ import {
27 Table, 27 Table,
28 UpdatedAt 28 UpdatedAt
29} from 'sequelize-typescript' 29} from 'sequelize-typescript'
30import { VideoPrivacy, VideoState } from '../../../shared' 30import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
31import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 31import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
32import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 32import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
33import { VideoFilter } from '../../../shared/models/videos/video-query.type' 33import { VideoFilter } from '../../../shared/models/videos/video-query.type'
@@ -70,7 +70,7 @@ import { AccountVideoRateModel } from '../account/account-video-rate'
70import { ActorModel } from '../activitypub/actor' 70import { ActorModel } from '../activitypub/actor'
71import { AvatarModel } from '../avatar/avatar' 71import { AvatarModel } from '../avatar/avatar'
72import { ServerModel } from '../server/server' 72import { ServerModel } from '../server/server'
73import { buildTrigramSearchIndex, createSimilarityAttribute, getVideoSort, throwIfNotValid } from '../utils' 73import { buildBlockedAccountSQL, buildTrigramSearchIndex, createSimilarityAttribute, getVideoSort, throwIfNotValid } from '../utils'
74import { TagModel } from './tag' 74import { TagModel } from './tag'
75import { VideoAbuseModel } from './video-abuse' 75import { VideoAbuseModel } from './video-abuse'
76import { VideoChannelModel } from './video-channel' 76import { VideoChannelModel } from './video-channel'
@@ -93,6 +93,7 @@ import {
93} from './video-format-utils' 93} from './video-format-utils'
94import * as validator from 'validator' 94import * as validator from 'validator'
95import { UserVideoHistoryModel } from '../account/user-video-history' 95import { UserVideoHistoryModel } from '../account/user-video-history'
96import { UserModel } from '../account/user'
96 97
97// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 98// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
98const indexes: Sequelize.DefineIndexesOptions[] = [ 99const indexes: Sequelize.DefineIndexesOptions[] = [
@@ -138,6 +139,7 @@ type ForAPIOptions = {
138} 139}
139 140
140type AvailableForListIDsOptions = { 141type AvailableForListIDsOptions = {
142 serverAccountId: number
141 actorId: number 143 actorId: number
142 includeLocalVideos: boolean 144 includeLocalVideos: boolean
143 filter?: VideoFilter 145 filter?: VideoFilter
@@ -151,6 +153,7 @@ type AvailableForListIDsOptions = {
151 accountId?: number 153 accountId?: number
152 videoChannelId?: number 154 videoChannelId?: number
153 trendingDays?: number 155 trendingDays?: number
156 user?: UserModel
154} 157}
155 158
156@Scopes({ 159@Scopes({
@@ -235,6 +238,15 @@ type AvailableForListIDsOptions = {
235 ) 238 )
236 } 239 }
237 ] 240 ]
241 },
242 channelId: {
243 [ Sequelize.Op.notIn ]: Sequelize.literal(
244 '(' +
245 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' +
246 buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) +
247 ')' +
248 ')'
249 )
238 } 250 }
239 }, 251 },
240 include: [] 252 include: []
@@ -975,10 +987,10 @@ export class VideoModel extends Model<VideoModel> {
975 videoChannelId?: number, 987 videoChannelId?: number,
976 actorId?: number 988 actorId?: number
977 trendingDays?: number, 989 trendingDays?: number,
978 userId?: number 990 user?: UserModel
979 }, countVideos = true) { 991 }, countVideos = true) {
980 if (options.filter && options.filter === 'all-local' && !options.userId) { 992 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
981 throw new Error('Try to filter all-local but no userId is provided') 993 throw new Error('Try to filter all-local but no user has not the see all videos right')
982 } 994 }
983 995
984 const query: IFindOptions<VideoModel> = { 996 const query: IFindOptions<VideoModel> = {
@@ -994,11 +1006,14 @@ export class VideoModel extends Model<VideoModel> {
994 query.group = 'VideoModel.id' 1006 query.group = 'VideoModel.id'
995 } 1007 }
996 1008
1009 const serverActor = await getServerActor()
1010
997 // actorId === null has a meaning, so just check undefined 1011 // actorId === null has a meaning, so just check undefined
998 const actorId = options.actorId !== undefined ? options.actorId : (await getServerActor()).id 1012 const actorId = options.actorId !== undefined ? options.actorId : serverActor.id
999 1013
1000 const queryOptions = { 1014 const queryOptions = {
1001 actorId, 1015 actorId,
1016 serverAccountId: serverActor.Account.id,
1002 nsfw: options.nsfw, 1017 nsfw: options.nsfw,
1003 categoryOneOf: options.categoryOneOf, 1018 categoryOneOf: options.categoryOneOf,
1004 licenceOneOf: options.licenceOneOf, 1019 licenceOneOf: options.licenceOneOf,
@@ -1010,7 +1025,7 @@ export class VideoModel extends Model<VideoModel> {
1010 accountId: options.accountId, 1025 accountId: options.accountId,
1011 videoChannelId: options.videoChannelId, 1026 videoChannelId: options.videoChannelId,
1012 includeLocalVideos: options.includeLocalVideos, 1027 includeLocalVideos: options.includeLocalVideos,
1013 userId: options.userId, 1028 user: options.user,
1014 trendingDays 1029 trendingDays
1015 } 1030 }
1016 1031
@@ -1033,7 +1048,7 @@ export class VideoModel extends Model<VideoModel> {
1033 tagsAllOf?: string[] 1048 tagsAllOf?: string[]
1034 durationMin?: number // seconds 1049 durationMin?: number // seconds
1035 durationMax?: number // seconds 1050 durationMax?: number // seconds
1036 userId?: number, 1051 user?: UserModel,
1037 filter?: VideoFilter 1052 filter?: VideoFilter
1038 }) { 1053 }) {
1039 const whereAnd = [] 1054 const whereAnd = []
@@ -1104,6 +1119,7 @@ export class VideoModel extends Model<VideoModel> {
1104 const serverActor = await getServerActor() 1119 const serverActor = await getServerActor()
1105 const queryOptions = { 1120 const queryOptions = {
1106 actorId: serverActor.id, 1121 actorId: serverActor.id,
1122 serverAccountId: serverActor.Account.id,
1107 includeLocalVideos: options.includeLocalVideos, 1123 includeLocalVideos: options.includeLocalVideos,
1108 nsfw: options.nsfw, 1124 nsfw: options.nsfw,
1109 categoryOneOf: options.categoryOneOf, 1125 categoryOneOf: options.categoryOneOf,
@@ -1111,7 +1127,7 @@ export class VideoModel extends Model<VideoModel> {
1111 languageOneOf: options.languageOneOf, 1127 languageOneOf: options.languageOneOf,
1112 tagsOneOf: options.tagsOneOf, 1128 tagsOneOf: options.tagsOneOf,
1113 tagsAllOf: options.tagsAllOf, 1129 tagsAllOf: options.tagsAllOf,
1114 userId: options.userId, 1130 user: options.user,
1115 filter: options.filter 1131 filter: options.filter
1116 } 1132 }
1117 1133
@@ -1287,7 +1303,7 @@ export class VideoModel extends Model<VideoModel> {
1287 1303
1288 private static async getAvailableForApi ( 1304 private static async getAvailableForApi (
1289 query: IFindOptions<VideoModel>, 1305 query: IFindOptions<VideoModel>,
1290 options: AvailableForListIDsOptions & { userId?: number}, 1306 options: AvailableForListIDsOptions,
1291 countVideos = true 1307 countVideos = true
1292 ) { 1308 ) {
1293 const idsScope = { 1309 const idsScope = {
@@ -1320,8 +1336,8 @@ export class VideoModel extends Model<VideoModel> {
1320 } 1336 }
1321 ] 1337 ]
1322 1338
1323 if (options.userId) { 1339 if (options.user) {
1324 apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.userId ] }) 1340 apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.user.id ] })
1325 } 1341 }
1326 1342
1327 const secondQuery = { 1343 const secondQuery = {