diff options
Diffstat (limited to 'server/controllers')
-rw-r--r-- | server/controllers/activitypub/client.ts | 15 | ||||
-rw-r--r-- | server/controllers/api/config.ts | 8 | ||||
-rw-r--r-- | server/controllers/api/users/index.ts | 19 | ||||
-rw-r--r-- | server/controllers/api/users/me.ts | 2 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 19 | ||||
-rw-r--r-- | server/controllers/static.ts | 9 | ||||
-rw-r--r-- | server/controllers/tracker.ts | 25 |
7 files changed, 63 insertions, 34 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 7e87f6f3b..31c0a5fbd 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -33,7 +33,7 @@ import { | |||
33 | getVideoSharesActivityPubUrl | 33 | getVideoSharesActivityPubUrl |
34 | } from '../../lib/activitypub' | 34 | } from '../../lib/activitypub' |
35 | import { VideoCaptionModel } from '../../models/video/video-caption' | 35 | import { VideoCaptionModel } from '../../models/video/video-caption' |
36 | import { videoRedundancyGetValidator } from '../../middlewares/validators/redundancy' | 36 | import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy' |
37 | import { getServerActor } from '../../helpers/utils' | 37 | import { getServerActor } from '../../helpers/utils' |
38 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' | 38 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
39 | import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' | 39 | import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' |
@@ -63,11 +63,11 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', | |||
63 | 63 | ||
64 | activityPubClientRouter.get('/videos/watch/:id', | 64 | activityPubClientRouter.get('/videos/watch/:id', |
65 | executeIfActivityPub(asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS))), | 65 | executeIfActivityPub(asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS))), |
66 | executeIfActivityPub(asyncMiddleware(videosGetValidator)), | 66 | executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video-with-rights'))), |
67 | executeIfActivityPub(asyncMiddleware(videoController)) | 67 | executeIfActivityPub(asyncMiddleware(videoController)) |
68 | ) | 68 | ) |
69 | activityPubClientRouter.get('/videos/watch/:id/activity', | 69 | activityPubClientRouter.get('/videos/watch/:id/activity', |
70 | executeIfActivityPub(asyncMiddleware(videosGetValidator)), | 70 | executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video-with-rights'))), |
71 | executeIfActivityPub(asyncMiddleware(videoController)) | 71 | executeIfActivityPub(asyncMiddleware(videoController)) |
72 | ) | 72 | ) |
73 | activityPubClientRouter.get('/videos/watch/:id/announces', | 73 | activityPubClientRouter.get('/videos/watch/:id/announces', |
@@ -113,7 +113,11 @@ activityPubClientRouter.get('/video-channels/:name/following', | |||
113 | ) | 113 | ) |
114 | 114 | ||
115 | activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?', | 115 | activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?', |
116 | executeIfActivityPub(asyncMiddleware(videoRedundancyGetValidator)), | 116 | executeIfActivityPub(asyncMiddleware(videoFileRedundancyGetValidator)), |
117 | executeIfActivityPub(asyncMiddleware(videoRedundancyController)) | ||
118 | ) | ||
119 | activityPubClientRouter.get('/redundancy/video-playlists/:streamingPlaylistType/:videoId', | ||
120 | executeIfActivityPub(asyncMiddleware(videoPlaylistRedundancyGetValidator)), | ||
117 | executeIfActivityPub(asyncMiddleware(videoRedundancyController)) | 121 | executeIfActivityPub(asyncMiddleware(videoRedundancyController)) |
118 | ) | 122 | ) |
119 | 123 | ||
@@ -160,7 +164,8 @@ function getAccountVideoRate (rateType: VideoRateType) { | |||
160 | } | 164 | } |
161 | 165 | ||
162 | async function videoController (req: express.Request, res: express.Response) { | 166 | async function videoController (req: express.Request, res: express.Response) { |
163 | const video: VideoModel = res.locals.video | 167 | // We need more attributes |
168 | const video: VideoModel = await VideoModel.loadForGetAPI(res.locals.video.id) | ||
164 | 169 | ||
165 | if (video.url.startsWith(CONFIG.WEBSERVER.URL) === false) return res.redirect(video.url) | 170 | if (video.url.startsWith(CONFIG.WEBSERVER.URL) === false) return res.redirect(video.url) |
166 | 171 | ||
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 255026f46..1f3341bc0 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { omit, snakeCase } from 'lodash' | 2 | import { snakeCase } from 'lodash' |
3 | import { ServerConfig, UserRight } from '../../../shared' | 3 | import { ServerConfig, UserRight } from '../../../shared' |
4 | import { About } from '../../../shared/models/server/about.model' | 4 | import { About } from '../../../shared/models/server/about.model' |
5 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' | 5 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' |
@@ -78,6 +78,9 @@ async function getConfig (req: express.Request, res: express.Response) { | |||
78 | requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION | 78 | requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION |
79 | }, | 79 | }, |
80 | transcoding: { | 80 | transcoding: { |
81 | hls: { | ||
82 | enabled: CONFIG.TRANSCODING.HLS.ENABLED | ||
83 | }, | ||
81 | enabledResolutions | 84 | enabledResolutions |
82 | }, | 85 | }, |
83 | import: { | 86 | import: { |
@@ -246,6 +249,9 @@ function customConfig (): CustomConfig { | |||
246 | '480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ], | 249 | '480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ], |
247 | '720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ], | 250 | '720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ], |
248 | '1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ] | 251 | '1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ] |
252 | }, | ||
253 | hls: { | ||
254 | enabled: CONFIG.TRANSCODING.HLS.ENABLED | ||
249 | } | 255 | } |
250 | }, | 256 | }, |
251 | import: { | 257 | import: { |
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index dbe0718d4..e3533a7f6 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -229,7 +229,7 @@ async function unblockUser (req: express.Request, res: express.Response, next: e | |||
229 | return res.status(204).end() | 229 | return res.status(204).end() |
230 | } | 230 | } |
231 | 231 | ||
232 | async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 232 | async function blockUser (req: express.Request, res: express.Response) { |
233 | const user: UserModel = res.locals.user | 233 | const user: UserModel = res.locals.user |
234 | const reason = req.body.reason | 234 | const reason = req.body.reason |
235 | 235 | ||
@@ -238,23 +238,23 @@ async function blockUser (req: express.Request, res: express.Response, next: exp | |||
238 | return res.status(204).end() | 238 | return res.status(204).end() |
239 | } | 239 | } |
240 | 240 | ||
241 | function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 241 | function getUser (req: express.Request, res: express.Response) { |
242 | return res.json((res.locals.user as UserModel).toFormattedJSON()) | 242 | return res.json((res.locals.user as UserModel).toFormattedJSON()) |
243 | } | 243 | } |
244 | 244 | ||
245 | async function autocompleteUsers (req: express.Request, res: express.Response, next: express.NextFunction) { | 245 | async function autocompleteUsers (req: express.Request, res: express.Response) { |
246 | const resultList = await UserModel.autoComplete(req.query.search as string) | 246 | const resultList = await UserModel.autoComplete(req.query.search as string) |
247 | 247 | ||
248 | return res.json(resultList) | 248 | return res.json(resultList) |
249 | } | 249 | } |
250 | 250 | ||
251 | async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) { | 251 | async function listUsers (req: express.Request, res: express.Response) { |
252 | const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search) | 252 | const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search) |
253 | 253 | ||
254 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 254 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
255 | } | 255 | } |
256 | 256 | ||
257 | async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 257 | async function removeUser (req: express.Request, res: express.Response) { |
258 | const user: UserModel = res.locals.user | 258 | const user: UserModel = res.locals.user |
259 | 259 | ||
260 | await user.destroy() | 260 | await user.destroy() |
@@ -264,12 +264,13 @@ async function removeUser (req: express.Request, res: express.Response, next: ex | |||
264 | return res.sendStatus(204) | 264 | return res.sendStatus(204) |
265 | } | 265 | } |
266 | 266 | ||
267 | async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) { | 267 | async function updateUser (req: express.Request, res: express.Response) { |
268 | const body: UserUpdate = req.body | 268 | const body: UserUpdate = req.body |
269 | const userToUpdate = res.locals.user as UserModel | 269 | const userToUpdate = res.locals.user as UserModel |
270 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) | 270 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) |
271 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role | 271 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role |
272 | 272 | ||
273 | if (body.password !== undefined) userToUpdate.password = body.password | ||
273 | if (body.email !== undefined) userToUpdate.email = body.email | 274 | if (body.email !== undefined) userToUpdate.email = body.email |
274 | if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified | 275 | if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified |
275 | if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota | 276 | if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota |
@@ -279,11 +280,11 @@ async function updateUser (req: express.Request, res: express.Response, next: ex | |||
279 | const user = await userToUpdate.save() | 280 | const user = await userToUpdate.save() |
280 | 281 | ||
281 | // Destroy user token to refresh rights | 282 | // Destroy user token to refresh rights |
282 | if (roleChanged) await deleteUserToken(userToUpdate.id) | 283 | if (roleChanged || body.password !== undefined) await deleteUserToken(userToUpdate.id) |
283 | 284 | ||
284 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) | 285 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) |
285 | 286 | ||
286 | // Don't need to send this update to followers, these attributes are not propagated | 287 | // Don't need to send this update to followers, these attributes are not federated |
287 | 288 | ||
288 | return res.sendStatus(204) | 289 | return res.sendStatus(204) |
289 | } | 290 | } |
@@ -293,7 +294,7 @@ async function askResetUserPassword (req: express.Request, res: express.Response | |||
293 | 294 | ||
294 | const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id) | 295 | const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id) |
295 | const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString | 296 | const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString |
296 | await Emailer.Instance.addForgetPasswordEmailJob(user.email, url) | 297 | await Emailer.Instance.addPasswordResetEmailJob(user.email, url) |
297 | 298 | ||
298 | return res.status(204).end() | 299 | return res.status(204).end() |
299 | } | 300 | } |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 94a2b8732..d5e154869 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -167,7 +167,7 @@ async function deleteMe (req: express.Request, res: express.Response) { | |||
167 | return res.sendStatus(204) | 167 | return res.sendStatus(204) |
168 | } | 168 | } |
169 | 169 | ||
170 | async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) { | 170 | async function updateMe (req: express.Request, res: express.Response) { |
171 | const body: UserUpdateMe = req.body | 171 | const body: UserUpdateMe = req.body |
172 | 172 | ||
173 | const user: UserModel = res.locals.oauth.token.user | 173 | const user: UserModel = res.locals.oauth.token.user |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 8414ca42c..3b1c2d255 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -36,6 +36,7 @@ import { | |||
36 | setDefaultPagination, | 36 | setDefaultPagination, |
37 | setDefaultSort, | 37 | setDefaultSort, |
38 | videosAddValidator, | 38 | videosAddValidator, |
39 | videosCustomGetValidator, | ||
39 | videosGetValidator, | 40 | videosGetValidator, |
40 | videosRemoveValidator, | 41 | videosRemoveValidator, |
41 | videosSortValidator, | 42 | videosSortValidator, |
@@ -123,9 +124,9 @@ videosRouter.get('/:id/description', | |||
123 | ) | 124 | ) |
124 | videosRouter.get('/:id', | 125 | videosRouter.get('/:id', |
125 | optionalAuthenticate, | 126 | optionalAuthenticate, |
126 | asyncMiddleware(videosGetValidator), | 127 | asyncMiddleware(videosCustomGetValidator('only-video-with-rights')), |
127 | asyncMiddleware(checkVideoFollowConstraints), | 128 | asyncMiddleware(checkVideoFollowConstraints), |
128 | getVideo | 129 | asyncMiddleware(getVideo) |
129 | ) | 130 | ) |
130 | videosRouter.post('/:id/views', | 131 | videosRouter.post('/:id/views', |
131 | asyncMiddleware(videosGetValidator), | 132 | asyncMiddleware(videosGetValidator), |
@@ -395,15 +396,17 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
395 | return res.type('json').status(204).end() | 396 | return res.type('json').status(204).end() |
396 | } | 397 | } |
397 | 398 | ||
398 | function getVideo (req: express.Request, res: express.Response) { | 399 | async function getVideo (req: express.Request, res: express.Response) { |
399 | const videoInstance = res.locals.video | 400 | // We need more attributes |
401 | const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null | ||
402 | const video: VideoModel = await VideoModel.loadForGetAPI(res.locals.video.id, undefined, userId) | ||
400 | 403 | ||
401 | if (videoInstance.isOutdated()) { | 404 | if (video.isOutdated()) { |
402 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: videoInstance.url } }) | 405 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) |
403 | .catch(err => logger.error('Cannot create AP refresher job for video %s.', videoInstance.url, { err })) | 406 | .catch(err => logger.error('Cannot create AP refresher job for video %s.', video.url, { err })) |
404 | } | 407 | } |
405 | 408 | ||
406 | return res.json(videoInstance.toFormattedDetailsJSON()) | 409 | return res.json(video.toFormattedDetailsJSON()) |
407 | } | 410 | } |
408 | 411 | ||
409 | async function viewVideo (req: express.Request, res: express.Response) { | 412 | async function viewVideo (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 4fd58f70c..b21f9da00 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as cors from 'cors' | 1 | import * as cors from 'cors' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { CONFIG, ROUTE_CACHE_LIFETIME, STATIC_DOWNLOAD_PATHS, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers' | 3 | import { CONFIG, HLS_PLAYLIST_DIRECTORY, ROUTE_CACHE_LIFETIME, STATIC_DOWNLOAD_PATHS, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers' |
4 | import { VideosPreviewCache } from '../lib/cache' | 4 | import { VideosPreviewCache } from '../lib/cache' |
5 | import { cacheRoute } from '../middlewares/cache' | 5 | import { cacheRoute } from '../middlewares/cache' |
6 | import { asyncMiddleware, videosGetValidator } from '../middlewares' | 6 | import { asyncMiddleware, videosGetValidator } from '../middlewares' |
@@ -51,6 +51,13 @@ staticRouter.use( | |||
51 | asyncMiddleware(downloadVideoFile) | 51 | asyncMiddleware(downloadVideoFile) |
52 | ) | 52 | ) |
53 | 53 | ||
54 | // HLS | ||
55 | staticRouter.use( | ||
56 | STATIC_PATHS.PLAYLISTS.HLS, | ||
57 | cors(), | ||
58 | express.static(HLS_PLAYLIST_DIRECTORY, { fallthrough: false }) // 404 if the file does not exist | ||
59 | ) | ||
60 | |||
54 | // Thumbnails path for express | 61 | // Thumbnails path for express |
55 | const thumbnailsPhysicalPath = CONFIG.STORAGE.THUMBNAILS_DIR | 62 | const thumbnailsPhysicalPath = CONFIG.STORAGE.THUMBNAILS_DIR |
56 | staticRouter.use( | 63 | staticRouter.use( |
diff --git a/server/controllers/tracker.ts b/server/controllers/tracker.ts index 1deb8c402..8b77d9de7 100644 --- a/server/controllers/tracker.ts +++ b/server/controllers/tracker.ts | |||
@@ -7,6 +7,7 @@ import { Server as WebSocketServer } from 'ws' | |||
7 | import { CONFIG, TRACKER_RATE_LIMITS } from '../initializers/constants' | 7 | import { CONFIG, TRACKER_RATE_LIMITS } from '../initializers/constants' |
8 | import { VideoFileModel } from '../models/video/video-file' | 8 | import { VideoFileModel } from '../models/video/video-file' |
9 | import { parse } from 'url' | 9 | import { parse } from 'url' |
10 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | ||
10 | 11 | ||
11 | const TrackerServer = bitTorrentTracker.Server | 12 | const TrackerServer = bitTorrentTracker.Server |
12 | 13 | ||
@@ -21,7 +22,7 @@ const trackerServer = new TrackerServer({ | |||
21 | udp: false, | 22 | udp: false, |
22 | ws: false, | 23 | ws: false, |
23 | dht: false, | 24 | dht: false, |
24 | filter: function (infoHash, params, cb) { | 25 | filter: async function (infoHash, params, cb) { |
25 | let ip: string | 26 | let ip: string |
26 | 27 | ||
27 | if (params.type === 'ws') { | 28 | if (params.type === 'ws') { |
@@ -32,19 +33,25 @@ const trackerServer = new TrackerServer({ | |||
32 | 33 | ||
33 | const key = ip + '-' + infoHash | 34 | const key = ip + '-' + infoHash |
34 | 35 | ||
35 | peersIps[ip] = peersIps[ip] ? peersIps[ip] + 1 : 1 | 36 | peersIps[ ip ] = peersIps[ ip ] ? peersIps[ ip ] + 1 : 1 |
36 | peersIpInfoHash[key] = peersIpInfoHash[key] ? peersIpInfoHash[key] + 1 : 1 | 37 | peersIpInfoHash[ key ] = peersIpInfoHash[ key ] ? peersIpInfoHash[ key ] + 1 : 1 |
37 | 38 | ||
38 | if (peersIpInfoHash[key] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { | 39 | if (peersIpInfoHash[ key ] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { |
39 | return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`)) | 40 | return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`)) |
40 | } | 41 | } |
41 | 42 | ||
42 | VideoFileModel.isInfohashExists(infoHash) | 43 | try { |
43 | .then(exists => { | 44 | const videoFileExists = await VideoFileModel.doesInfohashExist(infoHash) |
44 | if (exists === false) return cb(new Error(`Unknown infoHash ${infoHash}`)) | 45 | if (videoFileExists === true) return cb() |
45 | 46 | ||
46 | return cb() | 47 | const playlistExists = await VideoStreamingPlaylistModel.doesInfohashExist(infoHash) |
47 | }) | 48 | if (playlistExists === true) return cb() |
49 | |||
50 | return cb(new Error(`Unknown infoHash ${infoHash}`)) | ||
51 | } catch (err) { | ||
52 | logger.error('Error in tracker filter.', { err }) | ||
53 | return cb(err) | ||
54 | } | ||
48 | } | 55 | } |
49 | }) | 56 | }) |
50 | 57 | ||