abuse-query-builder.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { exists } from '@server/helpers/custom-validators/misc'
  2. import { AbuseFilter, AbuseState, AbuseVideoIs } from '@shared/models'
  3. import { buildBlockedAccountSQL, buildDirectionAndField } from '../utils'
  4. export type BuildAbusesQueryOptions = {
  5. start: number
  6. count: number
  7. sort: string
  8. // search
  9. search?: string
  10. searchReporter?: string
  11. searchReportee?: string
  12. // video related
  13. searchVideo?: string
  14. searchVideoChannel?: string
  15. videoIs?: AbuseVideoIs
  16. // filters
  17. id?: number
  18. predefinedReasonId?: number
  19. filter?: AbuseFilter
  20. state?: AbuseState
  21. // accountIds
  22. serverAccountId?: number
  23. userAccountId?: number
  24. reporterAccountId?: number
  25. }
  26. function buildAbuseListQuery (options: BuildAbusesQueryOptions, type: 'count' | 'id') {
  27. const whereAnd: string[] = []
  28. const replacements: any = {}
  29. const joins = [
  30. 'LEFT JOIN "videoAbuse" ON "videoAbuse"."abuseId" = "abuse"."id"',
  31. 'LEFT JOIN "video" ON "videoAbuse"."videoId" = "video"."id"',
  32. 'LEFT JOIN "videoBlacklist" ON "videoBlacklist"."videoId" = "video"."id"',
  33. 'LEFT JOIN "videoChannel" ON "video"."channelId" = "videoChannel"."id"',
  34. 'LEFT JOIN "account" "reporterAccount" ON "reporterAccount"."id" = "abuse"."reporterAccountId"',
  35. 'LEFT JOIN "account" "flaggedAccount" ON "flaggedAccount"."id" = "abuse"."flaggedAccountId"',
  36. 'LEFT JOIN "commentAbuse" ON "commentAbuse"."abuseId" = "abuse"."id"',
  37. 'LEFT JOIN "videoComment" ON "commentAbuse"."videoCommentId" = "videoComment"."id"'
  38. ]
  39. if (options.serverAccountId || options.userAccountId) {
  40. whereAnd.push(
  41. '"abuse"."reporterAccountId" IS NULL OR ' +
  42. '"abuse"."reporterAccountId" NOT IN (' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')'
  43. )
  44. }
  45. if (options.reporterAccountId) {
  46. whereAnd.push('"abuse"."reporterAccountId" = :reporterAccountId')
  47. replacements.reporterAccountId = options.reporterAccountId
  48. }
  49. if (options.search) {
  50. const searchWhereOr = [
  51. '"video"."name" ILIKE :search',
  52. '"videoChannel"."name" ILIKE :search',
  53. `"videoAbuse"."deletedVideo"->>'name' ILIKE :search`,
  54. `"videoAbuse"."deletedVideo"->'channel'->>'displayName' ILIKE :search`,
  55. '"reporterAccount"."name" ILIKE :search',
  56. '"flaggedAccount"."name" ILIKE :search'
  57. ]
  58. replacements.search = `%${options.search}%`
  59. whereAnd.push('(' + searchWhereOr.join(' OR ') + ')')
  60. }
  61. if (options.searchVideo) {
  62. whereAnd.push('"video"."name" ILIKE :searchVideo')
  63. replacements.searchVideo = `%${options.searchVideo}%`
  64. }
  65. if (options.searchVideoChannel) {
  66. whereAnd.push('"videoChannel"."name" ILIKE :searchVideoChannel')
  67. replacements.searchVideoChannel = `%${options.searchVideoChannel}%`
  68. }
  69. if (options.id) {
  70. whereAnd.push('"abuse"."id" = :id')
  71. replacements.id = options.id
  72. }
  73. if (options.state) {
  74. whereAnd.push('"abuse"."state" = :state')
  75. replacements.state = options.state
  76. }
  77. if (options.videoIs === 'deleted') {
  78. whereAnd.push('"videoAbuse"."deletedVideo" IS NOT NULL')
  79. } else if (options.videoIs === 'blacklisted') {
  80. whereAnd.push('"videoBlacklist"."id" IS NOT NULL')
  81. }
  82. if (options.predefinedReasonId) {
  83. whereAnd.push(':predefinedReasonId = ANY("abuse"."predefinedReasons")')
  84. replacements.predefinedReasonId = options.predefinedReasonId
  85. }
  86. if (options.filter === 'video') {
  87. whereAnd.push('"videoAbuse"."id" IS NOT NULL')
  88. } else if (options.filter === 'comment') {
  89. whereAnd.push('"commentAbuse"."id" IS NOT NULL')
  90. } else if (options.filter === 'account') {
  91. whereAnd.push('"videoAbuse"."id" IS NULL AND "commentAbuse"."id" IS NULL')
  92. }
  93. if (options.searchReporter) {
  94. whereAnd.push('"reporterAccount"."name" ILIKE :searchReporter')
  95. replacements.searchReporter = `%${options.searchReporter}%`
  96. }
  97. if (options.searchReportee) {
  98. whereAnd.push('"flaggedAccount"."name" ILIKE :searchReportee')
  99. replacements.searchReportee = `%${options.searchReportee}%`
  100. }
  101. const prefix = type === 'count'
  102. ? 'SELECT COUNT("abuse"."id") AS "total"'
  103. : 'SELECT "abuse"."id" '
  104. let suffix = ''
  105. if (type !== 'count') {
  106. if (options.sort) {
  107. const order = buildAbuseOrder(options.sort)
  108. suffix += `${order} `
  109. }
  110. if (exists(options.count)) {
  111. const count = parseInt(options.count + '', 10)
  112. suffix += `LIMIT ${count} `
  113. }
  114. if (exists(options.start)) {
  115. const start = parseInt(options.start + '', 10)
  116. suffix += `OFFSET ${start} `
  117. }
  118. }
  119. const where = whereAnd.length !== 0
  120. ? `WHERE ${whereAnd.join(' AND ')}`
  121. : ''
  122. return {
  123. query: `${prefix} FROM "abuse" ${joins.join(' ')} ${where} ${suffix}`,
  124. replacements
  125. }
  126. }
  127. function buildAbuseOrder (value: string) {
  128. const { direction, field } = buildDirectionAndField(value)
  129. return `ORDER BY "abuse"."${field}" ${direction}`
  130. }
  131. export {
  132. buildAbuseListQuery
  133. }