diff options
author | Chocobozzz <me@florianbigard.com> | 2021-05-27 16:12:41 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-05-27 16:12:41 +0200 |
commit | 8f608a4cb22ab232cfab20665050764b38bac9c7 (patch) | |
tree | 6a6785aae79bf5939ad7b7a50a1bd8031268d2b4 /server/middlewares | |
parent | 030ccfce59a8cb8f2fee6ea8dd363ba635c5c5c2 (diff) | |
parent | c215e627b575d2c4085ccb222f4ca8d0237b7552 (diff) | |
download | PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.tar.gz PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.tar.zst PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.zip |
Merge branch 'develop' into shorter-URLs-channels-accounts
Diffstat (limited to 'server/middlewares')
-rw-r--r-- | server/middlewares/validators/follows.ts | 16 | ||||
-rw-r--r-- | server/middlewares/validators/plugins.ts | 14 | ||||
-rw-r--r-- | server/middlewares/validators/user-subscriptions.ts | 8 | ||||
-rw-r--r-- | server/middlewares/validators/users.ts | 5 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-channels.ts | 4 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-imports.ts | 3 | ||||
-rw-r--r-- | server/middlewares/validators/videos/videos.ts | 186 | ||||
-rw-r--r-- | server/middlewares/validators/webfinger.ts | 6 |
8 files changed, 180 insertions, 62 deletions
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts index bb849dc72..1d18de8cd 100644 --- a/server/middlewares/validators/follows.ts +++ b/server/middlewares/validators/follows.ts | |||
@@ -1,18 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { isFollowStateValid } from '@server/helpers/custom-validators/follows' | ||
4 | import { getServerActor } from '@server/models/application/application' | ||
5 | import { MActorFollowActorsDefault } from '@server/types/models' | ||
6 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { isTestInstance } from '../../helpers/core-utils' | 7 | import { isTestInstance } from '../../helpers/core-utils' |
8 | import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | ||
4 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' | 9 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' |
5 | import { logger } from '../../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
11 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | ||
6 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' | 12 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' |
7 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | 13 | import { ActorModel } from '../../models/actor/actor' |
14 | import { ActorFollowModel } from '../../models/actor/actor-follow' | ||
8 | import { areValidationErrors } from './utils' | 15 | import { areValidationErrors } from './utils' |
9 | import { ActorModel } from '../../models/activitypub/actor' | ||
10 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | ||
11 | import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | ||
12 | import { MActorFollowActorsDefault } from '@server/types/models' | ||
13 | import { isFollowStateValid } from '@server/helpers/custom-validators/follows' | ||
14 | import { getServerActor } from '@server/models/application/application' | ||
15 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
16 | 16 | ||
17 | const listFollowsValidator = [ | 17 | const listFollowsValidator = [ |
18 | query('state') | 18 | query('state') |
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts index ab87fe720..2c47ec5bb 100644 --- a/server/middlewares/validators/plugins.ts +++ b/server/middlewares/validators/plugins.ts | |||
@@ -1,15 +1,15 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query, ValidationChain } from 'express-validator' | 2 | import { body, param, query, ValidationChain } from 'express-validator' |
3 | import { logger } from '../../helpers/logger' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { areValidationErrors } from './utils' | 4 | import { PluginType } from '../../../shared/models/plugins/plugin.type' |
5 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/server/api/install-plugin.model' | ||
6 | import { exists, isBooleanValid, isSafePath, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' | ||
5 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' | 7 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' |
8 | import { logger } from '../../helpers/logger' | ||
9 | import { CONFIG } from '../../initializers/config' | ||
6 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 10 | import { PluginManager } from '../../lib/plugins/plugin-manager' |
7 | import { isBooleanValid, isSafePath, toBooleanOrNull, exists, toIntOrNull } from '../../helpers/custom-validators/misc' | ||
8 | import { PluginModel } from '../../models/server/plugin' | 11 | import { PluginModel } from '../../models/server/plugin' |
9 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model' | 12 | import { areValidationErrors } from './utils' |
10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | ||
11 | import { CONFIG } from '../../initializers/config' | ||
12 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
13 | 13 | ||
14 | const getPluginValidator = (pluginType: PluginType, withVersion = true) => { | 14 | const getPluginValidator = (pluginType: PluginType, withVersion = true) => { |
15 | const validators: (ValidationChain | express.Handler)[] = [ | 15 | const validators: (ValidationChain | express.Handler)[] = [ |
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts index 0d0c8ccbf..1823892b6 100644 --- a/server/middlewares/validators/user-subscriptions.ts +++ b/server/middlewares/validators/user-subscriptions.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { logger } from '../../helpers/logger' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { areValidationErrors } from './utils' | ||
5 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | ||
6 | import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | 4 | import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' |
7 | import { toArray } from '../../helpers/custom-validators/misc' | 5 | import { toArray } from '../../helpers/custom-validators/misc' |
6 | import { logger } from '../../helpers/logger' | ||
8 | import { WEBSERVER } from '../../initializers/constants' | 7 | import { WEBSERVER } from '../../initializers/constants' |
9 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 8 | import { ActorFollowModel } from '../../models/actor/actor-follow' |
9 | import { areValidationErrors } from './utils' | ||
10 | 10 | ||
11 | const userSubscriptionListValidator = [ | 11 | const userSubscriptionListValidator = [ |
12 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), | 12 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 9cff51d45..548d5df4d 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -34,8 +34,8 @@ import { doesVideoExist } from '../../helpers/middlewares' | |||
34 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' | 34 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' |
35 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' | 35 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' |
36 | import { Redis } from '../../lib/redis' | 36 | import { Redis } from '../../lib/redis' |
37 | import { UserModel } from '../../models/account/user' | 37 | import { UserModel } from '../../models/user/user' |
38 | import { ActorModel } from '../../models/activitypub/actor' | 38 | import { ActorModel } from '../../models/actor/actor' |
39 | import { areValidationErrors } from './utils' | 39 | import { areValidationErrors } from './utils' |
40 | 40 | ||
41 | const usersListValidator = [ | 41 | const usersListValidator = [ |
@@ -196,6 +196,7 @@ const deleteMeValidator = [ | |||
196 | 196 | ||
197 | const usersUpdateValidator = [ | 197 | const usersUpdateValidator = [ |
198 | param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), | 198 | param('id').isInt().not().isEmpty().withMessage('Should have a valid id'), |
199 | |||
199 | body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'), | 200 | body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'), |
200 | body('email').optional().isEmail().withMessage('Should have a valid email attribute'), | 201 | body('email').optional().isEmail().withMessage('Should have a valid email attribute'), |
201 | body('emailVerified').optional().isBoolean().withMessage('Should have a valid email verified attribute'), | 202 | body('emailVerified').optional().isBoolean().withMessage('Should have a valid email verified attribute'), |
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts index 2463d281c..e881f0d3e 100644 --- a/server/middlewares/validators/videos/video-channels.ts +++ b/server/middlewares/validators/videos/video-channels.ts | |||
@@ -3,6 +3,7 @@ import { body, param, query } from 'express-validator' | |||
3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' | 3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' |
4 | import { MChannelAccountDefault, MUser } from '@server/types/models' | 4 | import { MChannelAccountDefault, MUser } from '@server/types/models' |
5 | import { UserRight } from '../../../../shared' | 5 | import { UserRight } from '../../../../shared' |
6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
6 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' | 7 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' |
7 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' | 8 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' |
8 | import { | 9 | import { |
@@ -12,10 +13,9 @@ import { | |||
12 | } from '../../../helpers/custom-validators/video-channels' | 13 | } from '../../../helpers/custom-validators/video-channels' |
13 | import { logger } from '../../../helpers/logger' | 14 | import { logger } from '../../../helpers/logger' |
14 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' | 15 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' |
15 | import { ActorModel } from '../../../models/activitypub/actor' | 16 | import { ActorModel } from '../../../models/actor/actor' |
16 | import { VideoChannelModel } from '../../../models/video/video-channel' | 17 | import { VideoChannelModel } from '../../../models/video/video-channel' |
17 | import { areValidationErrors } from '../utils' | 18 | import { areValidationErrors } from '../utils' |
18 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
19 | 19 | ||
20 | const videoChannelsAddValidator = [ | 20 | const videoChannelsAddValidator = [ |
21 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), | 21 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), |
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts index c53af3861..d0643ff26 100644 --- a/server/middlewares/validators/videos/video-imports.ts +++ b/server/middlewares/validators/videos/video-imports.ts | |||
@@ -47,14 +47,12 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
47 | cleanUpReqFiles(req) | 47 | cleanUpReqFiles(req) |
48 | return res.status(HttpStatusCode.CONFLICT_409) | 48 | return res.status(HttpStatusCode.CONFLICT_409) |
49 | .json({ error: 'HTTP import is not enabled on this instance.' }) | 49 | .json({ error: 'HTTP import is not enabled on this instance.' }) |
50 | .end() | ||
51 | } | 50 | } |
52 | 51 | ||
53 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { | 52 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { |
54 | cleanUpReqFiles(req) | 53 | cleanUpReqFiles(req) |
55 | return res.status(HttpStatusCode.CONFLICT_409) | 54 | return res.status(HttpStatusCode.CONFLICT_409) |
56 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) | 55 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) |
57 | .end() | ||
58 | } | 56 | } |
59 | 57 | ||
60 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | 58 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) |
@@ -65,7 +63,6 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
65 | 63 | ||
66 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 64 | return res.status(HttpStatusCode.BAD_REQUEST_400) |
67 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) | 65 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) |
68 | .end() | ||
69 | } | 66 | } |
70 | 67 | ||
71 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) | 68 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) |
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index bb617d77c..3219e10d4 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts | |||
@@ -1,12 +1,13 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query, ValidationChain } from 'express-validator' | 2 | import { body, header, param, query, ValidationChain } from 'express-validator' |
3 | import { getResumableUploadPath } from '@server/helpers/upload' | ||
3 | import { isAbleToUploadVideo } from '@server/lib/user' | 4 | import { isAbleToUploadVideo } from '@server/lib/user' |
4 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
5 | import { ExpressPromiseHandler } from '@server/types/express' | 6 | import { ExpressPromiseHandler } from '@server/types/express' |
6 | import { MVideoWithRights } from '@server/types/models' | 7 | import { MUserAccountId, MVideoWithRights } from '@server/types/models' |
7 | import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' | 8 | import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' |
8 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 9 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
9 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' | 10 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/change-ownership/video-change-ownership-accept.model' |
10 | import { | 11 | import { |
11 | exists, | 12 | exists, |
12 | isBooleanValid, | 13 | isBooleanValid, |
@@ -47,6 +48,7 @@ import { | |||
47 | doesVideoExist, | 48 | doesVideoExist, |
48 | doesVideoFileOfVideoExist | 49 | doesVideoFileOfVideoExist |
49 | } from '../../../helpers/middlewares' | 50 | } from '../../../helpers/middlewares' |
51 | import { deleteFileAndCatch } from '../../../helpers/utils' | ||
50 | import { getVideoWithAttributes } from '../../../helpers/video' | 52 | import { getVideoWithAttributes } from '../../../helpers/video' |
51 | import { CONFIG } from '../../../initializers/config' | 53 | import { CONFIG } from '../../../initializers/config' |
52 | import { CONSTRAINTS_FIELDS, OVERVIEWS } from '../../../initializers/constants' | 54 | import { CONSTRAINTS_FIELDS, OVERVIEWS } from '../../../initializers/constants' |
@@ -57,7 +59,7 @@ import { VideoModel } from '../../../models/video/video' | |||
57 | import { authenticatePromiseIfNeeded } from '../../auth' | 59 | import { authenticatePromiseIfNeeded } from '../../auth' |
58 | import { areValidationErrors } from '../utils' | 60 | import { areValidationErrors } from '../utils' |
59 | 61 | ||
60 | const videosAddValidator = getCommonVideoEditAttributes().concat([ | 62 | const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ |
61 | body('videofile') | 63 | body('videofile') |
62 | .custom((value, { req }) => isFileFieldValid(req.files, 'videofile')) | 64 | .custom((value, { req }) => isFileFieldValid(req.files, 'videofile')) |
63 | .withMessage('Should have a file'), | 65 | .withMessage('Should have a file'), |
@@ -73,54 +75,117 @@ const videosAddValidator = getCommonVideoEditAttributes().concat([ | |||
73 | logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) | 75 | logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) |
74 | 76 | ||
75 | if (areValidationErrors(req, res)) return cleanUpReqFiles(req) | 77 | if (areValidationErrors(req, res)) return cleanUpReqFiles(req) |
76 | if (areErrorsInScheduleUpdate(req, res)) return cleanUpReqFiles(req) | ||
77 | 78 | ||
78 | const videoFile: Express.Multer.File & { duration?: number } = req.files['videofile'][0] | 79 | const videoFile: express.VideoUploadFile = req.files['videofile'][0] |
79 | const user = res.locals.oauth.token.User | 80 | const user = res.locals.oauth.token.User |
80 | 81 | ||
81 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | 82 | if (!await commonVideoChecksPass({ req, res, user, videoFileSize: videoFile.size, files: req.files })) { |
82 | |||
83 | if (!isVideoFileMimeTypeValid(req.files)) { | ||
84 | res.status(HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) | ||
85 | .json({ | ||
86 | error: 'This file is not supported. Please, make sure it is of the following type: ' + | ||
87 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') | ||
88 | }) | ||
89 | |||
90 | return cleanUpReqFiles(req) | 83 | return cleanUpReqFiles(req) |
91 | } | 84 | } |
92 | 85 | ||
93 | if (!isVideoFileSizeValid(videoFile.size.toString())) { | 86 | try { |
94 | res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) | 87 | if (!videoFile.duration) await addDurationToVideo(videoFile) |
95 | .json({ | 88 | } catch (err) { |
96 | error: 'This file is too large.' | 89 | logger.error('Invalid input file in videosAddLegacyValidator.', { err }) |
97 | }) | 90 | res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422) |
91 | .json({ error: 'Video file unreadable.' }) | ||
98 | 92 | ||
99 | return cleanUpReqFiles(req) | 93 | return cleanUpReqFiles(req) |
100 | } | 94 | } |
101 | 95 | ||
102 | if (await isAbleToUploadVideo(user.id, videoFile.size) === false) { | 96 | if (!await isVideoAccepted(req, res, videoFile)) return cleanUpReqFiles(req) |
103 | res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) | ||
104 | .json({ error: 'The user video quota is exceeded with this video.' }) | ||
105 | 97 | ||
106 | return cleanUpReqFiles(req) | 98 | return next() |
107 | } | 99 | } |
100 | ]) | ||
101 | |||
102 | /** | ||
103 | * Gets called after the last PUT request | ||
104 | */ | ||
105 | const videosAddResumableValidator = [ | ||
106 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
107 | const user = res.locals.oauth.token.User | ||
108 | |||
109 | const body: express.CustomUploadXFile<express.UploadXFileMetadata> = req.body | ||
110 | const file = { ...body, duration: undefined, path: getResumableUploadPath(body.id), filename: body.metadata.filename } | ||
111 | |||
112 | const cleanup = () => deleteFileAndCatch(file.path) | ||
108 | 113 | ||
109 | let duration: number | 114 | if (!await doesVideoChannelOfAccountExist(file.metadata.channelId, user, res)) return cleanup() |
110 | 115 | ||
111 | try { | 116 | try { |
112 | duration = await getDurationFromVideoFile(videoFile.path) | 117 | if (!file.duration) await addDurationToVideo(file) |
113 | } catch (err) { | 118 | } catch (err) { |
114 | logger.error('Invalid input file in videosAddValidator.', { err }) | 119 | logger.error('Invalid input file in videosAddResumableValidator.', { err }) |
115 | res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422) | 120 | res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422) |
116 | .json({ error: 'Video file unreadable.' }) | 121 | .json({ error: 'Video file unreadable.' }) |
117 | 122 | ||
118 | return cleanUpReqFiles(req) | 123 | return cleanup() |
119 | } | 124 | } |
120 | 125 | ||
121 | videoFile.duration = duration | 126 | if (!await isVideoAccepted(req, res, file)) return cleanup() |
122 | 127 | ||
123 | if (!await isVideoAccepted(req, res, videoFile)) return cleanUpReqFiles(req) | 128 | res.locals.videoFileResumable = file |
129 | |||
130 | return next() | ||
131 | } | ||
132 | ] | ||
133 | |||
134 | /** | ||
135 | * File is created in POST initialisation, and its body is saved as a 'metadata' field is saved by uploadx for later use. | ||
136 | * see https://github.com/kukhariev/node-uploadx/blob/dc9fb4a8ac5a6f481902588e93062f591ec6ef03/packages/core/src/handlers/uploadx.ts | ||
137 | * | ||
138 | * Uploadx doesn't use next() until the upload completes, so this middleware has to be placed before uploadx | ||
139 | * see https://github.com/kukhariev/node-uploadx/blob/dc9fb4a8ac5a6f481902588e93062f591ec6ef03/packages/core/src/handlers/base-handler.ts | ||
140 | * | ||
141 | */ | ||
142 | const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([ | ||
143 | body('filename') | ||
144 | .isString() | ||
145 | .exists() | ||
146 | .withMessage('Should have a valid filename'), | ||
147 | body('name') | ||
148 | .trim() | ||
149 | .custom(isVideoNameValid) | ||
150 | .withMessage('Should have a valid name'), | ||
151 | body('channelId') | ||
152 | .customSanitizer(toIntOrNull) | ||
153 | .custom(isIdValid).withMessage('Should have correct video channel id'), | ||
154 | |||
155 | header('x-upload-content-length') | ||
156 | .isNumeric() | ||
157 | .exists() | ||
158 | .withMessage('Should specify the file length'), | ||
159 | header('x-upload-content-type') | ||
160 | .isString() | ||
161 | .exists() | ||
162 | .withMessage('Should specify the file mimetype'), | ||
163 | |||
164 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
165 | const videoFileMetadata = { | ||
166 | mimetype: req.headers['x-upload-content-type'] as string, | ||
167 | size: +req.headers['x-upload-content-length'], | ||
168 | originalname: req.body.name | ||
169 | } | ||
170 | |||
171 | const user = res.locals.oauth.token.User | ||
172 | const cleanup = () => cleanUpReqFiles(req) | ||
173 | |||
174 | logger.debug('Checking videosAddResumableInitValidator parameters and headers', { | ||
175 | parameters: req.body, | ||
176 | headers: req.headers, | ||
177 | files: req.files | ||
178 | }) | ||
179 | |||
180 | if (areValidationErrors(req, res)) return cleanup() | ||
181 | |||
182 | const files = { videofile: [ videoFileMetadata ] } | ||
183 | if (!await commonVideoChecksPass({ req, res, user, videoFileSize: videoFileMetadata.size, files })) return cleanup() | ||
184 | |||
185 | // multer required unsetting the Content-Type, now we can set it for node-uploadx | ||
186 | req.headers['content-type'] = 'application/json; charset=utf-8' | ||
187 | // place previewfile in metadata so that uploadx saves it in .META | ||
188 | if (req.files['previewfile']) req.body.previewfile = req.files['previewfile'] | ||
124 | 189 | ||
125 | return next() | 190 | return next() |
126 | } | 191 | } |
@@ -478,7 +543,10 @@ const commonVideosFiltersValidator = [ | |||
478 | // --------------------------------------------------------------------------- | 543 | // --------------------------------------------------------------------------- |
479 | 544 | ||
480 | export { | 545 | export { |
481 | videosAddValidator, | 546 | videosAddLegacyValidator, |
547 | videosAddResumableValidator, | ||
548 | videosAddResumableInitValidator, | ||
549 | |||
482 | videosUpdateValidator, | 550 | videosUpdateValidator, |
483 | videosGetValidator, | 551 | videosGetValidator, |
484 | videoFileMetadataGetValidator, | 552 | videoFileMetadataGetValidator, |
@@ -515,7 +583,51 @@ function areErrorsInScheduleUpdate (req: express.Request, res: express.Response) | |||
515 | return false | 583 | return false |
516 | } | 584 | } |
517 | 585 | ||
518 | async function isVideoAccepted (req: express.Request, res: express.Response, videoFile: Express.Multer.File & { duration?: number }) { | 586 | async function commonVideoChecksPass (parameters: { |
587 | req: express.Request | ||
588 | res: express.Response | ||
589 | user: MUserAccountId | ||
590 | videoFileSize: number | ||
591 | files: express.UploadFilesForCheck | ||
592 | }): Promise<boolean> { | ||
593 | const { req, res, user, videoFileSize, files } = parameters | ||
594 | |||
595 | if (areErrorsInScheduleUpdate(req, res)) return false | ||
596 | |||
597 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return false | ||
598 | |||
599 | if (!isVideoFileMimeTypeValid(files)) { | ||
600 | res.status(HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) | ||
601 | .json({ | ||
602 | error: 'This file is not supported. Please, make sure it is of the following type: ' + | ||
603 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') | ||
604 | }) | ||
605 | |||
606 | return false | ||
607 | } | ||
608 | |||
609 | if (!isVideoFileSizeValid(videoFileSize.toString())) { | ||
610 | res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) | ||
611 | .json({ error: 'This file is too large. It exceeds the maximum file size authorized.' }) | ||
612 | |||
613 | return false | ||
614 | } | ||
615 | |||
616 | if (await isAbleToUploadVideo(user.id, videoFileSize) === false) { | ||
617 | res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) | ||
618 | .json({ error: 'The user video quota is exceeded with this video.' }) | ||
619 | |||
620 | return false | ||
621 | } | ||
622 | |||
623 | return true | ||
624 | } | ||
625 | |||
626 | export async function isVideoAccepted ( | ||
627 | req: express.Request, | ||
628 | res: express.Response, | ||
629 | videoFile: express.VideoUploadFile | ||
630 | ) { | ||
519 | // Check we accept this video | 631 | // Check we accept this video |
520 | const acceptParameters = { | 632 | const acceptParameters = { |
521 | videoBody: req.body, | 633 | videoBody: req.body, |
@@ -538,3 +650,11 @@ async function isVideoAccepted (req: express.Request, res: express.Response, vid | |||
538 | 650 | ||
539 | return true | 651 | return true |
540 | } | 652 | } |
653 | |||
654 | async function addDurationToVideo (videoFile: { path: string, duration?: number }) { | ||
655 | const duration: number = await getDurationFromVideoFile(videoFile.path) | ||
656 | |||
657 | if (isNaN(duration)) throw new Error(`Couldn't get video duration`) | ||
658 | |||
659 | videoFile.duration = duration | ||
660 | } | ||
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts index a71422ed8..c2dfccc96 100644 --- a/server/middlewares/validators/webfinger.ts +++ b/server/middlewares/validators/webfinger.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { query } from 'express-validator' | 2 | import { query } from 'express-validator' |
3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' | 4 | import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' |
5 | import { getHostWithPort } from '../../helpers/express-utils' | ||
4 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
5 | import { ActorModel } from '../../models/activitypub/actor' | 7 | import { ActorModel } from '../../models/actor/actor' |
6 | import { areValidationErrors } from './utils' | 8 | import { areValidationErrors } from './utils' |
7 | import { getHostWithPort } from '../../helpers/express-utils' | ||
8 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
9 | 9 | ||
10 | const webfingerValidator = [ | 10 | const webfingerValidator = [ |
11 | query('resource').custom(isWebfingerLocalResourceValid).withMessage('Should have a valid webfinger resource'), | 11 | query('resource').custom(isWebfingerLocalResourceValid).withMessage('Should have a valid webfinger resource'), |