diff options
Diffstat (limited to 'server/middlewares/validators/oembed.ts')
-rw-r--r-- | server/middlewares/validators/oembed.ts | 158 |
1 files changed, 0 insertions, 158 deletions
diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts deleted file mode 100644 index ef9a227a0..000000000 --- a/server/middlewares/validators/oembed.ts +++ /dev/null | |||
@@ -1,158 +0,0 @@ | |||
1 | import express from 'express' | ||
2 | import { query } from 'express-validator' | ||
3 | import { join } from 'path' | ||
4 | import { loadVideo } from '@server/lib/model-loaders' | ||
5 | import { VideoPlaylistModel } from '@server/models/video/video-playlist' | ||
6 | import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' | ||
7 | import { HttpStatusCode } from '../../../shared/models/http/http-error-codes' | ||
8 | import { isTestOrDevInstance } from '../../helpers/core-utils' | ||
9 | import { isIdOrUUIDValid, isUUIDValid, toCompleteUUID } from '../../helpers/custom-validators/misc' | ||
10 | import { WEBSERVER } from '../../initializers/constants' | ||
11 | import { areValidationErrors } from './shared' | ||
12 | |||
13 | const playlistPaths = [ | ||
14 | join('videos', 'watch', 'playlist'), | ||
15 | join('w', 'p') | ||
16 | ] | ||
17 | |||
18 | const videoPaths = [ | ||
19 | join('videos', 'watch'), | ||
20 | 'w' | ||
21 | ] | ||
22 | |||
23 | function buildUrls (paths: string[]) { | ||
24 | return paths.map(p => WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, p) + '/') | ||
25 | } | ||
26 | |||
27 | const startPlaylistURLs = buildUrls(playlistPaths) | ||
28 | const startVideoURLs = buildUrls(videoPaths) | ||
29 | |||
30 | const isURLOptions = { | ||
31 | require_host: true, | ||
32 | require_tld: true | ||
33 | } | ||
34 | |||
35 | // We validate 'localhost', so we don't have the top level domain | ||
36 | if (isTestOrDevInstance()) { | ||
37 | isURLOptions.require_tld = false | ||
38 | } | ||
39 | |||
40 | const oembedValidator = [ | ||
41 | query('url') | ||
42 | .isURL(isURLOptions), | ||
43 | query('maxwidth') | ||
44 | .optional() | ||
45 | .isInt(), | ||
46 | query('maxheight') | ||
47 | .optional() | ||
48 | .isInt(), | ||
49 | query('format') | ||
50 | .optional() | ||
51 | .isIn([ 'xml', 'json' ]), | ||
52 | |||
53 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
54 | if (areValidationErrors(req, res)) return | ||
55 | |||
56 | if (req.query.format !== undefined && req.query.format !== 'json') { | ||
57 | return res.fail({ | ||
58 | status: HttpStatusCode.NOT_IMPLEMENTED_501, | ||
59 | message: 'Requested format is not implemented on server.', | ||
60 | data: { | ||
61 | format: req.query.format | ||
62 | } | ||
63 | }) | ||
64 | } | ||
65 | |||
66 | const url = req.query.url as string | ||
67 | |||
68 | let urlPath: string | ||
69 | |||
70 | try { | ||
71 | urlPath = new URL(url).pathname | ||
72 | } catch (err) { | ||
73 | return res.fail({ | ||
74 | status: HttpStatusCode.BAD_REQUEST_400, | ||
75 | message: err.message, | ||
76 | data: { | ||
77 | url | ||
78 | } | ||
79 | }) | ||
80 | } | ||
81 | |||
82 | const isPlaylist = startPlaylistURLs.some(u => url.startsWith(u)) | ||
83 | const isVideo = isPlaylist ? false : startVideoURLs.some(u => url.startsWith(u)) | ||
84 | |||
85 | const startIsOk = isVideo || isPlaylist | ||
86 | |||
87 | const parts = urlPath.split('/') | ||
88 | |||
89 | if (startIsOk === false || parts.length === 0) { | ||
90 | return res.fail({ | ||
91 | status: HttpStatusCode.BAD_REQUEST_400, | ||
92 | message: 'Invalid url.', | ||
93 | data: { | ||
94 | url | ||
95 | } | ||
96 | }) | ||
97 | } | ||
98 | |||
99 | const elementId = toCompleteUUID(parts.pop()) | ||
100 | if (isIdOrUUIDValid(elementId) === false) { | ||
101 | return res.fail({ message: 'Invalid video or playlist id.' }) | ||
102 | } | ||
103 | |||
104 | if (isVideo) { | ||
105 | const video = await loadVideo(elementId, 'all') | ||
106 | |||
107 | if (!video) { | ||
108 | return res.fail({ | ||
109 | status: HttpStatusCode.NOT_FOUND_404, | ||
110 | message: 'Video not found' | ||
111 | }) | ||
112 | } | ||
113 | |||
114 | if ( | ||
115 | video.privacy === VideoPrivacy.PUBLIC || | ||
116 | (video.privacy === VideoPrivacy.UNLISTED && isUUIDValid(elementId) === true) | ||
117 | ) { | ||
118 | res.locals.videoAll = video | ||
119 | return next() | ||
120 | } | ||
121 | |||
122 | return res.fail({ | ||
123 | status: HttpStatusCode.FORBIDDEN_403, | ||
124 | message: 'Video is not publicly available' | ||
125 | }) | ||
126 | } | ||
127 | |||
128 | // Is playlist | ||
129 | |||
130 | const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(elementId, undefined) | ||
131 | if (!videoPlaylist) { | ||
132 | return res.fail({ | ||
133 | status: HttpStatusCode.NOT_FOUND_404, | ||
134 | message: 'Video playlist not found' | ||
135 | }) | ||
136 | } | ||
137 | |||
138 | if ( | ||
139 | videoPlaylist.privacy === VideoPlaylistPrivacy.PUBLIC || | ||
140 | (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED && isUUIDValid(elementId)) | ||
141 | ) { | ||
142 | res.locals.videoPlaylistSummary = videoPlaylist | ||
143 | return next() | ||
144 | } | ||
145 | |||
146 | return res.fail({ | ||
147 | status: HttpStatusCode.FORBIDDEN_403, | ||
148 | message: 'Playlist is not public' | ||
149 | }) | ||
150 | } | ||
151 | |||
152 | ] | ||
153 | |||
154 | // --------------------------------------------------------------------------- | ||
155 | |||
156 | export { | ||
157 | oembedValidator | ||
158 | } | ||