diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/models/utils.ts | 51 | ||||
-rw-r--r-- | server/models/video/video-abuse.ts | 66 |
2 files changed, 113 insertions, 4 deletions
diff --git a/server/models/utils.ts b/server/models/utils.ts index bdf2291f0..3e3825b32 100644 --- a/server/models/utils.ts +++ b/server/models/utils.ts | |||
@@ -219,6 +219,54 @@ function searchAttribute (sourceField, targetField) { | |||
219 | } | 219 | } |
220 | } | 220 | } |
221 | 221 | ||
222 | interface QueryStringFilterPrefixes { | ||
223 | [key: string]: string | { prefix: string, handler: Function, multiple?: boolean } | ||
224 | } | ||
225 | |||
226 | function parseQueryStringFilter (q: string, prefixes: QueryStringFilterPrefixes) { | ||
227 | const tokens = q // tokenize only if we have a querystring | ||
228 | ? [].concat.apply([], q.split('"').map((v, i) => i % 2 ? v : v.split(' '))).filter(Boolean) | ||
229 | : [] | ||
230 | |||
231 | // TODO: when Typescript supports Object.fromEntries, replace with the Object method | ||
232 | function fromEntries<T> (entries: [keyof T, T[keyof T]][]): T { | ||
233 | return entries.reduce( | ||
234 | (acc, [ key, value ]) => ({ ...acc, [key]: value }), | ||
235 | {} as T | ||
236 | ) | ||
237 | } | ||
238 | |||
239 | const objectMap = (obj, fn) => fromEntries( | ||
240 | Object.entries(obj).map( | ||
241 | ([ k, v ], i) => [ k, fn(v, k, i) ] | ||
242 | ) | ||
243 | ) | ||
244 | |||
245 | return { | ||
246 | // search is the querystring minus defined filters | ||
247 | search: tokens.filter(e => !Object.values(prefixes).some(p => { | ||
248 | if (typeof p === "string") { | ||
249 | return e.startsWith(p) | ||
250 | } else { | ||
251 | return e.startsWith(p.prefix) | ||
252 | } | ||
253 | })).join(' '), | ||
254 | // filters defined in prefixes are added under their own name | ||
255 | ...objectMap(prefixes, v => { | ||
256 | if (typeof v === "string") { | ||
257 | return tokens.filter(e => e.startsWith(v)).map(e => e.slice(v.length)) | ||
258 | } else { | ||
259 | const _tokens = tokens.filter(e => e.startsWith(v.prefix)).map(e => e.slice(v.prefix.length)).map(v.handler) | ||
260 | return !v.multiple | ||
261 | ? _tokens.length > 0 | ||
262 | ? _tokens[0] | ||
263 | : '' | ||
264 | : _tokens | ||
265 | } | ||
266 | }) | ||
267 | } | ||
268 | } | ||
269 | |||
222 | // --------------------------------------------------------------------------- | 270 | // --------------------------------------------------------------------------- |
223 | 271 | ||
224 | export { | 272 | export { |
@@ -241,7 +289,8 @@ export { | |||
241 | getFollowsSort, | 289 | getFollowsSort, |
242 | buildDirectionAndField, | 290 | buildDirectionAndField, |
243 | createSafeIn, | 291 | createSafeIn, |
244 | searchAttribute | 292 | searchAttribute, |
293 | parseQueryStringFilter | ||
245 | } | 294 | } |
246 | 295 | ||
247 | // --------------------------------------------------------------------------- | 296 | // --------------------------------------------------------------------------- |
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts index 628f1caa6..b1f8fed90 100644 --- a/server/models/video/video-abuse.ts +++ b/server/models/video/video-abuse.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | isVideoAbuseStateValid | 9 | isVideoAbuseStateValid |
10 | } from '../../helpers/custom-validators/video-abuses' | 10 | } from '../../helpers/custom-validators/video-abuses' |
11 | import { AccountModel } from '../account/account' | 11 | import { AccountModel } from '../account/account' |
12 | import { buildBlockedAccountSQL, getSort, throwIfNotValid, searchAttribute } from '../utils' | 12 | import { buildBlockedAccountSQL, getSort, throwIfNotValid, searchAttribute, parseQueryStringFilter } from '../utils' |
13 | import { VideoModel } from './video' | 13 | import { VideoModel } from './video' |
14 | import { VideoAbuseState, VideoDetails } from '../../../shared' | 14 | import { VideoAbuseState, VideoDetails } from '../../../shared' |
15 | import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' | 15 | import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' |
@@ -26,10 +26,17 @@ export enum ScopeNames { | |||
26 | 26 | ||
27 | @Scopes(() => ({ | 27 | @Scopes(() => ({ |
28 | [ScopeNames.FOR_API]: (options: { | 28 | [ScopeNames.FOR_API]: (options: { |
29 | // search | ||
29 | search?: string | 30 | search?: string |
30 | searchReporter?: string | 31 | searchReporter?: string |
32 | searchReportee?: string | ||
31 | searchVideo?: string | 33 | searchVideo?: string |
32 | searchVideoChannel?: string | 34 | searchVideoChannel?: string |
35 | // filters | ||
36 | id?: number | ||
37 | state?: VideoAbuseState | ||
38 | is?: any | ||
39 | // accountIds | ||
33 | serverAccountId: number | 40 | serverAccountId: number |
34 | userAccountId: number | 41 | userAccountId: number |
35 | }) => { | 42 | }) => { |
@@ -71,6 +78,24 @@ export enum ScopeNames { | |||
71 | }) | 78 | }) |
72 | } | 79 | } |
73 | 80 | ||
81 | if (options.id) { | ||
82 | where = Object.assign(where, { | ||
83 | id: options.id | ||
84 | }) | ||
85 | } | ||
86 | |||
87 | if (options.state) { | ||
88 | where = Object.assign(where, { | ||
89 | state: options.state | ||
90 | }) | ||
91 | } | ||
92 | |||
93 | if (options.is) { | ||
94 | where = Object.assign(where, { | ||
95 | ...options.is | ||
96 | }) | ||
97 | } | ||
98 | |||
74 | return { | 99 | return { |
75 | attributes: { | 100 | attributes: { |
76 | include: [ | 101 | include: [ |
@@ -167,7 +192,13 @@ export enum ScopeNames { | |||
167 | }, | 192 | }, |
168 | { | 193 | { |
169 | model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, { withAccount: true } as SummaryOptions ] }), | 194 | model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, { withAccount: true } as SummaryOptions ] }), |
170 | where: searchAttribute(options.searchVideoChannel, 'name') | 195 | where: searchAttribute(options.searchVideoChannel, 'name'), |
196 | include: [ | ||
197 | { | ||
198 | model: AccountModel, | ||
199 | where: searchAttribute(options.searchReportee, 'name') | ||
200 | } | ||
201 | ] | ||
171 | }, | 202 | }, |
172 | { | 203 | { |
173 | attributes: [ 'id', 'reason', 'unfederated' ], | 204 | attributes: [ 'id', 'reason', 'unfederated' ], |
@@ -280,7 +311,36 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> { | |||
280 | } | 311 | } |
281 | 312 | ||
282 | const filters = { | 313 | const filters = { |
283 | search, | 314 | ...parseQueryStringFilter(search, { |
315 | id: { | ||
316 | prefix: '#', | ||
317 | handler: v => v | ||
318 | }, | ||
319 | state: { | ||
320 | prefix: 'state:', | ||
321 | handler: v => { | ||
322 | if (v === "accepted") return VideoAbuseState.ACCEPTED | ||
323 | if (v === "pending") return VideoAbuseState.PENDING | ||
324 | if (v === "rejected") return VideoAbuseState.REJECTED | ||
325 | return undefined | ||
326 | } | ||
327 | }, | ||
328 | is: { | ||
329 | prefix: 'is:', | ||
330 | handler: v => { | ||
331 | if (v === "deleted") return { deletedVideo: { [Op.not]: null } } | ||
332 | return undefined | ||
333 | } | ||
334 | }, | ||
335 | searchReporter: { | ||
336 | prefix: 'reporter:', | ||
337 | handler: v => v | ||
338 | }, | ||
339 | searchReportee: { | ||
340 | prefix: 'reportee:', | ||
341 | handler: v => v | ||
342 | } | ||
343 | }), | ||
284 | serverAccountId, | 344 | serverAccountId, |
285 | userAccountId | 345 | userAccountId |
286 | } | 346 | } |