abuse-query-builder.ts 4.9 KB

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