Send an email on video abuse report
authorChocobozzz <me@florianbigard.com>
Thu, 1 Feb 2018 10:08:10 +0000 (11:08 +0100)
committerChocobozzz <me@florianbigard.com>
Thu, 1 Feb 2018 10:08:29 +0000 (11:08 +0100)
server/lib/emailer.ts
server/models/account/user.ts
server/models/video/video-abuse.ts
server/tests/api/server/email.ts
server/tests/utils/videos/video-abuses.ts
shared/models/users/user-role.ts

index 317cec70689693aba2b344fd4d0b257df203e7f4..bc0061c998c00d11bdcf3a5c5fdeef8f4cc94d13 100644 (file)
@@ -1,7 +1,10 @@
 import { createTransport, Transporter } from 'nodemailer'
+import { UserRight } from '../../shared/models/users'
 import { isTestInstance } from '../helpers/core-utils'
 import { logger } from '../helpers/logger'
 import { CONFIG } from '../initializers'
+import { UserModel } from '../models/account/user'
+import { VideoModel } from '../models/video/video'
 import { JobQueue } from './job-queue'
 import { EmailPayload } from './job-queue/handlers/email'
 import { readFileSync } from 'fs'
@@ -82,6 +85,24 @@ class Emailer {
     return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
   }
 
+  async addVideoAbuseReport (videoId: number) {
+    const video = await VideoModel.load(videoId)
+
+    const text = `Hi,\n\n` +
+      `Your instance received an abuse for video the following video ${video.url}\n\n` +
+      `Cheers,\n` +
+      `PeerTube.`
+
+    const to = await UserModel.listEmailsWithRight(UserRight.MANAGE_VIDEO_ABUSES)
+    const emailPayload: EmailPayload = {
+      to,
+      subject: '[PeerTube] Received a video abuse',
+      text
+    }
+
+    return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
+  }
+
   sendMail (to: string[], subject: string, text: string) {
     if (!this.transporter) {
       throw new Error('Cannot send mail because SMTP is not configured.')
index 026a8c9a0d6841d83180cdcf1d3b25625f401cf2..65392190781aae67982c2d165919354c71c01fab 100644 (file)
@@ -4,7 +4,7 @@ import {
   Scopes, Table, UpdatedAt
 } from 'sequelize-typescript'
 import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
-import { User } from '../../../shared/models/users'
+import { User, UserRole } from '../../../shared/models/users'
 import {
   isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid,
   isUserVideoQuotaValid
@@ -137,6 +137,27 @@ export class UserModel extends Model<UserModel> {
       })
   }
 
+  static listEmailsWithRight (right: UserRight) {
+    const roles = Object.keys(USER_ROLE_LABELS)
+      .map(k => parseInt(k, 10) as UserRole)
+      .filter(role => hasUserRight(role, right))
+
+    console.log(roles)
+
+    const query = {
+      attribute: [ 'email' ],
+      where: {
+        role: {
+          [Sequelize.Op.in]: roles
+        }
+      }
+    }
+
+    return UserModel.unscoped()
+      .findAll(query)
+      .then(u => u.map(u => u.email))
+  }
+
   static loadById (id: number) {
     return UserModel.findById(id)
   }
index 182971c4e018a2aa8aa0735d94a71940814c0d2e..cc7078ae7966541ecea1c3bb79a542af19fc178c 100644 (file)
@@ -1,7 +1,8 @@
-import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { AfterCreate, AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
 import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
 import { isVideoAbuseReasonValid } from '../../helpers/custom-validators/videos'
 import { CONFIG } from '../../initializers'
+import { Emailer } from '../../lib/emailer'
 import { AccountModel } from '../account/account'
 import { getSort, throwIfNotValid } from '../utils'
 import { VideoModel } from './video'
@@ -54,6 +55,11 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
   })
   Video: VideoModel
 
+  @AfterCreate
+  static sendEmailNotification (instance: VideoAbuseModel) {
+    return Emailer.Instance.addVideoAbuseReport(instance.videoId)
+  }
+
   static listForApi (start: number, count: number, sort: string) {
     const query = {
       offset: start,
index 8eb9c0fa4058f8aff060752b250aaa902e5396ae..068e820c8e5c44913c8e3d0f827f80fad21f4083 100644 (file)
@@ -2,7 +2,7 @@
 
 import * as chai from 'chai'
 import 'mocha'
-import { askResetPassword, createUser, resetPassword, runServer, userLogin, wait } from '../../utils'
+import { askResetPassword, createUser, reportVideoAbuse, resetPassword, runServer, uploadVideo, userLogin, wait } from '../../utils'
 import { flushTests, killallServers, ServerInfo, setAccessTokensToServers } from '../../utils/index'
 import { mockSmtpServer } from '../../utils/miscs/email'
 
@@ -11,6 +11,7 @@ const expect = chai.expect
 describe('Test emails', function () {
   let server: ServerInfo
   let userId: number
+  let videoUUID: string
   let verificationString: string
   const emails: object[] = []
   const user = {
@@ -35,8 +36,18 @@ describe('Test emails', function () {
     await wait(5000)
     await setAccessTokensToServers([ server ])
 
-    const res = await createUser(server.url, server.accessToken, user.username, user.password)
-    userId = res.body.user.id
+    {
+      const res = await createUser(server.url, server.accessToken, user.username, user.password)
+      userId = res.body.user.id
+    }
+
+    {
+      const attributes = {
+        name: 'my super name'
+      }
+      const res = await uploadVideo(server.url, server.accessToken, attributes)
+      videoUUID = res.body.video.uuid
+    }
   })
 
   describe('When resetting user password', function () {
@@ -83,6 +94,25 @@ describe('Test emails', function () {
     })
   })
 
+  describe('When creating a video abuse', function () {
+    it('Should send the notification email', async function () {
+      this.timeout(10000)
+
+      const reason = 'my super bad reason'
+      await reportVideoAbuse(server.url, server.accessToken, videoUUID, reason)
+
+      await wait(3000)
+      expect(emails).to.have.lengthOf(2)
+
+      const email = emails[1]
+
+      expect(email['from'][0]['address']).equal('test-admin@localhost')
+      expect(email['to'][0]['address']).equal('admin1@example.com')
+      expect(email['subject']).contains('abuse')
+      expect(email['text']).contains(videoUUID)
+    })
+  })
+
   after(async function () {
     killallServers([ server ])
 
index f0080923446c128d586066f889afa74633b00f40..0d72bf457a8384a0fde325df6cad0a0c3a72be5e 100644 (file)
@@ -1,6 +1,6 @@
 import * as request from 'supertest'
 
-function reportVideoAbuse (url: string, token: string, videoId: number, reason: string, specialStatus = 204) {
+function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 204) {
   const path = '/api/v1/videos/' + videoId + '/abuse'
 
   return request(url)
index 0e75444f8f35f5667ae9f361aeaf3ffe6fef5ab7..271c9a46f23d77b6dbf799501172cd895cd41202 100644 (file)
@@ -7,7 +7,8 @@ export enum UserRole {
   USER = 2
 }
 
-export const USER_ROLE_LABELS = {
+// TODO: use UserRole for key once https://github.com/Microsoft/TypeScript/issues/13042 is fixed
+export const USER_ROLE_LABELS: { [ id: number ]: string } = {
   [UserRole.USER]: 'User',
   [UserRole.MODERATOR]: 'Moderator',
   [UserRole.ADMINISTRATOR]: 'Administrator'