diff options
Diffstat (limited to 'server/middlewares/validators/videos/video-imports.ts')
-rw-r--r-- | server/middlewares/validators/videos/video-imports.ts | 209 |
1 files changed, 0 insertions, 209 deletions
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts deleted file mode 100644 index a1cb65b70..000000000 --- a/server/middlewares/validators/videos/video-imports.ts +++ /dev/null | |||
@@ -1,209 +0,0 @@ | |||
1 | import express from 'express' | ||
2 | import { body, param, query } from 'express-validator' | ||
3 | import { isResolvingToUnicastOnly } from '@server/helpers/dns' | ||
4 | import { isPreImportVideoAccepted } from '@server/lib/moderation' | ||
5 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | import { MUserAccountId, MVideoImport } from '@server/types/models' | ||
7 | import { forceNumber } from '@shared/core-utils' | ||
8 | import { HttpStatusCode, UserRight, VideoImportState } from '@shared/models' | ||
9 | import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model' | ||
10 | import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc' | ||
11 | import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports' | ||
12 | import { | ||
13 | isValidPasswordProtectedPrivacy, | ||
14 | isVideoMagnetUriValid, | ||
15 | isVideoNameValid | ||
16 | } from '../../../helpers/custom-validators/videos' | ||
17 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | ||
18 | import { logger } from '../../../helpers/logger' | ||
19 | import { CONFIG } from '../../../initializers/config' | ||
20 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' | ||
21 | import { areValidationErrors, doesVideoChannelOfAccountExist, doesVideoImportExist } from '../shared' | ||
22 | import { getCommonVideoEditAttributes } from './videos' | ||
23 | |||
24 | const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | ||
25 | body('channelId') | ||
26 | .customSanitizer(toIntOrNull) | ||
27 | .custom(isIdValid), | ||
28 | body('targetUrl') | ||
29 | .optional() | ||
30 | .custom(isVideoImportTargetUrlValid), | ||
31 | body('magnetUri') | ||
32 | .optional() | ||
33 | .custom(isVideoMagnetUriValid), | ||
34 | body('torrentfile') | ||
35 | .custom((value, { req }) => isVideoImportTorrentFile(req.files)) | ||
36 | .withMessage( | ||
37 | 'This torrent file is not supported or too large. Please, make sure it is of the following type: ' + | ||
38 | CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_FILE.EXTNAME.join(', ') | ||
39 | ), | ||
40 | body('name') | ||
41 | .optional() | ||
42 | .custom(isVideoNameValid).withMessage( | ||
43 | `Should have a video name between ${CONSTRAINTS_FIELDS.VIDEOS.NAME.min} and ${CONSTRAINTS_FIELDS.VIDEOS.NAME.max} characters long` | ||
44 | ), | ||
45 | body('videoPasswords') | ||
46 | .optional() | ||
47 | .isArray() | ||
48 | .withMessage('Video passwords should be an array.'), | ||
49 | |||
50 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
51 | const user = res.locals.oauth.token.User | ||
52 | const torrentFile = req.files?.['torrentfile'] ? req.files['torrentfile'][0] : undefined | ||
53 | |||
54 | if (areValidationErrors(req, res)) return cleanUpReqFiles(req) | ||
55 | |||
56 | if (!isValidPasswordProtectedPrivacy(req, res)) return cleanUpReqFiles(req) | ||
57 | |||
58 | if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED !== true && req.body.targetUrl) { | ||
59 | cleanUpReqFiles(req) | ||
60 | |||
61 | return res.fail({ | ||
62 | status: HttpStatusCode.CONFLICT_409, | ||
63 | message: 'HTTP import is not enabled on this instance.' | ||
64 | }) | ||
65 | } | ||
66 | |||
67 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { | ||
68 | cleanUpReqFiles(req) | ||
69 | |||
70 | return res.fail({ | ||
71 | status: HttpStatusCode.CONFLICT_409, | ||
72 | message: 'Torrent/magnet URI import is not enabled on this instance.' | ||
73 | }) | ||
74 | } | ||
75 | |||
76 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | ||
77 | |||
78 | // Check we have at least 1 required param | ||
79 | if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) { | ||
80 | cleanUpReqFiles(req) | ||
81 | |||
82 | return res.fail({ message: 'Should have a magnetUri or a targetUrl or a torrent file.' }) | ||
83 | } | ||
84 | |||
85 | if (req.body.targetUrl) { | ||
86 | const hostname = new URL(req.body.targetUrl).hostname | ||
87 | |||
88 | if (await isResolvingToUnicastOnly(hostname) !== true) { | ||
89 | cleanUpReqFiles(req) | ||
90 | |||
91 | return res.fail({ | ||
92 | status: HttpStatusCode.FORBIDDEN_403, | ||
93 | message: 'Cannot use non unicast IP as targetUrl.' | ||
94 | }) | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) | ||
99 | |||
100 | return next() | ||
101 | } | ||
102 | ]) | ||
103 | |||
104 | const getMyVideoImportsValidator = [ | ||
105 | query('videoChannelSyncId') | ||
106 | .optional() | ||
107 | .custom(isIdValid), | ||
108 | |||
109 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
110 | if (areValidationErrors(req, res)) return | ||
111 | |||
112 | return next() | ||
113 | } | ||
114 | ] | ||
115 | |||
116 | const videoImportDeleteValidator = [ | ||
117 | param('id') | ||
118 | .custom(isIdValid), | ||
119 | |||
120 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
121 | if (areValidationErrors(req, res)) return | ||
122 | |||
123 | if (!await doesVideoImportExist(parseInt(req.params.id), res)) return | ||
124 | if (!checkUserCanManageImport(res.locals.oauth.token.user, res.locals.videoImport, res)) return | ||
125 | |||
126 | if (res.locals.videoImport.state === VideoImportState.PENDING) { | ||
127 | return res.fail({ | ||
128 | status: HttpStatusCode.CONFLICT_409, | ||
129 | message: 'Cannot delete a pending video import. Cancel it or wait for the end of the import first.' | ||
130 | }) | ||
131 | } | ||
132 | |||
133 | return next() | ||
134 | } | ||
135 | ] | ||
136 | |||
137 | const videoImportCancelValidator = [ | ||
138 | param('id') | ||
139 | .custom(isIdValid), | ||
140 | |||
141 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
142 | if (areValidationErrors(req, res)) return | ||
143 | |||
144 | if (!await doesVideoImportExist(forceNumber(req.params.id), res)) return | ||
145 | if (!checkUserCanManageImport(res.locals.oauth.token.user, res.locals.videoImport, res)) return | ||
146 | |||
147 | if (res.locals.videoImport.state !== VideoImportState.PENDING) { | ||
148 | return res.fail({ | ||
149 | status: HttpStatusCode.CONFLICT_409, | ||
150 | message: 'Cannot cancel a non pending video import.' | ||
151 | }) | ||
152 | } | ||
153 | |||
154 | return next() | ||
155 | } | ||
156 | ] | ||
157 | |||
158 | // --------------------------------------------------------------------------- | ||
159 | |||
160 | export { | ||
161 | videoImportAddValidator, | ||
162 | videoImportCancelValidator, | ||
163 | videoImportDeleteValidator, | ||
164 | getMyVideoImportsValidator | ||
165 | } | ||
166 | |||
167 | // --------------------------------------------------------------------------- | ||
168 | |||
169 | async function isImportAccepted (req: express.Request, res: express.Response) { | ||
170 | const body: VideoImportCreate = req.body | ||
171 | const hookName = body.targetUrl | ||
172 | ? 'filter:api.video.pre-import-url.accept.result' | ||
173 | : 'filter:api.video.pre-import-torrent.accept.result' | ||
174 | |||
175 | // Check we accept this video | ||
176 | const acceptParameters = { | ||
177 | videoImportBody: body, | ||
178 | user: res.locals.oauth.token.User | ||
179 | } | ||
180 | const acceptedResult = await Hooks.wrapFun( | ||
181 | isPreImportVideoAccepted, | ||
182 | acceptParameters, | ||
183 | hookName | ||
184 | ) | ||
185 | |||
186 | if (!acceptedResult || acceptedResult.accepted !== true) { | ||
187 | logger.info('Refused to import video.', { acceptedResult, acceptParameters }) | ||
188 | |||
189 | res.fail({ | ||
190 | status: HttpStatusCode.FORBIDDEN_403, | ||
191 | message: acceptedResult.errorMessage || 'Refused to import video' | ||
192 | }) | ||
193 | return false | ||
194 | } | ||
195 | |||
196 | return true | ||
197 | } | ||
198 | |||
199 | function checkUserCanManageImport (user: MUserAccountId, videoImport: MVideoImport, res: express.Response) { | ||
200 | if (user.hasRight(UserRight.MANAGE_VIDEO_IMPORTS) === false && videoImport.userId !== user.id) { | ||
201 | res.fail({ | ||
202 | status: HttpStatusCode.FORBIDDEN_403, | ||
203 | message: 'Cannot manage video import of another user' | ||
204 | }) | ||
205 | return false | ||
206 | } | ||
207 | |||
208 | return true | ||
209 | } | ||