abuse-query-builder.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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 releated
  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('"abuse"."reporterAccountId" NOT IN (' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
  41. }
  42. if (options.reporterAccountId) {
  43. whereAnd.push('"abuse"."reporterAccountId" = :reporterAccountId')
  44. replacements.reporterAccountId = options.reporterAccountId
  45. }
  46. if (options.search) {
  47. const searchWhereOr = [
  48. '"video"."name" ILIKE :search',
  49. '"videoChannel"."name" ILIKE :search',
  50. `"videoAbuse"."deletedVideo"->>'name' ILIKE :search`,
  51. `"videoAbuse"."deletedVideo"->'channel'->>'displayName' ILIKE :search`,
  52. '"reporterAccount"."name" ILIKE :search',
  53. '"flaggedAccount"."name" ILIKE :search'
  54. ]
  55. replacements.search = `%${options.search}%`
  56. whereAnd.push('(' + searchWhereOr.join(' OR ') + ')')
  57. }
  58. if (options.searchVideo) {
  59. whereAnd.push('"video"."name" ILIKE :searchVideo')
  60. replacements.searchVideo = `%${options.searchVideo}%`
  61. }
  62. if (options.searchVideoChannel) {
  63. whereAnd.push('"videoChannel"."name" ILIKE :searchVideoChannel')
  64. replacements.searchVideoChannel = `%${options.searchVideoChannel}%`
  65. }
  66. if (options.id) {
  67. whereAnd.push('"abuse"."id" = :id')
  68. replacements.id = options.id
  69. }
  70. if (options.state) {
  71. whereAnd.push('"abuse"."state" = :state')
  72. replacements.state = options.state
  73. }
  74. if (options.videoIs === 'deleted') {
  75. whereAnd.push('"videoAbuse"."deletedVideo" IS NOT NULL')
  76. } else if (options.videoIs === 'blacklisted') {
  77. whereAnd.push('"videoBlacklist"."id" IS NOT NULL')
  78. }
  79. if (options.predefinedReasonId) {
  80. whereAnd.push(':predefinedReasonId = ANY("abuse"."predefinedReasons")')
  81. replacements.predefinedReasonId = options.predefinedReasonId
  82. }
  83. if (options.filter === 'video') {
  84. whereAnd.push('"videoAbuse"."id" IS NOT NULL')
  85. } else if (options.filter === 'comment') {
  86. whereAnd.push('"commentAbuse"."id" IS NOT NULL')
  87. } else if (options.filter === 'account') {
  88. whereAnd.push('"videoAbuse"."id" IS NULL AND "commentAbuse"."id" IS NULL')
  89. }
  90. if (options.searchReporter) {
  91. whereAnd.push('"reporterAccount"."name" ILIKE :searchReporter')
  92. replacements.searchReporter = `%${options.searchReporter}%`
  93. }
  94. if (options.searchReportee) {
  95. whereAnd.push('"flaggedAccount"."name" ILIKE :searchReportee')
  96. replacements.searchReportee = `%${options.searchReportee}%`
  97. }
  98. const prefix = type === 'count'
  99. ? 'SELECT COUNT("abuse"."id") AS "total"'
  100. : 'SELECT "abuse"."id" '
  101. let suffix = ''
  102. if (type !== 'count') {
  103. if (options.sort) {
  104. const order = buildAbuseOrder(options.sort)
  105. suffix += `${order} `
  106. }
  107. if (exists(options.count)) {
  108. const count = parseInt(options.count + '', 10)
  109. suffix += `LIMIT ${count} `
  110. }
  111. if (exists(options.start)) {
  112. const start = parseInt(options.start + '', 10)
  113. suffix += `OFFSET ${start} `
  114. }
  115. }
  116. const where = whereAnd.length !== 0
  117. ? `WHERE ${whereAnd.join(' AND ')}`
  118. : ''
  119. return {
  120. query: `${prefix} FROM "abuse" ${joins.join(' ')} ${where} ${suffix}`,
  121. replacements
  122. }
  123. }
  124. function buildAbuseOrder (value: string) {
  125. const { direction, field } = buildDirectionAndField(value)
  126. return `ORDER BY "abuse"."${field}" ${direction}`
  127. }
  128. export {
  129. buildAbuseListQuery
  130. }