import * as express from 'express'
import { createAccountAbuse, createVideoAbuse, createVideoCommentAbuse } from '@server/lib/moderation'
import { AbuseModel } from '@server/models/abuse/abuse'
+import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
import { getServerActor } from '@server/models/application/application'
import { AbuseCreate, abusePredefinedReasonsMap, AbuseState, UserRight } from '../../../shared'
import { getFormattedObjects } from '../../helpers/utils'
import { sequelizeTypescript } from '../../initializers/database'
import {
abuseGetValidator,
- abuseListValidator,
+ abuseListForAdminsValidator,
abuseReportValidator,
abusesSortValidator,
abuseUpdateValidator,
+ addAbuseMessageValidator,
asyncMiddleware,
asyncRetryTransactionMiddleware,
authenticate,
+ deleteAbuseMessageValidator,
ensureUserHasRight,
+ getAbuseValidator,
paginationValidator,
setDefaultPagination,
setDefaultSort
abusesSortValidator,
setDefaultSort,
setDefaultPagination,
- abuseListValidator,
- asyncMiddleware(listAbuses)
+ abuseListForAdminsValidator,
+ asyncMiddleware(listAbusesForAdmins)
)
abuseRouter.put('/:id',
authenticate,
asyncRetryTransactionMiddleware(deleteAbuse)
)
+abuseRouter.get('/:id/messages',
+ authenticate,
+ asyncMiddleware(getAbuseValidator),
+ asyncRetryTransactionMiddleware(listAbuseMessages)
+)
+
+abuseRouter.post('/:id/messages',
+ authenticate,
+ asyncMiddleware(getAbuseValidator),
+ addAbuseMessageValidator,
+ asyncRetryTransactionMiddleware(addAbuseMessage)
+)
+
+abuseRouter.delete('/:id/messages/:messageId',
+ authenticate,
+ asyncMiddleware(getAbuseValidator),
+ asyncMiddleware(deleteAbuseMessageValidator),
+ asyncRetryTransactionMiddleware(deleteAbuseMessage)
+)
+
// ---------------------------------------------------------------------------
export {
abuseRouter,
// FIXME: deprecated in 2.3. Remove these exports
- listAbuses,
+ listAbusesForAdmins,
updateAbuse,
deleteAbuse,
reportAbuse
// ---------------------------------------------------------------------------
-async function listAbuses (req: express.Request, res: express.Response) {
+async function listAbusesForAdmins (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.user
const serverActor = await getServerActor()
- const resultList = await AbuseModel.listForApi({
+ const resultList = await AbuseModel.listForAdminApi({
start: req.query.start,
count: req.query.count,
sort: req.query.sort,
user
})
- return res.json(getFormattedObjects(resultList.data, resultList.total))
+ return res.json({
+ total: resultList.total,
+ data: resultList.data.map(d => d.toFormattedAdminJSON())
+ })
}
async function updateAbuse (req: express.Request, res: express.Response) {
return abuse.save({ transaction: t })
})
+ // TODO: Notification
+
// Do not send the delete to other instances, we updated OUR copy of this abuse
return res.type('json').status(204).end()
return res.json({ abuse: { id } })
}
+
+async function listAbuseMessages (req: express.Request, res: express.Response) {
+ const abuse = res.locals.abuse
+
+ const resultList = await AbuseMessageModel.listForApi(abuse.id)
+
+ return res.json(getFormattedObjects(resultList.data, resultList.total))
+}
+
+async function addAbuseMessage (req: express.Request, res: express.Response) {
+ const abuse = res.locals.abuse
+ const user = res.locals.oauth.token.user
+
+ const abuseMessage = await AbuseMessageModel.create({
+ message: req.body.message,
+ byModerator: abuse.reporterAccountId !== user.Account.id,
+ accountId: user.Account.id,
+ abuseId: abuse.id
+ })
+
+ // TODO: Notification
+
+ return res.json({
+ abuseMessage: {
+ id: abuseMessage.id
+ }
+ })
+}
+
+async function deleteAbuseMessage (req: express.Request, res: express.Response) {
+ const abuseMessage = res.locals.abuseMessage
+
+ await sequelizeTypescript.transaction(t => {
+ return abuseMessage.destroy({ transaction: t })
+ })
+
+ return res.sendStatus(204)
+}
import * as express from 'express'
import * as RateLimit from 'express-rate-limit'
+import { tokensRouter } from '@server/controllers/api/users/token'
+import { Hooks } from '@server/lib/plugins/hooks'
+import { MUser, MUserAccountDefault } from '@server/types/models'
import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared'
+import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
+import { UserRegister } from '../../../../shared/models/users/user-register.model'
+import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
import { logger } from '../../../helpers/logger'
import { generateRandomString, getFormattedObjects } from '../../../helpers/utils'
+import { CONFIG } from '../../../initializers/config'
import { WEBSERVER } from '../../../initializers/constants'
+import { sequelizeTypescript } from '../../../initializers/database'
import { Emailer } from '../../../lib/emailer'
+import { Notifier } from '../../../lib/notifier'
+import { deleteUserToken } from '../../../lib/oauth-model'
import { Redis } from '../../../lib/redis'
import { createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user'
import {
setDefaultPagination,
setDefaultSort,
userAutocompleteValidator,
- usersListValidator,
usersAddValidator,
usersGetValidator,
+ usersListValidator,
usersRegisterValidator,
usersRemoveValidator,
usersSortValidator,
usersVerifyEmailValidator
} from '../../../middlewares/validators'
import { UserModel } from '../../../models/account/user'
-import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
import { meRouter } from './me'
-import { deleteUserToken } from '../../../lib/oauth-model'
+import { myAbusesRouter } from './my-abuses'
import { myBlocklistRouter } from './my-blocklist'
-import { myVideoPlaylistsRouter } from './my-video-playlists'
import { myVideosHistoryRouter } from './my-history'
import { myNotificationsRouter } from './my-notifications'
-import { Notifier } from '../../../lib/notifier'
import { mySubscriptionsRouter } from './my-subscriptions'
-import { CONFIG } from '../../../initializers/config'
-import { sequelizeTypescript } from '../../../initializers/database'
-import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
-import { UserRegister } from '../../../../shared/models/users/user-register.model'
-import { MUser, MUserAccountDefault } from '@server/types/models'
-import { Hooks } from '@server/lib/plugins/hooks'
-import { tokensRouter } from '@server/controllers/api/users/token'
+import { myVideoPlaylistsRouter } from './my-video-playlists'
const auditLogger = auditLoggerFactory('users')
usersRouter.use('/', myBlocklistRouter)
usersRouter.use('/', myVideosHistoryRouter)
usersRouter.use('/', myVideoPlaylistsRouter)
+usersRouter.use('/', myAbusesRouter)
usersRouter.use('/', meRouter)
usersRouter.get('/autocomplete',
--- /dev/null
+import * as express from 'express'
+import { AbuseModel } from '@server/models/abuse/abuse'
+import {
+ abuseListForUserValidator,
+ abusesSortValidator,
+ asyncMiddleware,
+ authenticate,
+ paginationValidator,
+ setDefaultPagination,
+ setDefaultSort
+} from '../../../middlewares'
+
+const myAbusesRouter = express.Router()
+
+myAbusesRouter.get('/me/abuses',
+ authenticate,
+ paginationValidator,
+ abusesSortValidator,
+ setDefaultSort,
+ setDefaultPagination,
+ abuseListForUserValidator,
+ asyncMiddleware(listMyAbuses)
+)
+
+// ---------------------------------------------------------------------------
+
+export {
+ myAbusesRouter
+}
+
+// ---------------------------------------------------------------------------
+
+async function listMyAbuses (req: express.Request, res: express.Response) {
+ const resultList = await AbuseModel.listForUserApi({
+ start: req.query.start,
+ count: req.query.count,
+ sort: req.query.sort,
+ id: req.query.id,
+ search: req.query.search,
+ state: req.query.state,
+ user: res.locals.oauth.token.User
+ })
+
+ return res.json({
+ total: resultList.total,
+ data: resultList.data.map(d => d.toFormattedAdminJSON())
+ })
+}
import { AbuseModel } from '@server/models/abuse/abuse'
import { getServerActor } from '@server/models/application/application'
import { AbuseCreate, UserRight, VideoAbuseCreate } from '../../../../shared'
-import { getFormattedObjects } from '../../../helpers/utils'
import {
abusesSortValidator,
asyncMiddleware,
const user = res.locals.oauth.token.user
const serverActor = await getServerActor()
- const resultList = await AbuseModel.listForApi({
+ const resultList = await AbuseModel.listForAdminApi({
start: req.query.start,
count: req.query.count,
sort: req.query.sort,
user
})
- return res.json(getFormattedObjects(resultList.data, resultList.total))
+ return res.json({
+ total: resultList.total,
+ data: resultList.data.map(d => d.toFormattedAdminJSON())
+ })
}
async function updateVideoAbuse (req: express.Request, res: express.Response) {
import * as path from 'path'
import * as winston from 'winston'
import { AUDIT_LOG_FILENAME } from '@server/initializers/constants'
-import { Abuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared'
+import { AdminAbuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared'
import { CustomConfig } from '../../shared/models/server/custom-config.model'
import { VideoComment } from '../../shared/models/videos/video-comment.model'
import { CONFIG } from '../initializers/config'
'createdAt'
]
class AbuseAuditView extends EntityAuditView {
- constructor (private readonly abuse: Abuse) {
+ constructor (private readonly abuse: AdminAbuse) {
super(abuseKeysToKeep, 'abuse', abuse)
}
}
import { exists, isArray } from './misc'
const ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSES
+const ABUSE_MESSAGES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSE_MESSAGES
function isAbuseReasonValid (value: string) {
return exists(value) && validator.isLength(value, ABUSES_CONSTRAINTS_FIELDS.REASON)
)
}
+function isAbuseMessageValid (value: string) {
+ return exists(value) && validator.isLength(value, ABUSE_MESSAGES_CONSTRAINTS_FIELDS.MESSAGE)
+}
+
// ---------------------------------------------------------------------------
export {
isAbuseReasonValid,
isAbuseFilterValid,
isAbusePredefinedReasonValid,
- areAbusePredefinedReasonsValid as isAbusePredefinedReasonsValid,
+ isAbuseMessageValid,
+ areAbusePredefinedReasonsValid,
isAbuseTimestampValid,
isAbuseTimestampCoherent,
isAbuseModerationCommentValid,
REASON: { min: 2, max: 3000 }, // Length
MODERATION_COMMENT: { min: 2, max: 3000 } // Length
},
+ ABUSE_MESSAGES: {
+ MESSAGE: { min: 2, max: 3000 } // Length
+ },
VIDEO_BLACKLIST: {
REASON: { min: 2, max: 300 } // Length
},
import { QueryTypes, Transaction } from 'sequelize'
import { Sequelize as SequelizeTypescript } from 'sequelize-typescript'
import { AbuseModel } from '@server/models/abuse/abuse'
+import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
import { VideoAbuseModel } from '@server/models/abuse/video-abuse'
import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse'
import { isTestInstance } from '../helpers/core-utils'
TagModel,
AccountVideoRateModel,
UserModel,
+ AbuseMessageModel,
AbuseModel,
VideoCommentAbuseModel,
VideoAbuseModel,
import { VideoChannelModel } from '@server/models/video/video-channel'
import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import'
-import { Abuse, EmailPayload } from '@shared/models'
+import { UserAbuse, EmailPayload } from '@shared/models'
import { SendEmailOptions } from '../../shared/models/server/emailer.model'
import { isTestInstance, root } from '../helpers/core-utils'
import { bunyanLogger, logger } from '../helpers/logger'
}
addAbuseModeratorsNotification (to: string[], parameters: {
- abuse: Abuse
+ abuse: UserAbuse
abuseInstance: MAbuseFull
reporter: string
}) {
await sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
}
- const abuseJSON = abuseInstance.toFormattedJSON()
+ const abuseJSON = abuseInstance.toFormattedAdminJSON()
auditLogger.create(reporterAccount.Actor.getIdentifier(), new AbuseAuditView(abuseJSON))
Notifier.Instance.notifyOnNewAbuse({
} from '@server/types/models/user'
import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
import { MVideoImportVideo } from '@server/types/models/video/video-import'
-import { Abuse } from '@shared/models'
+import { UserAbuse } from '@shared/models'
import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users'
import { VideoPrivacy, VideoState } from '../../shared/models/videos'
import { logger } from '../helpers/logger'
.catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err }))
}
- notifyOnNewAbuse (parameters: { abuse: Abuse, abuseInstance: MAbuseFull, reporter: string }): void {
+ notifyOnNewAbuse (parameters: { abuse: UserAbuse, abuseInstance: MAbuseFull, reporter: string }): void {
this.notifyModeratorsOfNewAbuse(parameters)
.catch(err => logger.error('Cannot notify of new abuse %d.', parameters.abuseInstance.id, { err }))
}
}
private async notifyModeratorsOfNewAbuse (parameters: {
- abuse: Abuse
+ abuse: UserAbuse
abuseInstance: MAbuseFull
reporter: string
}) {
const message = `User ${user.username} does not have right ${UserRight[userRight]} to access to ${req.path}.`
logger.info(message)
- return res.status(403)
- .json({
- error: message
- })
- .end()
+ return res.status(403).json({ error: message })
}
return next()
import { body, param, query } from 'express-validator'
import {
isAbuseFilterValid,
+ isAbuseMessageValid,
isAbuseModerationCommentValid,
- isAbusePredefinedReasonsValid,
+ areAbusePredefinedReasonsValid,
isAbusePredefinedReasonValid,
isAbuseReasonValid,
isAbuseStateValid,
import { doesCommentIdExist } from '@server/helpers/custom-validators/video-comments'
import { logger } from '@server/helpers/logger'
import { doesAbuseExist, doesAccountIdExist, doesVideoAbuseExist, doesVideoExist } from '@server/helpers/middlewares'
-import { AbuseCreate } from '@shared/models'
+import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
+import { AbuseCreate, UserRight } from '@shared/models'
import { areValidationErrors } from './utils'
const abuseReportValidator = [
body('predefinedReasons')
.optional()
- .custom(isAbusePredefinedReasonsValid)
+ .custom(areAbusePredefinedReasonsValid)
.withMessage('Should have a valid list of predefined reasons'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
}
]
-const abuseListValidator = [
+const abuseListForAdminsValidator = [
query('id')
.optional()
.custom(isIdValid).withMessage('Should have a valid id'),
.custom(exists).withMessage('Should have a valid video channel search'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
- logger.debug('Checking abuseListValidator parameters', { parameters: req.body })
+ logger.debug('Checking abuseListForAdminsValidator parameters', { parameters: req.body })
if (areValidationErrors(req, res)) return
}
]
+const abuseListForUserValidator = [
+ query('id')
+ .optional()
+ .custom(isIdValid).withMessage('Should have a valid id'),
+
+ query('search')
+ .optional()
+ .custom(exists).withMessage('Should have a valid search'),
+
+ query('state')
+ .optional()
+ .custom(isAbuseStateValid).withMessage('Should have a valid abuse state'),
+
+ (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking abuseListForUserValidator parameters', { parameters: req.body })
+
+ if (areValidationErrors(req, res)) return
+
+ return next()
+ }
+]
+
+const getAbuseValidator = [
+ param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'),
+
+ async (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking getAbuseValidator parameters', { parameters: req.body })
+
+ if (areValidationErrors(req, res)) return
+ if (!await doesAbuseExist(req.params.id, res)) return
+
+ const user = res.locals.oauth.token.user
+ const abuse = res.locals.abuse
+
+ if (user.hasRight(UserRight.MANAGE_ABUSES) !== true && abuse.reporterAccountId !== user.Account.id) {
+ const message = `User ${user.username} does not have right to get abuse ${abuse.id}`
+ logger.warn(message)
+
+ return res.status(403).json({ error: message })
+ }
+
+ return next()
+ }
+]
+
+const addAbuseMessageValidator = [
+ body('message').custom(isAbuseMessageValid).not().isEmpty().withMessage('Should have a valid abuse message'),
+
+ (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking addAbuseMessageValidator parameters', { parameters: req.body })
+
+ if (areValidationErrors(req, res)) return
+
+ return next()
+ }
+]
+
+const deleteAbuseMessageValidator = [
+ param('messageId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid message id'),
+
+ async (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking deleteAbuseMessageValidator parameters', { parameters: req.body })
+
+ if (areValidationErrors(req, res)) return
+
+ const user = res.locals.oauth.token.user
+ const abuse = res.locals.abuse
+
+ const messageId = parseInt(req.params.messageId + '', 10)
+ const abuseMessage = await AbuseMessageModel.loadByIdAndAbuseId(messageId, abuse.id)
+
+ if (!abuseMessage) {
+ return res.status(404).json({ error: 'Abuse message not found' })
+ }
+
+ if (user.hasRight(UserRight.MANAGE_ABUSES) !== true && abuseMessage.accountId !== user.Account.id) {
+ return res.status(403).json({ error: 'Cannot delete this abuse message' })
+ }
+
+ res.locals.abuseMessage = abuseMessage
+
+ return next()
+ }
+]
+
// FIXME: deprecated in 2.3. Remove these validators
const videoAbuseReportValidator = [
.withMessage('Should have a valid reason'),
body('predefinedReasons')
.optional()
- .custom(isAbusePredefinedReasonsValid)
+ .custom(areAbusePredefinedReasonsValid)
.withMessage('Should have a valid list of predefined reasons'),
body('startAt')
.optional()
// ---------------------------------------------------------------------------
export {
- abuseListValidator,
+ abuseListForAdminsValidator,
abuseReportValidator,
abuseGetValidator,
+ addAbuseMessageValidator,
abuseUpdateValidator,
+ deleteAbuseMessageValidator,
+ abuseListForUserValidator,
+ getAbuseValidator,
videoAbuseReportValidator,
videoAbuseGetValidator,
videoAbuseUpdateValidator,
--- /dev/null
+import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses'
+import { AbuseMessage } from '@shared/models'
+import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
+import { throwIfNotValid, getSort } from '../utils'
+import { AbuseModel } from './abuse'
+import { MAbuseMessageFormattable, MAbuseMessage } from '@server/types/models'
+
+@Table({
+ tableName: 'abuseMessage',
+ indexes: [
+ {
+ fields: [ 'abuseId' ]
+ },
+ {
+ fields: [ 'accountId' ]
+ }
+ ]
+})
+export class AbuseMessageModel extends Model<AbuseMessageModel> {
+
+ @AllowNull(false)
+ @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message'))
+ @Column(DataType.TEXT)
+ message: string
+
+ @AllowNull(false)
+ @Column
+ byModerator: boolean
+
+ @CreatedAt
+ createdAt: Date
+
+ @UpdatedAt
+ updatedAt: Date
+
+ @ForeignKey(() => AccountModel)
+ @Column
+ accountId: number
+
+ @BelongsTo(() => AccountModel, {
+ foreignKey: {
+ name: 'accountId',
+ allowNull: true
+ },
+ onDelete: 'set null'
+ })
+ Account: AccountModel
+
+ @ForeignKey(() => AbuseModel)
+ @Column
+ abuseId: number
+
+ @BelongsTo(() => AbuseModel, {
+ foreignKey: {
+ name: 'abuseId',
+ allowNull: false
+ },
+ onDelete: 'cascade'
+ })
+ Abuse: AbuseModel
+
+ static listForApi (abuseId: number) {
+ const options = {
+ where: { abuseId },
+
+ order: getSort('createdAt'),
+
+ include: [
+ {
+ model: AccountModel.scope(AccountScopeNames.SUMMARY),
+ required: false
+ }
+ ]
+ }
+
+ return AbuseMessageModel.findAndCountAll(options)
+ .then(({ rows, count }) => ({ data: rows, total: count }))
+ }
+
+ static loadByIdAndAbuseId (messageId: number, abuseId: number): Promise<MAbuseMessage> {
+ return AbuseMessageModel.findOne({
+ where: {
+ id: messageId,
+ abuseId
+ }
+ })
+ }
+
+ toFormattedJSON (this: MAbuseMessageFormattable): AbuseMessage {
+ const account = this.Account
+ ? this.Account.toFormattedSummaryJSON()
+ : null
+
+ return {
+ id: this.id,
+ byModerator: this.byModerator,
+ message: this.message,
+
+ account
+ }
+ }
+}
state?: AbuseState
// accountIds
- serverAccountId: number
- userAccountId: number
+ serverAccountId?: number
+ userAccountId?: number
+
+ reporterAccountId?: number
}
function buildAbuseListQuery (options: BuildAbusesQueryOptions, type: 'count' | 'id') {
'LEFT JOIN "videoComment" ON "commentAbuse"."videoCommentId" = "videoComment"."id"'
]
- whereAnd.push('"abuse"."reporterAccountId" NOT IN (' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
+ if (options.serverAccountId || options.userAccountId) {
+ whereAnd.push('"abuse"."reporterAccountId" NOT IN (' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
+ }
+
+ if (options.reporterAccountId) {
+ whereAnd.push('"abuse"."reporterAccountId" = :reporterAccountId')
+ replacements.reporterAccountId = options.reporterAccountId
+ }
if (options.search) {
const searchWhereOr = [
} from 'sequelize-typescript'
import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
import {
- Abuse,
AbuseFilter,
AbuseObject,
AbusePredefinedReasons,
AbusePredefinedReasonsString,
AbuseState,
AbuseVideoIs,
- VideoAbuse,
- VideoCommentAbuse
+ AdminVideoAbuse,
+ AdminAbuse,
+ AdminVideoCommentAbuse,
+ UserAbuse,
+ UserVideoAbuse
} from '@shared/models'
import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants'
-import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models'
+import { MAbuse, MAbuseAdminFormattable, MAbuseAP, MUserAccountId, MAbuseUserFormattable } from '../../types/models'
import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account'
import { getSort, throwIfNotValid } from '../utils'
import { ThumbnailModel } from '../video/thumbnail'
return {
attributes: {
include: [
+ [
+ literal(
+ '(' +
+ 'SELECT count(*) ' +
+ 'FROM "abuseMessage" ' +
+ 'WHERE "abuseId" = "AbuseModel"."id"' +
+ ')'
+ ),
+ 'countMessages'
+ ],
[
// we don't care about this count for deleted videos, so there are not included
literal(
return AbuseModel.findOne(query)
}
- static async listForApi (parameters: {
+ static async listForAdminApi (parameters: {
start: number
count: number
sort: string
return { total, data }
}
- toFormattedJSON (this: MAbuseFormattable): Abuse {
- const predefinedReasons = AbuseModel.getPredefinedReasonsStrings(this.predefinedReasons)
+ static async listForUserApi (parameters: {
+ user: MUserAccountId
- const countReportsForVideo = this.get('countReportsForVideo') as number
- const nthReportForVideo = this.get('nthReportForVideo') as number
+ start: number
+ count: number
+ sort: string
- const countReportsForReporter = this.get('countReportsForReporter') as number
- const countReportsForReportee = this.get('countReportsForReportee') as number
+ id?: number
+ search?: string
+ state?: AbuseState
+ }) {
+ const {
+ start,
+ count,
+ sort,
+ search,
+ user,
+ state,
+ id
+ } = parameters
- let video: VideoAbuse = null
- let comment: VideoCommentAbuse = null
+ const queryOptions: BuildAbusesQueryOptions = {
+ start,
+ count,
+ sort,
+ id,
+ search,
+ state,
+ reporterAccountId: user.Account.id
+ }
+
+ const [ total, data ] = await Promise.all([
+ AbuseModel.internalCountForApi(queryOptions),
+ AbuseModel.internalListForApi(queryOptions)
+ ])
+
+ return { total, data }
+ }
- if (this.VideoAbuse) {
- const abuseModel = this.VideoAbuse
- const entity = abuseModel.Video || abuseModel.deletedVideo
+ buildBaseVideoCommentAbuse (this: MAbuseUserFormattable) {
+ if (!this.VideoCommentAbuse) return null
- video = {
- id: entity.id,
- uuid: entity.uuid,
- name: entity.name,
- nsfw: entity.nsfw,
+ const abuseModel = this.VideoCommentAbuse
+ const entity = abuseModel.VideoComment
- startAt: abuseModel.startAt,
- endAt: abuseModel.endAt,
+ return {
+ id: entity.id,
+ threadId: entity.getThreadId(),
- deleted: !abuseModel.Video,
- blacklisted: abuseModel.Video?.isBlacklisted() || false,
- thumbnailPath: abuseModel.Video?.getMiniatureStaticPath(),
+ text: entity.text ?? '',
- channel: abuseModel.Video?.VideoChannel.toFormattedJSON() || abuseModel.deletedVideo?.channel,
+ deleted: entity.isDeleted(),
- countReports: countReportsForVideo,
- nthReport: nthReportForVideo
+ video: {
+ id: entity.Video.id,
+ name: entity.Video.name,
+ uuid: entity.Video.uuid
}
}
+ }
- if (this.VideoCommentAbuse) {
- const abuseModel = this.VideoCommentAbuse
- const entity = abuseModel.VideoComment
+ buildBaseVideoAbuse (this: MAbuseUserFormattable): UserVideoAbuse {
+ if (!this.VideoAbuse) return null
- comment = {
- id: entity.id,
- threadId: entity.getThreadId(),
+ const abuseModel = this.VideoAbuse
+ const entity = abuseModel.Video || abuseModel.deletedVideo
- text: entity.text ?? '',
+ return {
+ id: entity.id,
+ uuid: entity.uuid,
+ name: entity.name,
+ nsfw: entity.nsfw,
- deleted: entity.isDeleted(),
+ startAt: abuseModel.startAt,
+ endAt: abuseModel.endAt,
- video: {
- id: entity.Video.id,
- name: entity.Video.name,
- uuid: entity.Video.uuid
- }
- }
+ deleted: !abuseModel.Video,
+ blacklisted: abuseModel.Video?.isBlacklisted() || false,
+ thumbnailPath: abuseModel.Video?.getMiniatureStaticPath(),
+
+ channel: abuseModel.Video?.VideoChannel.toFormattedJSON() || abuseModel.deletedVideo?.channel,
}
+ }
+
+ buildBaseAbuse (this: MAbuseUserFormattable, countMessages: number): UserAbuse {
+ const predefinedReasons = AbuseModel.getPredefinedReasonsStrings(this.predefinedReasons)
return {
id: this.id,
reason: this.reason,
predefinedReasons,
- reporterAccount: this.ReporterAccount
- ? this.ReporterAccount.toFormattedJSON()
- : null,
-
flaggedAccount: this.FlaggedAccount
? this.FlaggedAccount.toFormattedJSON()
: null,
moderationComment: this.moderationComment,
+ countMessages,
+
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt
+ }
+ }
+
+ toFormattedAdminJSON (this: MAbuseAdminFormattable): AdminAbuse {
+ const countReportsForVideo = this.get('countReportsForVideo') as number
+ const nthReportForVideo = this.get('nthReportForVideo') as number
+
+ const countReportsForReporter = this.get('countReportsForReporter') as number
+ const countReportsForReportee = this.get('countReportsForReportee') as number
+
+ const countMessages = this.get('countMessages') as number
+
+ const baseVideo = this.buildBaseVideoAbuse()
+ const video: AdminVideoAbuse = baseVideo
+ ? Object.assign(baseVideo, {
+ countReports: countReportsForVideo,
+ nthReport: nthReportForVideo
+ })
+ : null
+
+ const comment: AdminVideoCommentAbuse = this.buildBaseVideoCommentAbuse()
+
+ const abuse = this.buildBaseAbuse(countMessages || 0)
+
+ return Object.assign(abuse, {
video,
comment,
- createdAt: this.createdAt,
- updatedAt: this.updatedAt,
+ reporterAccount: this.ReporterAccount
+ ? this.ReporterAccount.toFormattedJSON()
+ : null,
countReportsForReporter: (countReportsForReporter || 0),
countReportsForReportee: (countReportsForReportee || 0),
endAt: null,
count: countReportsForVideo || 0,
nth: nthReportForVideo || 0
- }
+ })
+ }
+
+ toFormattedUserJSON (this: MAbuseUserFormattable): UserAbuse {
+ const countMessages = this.get('countMessages') as number
+
+ const video = this.buildBaseVideoAbuse()
+ const comment: AdminVideoCommentAbuse = this.buildBaseVideoCommentAbuse()
+ const abuse = this.buildBaseAbuse(countMessages || 0)
+
+ return Object.assign(abuse, {
+ video,
+ comment
+ })
}
toActivityPubObject (this: MAbuseAP): AbuseObject {
setAccessTokensToServers,
updateAbuse,
uploadVideo,
- userLogin
+ userLogin,
+ generateUserAccessToken,
+ addAbuseMessage,
+ listAbuseMessages,
+ deleteAbuseMessage
} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
let server: ServerInfo
let userAccessToken = ''
+ let userAccessToken2 = ''
let abuseId: number
+ let messageId: number
// ---------------------------------------------------------------
await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
userAccessToken = await userLogin(server, { username, password })
+ {
+ userAccessToken2 = await generateUserAccessToken(server, 'user_2')
+ }
+
const res = await uploadVideo(server.url, server.accessToken, {})
server.video = res.body.video
})
- describe('When listing abuses', function () {
+ describe('When listing abuses for admins', function () {
const path = basePath
it('Should fail with a bad start pagination', async function () {
})
})
+ describe('When listing abuses for users', function () {
+ const path = '/api/v1/users/me/abuses'
+
+ it('Should fail with a bad start pagination', async function () {
+ await checkBadStartPagination(server.url, path, userAccessToken)
+ })
+
+ it('Should fail with a bad count pagination', async function () {
+ await checkBadCountPagination(server.url, path, userAccessToken)
+ })
+
+ it('Should fail with an incorrect sort', async function () {
+ await checkBadSortPagination(server.url, path, userAccessToken)
+ })
+
+ it('Should fail with a non authenticated user', async function () {
+ await makeGetRequest({
+ url: server.url,
+ path,
+ statusCodeExpected: 401
+ })
+ })
+
+ it('Should fail with a bad id filter', async function () {
+ await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { id: 'toto' } })
+ })
+
+ it('Should fail with a bad state filter', async function () {
+ await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 'toto' } })
+ await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 0 } })
+ })
+
+ it('Should succeed with the correct params', async function () {
+ const query = {
+ id: 13,
+ state: 2
+ }
+
+ await makeGetRequest({ url: server.url, path, token: userAccessToken, query, statusCodeExpected: 200 })
+ })
+ })
+
describe('When reporting an abuse', function () {
const path = basePath
it('Should fail with nothing', async function () {
const fields = {}
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
})
it('Should fail with a wrong video', async function () {
const fields = { video: { id: 'blabla' }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields })
})
it('Should fail with an unknown video', async function () {
const fields = { video: { id: 42 }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 })
+ await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 404 })
})
it('Should fail with a wrong comment', async function () {
const fields = { comment: { id: 'blabla' }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields })
})
it('Should fail with an unknown comment', async function () {
const fields = { comment: { id: 42 }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 })
+ await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 404 })
})
it('Should fail with a wrong account', async function () {
const fields = { account: { id: 'blabla' }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields })
})
it('Should fail with an unknown account', async function () {
const fields = { account: { id: 42 }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 })
+ await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 404 })
})
it('Should fail with not account, comment or video', async function () {
const fields = { reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 400 })
+ await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 400 })
})
it('Should fail with a non authenticated user', async function () {
it('Should fail with a reason too short', async function () {
const fields = { video: { id: server.video.id }, reason: 'h' }
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
})
it('Should fail with a too big reason', async function () {
const fields = { video: { id: server.video.id }, reason: 'super'.repeat(605) }
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
})
it('Should succeed with the correct parameters (basic)', async function () {
const fields: AbuseCreate = { video: { id: server.video.id }, reason: 'my super reason' }
- const res = await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 200 })
+ const res = await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: 200 })
abuseId = res.body.abuse.id
})
it('Should fail with a wrong predefined reason', async function () {
const fields = { video: { id: server.video.id }, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] }
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
})
it('Should fail with negative timestamps', async function () {
const fields = { video: { id: server.video.id, startAt: -1 }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
})
it('Should fail mith misordered startAt/endAt', async function () {
const fields = { video: { id: server.video.id, startAt: 5, endAt: 1 }, reason: 'my super reason' }
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+ await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
})
it('Should succeed with the corret parameters (advanced)', async function () {
predefinedReasons: [ 'serverRules' ]
}
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 200 })
+ await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: 200 })
})
})
})
})
+ describe('When creating an abuse message', function () {
+ const message = 'my super message'
+
+ it('Should fail with an invalid abuse id', async function () {
+ await addAbuseMessage(server.url, userAccessToken2, 888, message, 404)
+ })
+
+ it('Should fail with a non authenticated user', async function () {
+ await addAbuseMessage(server.url, 'fake_token', abuseId, message, 401)
+ })
+
+ it('Should fail with an invalid logged in user', async function () {
+ await addAbuseMessage(server.url, userAccessToken2, abuseId, message, 403)
+ })
+
+ it('Should fail with an invalid message', async function () {
+ await addAbuseMessage(server.url, userAccessToken, abuseId, 'a'.repeat(5000), 400)
+ })
+
+ it('Should suceed with the correct params', async function () {
+ const res = await addAbuseMessage(server.url, userAccessToken, abuseId, message)
+ messageId = res.body.abuseMessage.id
+ })
+ })
+
+ describe('When listing abuse message', function () {
+
+ it('Should fail with an invalid abuse id', async function () {
+ await listAbuseMessages(server.url, userAccessToken, 888, 404)
+ })
+
+ it('Should fail with a non authenticated user', async function () {
+ await listAbuseMessages(server.url, 'fake_token', abuseId, 401)
+ })
+
+ it('Should fail with an invalid logged in user', async function () {
+ await listAbuseMessages(server.url, userAccessToken2, abuseId, 403)
+ })
+
+ it('Should succeed with the correct params', async function () {
+ await listAbuseMessages(server.url, userAccessToken, abuseId)
+ })
+ })
+
+ describe('When deleting an abuse message', function () {
+
+ it('Should fail with an invalid abuse id', async function () {
+ await deleteAbuseMessage(server.url, userAccessToken, 888, messageId, 404)
+ })
+
+ it('Should fail with an invalid message id', async function () {
+ await deleteAbuseMessage(server.url, userAccessToken, abuseId, 888, 404)
+ })
+
+ it('Should fail with a non authenticated user', async function () {
+ await deleteAbuseMessage(server.url, 'fake_token', abuseId, messageId, 401)
+ })
+
+ it('Should fail with an invalid logged in user', async function () {
+ await deleteAbuseMessage(server.url, userAccessToken2, abuseId, messageId, 403)
+ })
+
+ it('Should succeed with the correct params', async function () {
+ await deleteAbuseMessage(server.url, userAccessToken, abuseId, messageId)
+ })
+ })
+
describe('When deleting a video abuse', function () {
it('Should fail with a non authenticated user', async function () {
import 'mocha'
import * as chai from 'chai'
-import { Abuse, AbuseFilter, AbusePredefinedReasonsString, AbuseState, VideoComment, Account } from '@shared/models'
+import { AbuseFilter, AbusePredefinedReasonsString, AbuseState, Account, AdminAbuse, UserAbuse, VideoComment, AbuseMessage } from '@shared/models'
import {
addVideoCommentThread,
cleanupTests,
deleteAbuse,
deleteVideoComment,
flushAndRunMultipleServers,
- getAbusesList,
+ generateUserAccessToken,
+ getAccount,
+ getAdminAbusesList,
+ getUserAbusesList,
getVideoCommentThreads,
getVideoIdFromUUID,
getVideosList,
immutableAssign,
+ removeUser,
removeVideo,
reportAbuse,
ServerInfo,
uploadVideo,
uploadVideoAndGetId,
userLogin,
- getAccount,
- removeUser,
- generateUserAccessToken
+ addAbuseMessage,
+ listAbuseMessages,
+ deleteAbuseMessage
} from '../../../../shared/extra-utils/index'
import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
describe('Test abuses', function () {
let servers: ServerInfo[] = []
- let abuseServer1: Abuse
- let abuseServer2: Abuse
+ let abuseServer1: AdminAbuse
+ let abuseServer2: AdminAbuse
before(async function () {
this.timeout(50000)
})
it('Should not have abuses', async function () {
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res.body.total).to.equal(0)
expect(res.body.data).to.be.an('array')
})
it('Should have 1 video abuses on server 1 and 0 on server 2', async function () {
- const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res1.body.total).to.equal(1)
expect(res1.body.data).to.be.an('array')
expect(res1.body.data.length).to.equal(1)
- const abuse: Abuse = res1.body.data[0]
+ const abuse: AdminAbuse = res1.body.data[0]
expect(abuse.reason).to.equal('my super bad reason')
expect(abuse.reporterAccount.name).to.equal('root')
expect(abuse.countReportsForReporter).to.equal(1)
expect(abuse.countReportsForReportee).to.equal(1)
- const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken })
+ const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken })
expect(res2.body.total).to.equal(0)
expect(res2.body.data).to.be.an('array')
expect(res2.body.data.length).to.equal(0)
this.timeout(10000)
const reason = 'my super bad reason 2'
- await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: servers[1].video.id, reason })
+ const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid)
+ await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId, reason })
// We wait requests propagation
await waitJobs(servers)
})
it('Should have 2 video abuses on server 1 and 1 on server 2', async function () {
- const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res1.body.total).to.equal(2)
expect(res1.body.data.length).to.equal(2)
- const abuse1: Abuse = res1.body.data[0]
+ const abuse1: AdminAbuse = res1.body.data[0]
expect(abuse1.reason).to.equal('my super bad reason')
expect(abuse1.reporterAccount.name).to.equal('root')
expect(abuse1.reporterAccount.host).to.equal(servers[0].host)
expect(abuse1.state.label).to.equal('Pending')
expect(abuse1.moderationComment).to.be.null
- const abuse2: Abuse = res1.body.data[1]
+ const abuse2: AdminAbuse = res1.body.data[1]
expect(abuse2.reason).to.equal('my super bad reason 2')
expect(abuse2.reporterAccount.name).to.equal('root')
expect(abuse2.state.label).to.equal('Pending')
expect(abuse2.moderationComment).to.be.null
- const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken })
+ const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken })
expect(res2.body.total).to.equal(1)
expect(res2.body.data.length).to.equal(1)
await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'will mute this' })
await waitJobs(servers)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res.body.total).to.equal(3)
}
{
await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res.body.total).to.equal(2)
const abuse = res.body.data.find(a => a.reason === 'will mute this')
{
await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res.body.total).to.equal(3)
}
})
{
await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res.body.total).to.equal(2)
const abuse = res.body.data.find(a => a.reason === 'will mute this')
{
await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, serverToBlock)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res.body.total).to.equal(3)
}
})
await waitJobs(servers)
- const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken })
+ const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken })
expect(res.body.total).to.equal(2, "wrong number of videos returned")
expect(res.body.data).to.have.lengthOf(2, "wrong number of videos returned")
- const abuse: Abuse = res.body.data[0]
+ const abuse: AdminAbuse = res.body.data[0]
expect(abuse.id).to.equal(abuseServer2.id, "wrong origin server id for first video")
expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id")
expect(abuse.video.channel).to.exist
await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: reason4 })
{
- const res2 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
- const abuses = res2.body.data as Abuse[]
+ const res2 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const abuses = res2.body.data as AdminAbuse[]
const abuseVideo3 = res2.body.data.find(a => a.video.id === video3.id)
expect(abuseVideo3).to.not.be.undefined
endAt: 5
})).body.abuse
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
{
- const abuse = (res.body.data as Abuse[]).find(a => a.id === createdAbuse.id)
+ const abuse = (res.body.data as AdminAbuse[]).find(a => a.id === createdAbuse.id)
expect(abuse.reason).to.equals(reason5)
expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported")
expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported")
await waitJobs(servers)
{
- const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken })
+ const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken })
expect(res.body.total).to.equal(1)
expect(res.body.data.length).to.equal(1)
expect(res.body.data[0].id).to.not.equal(abuseServer2.id)
}
{
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken })
expect(res.body.total).to.equal(6)
}
})
it('Should list and filter video abuses', async function () {
this.timeout(10000)
- async function list (query: Omit<Parameters<typeof getAbusesList>[0], 'url' | 'token'>) {
+ async function list (query: Omit<Parameters<typeof getAdminAbusesList>[0], 'url' | 'token'>) {
const options = {
url: servers[0].url,
token: servers[0].accessToken
Object.assign(options, query)
- const res = await getAbusesList(options)
+ const res = await getAdminAbusesList(options)
- return res.body.data as Abuse[]
+ return res.body.data as AdminAbuse[]
}
expect(await list({ id: 56 })).to.have.lengthOf(0)
it('Should have 1 comment abuse on server 1 and 0 on server 2', async function () {
{
const comment = await getComment(servers[0].url, servers[0].video.id)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
expect(res.body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1)
- const abuse: Abuse = res.body.data[0]
+ const abuse: AdminAbuse = res.body.data[0]
expect(abuse.reason).to.equal('it is a bad comment')
expect(abuse.reporterAccount.name).to.equal('root')
}
{
- const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
+ const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
expect(res.body.total).to.equal(0)
expect(res.body.data.length).to.equal(0)
}
it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () {
const commentServer2 = await getComment(servers[0].url, servers[1].video.id)
- const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
+ const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
expect(res1.body.total).to.equal(2)
expect(res1.body.data.length).to.equal(2)
- const abuse: Abuse = res1.body.data[0]
+ const abuse: AdminAbuse = res1.body.data[0]
expect(abuse.reason).to.equal('it is a bad comment')
expect(abuse.countReportsForReporter).to.equal(6)
expect(abuse.countReportsForReportee).to.equal(5)
- const abuse2: Abuse = res1.body.data[1]
+ const abuse2: AdminAbuse = res1.body.data[1]
expect(abuse2.reason).to.equal('it is a really bad comment')
expect(abuse2.countReportsForReporter).to.equal(6)
expect(abuse2.countReportsForReportee).to.equal(2)
- const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
+ const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
expect(res2.body.total).to.equal(1)
expect(res2.body.data.length).to.equal(1)
await waitJobs(servers)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
expect(res.body.total).to.equal(2)
expect(res.body.data).to.have.lengthOf(2)
- const abuse = (res.body.data as Abuse[]).find(a => a.comment?.id === commentServer2.id)
+ const abuse = (res.body.data as AdminAbuse[]).find(a => a.comment?.id === commentServer2.id)
expect(abuse).to.not.be.undefined
expect(abuse.comment.text).to.be.empty
await waitJobs(servers)
{
- const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
+ const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
expect(res.body.total).to.equal(0)
expect(res.body.data.length).to.equal(0)
}
{
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' })
expect(res.body.total).to.equal(2)
}
})
it('Should list and filter video abuses', async function () {
{
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment', searchReportee: 'foo' })
+ const res = await getAdminAbusesList({
+ url: servers[0].url,
+ token: servers[0].accessToken,
+ filter: 'comment',
+ searchReportee: 'foo'
+ })
expect(res.body.total).to.equal(0)
}
{
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment', searchReportee: 'ot' })
+ const res = await getAdminAbusesList({
+ url: servers[0].url,
+ token: servers[0].accessToken,
+ filter: 'comment',
+ searchReportee: 'ot'
+ })
expect(res.body.total).to.equal(2)
}
{
const baseParams = { url: servers[0].url, token: servers[0].accessToken, filter: 'comment' as AbuseFilter, start: 1, count: 1 }
- const res1 = await getAbusesList(immutableAssign(baseParams, { sort: 'createdAt' }))
+ const res1 = await getAdminAbusesList(immutableAssign(baseParams, { sort: 'createdAt' }))
expect(res1.body.data).to.have.lengthOf(1)
expect(res1.body.data[0].comment.text).to.be.empty
- const res2 = await getAbusesList(immutableAssign(baseParams, { sort: '-createdAt' }))
+ const res2 = await getAdminAbusesList(immutableAssign(baseParams, { sort: '-createdAt' }))
expect(res2.body.data).to.have.lengthOf(1)
expect(res2.body.data[0].comment.text).to.equal('comment server 1')
}
it('Should have 1 account abuse on server 1 and 0 on server 2', async function () {
{
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
expect(res.body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1)
- const abuse: Abuse = res.body.data[0]
+ const abuse: AdminAbuse = res.body.data[0]
expect(abuse.reason).to.equal('it is a bad account')
expect(abuse.reporterAccount.name).to.equal('root')
}
{
- const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
+ const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' })
expect(res.body.total).to.equal(0)
expect(res.body.data.length).to.equal(0)
}
})
it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () {
- const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
+ const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
expect(res1.body.total).to.equal(2)
expect(res1.body.data.length).to.equal(2)
- const abuse: Abuse = res1.body.data[0]
+ const abuse: AdminAbuse = res1.body.data[0]
expect(abuse.reason).to.equal('it is a bad account')
- const abuse2: Abuse = res1.body.data[1]
+ const abuse2: AdminAbuse = res1.body.data[1]
expect(abuse2.reason).to.equal('it is a really bad account')
expect(abuse2.reporterAccount.name).to.equal('root')
expect(abuse2.moderationComment).to.be.null
- const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' })
+ const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' })
expect(res2.body.total).to.equal(1)
expect(res2.body.data.length).to.equal(1)
await waitJobs(servers)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
expect(res.body.total).to.equal(2)
expect(res.body.data).to.have.lengthOf(2)
- const abuse = (res.body.data as Abuse[]).find(a => a.reason === 'it is a really bad account')
+ const abuse = (res.body.data as AdminAbuse[]).find(a => a.reason === 'it is a really bad account')
expect(abuse).to.not.be.undefined
})
await waitJobs(servers)
{
- const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' })
+ const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' })
expect(res.body.total).to.equal(0)
expect(res.body.data.length).to.equal(0)
}
{
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' })
expect(res.body.total).to.equal(2)
abuseServer1 = res.body.data[0]
const body = { state: AbuseState.REJECTED }
await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id })
expect(res.body.data[0].state.id).to.equal(AbuseState.REJECTED)
})
const body = { state: AbuseState.ACCEPTED, moderationComment: 'It is valid' }
await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body)
- const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id })
+ const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id })
expect(res.body.data[0].state.id).to.equal(AbuseState.ACCEPTED)
expect(res.body.data[0].moderationComment).to.equal('It is valid')
})
})
+ describe('My abuses', async function () {
+ let abuseId1: number
+ let userAccessToken: string
+
+ before(async function () {
+ userAccessToken = await generateUserAccessToken(servers[0], 'user_42')
+
+ await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: 'user reason 1' })
+
+ const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid)
+ await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId, reason: 'user reason 2' })
+ })
+
+ it('Should correctly list my abuses', async function () {
+ {
+ const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 5, sort: 'createdAt' })
+ expect(res.body.total).to.equal(2)
+
+ const abuses: UserAbuse[] = res.body.data
+ expect(abuses[0].reason).to.equal('user reason 1')
+ expect(abuses[1].reason).to.equal('user reason 2')
+
+ abuseId1 = abuses[0].id
+ }
+
+ {
+ const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: 'createdAt' })
+ expect(res.body.total).to.equal(2)
+
+ const abuses: UserAbuse[] = res.body.data
+ expect(abuses[0].reason).to.equal('user reason 2')
+ }
+
+ {
+ const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: '-createdAt' })
+ expect(res.body.total).to.equal(2)
+
+ const abuses: UserAbuse[] = res.body.data
+ expect(abuses[0].reason).to.equal('user reason 1')
+ }
+ })
+
+ it('Should correctly filter my abuses by id', async function () {
+ const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, id: abuseId1 })
+
+ expect(res.body.total).to.equal(1)
+
+ const abuses: UserAbuse[] = res.body.data
+ expect(abuses[0].reason).to.equal('user reason 1')
+ })
+
+ it('Should correctly filter my abuses by search', async function () {
+ const res = await getUserAbusesList({
+ url: servers[0].url,
+ token: userAccessToken,
+ search: 'server 2'
+ })
+
+ expect(res.body.total).to.equal(1)
+
+ const abuses: UserAbuse[] = res.body.data
+ expect(abuses[0].reason).to.equal('user reason 2')
+ })
+
+ it('Should correctly filter my abuses by state', async function () {
+ const body = { state: AbuseState.REJECTED }
+ await updateAbuse(servers[0].url, servers[0].accessToken, abuseId1, body)
+
+ const res = await getUserAbusesList({
+ url: servers[0].url,
+ token: userAccessToken,
+ state: AbuseState.REJECTED
+ })
+
+ expect(res.body.total).to.equal(1)
+
+ const abuses: UserAbuse[] = res.body.data
+ expect(abuses[0].reason).to.equal('user reason 1')
+ })
+ })
+
+ describe('Abuse messages', async function () {
+ let abuseId: number
+ let userAccessToken: string
+ let abuseMessageUserId: number
+ let abuseMessageModerationId: number
+
+ before(async function () {
+ userAccessToken = await generateUserAccessToken(servers[0], 'user_43')
+
+ const res = await reportAbuse({
+ url: servers[0].url,
+ token: userAccessToken,
+ videoId: servers[0].video.id,
+ reason: 'user 43 reason 1'
+ })
+
+ abuseId = res.body.abuse.id
+ })
+
+ it('Should create some messages on the abuse', async function () {
+ await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 1')
+ await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 2')
+ await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 3')
+ await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 4')
+ })
+
+ it('Should have the correct messages count when listing abuses', async function () {
+ const results = await Promise.all([
+ getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, start: 0, count: 50 }),
+ getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 50 })
+ ])
+
+ for (const res of results) {
+ const abuses: AdminAbuse[] = res.body.data
+ const abuse = abuses.find(a => a.id === abuseId)
+ expect(abuse.countMessages).to.equal(4)
+ }
+ })
+
+ it('Should correctly list messages of this abuse', async function () {
+ const results = await Promise.all([
+ listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId),
+ listAbuseMessages(servers[0].url, userAccessToken, abuseId)
+ ])
+
+ for (const res of results) {
+ expect(res.body.total).to.equal(4)
+
+ const abuseMessages: AbuseMessage[] = res.body.data
+
+ expect(abuseMessages[0].message).to.equal('message 1')
+ expect(abuseMessages[0].byModerator).to.be.false
+ expect(abuseMessages[0].account.name).to.equal('user_43')
+
+ abuseMessageUserId = abuseMessages[0].id
+
+ expect(abuseMessages[1].message).to.equal('message 2')
+ expect(abuseMessages[1].byModerator).to.be.true
+ expect(abuseMessages[1].account.name).to.equal('root')
+
+ expect(abuseMessages[2].message).to.equal('message 3')
+ expect(abuseMessages[2].byModerator).to.be.true
+ expect(abuseMessages[2].account.name).to.equal('root')
+ abuseMessageModerationId = abuseMessages[2].id
+
+ expect(abuseMessages[3].message).to.equal('message 4')
+ expect(abuseMessages[3].byModerator).to.be.false
+ expect(abuseMessages[3].account.name).to.equal('user_43')
+ }
+ })
+
+ it('Should delete messages', async function () {
+ await deleteAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, abuseMessageModerationId)
+ await deleteAbuseMessage(servers[0].url, userAccessToken, abuseId, abuseMessageUserId)
+
+ const results = await Promise.all([
+ listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId),
+ listAbuseMessages(servers[0].url, userAccessToken, abuseId)
+ ])
+
+ for (const res of results) {
+ expect(res.body.total).to.equal(2)
+
+ const abuseMessages: AbuseMessage[] = res.body.data
+
+ expect(abuseMessages[0].message).to.equal('message 2')
+ expect(abuseMessages[1].message).to.equal('message 4')
+ }
+ })
+ })
+
after(async function () {
await cleanupTests(servers)
})
createUser,
deleteMe,
flushAndRunServer,
- getAbusesList,
getAccountRatings,
+ getAdminAbusesList,
getBlacklistedVideosList,
getCustomConfig,
getMyUserInformation,
const reason = 'my super bad reason'
await reportAbuse({ url: server.url, token: user17AccessToken, videoId, reason })
- const res1 = await getAbusesList({ url: server.url, token: server.accessToken })
+ const res1 = await getAdminAbusesList({ url: server.url, token: server.accessToken })
const abuseId = res1.body.data[0].id
const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true)
import 'mocha'
import * as chai from 'chai'
-import { Abuse, AbusePredefinedReasonsString, AbuseState } from '@shared/models'
+import { AbusePredefinedReasonsString, AbuseState, AdminAbuse } from '@shared/models'
import {
cleanupTests,
createUser,
describe('Test video abuses', function () {
let servers: ServerInfo[] = []
- let abuseServer2: Abuse
+ let abuseServer2: AdminAbuse
before(async function () {
this.timeout(50000)
expect(res1.body.data).to.be.an('array')
expect(res1.body.data.length).to.equal(1)
- const abuse: Abuse = res1.body.data[0]
+ const abuse: AdminAbuse = res1.body.data[0]
expect(abuse.reason).to.equal('my super bad reason')
expect(abuse.reporterAccount.name).to.equal('root')
expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port)
expect(res1.body.data).to.be.an('array')
expect(res1.body.data.length).to.equal(2)
- const abuse1: Abuse = res1.body.data[0]
+ const abuse1: AdminAbuse = res1.body.data[0]
expect(abuse1.reason).to.equal('my super bad reason')
expect(abuse1.reporterAccount.name).to.equal('root')
expect(abuse1.reporterAccount.host).to.equal('localhost:' + servers[0].port)
expect(abuse1.video.countReports).to.equal(1)
expect(abuse1.video.nthReport).to.equal(1)
- const abuse2: Abuse = res1.body.data[1]
+ const abuse2: AdminAbuse = res1.body.data[1]
expect(abuse2.reason).to.equal('my super bad reason 2')
expect(abuse2.reporterAccount.name).to.equal('root')
expect(abuse2.reporterAccount.host).to.equal('localhost:' + servers[0].port)
expect(res.body.data.length).to.equal(2, "wrong number of videos returned")
expect(res.body.data[0].id).to.equal(abuseServer2.id, "wrong origin server id for first video")
- const abuse: Abuse = res.body.data[0]
+ const abuse: AdminAbuse = res.body.data[0]
expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id")
expect(abuse.video.channel).to.exist
expect(abuse.video.deleted).to.be.true
const res2 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
{
- for (const abuse of res2.body.data as Abuse[]) {
+ for (const abuse of res2.body.data as AdminAbuse[]) {
if (abuse.video.id === video3.id) {
expect(abuse.video.countReports).to.equal(1, "wrong reports count for video 3")
expect(abuse.video.nthReport).to.equal(1, "wrong report position in report list for video 3")
const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
{
- const abuse = (res.body.data as Abuse[]).find(a => a.id === createdAbuse.id)
+ const abuse = (res.body.data as AdminAbuse[]).find(a => a.id === createdAbuse.id)
expect(abuse.reason).to.equals(reason5)
expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported")
expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported")
const res = await getVideoAbusesList(options)
- return res.body.data as Abuse[]
+ return res.body.data as AdminAbuse[]
}
expect(await list({ id: 56 })).to.have.lengthOf(0)
--- /dev/null
+import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
+import { PickWith } from '@shared/core-utils'
+import { AbuseModel } from '../../../models/abuse/abuse'
+import { MAccountFormattable } from '../account'
+
+type Use<K extends keyof AbuseMessageModel, M> = PickWith<AbuseMessageModel, K, M>
+
+// ############################################################################
+
+export type MAbuseMessage = Omit<AbuseMessageModel, 'Account' | 'Abuse' | 'toFormattedJSON'>
+
+export type MAbuseMessageId = Pick<AbuseModel, 'id'>
+
+// ############################################################################
+
+// Format for API
+
+export type MAbuseMessageFormattable =
+ MAbuseMessage &
+ Use<'Account', MAccountFormattable>
// Format for API or AP object
-export type MAbuseFormattable =
+export type MAbuseAdminFormattable =
MAbuse &
Use<'ReporterAccount', MAccountFormattable> &
Use<'FlaggedAccount', MAccountFormattable> &
Use<'VideoAbuse', MVideoAbuseFormattable> &
Use<'VideoCommentAbuse', MCommentAbuseFormattable>
+
+export type MAbuseUserFormattable =
+ MAbuse &
+ Use<'FlaggedAccount', MAccountFormattable> &
+ Use<'VideoAbuse', MVideoAbuseFormattable> &
+ Use<'VideoCommentAbuse', MCommentAbuseFormattable>
export * from './abuse'
+export * from './abuse-message'
import { RegisterServerAuthExternalOptions } from '@server/types'
import {
MAbuse,
+ MAbuseMessage,
MAccountBlocklist,
MActorUrl,
MStreamingPlaylist,
videoCaption?: MVideoCaptionVideo
abuse?: MAbuse
+ abuseMessage?: MAbuseMessage
videoStreamingPlaylist?: MStreamingPlaylist
})
}
-function getAbusesList (options: {
+function getAdminAbusesList (options: {
url: string
token: string
})
}
+function getUserAbusesList (options: {
+ url: string
+ token: string
+
+ start?: number
+ count?: number
+ sort?: string
+
+ id?: number
+ search?: string
+ state?: AbuseState
+}) {
+ const {
+ url,
+ token,
+ start,
+ count,
+ sort,
+ id,
+ search,
+ state
+ } = options
+ const path = '/api/v1/users/me/abuses'
+
+ const query = {
+ id,
+ search,
+ state,
+ start,
+ count,
+ sort: sort || 'createdAt'
+ }
+
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ query,
+ statusCodeExpected: 200
+ })
+}
+
function updateAbuse (
url: string,
token: string,
})
}
+function listAbuseMessages (url: string, token: string, abuseId: number, statusCodeExpected = 200) {
+ const path = '/api/v1/abuses/' + abuseId + '/messages'
+
+ return makeGetRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+function deleteAbuseMessage (url: string, token: string, abuseId: number, messageId: number, statusCodeExpected = 204) {
+ const path = '/api/v1/abuses/' + abuseId + '/messages/' + messageId
+
+ return makeDeleteRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+function addAbuseMessage (url: string, token: string, abuseId: number, message: string, statusCodeExpected = 200) {
+ const path = '/api/v1/abuses/' + abuseId + '/messages'
+
+ return makePostBodyRequest({
+ url,
+ token,
+ path,
+ fields: { message },
+ statusCodeExpected
+ })
+}
+
// ---------------------------------------------------------------------------
export {
reportAbuse,
- getAbusesList,
+ getAdminAbusesList,
updateAbuse,
- deleteAbuse
+ deleteAbuse,
+ getUserAbusesList,
+ listAbuseMessages,
+ deleteAbuseMessage,
+ addAbuseMessage
}
--- /dev/null
+import { AccountSummary } from '@shared/models'
+
+export interface AbuseMessage {
+ id: number
+ message: string
+ byModerator: boolean
+
+ account: AccountSummary
+}
import { VideoConstant } from '../../videos/video-constant.model'
import { VideoChannel } from '../../videos/channel/video-channel.model'
-export interface VideoAbuse {
+export interface AdminVideoAbuse {
id: number
name: string
uuid: string
nthReport: number
}
-export interface VideoCommentAbuse {
+export interface AdminVideoCommentAbuse {
id: number
threadId: number
deleted: boolean
}
-export interface Abuse {
+export interface AdminAbuse {
id: number
reason: string
state: VideoConstant<AbuseState>
moderationComment?: string
- video?: VideoAbuse
- comment?: VideoCommentAbuse
+ video?: AdminVideoAbuse
+ comment?: AdminVideoCommentAbuse
createdAt: Date
updatedAt: Date
countReportsForReporter?: number
countReportsForReportee?: number
+ countMessages: number
+
// FIXME: deprecated in 2.3, remove the following properties
// @deprecated
// @deprecated
nth?: number
}
+
+export type UserVideoAbuse = Omit<AdminVideoAbuse, 'countReports' | 'nthReport'>
+
+export type UserVideoCommentAbuse = AdminVideoCommentAbuse
+
+export type UserAbuse = Omit<AdminAbuse, 'reporterAccount' | 'countReportsForReportee' | 'countReportsForReporter' | 'startAt' | 'endAt'
+| 'count' | 'nth'>
export * from './abuse-create.model'
export * from './abuse-filter.type'
+export * from './abuse-message.model'
export * from './abuse-reason.model'
export * from './abuse-state.model'
export * from './abuse-update.model'