diff options
18 files changed, 210 insertions, 125 deletions
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 25a18caa5..9f9d2d77f 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -25,7 +25,7 @@ import { | |||
25 | usersVideoRatingValidator | 25 | usersVideoRatingValidator |
26 | } from '../../../middlewares' | 26 | } from '../../../middlewares' |
27 | import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } from '../../../middlewares/validators' | 27 | import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } from '../../../middlewares/validators' |
28 | import { updateAvatarValidator } from '../../../middlewares/validators/avatar' | 28 | import { updateAvatarValidator } from '../../../middlewares/validators/actor-image' |
29 | import { AccountModel } from '../../../models/account/account' | 29 | import { AccountModel } from '../../../models/account/account' |
30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
31 | import { UserModel } from '../../../models/account/user' | 31 | import { UserModel } from '../../../models/account/user' |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 1c926722d..149d6cfb4 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -33,7 +33,7 @@ import { | |||
33 | videoPlaylistsSortValidator | 33 | videoPlaylistsSortValidator |
34 | } from '../../middlewares' | 34 | } from '../../middlewares' |
35 | import { videoChannelsNameWithHostValidator, videoChannelsOwnSearchValidator, videosSortValidator } from '../../middlewares/validators' | 35 | import { videoChannelsNameWithHostValidator, videoChannelsOwnSearchValidator, videosSortValidator } from '../../middlewares/validators' |
36 | import { updateAvatarValidator, updateBannerValidator } from '../../middlewares/validators/avatar' | 36 | import { updateAvatarValidator, updateBannerValidator } from '../../middlewares/validators/actor-image' |
37 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' | 37 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' |
38 | import { AccountModel } from '../../models/account/account' | 38 | import { AccountModel } from '../../models/account/account' |
39 | import { VideoModel } from '../../models/video/video' | 39 | import { VideoModel } from '../../models/video/video' |
diff --git a/server/helpers/custom-validators/actor-images.ts b/server/helpers/custom-validators/actor-images.ts new file mode 100644 index 000000000..4fb0b7c70 --- /dev/null +++ b/server/helpers/custom-validators/actor-images.ts | |||
@@ -0,0 +1,17 @@ | |||
1 | |||
2 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | ||
3 | import { isFileValid } from './misc' | ||
4 | |||
5 | const imageMimeTypes = CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME | ||
6 | .map(v => v.replace('.', '')) | ||
7 | .join('|') | ||
8 | const imageMimeTypesRegex = `image/(${imageMimeTypes})` | ||
9 | function isActorImageFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], fieldname: string) { | ||
10 | return isFileValid(files, imageMimeTypesRegex, fieldname, CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max) | ||
11 | } | ||
12 | |||
13 | // --------------------------------------------------------------------------- | ||
14 | |||
15 | export { | ||
16 | isActorImageFile | ||
17 | } | ||
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts index 85f3634c8..5b21c3529 100644 --- a/server/helpers/custom-validators/users.ts +++ b/server/helpers/custom-validators/users.ts | |||
@@ -3,7 +3,7 @@ import validator from 'validator' | |||
3 | import { UserRole } from '../../../shared' | 3 | import { UserRole } from '../../../shared' |
4 | import { isEmailEnabled } from '../../initializers/config' | 4 | import { isEmailEnabled } from '../../initializers/config' |
5 | import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers/constants' | 5 | import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers/constants' |
6 | import { exists, isArray, isBooleanValid, isFileValid } from './misc' | 6 | import { exists, isArray, isBooleanValid } from './misc' |
7 | 7 | ||
8 | const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS | 8 | const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS |
9 | 9 | ||
@@ -97,14 +97,6 @@ function isUserRoleValid (value: any) { | |||
97 | return exists(value) && validator.isInt('' + value) && UserRole[value] !== undefined | 97 | return exists(value) && validator.isInt('' + value) && UserRole[value] !== undefined |
98 | } | 98 | } |
99 | 99 | ||
100 | const avatarMimeTypes = CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME | ||
101 | .map(v => v.replace('.', '')) | ||
102 | .join('|') | ||
103 | const avatarMimeTypesRegex = `image/(${avatarMimeTypes})` | ||
104 | function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { | ||
105 | return isFileValid(files, avatarMimeTypesRegex, 'avatarfile', CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max) | ||
106 | } | ||
107 | |||
108 | // --------------------------------------------------------------------------- | 100 | // --------------------------------------------------------------------------- |
109 | 101 | ||
110 | export { | 102 | export { |
@@ -128,6 +120,5 @@ export { | |||
128 | isUserDisplayNameValid, | 120 | isUserDisplayNameValid, |
129 | isUserDescriptionValid, | 121 | isUserDescriptionValid, |
130 | isNoInstanceConfigWarningModal, | 122 | isNoInstanceConfigWarningModal, |
131 | isNoWelcomeModal, | 123 | isNoWelcomeModal |
132 | isAvatarFile | ||
133 | } | 124 | } |
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index fe4796a3d..917fed6ec 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -34,6 +34,7 @@ import { | |||
34 | MActorFull, | 34 | MActorFull, |
35 | MActorFullActor, | 35 | MActorFullActor, |
36 | MActorId, | 36 | MActorId, |
37 | MActorImage, | ||
37 | MActorImages, | 38 | MActorImages, |
38 | MChannel | 39 | MChannel |
39 | } from '../../types/models' | 40 | } from '../../types/models' |
@@ -169,38 +170,34 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ | |||
169 | } | 170 | } |
170 | } | 171 | } |
171 | 172 | ||
172 | type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string, type: ActorImageType } | 173 | type ImageInfo = { name: string, onDisk?: boolean, fileUrl: string } |
173 | async function updateActorImageInstance (actor: MActorImages, info: AvatarInfo, t: Transaction) { | 174 | async function updateActorImageInstance (actor: MActorImages, type: ActorImageType, imageInfo: ImageInfo | null, t: Transaction) { |
174 | if (!info.name) return actor | 175 | const oldImageModel = type === ActorImageType.AVATAR |
175 | |||
176 | const oldImageModel = info.type === ActorImageType.AVATAR | ||
177 | ? actor.Avatar | 176 | ? actor.Avatar |
178 | : actor.Banner | 177 | : actor.Banner |
179 | 178 | ||
180 | if (oldImageModel) { | 179 | if (oldImageModel) { |
181 | // Don't update the avatar if the file URL did not change | 180 | // Don't update the avatar if the file URL did not change |
182 | if (info.fileUrl && oldImageModel.fileUrl === info.fileUrl) return actor | 181 | if (imageInfo?.fileUrl && oldImageModel.fileUrl === imageInfo.fileUrl) return actor |
183 | 182 | ||
184 | try { | 183 | try { |
185 | await oldImageModel.destroy({ transaction: t }) | 184 | await oldImageModel.destroy({ transaction: t }) |
185 | |||
186 | setActorImage(actor, type, null) | ||
186 | } catch (err) { | 187 | } catch (err) { |
187 | logger.error('Cannot remove old actor image of actor %s.', actor.url, { err }) | 188 | logger.error('Cannot remove old actor image of actor %s.', actor.url, { err }) |
188 | } | 189 | } |
189 | } | 190 | } |
190 | 191 | ||
191 | const imageModel = await ActorImageModel.create({ | 192 | if (imageInfo) { |
192 | filename: info.name, | 193 | const imageModel = await ActorImageModel.create({ |
193 | onDisk: info.onDisk, | 194 | filename: imageInfo.name, |
194 | fileUrl: info.fileUrl, | 195 | onDisk: imageInfo.onDisk ?? false, |
195 | type: info.type | 196 | fileUrl: imageInfo.fileUrl, |
196 | }, { transaction: t }) | 197 | type: type |
198 | }, { transaction: t }) | ||
197 | 199 | ||
198 | if (info.type === ActorImageType.AVATAR) { | 200 | setActorImage(actor, type, imageModel) |
199 | actor.avatarId = imageModel.id | ||
200 | actor.Avatar = imageModel | ||
201 | } else { | ||
202 | actor.bannerId = imageModel.id | ||
203 | actor.Banner = imageModel | ||
204 | } | 201 | } |
205 | 202 | ||
206 | return actor | 203 | return actor |
@@ -310,27 +307,8 @@ async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannel | |||
310 | return sequelizeTypescript.transaction(async t => { | 307 | return sequelizeTypescript.transaction(async t => { |
311 | updateInstanceWithAnother(actor, result.actor) | 308 | updateInstanceWithAnother(actor, result.actor) |
312 | 309 | ||
313 | if (result.avatar !== undefined) { | 310 | await updateActorImageInstance(actor, ActorImageType.AVATAR, result.avatar, t) |
314 | const avatarInfo = { | 311 | await updateActorImageInstance(actor, ActorImageType.BANNER, result.banner, t) |
315 | name: result.avatar.name, | ||
316 | fileUrl: result.avatar.fileUrl, | ||
317 | onDisk: false, | ||
318 | type: ActorImageType.AVATAR | ||
319 | } | ||
320 | |||
321 | await updateActorImageInstance(actor, avatarInfo, t) | ||
322 | } | ||
323 | |||
324 | if (result.banner !== undefined) { | ||
325 | const bannerInfo = { | ||
326 | name: result.banner.name, | ||
327 | fileUrl: result.banner.fileUrl, | ||
328 | onDisk: false, | ||
329 | type: ActorImageType.BANNER | ||
330 | } | ||
331 | |||
332 | await updateActorImageInstance(actor, bannerInfo, t) | ||
333 | } | ||
334 | 312 | ||
335 | // Force update | 313 | // Force update |
336 | actor.setDataValue('updatedAt', new Date()) | 314 | actor.setDataValue('updatedAt', new Date()) |
@@ -381,6 +359,22 @@ export { | |||
381 | 359 | ||
382 | // --------------------------------------------------------------------------- | 360 | // --------------------------------------------------------------------------- |
383 | 361 | ||
362 | function setActorImage (actorModel: MActorImages, type: ActorImageType, imageModel: MActorImage) { | ||
363 | const id = imageModel | ||
364 | ? imageModel.id | ||
365 | : null | ||
366 | |||
367 | if (type === ActorImageType.AVATAR) { | ||
368 | actorModel.avatarId = id | ||
369 | actorModel.Avatar = imageModel | ||
370 | } else { | ||
371 | actorModel.bannerId = id | ||
372 | actorModel.Banner = imageModel | ||
373 | } | ||
374 | |||
375 | return actorModel | ||
376 | } | ||
377 | |||
384 | function saveActorAndServerAndModelIfNotExist ( | 378 | function saveActorAndServerAndModelIfNotExist ( |
385 | result: FetchRemoteActorResult, | 379 | result: FetchRemoteActorResult, |
386 | ownerActor?: MActorFullActor, | 380 | ownerActor?: MActorFullActor, |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index ad3bb392d..6df9b93b2 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -134,13 +134,8 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
134 | 134 | ||
135 | await updateActorInstance(actor, actorAttributesToUpdate) | 135 | await updateActorInstance(actor, actorAttributesToUpdate) |
136 | 136 | ||
137 | for (const imageInfo of [ avatarInfo, bannerInfo ]) { | 137 | await updateActorImageInstance(actor, ActorImageType.AVATAR, avatarInfo, t) |
138 | if (!imageInfo) continue | 138 | await updateActorImageInstance(actor, ActorImageType.BANNER, bannerInfo, t) |
139 | |||
140 | const imageOptions = Object.assign({}, imageInfo, { onDisk: false }) | ||
141 | |||
142 | await updateActorImageInstance(actor, imageOptions, t) | ||
143 | } | ||
144 | 139 | ||
145 | await actor.save({ transaction: t }) | 140 | await actor.save({ transaction: t }) |
146 | 141 | ||
diff --git a/server/lib/actor-image.ts b/server/lib/actor-image.ts index 59afa93bd..fa1a2a18a 100644 --- a/server/lib/actor-image.ts +++ b/server/lib/actor-image.ts | |||
@@ -34,11 +34,10 @@ async function updateLocalActorImageFile ( | |||
34 | const actorImageInfo = { | 34 | const actorImageInfo = { |
35 | name: imageName, | 35 | name: imageName, |
36 | fileUrl: null, | 36 | fileUrl: null, |
37 | type, | ||
38 | onDisk: true | 37 | onDisk: true |
39 | } | 38 | } |
40 | 39 | ||
41 | const updatedActor = await updateActorImageInstance(accountOrChannel.Actor, actorImageInfo, t) | 40 | const updatedActor = await updateActorImageInstance(accountOrChannel.Actor, type, actorImageInfo, t) |
42 | await updatedActor.save({ transaction: t }) | 41 | await updatedActor.save({ transaction: t }) |
43 | 42 | ||
44 | await sendUpdateActor(accountOrChannel, t) | 43 | await sendUpdateActor(accountOrChannel, t) |
diff --git a/server/middlewares/validators/avatar.ts b/server/middlewares/validators/actor-image.ts index f7eb367bd..961d7a7e5 100644 --- a/server/middlewares/validators/avatar.ts +++ b/server/middlewares/validators/actor-image.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body } from 'express-validator' | 2 | import { body } from 'express-validator' |
3 | import { isAvatarFile } from '../../helpers/custom-validators/users' | 3 | import { isActorImageFile } from '@server/helpers/custom-validators/actor-images' |
4 | import { areValidationErrors } from './utils' | ||
5 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | ||
6 | import { logger } from '../../helpers/logger' | ||
7 | import { cleanUpReqFiles } from '../../helpers/express-utils' | 4 | import { cleanUpReqFiles } from '../../helpers/express-utils' |
5 | import { logger } from '../../helpers/logger' | ||
6 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | ||
7 | import { areValidationErrors } from './utils' | ||
8 | 8 | ||
9 | const updateActorImageValidatorFactory = (fieldname: string) => ([ | 9 | const updateActorImageValidatorFactory = (fieldname: string) => ([ |
10 | body(fieldname).custom((value, { req }) => isAvatarFile(req.files)).withMessage( | 10 | body(fieldname).custom((value, { req }) => isActorImageFile(req.files, fieldname)).withMessage( |
11 | 'This file is not supported or too large. Please, make sure it is of the following type : ' + | 11 | 'This file is not supported or too large. Please, make sure it is of the following type : ' + |
12 | CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME.join(', ') | 12 | CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME.join(', ') |
13 | ), | 13 | ), |
diff --git a/server/middlewares/validators/index.ts b/server/middlewares/validators/index.ts index 4086d77aa..24faeea3e 100644 --- a/server/middlewares/validators/index.ts +++ b/server/middlewares/validators/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | export * from './abuse' | 1 | export * from './abuse' |
2 | export * from './account' | 2 | export * from './account' |
3 | export * from './actor-image' | ||
3 | export * from './blocklist' | 4 | export * from './blocklist' |
4 | export * from './oembed' | 5 | export * from './oembed' |
5 | export * from './activitypub' | 6 | export * from './activitypub' |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 74885edfb..d2a055f5b 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -99,7 +99,14 @@ export type SummaryOptions = { | |||
99 | } | 99 | } |
100 | } | 100 | } |
101 | ] | 101 | ] |
102 | } | 102 | }, |
103 | include: [ | ||
104 | { | ||
105 | model: ActorImageModel, | ||
106 | as: 'Banner', | ||
107 | required: false | ||
108 | } | ||
109 | ] | ||
103 | }, | 110 | }, |
104 | { | 111 | { |
105 | model: AccountModel, | 112 | model: AccountModel, |
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts index 0dd436426..bc2e6192e 100644 --- a/server/tests/api/check-params/video-channels.ts +++ b/server/tests/api/check-params/video-channels.ts | |||
@@ -234,7 +234,8 @@ describe('Test video channels API validator', function () { | |||
234 | }) | 234 | }) |
235 | }) | 235 | }) |
236 | 236 | ||
237 | describe('When updating video channel avatar', function () { | 237 | describe('When updating video channel avatar/banner', function () { |
238 | const types = [ 'avatar', 'banner' ] | ||
238 | let path: string | 239 | let path: string |
239 | 240 | ||
240 | before(async function () { | 241 | before(async function () { |
@@ -242,48 +243,57 @@ describe('Test video channels API validator', function () { | |||
242 | }) | 243 | }) |
243 | 244 | ||
244 | it('Should fail with an incorrect input file', async function () { | 245 | it('Should fail with an incorrect input file', async function () { |
245 | const fields = {} | 246 | for (const type of types) { |
246 | const attaches = { | 247 | const fields = {} |
247 | avatarfile: join(__dirname, '..', '..', 'fixtures', 'video_short.mp4') | 248 | const attaches = { |
249 | [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'video_short.mp4') | ||
250 | } | ||
251 | |||
252 | await makeUploadRequest({ url: server.url, path: `${path}/${type}/pick`, token: server.accessToken, fields, attaches }) | ||
248 | } | 253 | } |
249 | await makeUploadRequest({ url: server.url, path: path + '/avatar/pick', token: server.accessToken, fields, attaches }) | ||
250 | }) | 254 | }) |
251 | 255 | ||
252 | it('Should fail with a big file', async function () { | 256 | it('Should fail with a big file', async function () { |
253 | const fields = {} | 257 | for (const type of types) { |
254 | const attaches = { | 258 | const fields = {} |
255 | avatarfile: join(__dirname, '..', '..', 'fixtures', 'avatar-big.png') | 259 | const attaches = { |
260 | [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'avatar-big.png') | ||
261 | } | ||
262 | await makeUploadRequest({ url: server.url, path: `${path}/${type}/pick`, token: server.accessToken, fields, attaches }) | ||
256 | } | 263 | } |
257 | await makeUploadRequest({ url: server.url, path: path + '/avatar/pick', token: server.accessToken, fields, attaches }) | ||
258 | }) | 264 | }) |
259 | 265 | ||
260 | it('Should fail with an unauthenticated user', async function () { | 266 | it('Should fail with an unauthenticated user', async function () { |
261 | const fields = {} | 267 | for (const type of types) { |
262 | const attaches = { | 268 | const fields = {} |
263 | avatarfile: join(__dirname, '..', '..', 'fixtures', 'avatar.png') | 269 | const attaches = { |
270 | [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'avatar.png') | ||
271 | } | ||
272 | await makeUploadRequest({ | ||
273 | url: server.url, | ||
274 | path: `${path}/${type}/pick`, | ||
275 | fields, | ||
276 | attaches, | ||
277 | statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 | ||
278 | }) | ||
264 | } | 279 | } |
265 | await makeUploadRequest({ | ||
266 | url: server.url, | ||
267 | path: path + '/avatar/pick', | ||
268 | fields, | ||
269 | attaches, | ||
270 | statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 | ||
271 | }) | ||
272 | }) | 280 | }) |
273 | 281 | ||
274 | it('Should succeed with the correct params', async function () { | 282 | it('Should succeed with the correct params', async function () { |
275 | const fields = {} | 283 | for (const type of types) { |
276 | const attaches = { | 284 | const fields = {} |
277 | avatarfile: join(__dirname, '..', '..', 'fixtures', 'avatar.png') | 285 | const attaches = { |
286 | [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'avatar.png') | ||
287 | } | ||
288 | await makeUploadRequest({ | ||
289 | url: server.url, | ||
290 | path: `${path}/${type}/pick`, | ||
291 | token: server.accessToken, | ||
292 | fields, | ||
293 | attaches, | ||
294 | statusCodeExpected: HttpStatusCode.OK_200 | ||
295 | }) | ||
278 | } | 296 | } |
279 | await makeUploadRequest({ | ||
280 | url: server.url, | ||
281 | path: path + '/avatar/pick', | ||
282 | token: server.accessToken, | ||
283 | fields, | ||
284 | attaches, | ||
285 | statusCodeExpected: HttpStatusCode.OK_200 | ||
286 | }) | ||
287 | }) | 297 | }) |
288 | }) | 298 | }) |
289 | 299 | ||
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts index 367f99fdd..8033b9ba5 100644 --- a/server/tests/api/videos/video-channels.ts +++ b/server/tests/api/videos/video-channels.ts | |||
@@ -5,13 +5,14 @@ import * as chai from 'chai' | |||
5 | import { | 5 | import { |
6 | cleanupTests, | 6 | cleanupTests, |
7 | createUser, | 7 | createUser, |
8 | deleteVideoChannelImage, | ||
8 | doubleFollow, | 9 | doubleFollow, |
9 | flushAndRunMultipleServers, | 10 | flushAndRunMultipleServers, |
10 | getVideo, | 11 | getVideo, |
11 | getVideoChannelVideos, | 12 | getVideoChannelVideos, |
12 | testImage, | 13 | testImage, |
13 | updateVideo, | 14 | updateVideo, |
14 | updateVideoChannelAvatar, | 15 | updateVideoChannelImage, |
15 | uploadVideo, | 16 | uploadVideo, |
16 | userLogin, | 17 | userLogin, |
17 | wait | 18 | wait |
@@ -21,7 +22,6 @@ import { | |||
21 | deleteVideoChannel, | 22 | deleteVideoChannel, |
22 | getAccountVideoChannelsList, | 23 | getAccountVideoChannelsList, |
23 | getMyUserInformation, | 24 | getMyUserInformation, |
24 | getVideoChannel, | ||
25 | getVideoChannelsList, | 25 | getVideoChannelsList, |
26 | ServerInfo, | 26 | ServerInfo, |
27 | setAccessTokensToServers, | 27 | setAccessTokensToServers, |
@@ -33,6 +33,13 @@ import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/inde | |||
33 | 33 | ||
34 | const expect = chai.expect | 34 | const expect = chai.expect |
35 | 35 | ||
36 | async function findChannel (server: ServerInfo, channelId: number) { | ||
37 | const res = await getVideoChannelsList(server.url, 0, 5, '-name') | ||
38 | const videoChannel = res.body.data.find(c => c.id === channelId) | ||
39 | |||
40 | return videoChannel as VideoChannel | ||
41 | } | ||
42 | |||
36 | describe('Test video channels', function () { | 43 | describe('Test video channels', function () { |
37 | let servers: ServerInfo[] | 44 | let servers: ServerInfo[] |
38 | let userInfo: User | 45 | let userInfo: User |
@@ -262,38 +269,85 @@ describe('Test video channels', function () { | |||
262 | }) | 269 | }) |
263 | 270 | ||
264 | it('Should update video channel avatar', async function () { | 271 | it('Should update video channel avatar', async function () { |
265 | this.timeout(5000) | 272 | this.timeout(15000) |
266 | 273 | ||
267 | const fixture = 'avatar.png' | 274 | const fixture = 'avatar.png' |
268 | 275 | ||
269 | await updateVideoChannelAvatar({ | 276 | await updateVideoChannelImage({ |
270 | url: servers[0].url, | 277 | url: servers[0].url, |
271 | accessToken: servers[0].accessToken, | 278 | accessToken: servers[0].accessToken, |
272 | videoChannelName: 'second_video_channel', | 279 | videoChannelName: 'second_video_channel', |
273 | fixture | 280 | fixture, |
281 | type: 'avatar' | ||
274 | }) | 282 | }) |
275 | 283 | ||
276 | await waitJobs(servers) | 284 | await waitJobs(servers) |
285 | |||
286 | for (const server of servers) { | ||
287 | const videoChannel = await findChannel(server, secondVideoChannelId) | ||
288 | |||
289 | await testImage(server.url, 'avatar-resized', videoChannel.avatar.path, '.png') | ||
290 | } | ||
277 | }) | 291 | }) |
278 | 292 | ||
279 | it('Should have video channel avatar updated', async function () { | 293 | it('Should update video channel banner', async function () { |
294 | this.timeout(15000) | ||
295 | |||
296 | const fixture = 'banner.jpg' | ||
297 | |||
298 | await updateVideoChannelImage({ | ||
299 | url: servers[0].url, | ||
300 | accessToken: servers[0].accessToken, | ||
301 | videoChannelName: 'second_video_channel', | ||
302 | fixture, | ||
303 | type: 'banner' | ||
304 | }) | ||
305 | |||
306 | await waitJobs(servers) | ||
307 | |||
280 | for (const server of servers) { | 308 | for (const server of servers) { |
281 | const res = await getVideoChannelsList(server.url, 0, 1, '-name') | 309 | const videoChannel = await findChannel(server, secondVideoChannelId) |
282 | 310 | ||
283 | const videoChannel = res.body.data.find(c => c.id === secondVideoChannelId) | 311 | await testImage(server.url, 'banner-resized', videoChannel.banner.path) |
312 | } | ||
313 | }) | ||
284 | 314 | ||
285 | await testImage(server.url, 'avatar-resized', videoChannel.avatar.path, '.png') | 315 | it('Should delete the video channel avatar', async function () { |
316 | this.timeout(15000) | ||
317 | |||
318 | await deleteVideoChannelImage({ | ||
319 | url: servers[0].url, | ||
320 | accessToken: servers[0].accessToken, | ||
321 | videoChannelName: 'second_video_channel', | ||
322 | type: 'avatar' | ||
323 | }) | ||
324 | |||
325 | await waitJobs(servers) | ||
326 | |||
327 | for (const server of servers) { | ||
328 | const videoChannel = await findChannel(server, secondVideoChannelId) | ||
329 | |||
330 | expect(videoChannel.avatar).to.be.null | ||
286 | } | 331 | } |
287 | }) | 332 | }) |
288 | 333 | ||
289 | it('Should get video channel', async function () { | 334 | it('Should delete the video channel banner', async function () { |
290 | const res = await getVideoChannel(servers[0].url, 'second_video_channel') | 335 | this.timeout(15000) |
336 | |||
337 | await deleteVideoChannelImage({ | ||
338 | url: servers[0].url, | ||
339 | accessToken: servers[0].accessToken, | ||
340 | videoChannelName: 'second_video_channel', | ||
341 | type: 'banner' | ||
342 | }) | ||
291 | 343 | ||
292 | const videoChannel = res.body | 344 | await waitJobs(servers) |
293 | expect(videoChannel.name).to.equal('second_video_channel') | 345 | |
294 | expect(videoChannel.displayName).to.equal('video channel updated') | 346 | for (const server of servers) { |
295 | expect(videoChannel.description).to.equal('video channel description updated') | 347 | const videoChannel = await findChannel(server, secondVideoChannelId) |
296 | expect(videoChannel.support).to.equal('video channel support text updated') | 348 | |
349 | expect(videoChannel.banner).to.be.null | ||
350 | } | ||
297 | }) | 351 | }) |
298 | 352 | ||
299 | it('Should list the second video channel videos', async function () { | 353 | it('Should list the second video channel videos', async function () { |
diff --git a/server/tests/fixtures/banner-resized.jpg b/server/tests/fixtures/banner-resized.jpg new file mode 100644 index 000000000..13ea422cb --- /dev/null +++ b/server/tests/fixtures/banner-resized.jpg | |||
Binary files differ | |||
diff --git a/server/tests/fixtures/banner.jpg b/server/tests/fixtures/banner.jpg new file mode 100644 index 000000000..e5f284f59 --- /dev/null +++ b/server/tests/fixtures/banner.jpg | |||
Binary files differ | |||
diff --git a/server/typings/express/index.d.ts b/server/typings/express/index.d.ts index ee4faa35d..cf3e7ae34 100644 --- a/server/typings/express/index.d.ts +++ b/server/typings/express/index.d.ts | |||
@@ -3,7 +3,6 @@ import { | |||
3 | MAbuseMessage, | 3 | MAbuseMessage, |
4 | MAbuseReporter, | 4 | MAbuseReporter, |
5 | MAccountBlocklist, | 5 | MAccountBlocklist, |
6 | MActorFollowActors, | ||
7 | MActorFollowActorsDefault, | 6 | MActorFollowActorsDefault, |
8 | MActorUrl, | 7 | MActorUrl, |
9 | MChannelBannerAccountDefault, | 8 | MChannelBannerAccountDefault, |
diff --git a/shared/extra-utils/requests/requests.ts b/shared/extra-utils/requests/requests.ts index 3e773ee03..8b5cddf4a 100644 --- a/shared/extra-utils/requests/requests.ts +++ b/shared/extra-utils/requests/requests.ts | |||
@@ -152,11 +152,12 @@ function makeHTMLRequest (url: string, path: string) { | |||
152 | .expect(HttpStatusCode.OK_200) | 152 | .expect(HttpStatusCode.OK_200) |
153 | } | 153 | } |
154 | 154 | ||
155 | function updateAvatarRequest (options: { | 155 | function updateImageRequest (options: { |
156 | url: string | 156 | url: string |
157 | path: string | 157 | path: string |
158 | accessToken: string | 158 | accessToken: string |
159 | fixture: string | 159 | fixture: string |
160 | fieldname: string | ||
160 | }) { | 161 | }) { |
161 | let filePath = '' | 162 | let filePath = '' |
162 | if (isAbsolute(options.fixture)) { | 163 | if (isAbsolute(options.fixture)) { |
@@ -170,7 +171,7 @@ function updateAvatarRequest (options: { | |||
170 | path: options.path, | 171 | path: options.path, |
171 | token: options.accessToken, | 172 | token: options.accessToken, |
172 | fields: {}, | 173 | fields: {}, |
173 | attaches: { avatarfile: filePath }, | 174 | attaches: { [options.fieldname]: filePath }, |
174 | statusCodeExpected: HttpStatusCode.OK_200 | 175 | statusCodeExpected: HttpStatusCode.OK_200 |
175 | }) | 176 | }) |
176 | } | 177 | } |
@@ -191,5 +192,5 @@ export { | |||
191 | makePutBodyRequest, | 192 | makePutBodyRequest, |
192 | makeDeleteRequest, | 193 | makeDeleteRequest, |
193 | makeRawRequest, | 194 | makeRawRequest, |
194 | updateAvatarRequest | 195 | updateImageRequest |
195 | } | 196 | } |
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts index db532dbb0..6040dd9c0 100644 --- a/shared/extra-utils/users/users.ts +++ b/shared/extra-utils/users/users.ts | |||
@@ -4,7 +4,7 @@ import { UserUpdateMe } from '../../models/users' | |||
4 | import { UserAdminFlag } from '../../models/users/user-flag.model' | 4 | import { UserAdminFlag } from '../../models/users/user-flag.model' |
5 | import { UserRegister } from '../../models/users/user-register.model' | 5 | import { UserRegister } from '../../models/users/user-register.model' |
6 | import { UserRole } from '../../models/users/user-role' | 6 | import { UserRole } from '../../models/users/user-role' |
7 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests' | 7 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateImageRequest } from '../requests/requests' |
8 | import { ServerInfo } from '../server/servers' | 8 | import { ServerInfo } from '../server/servers' |
9 | import { userLogin } from './login' | 9 | import { userLogin } from './login' |
10 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 10 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
@@ -275,7 +275,7 @@ function updateMyAvatar (options: { | |||
275 | }) { | 275 | }) { |
276 | const path = '/api/v1/users/me/avatar/pick' | 276 | const path = '/api/v1/users/me/avatar/pick' |
277 | 277 | ||
278 | return updateAvatarRequest(Object.assign(options, { path })) | 278 | return updateImageRequest({ ...options, path, fieldname: 'avatarfile' }) |
279 | } | 279 | } |
280 | 280 | ||
281 | function updateUser (options: { | 281 | function updateUser (options: { |
diff --git a/shared/extra-utils/videos/video-channels.ts b/shared/extra-utils/videos/video-channels.ts index 3ff445c2a..d0dfb5856 100644 --- a/shared/extra-utils/videos/video-channels.ts +++ b/shared/extra-utils/videos/video-channels.ts | |||
@@ -3,7 +3,7 @@ | |||
3 | import * as request from 'supertest' | 3 | import * as request from 'supertest' |
4 | import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model' | 4 | import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model' |
5 | import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model' | 5 | import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model' |
6 | import { makeGetRequest, updateAvatarRequest } from '../requests/requests' | 6 | import { makeDeleteRequest, makeGetRequest, updateImageRequest } from '../requests/requests' |
7 | import { ServerInfo } from '../server/servers' | 7 | import { ServerInfo } from '../server/servers' |
8 | import { User } from '../../models/users/user.model' | 8 | import { User } from '../../models/users/user.model' |
9 | import { getMyUserInformation } from '../users/users' | 9 | import { getMyUserInformation } from '../users/users' |
@@ -129,16 +129,32 @@ function getVideoChannel (url: string, channelName: string) { | |||
129 | .expect('Content-Type', /json/) | 129 | .expect('Content-Type', /json/) |
130 | } | 130 | } |
131 | 131 | ||
132 | function updateVideoChannelAvatar (options: { | 132 | function updateVideoChannelImage (options: { |
133 | url: string | 133 | url: string |
134 | accessToken: string | 134 | accessToken: string |
135 | fixture: string | 135 | fixture: string |
136 | videoChannelName: string | number | 136 | videoChannelName: string | number |
137 | type: 'avatar' | 'banner' | ||
137 | }) { | 138 | }) { |
139 | const path = `/api/v1/video-channels/${options.videoChannelName}/${options.type}/pick` | ||
138 | 140 | ||
139 | const path = '/api/v1/video-channels/' + options.videoChannelName + '/avatar/pick' | 141 | return updateImageRequest({ ...options, path, fieldname: options.type + 'file' }) |
142 | } | ||
143 | |||
144 | function deleteVideoChannelImage (options: { | ||
145 | url: string | ||
146 | accessToken: string | ||
147 | videoChannelName: string | number | ||
148 | type: 'avatar' | 'banner' | ||
149 | }) { | ||
150 | const path = `/api/v1/video-channels/${options.videoChannelName}/${options.type}` | ||
140 | 151 | ||
141 | return updateAvatarRequest(Object.assign(options, { path })) | 152 | return makeDeleteRequest({ |
153 | url: options.url, | ||
154 | token: options.accessToken, | ||
155 | path, | ||
156 | statusCodeExpected: 204 | ||
157 | }) | ||
142 | } | 158 | } |
143 | 159 | ||
144 | function setDefaultVideoChannel (servers: ServerInfo[]) { | 160 | function setDefaultVideoChannel (servers: ServerInfo[]) { |
@@ -157,12 +173,13 @@ function setDefaultVideoChannel (servers: ServerInfo[]) { | |||
157 | // --------------------------------------------------------------------------- | 173 | // --------------------------------------------------------------------------- |
158 | 174 | ||
159 | export { | 175 | export { |
160 | updateVideoChannelAvatar, | 176 | updateVideoChannelImage, |
161 | getVideoChannelsList, | 177 | getVideoChannelsList, |
162 | getAccountVideoChannelsList, | 178 | getAccountVideoChannelsList, |
163 | addVideoChannel, | 179 | addVideoChannel, |
164 | updateVideoChannel, | 180 | updateVideoChannel, |
165 | deleteVideoChannel, | 181 | deleteVideoChannel, |
166 | getVideoChannel, | 182 | getVideoChannel, |
167 | setDefaultVideoChannel | 183 | setDefaultVideoChannel, |
184 | deleteVideoChannelImage | ||
168 | } | 185 | } |