diff options
-rw-r--r-- | server/lib/emailer.ts | 21 | ||||
-rw-r--r-- | server/models/account/user.ts | 23 | ||||
-rw-r--r-- | server/models/video/video-abuse.ts | 8 | ||||
-rw-r--r-- | server/tests/api/server/email.ts | 36 | ||||
-rw-r--r-- | server/tests/utils/videos/video-abuses.ts | 2 | ||||
-rw-r--r-- | shared/models/users/user-role.ts | 3 |
6 files changed, 86 insertions, 7 deletions
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 317cec706..bc0061c99 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -1,7 +1,10 @@ | |||
1 | import { createTransport, Transporter } from 'nodemailer' | 1 | import { createTransport, Transporter } from 'nodemailer' |
2 | import { UserRight } from '../../shared/models/users' | ||
2 | import { isTestInstance } from '../helpers/core-utils' | 3 | import { isTestInstance } from '../helpers/core-utils' |
3 | import { logger } from '../helpers/logger' | 4 | import { logger } from '../helpers/logger' |
4 | import { CONFIG } from '../initializers' | 5 | import { CONFIG } from '../initializers' |
6 | import { UserModel } from '../models/account/user' | ||
7 | import { VideoModel } from '../models/video/video' | ||
5 | import { JobQueue } from './job-queue' | 8 | import { JobQueue } from './job-queue' |
6 | import { EmailPayload } from './job-queue/handlers/email' | 9 | import { EmailPayload } from './job-queue/handlers/email' |
7 | import { readFileSync } from 'fs' | 10 | import { readFileSync } from 'fs' |
@@ -82,6 +85,24 @@ class Emailer { | |||
82 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 85 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
83 | } | 86 | } |
84 | 87 | ||
88 | async addVideoAbuseReport (videoId: number) { | ||
89 | const video = await VideoModel.load(videoId) | ||
90 | |||
91 | const text = `Hi,\n\n` + | ||
92 | `Your instance received an abuse for video the following video ${video.url}\n\n` + | ||
93 | `Cheers,\n` + | ||
94 | `PeerTube.` | ||
95 | |||
96 | const to = await UserModel.listEmailsWithRight(UserRight.MANAGE_VIDEO_ABUSES) | ||
97 | const emailPayload: EmailPayload = { | ||
98 | to, | ||
99 | subject: '[PeerTube] Received a video abuse', | ||
100 | text | ||
101 | } | ||
102 | |||
103 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
104 | } | ||
105 | |||
85 | sendMail (to: string[], subject: string, text: string) { | 106 | sendMail (to: string[], subject: string, text: string) { |
86 | if (!this.transporter) { | 107 | if (!this.transporter) { |
87 | throw new Error('Cannot send mail because SMTP is not configured.') | 108 | throw new Error('Cannot send mail because SMTP is not configured.') |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 026a8c9a0..653921907 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -4,7 +4,7 @@ import { | |||
4 | Scopes, Table, UpdatedAt | 4 | Scopes, Table, UpdatedAt |
5 | } from 'sequelize-typescript' | 5 | } from 'sequelize-typescript' |
6 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' | 6 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' |
7 | import { User } from '../../../shared/models/users' | 7 | import { User, UserRole } from '../../../shared/models/users' |
8 | import { | 8 | import { |
9 | isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, | 9 | isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, |
10 | isUserVideoQuotaValid | 10 | isUserVideoQuotaValid |
@@ -137,6 +137,27 @@ export class UserModel extends Model<UserModel> { | |||
137 | }) | 137 | }) |
138 | } | 138 | } |
139 | 139 | ||
140 | static listEmailsWithRight (right: UserRight) { | ||
141 | const roles = Object.keys(USER_ROLE_LABELS) | ||
142 | .map(k => parseInt(k, 10) as UserRole) | ||
143 | .filter(role => hasUserRight(role, right)) | ||
144 | |||
145 | console.log(roles) | ||
146 | |||
147 | const query = { | ||
148 | attribute: [ 'email' ], | ||
149 | where: { | ||
150 | role: { | ||
151 | [Sequelize.Op.in]: roles | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | return UserModel.unscoped() | ||
157 | .findAll(query) | ||
158 | .then(u => u.map(u => u.email)) | ||
159 | } | ||
160 | |||
140 | static loadById (id: number) { | 161 | static loadById (id: number) { |
141 | return UserModel.findById(id) | 162 | return UserModel.findById(id) |
142 | } | 163 | } |
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts index 182971c4e..cc7078ae7 100644 --- a/server/models/video/video-abuse.ts +++ b/server/models/video/video-abuse.ts | |||
@@ -1,7 +1,8 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AfterCreate, AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { VideoAbuseObject } from '../../../shared/models/activitypub/objects' | 2 | import { VideoAbuseObject } from '../../../shared/models/activitypub/objects' |
3 | import { isVideoAbuseReasonValid } from '../../helpers/custom-validators/videos' | 3 | import { isVideoAbuseReasonValid } from '../../helpers/custom-validators/videos' |
4 | import { CONFIG } from '../../initializers' | 4 | import { CONFIG } from '../../initializers' |
5 | import { Emailer } from '../../lib/emailer' | ||
5 | import { AccountModel } from '../account/account' | 6 | import { AccountModel } from '../account/account' |
6 | import { getSort, throwIfNotValid } from '../utils' | 7 | import { getSort, throwIfNotValid } from '../utils' |
7 | import { VideoModel } from './video' | 8 | import { VideoModel } from './video' |
@@ -54,6 +55,11 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> { | |||
54 | }) | 55 | }) |
55 | Video: VideoModel | 56 | Video: VideoModel |
56 | 57 | ||
58 | @AfterCreate | ||
59 | static sendEmailNotification (instance: VideoAbuseModel) { | ||
60 | return Emailer.Instance.addVideoAbuseReport(instance.videoId) | ||
61 | } | ||
62 | |||
57 | static listForApi (start: number, count: number, sort: string) { | 63 | static listForApi (start: number, count: number, sort: string) { |
58 | const query = { | 64 | const query = { |
59 | offset: start, | 65 | offset: start, |
diff --git a/server/tests/api/server/email.ts b/server/tests/api/server/email.ts index 8eb9c0fa4..068e820c8 100644 --- a/server/tests/api/server/email.ts +++ b/server/tests/api/server/email.ts | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { askResetPassword, createUser, resetPassword, runServer, userLogin, wait } from '../../utils' | 5 | import { askResetPassword, createUser, reportVideoAbuse, resetPassword, runServer, uploadVideo, userLogin, wait } from '../../utils' |
6 | import { flushTests, killallServers, ServerInfo, setAccessTokensToServers } from '../../utils/index' | 6 | import { flushTests, killallServers, ServerInfo, setAccessTokensToServers } from '../../utils/index' |
7 | import { mockSmtpServer } from '../../utils/miscs/email' | 7 | import { mockSmtpServer } from '../../utils/miscs/email' |
8 | 8 | ||
@@ -11,6 +11,7 @@ const expect = chai.expect | |||
11 | describe('Test emails', function () { | 11 | describe('Test emails', function () { |
12 | let server: ServerInfo | 12 | let server: ServerInfo |
13 | let userId: number | 13 | let userId: number |
14 | let videoUUID: string | ||
14 | let verificationString: string | 15 | let verificationString: string |
15 | const emails: object[] = [] | 16 | const emails: object[] = [] |
16 | const user = { | 17 | const user = { |
@@ -35,8 +36,18 @@ describe('Test emails', function () { | |||
35 | await wait(5000) | 36 | await wait(5000) |
36 | await setAccessTokensToServers([ server ]) | 37 | await setAccessTokensToServers([ server ]) |
37 | 38 | ||
38 | const res = await createUser(server.url, server.accessToken, user.username, user.password) | 39 | { |
39 | userId = res.body.user.id | 40 | const res = await createUser(server.url, server.accessToken, user.username, user.password) |
41 | userId = res.body.user.id | ||
42 | } | ||
43 | |||
44 | { | ||
45 | const attributes = { | ||
46 | name: 'my super name' | ||
47 | } | ||
48 | const res = await uploadVideo(server.url, server.accessToken, attributes) | ||
49 | videoUUID = res.body.video.uuid | ||
50 | } | ||
40 | }) | 51 | }) |
41 | 52 | ||
42 | describe('When resetting user password', function () { | 53 | describe('When resetting user password', function () { |
@@ -83,6 +94,25 @@ describe('Test emails', function () { | |||
83 | }) | 94 | }) |
84 | }) | 95 | }) |
85 | 96 | ||
97 | describe('When creating a video abuse', function () { | ||
98 | it('Should send the notification email', async function () { | ||
99 | this.timeout(10000) | ||
100 | |||
101 | const reason = 'my super bad reason' | ||
102 | await reportVideoAbuse(server.url, server.accessToken, videoUUID, reason) | ||
103 | |||
104 | await wait(3000) | ||
105 | expect(emails).to.have.lengthOf(2) | ||
106 | |||
107 | const email = emails[1] | ||
108 | |||
109 | expect(email['from'][0]['address']).equal('test-admin@localhost') | ||
110 | expect(email['to'][0]['address']).equal('admin1@example.com') | ||
111 | expect(email['subject']).contains('abuse') | ||
112 | expect(email['text']).contains(videoUUID) | ||
113 | }) | ||
114 | }) | ||
115 | |||
86 | after(async function () { | 116 | after(async function () { |
87 | killallServers([ server ]) | 117 | killallServers([ server ]) |
88 | 118 | ||
diff --git a/server/tests/utils/videos/video-abuses.ts b/server/tests/utils/videos/video-abuses.ts index f00809234..0d72bf457 100644 --- a/server/tests/utils/videos/video-abuses.ts +++ b/server/tests/utils/videos/video-abuses.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as request from 'supertest' | 1 | import * as request from 'supertest' |
2 | 2 | ||
3 | function reportVideoAbuse (url: string, token: string, videoId: number, reason: string, specialStatus = 204) { | 3 | function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 204) { |
4 | const path = '/api/v1/videos/' + videoId + '/abuse' | 4 | const path = '/api/v1/videos/' + videoId + '/abuse' |
5 | 5 | ||
6 | return request(url) | 6 | return request(url) |
diff --git a/shared/models/users/user-role.ts b/shared/models/users/user-role.ts index 0e75444f8..271c9a46f 100644 --- a/shared/models/users/user-role.ts +++ b/shared/models/users/user-role.ts | |||
@@ -7,7 +7,8 @@ export enum UserRole { | |||
7 | USER = 2 | 7 | USER = 2 |
8 | } | 8 | } |
9 | 9 | ||
10 | export const USER_ROLE_LABELS = { | 10 | // TODO: use UserRole for key once https://github.com/Microsoft/TypeScript/issues/13042 is fixed |
11 | export const USER_ROLE_LABELS: { [ id: number ]: string } = { | ||
11 | [UserRole.USER]: 'User', | 12 | [UserRole.USER]: 'User', |
12 | [UserRole.MODERATOR]: 'Moderator', | 13 | [UserRole.MODERATOR]: 'Moderator', |
13 | [UserRole.ADMINISTRATOR]: 'Administrator' | 14 | [UserRole.ADMINISTRATOR]: 'Administrator' |