diff options
Diffstat (limited to 'server/controllers')
38 files changed, 630 insertions, 325 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 62412cd62..e44f1c6ab 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | // Intercept ActivityPub client requests | ||
2 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as cors from 'cors' | ||
3 | import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' | 3 | import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' |
4 | import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub' | 4 | import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub' |
5 | import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../../initializers/constants' | 5 | import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../../initializers/constants' |
@@ -14,7 +14,7 @@ import { | |||
14 | videosCustomGetValidator, | 14 | videosCustomGetValidator, |
15 | videosShareValidator | 15 | videosShareValidator |
16 | } from '../../middlewares' | 16 | } from '../../middlewares' |
17 | import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators' | 17 | import { getAccountVideoRateValidatorFactory, videoCommentGetValidator } from '../../middlewares/validators' |
18 | import { AccountModel } from '../../models/account/account' | 18 | import { AccountModel } from '../../models/account/account' |
19 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | 19 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' |
20 | import { VideoModel } from '../../models/video/video' | 20 | import { VideoModel } from '../../models/video/video' |
@@ -24,22 +24,25 @@ import { cacheRoute } from '../../middlewares/cache' | |||
24 | import { activityPubResponse } from './utils' | 24 | import { activityPubResponse } from './utils' |
25 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | 25 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' |
26 | import { | 26 | import { |
27 | getRateUrl, | ||
28 | getVideoCommentsActivityPubUrl, | 27 | getVideoCommentsActivityPubUrl, |
29 | getVideoDislikesActivityPubUrl, | 28 | getVideoDislikesActivityPubUrl, |
30 | getVideoLikesActivityPubUrl, | 29 | getVideoLikesActivityPubUrl, |
31 | getVideoSharesActivityPubUrl | 30 | getVideoSharesActivityPubUrl |
32 | } from '../../lib/activitypub' | 31 | } from '../../lib/activitypub/url' |
33 | import { VideoCaptionModel } from '../../models/video/video-caption' | 32 | import { VideoCaptionModel } from '../../models/video/video-caption' |
34 | import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy' | 33 | import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy' |
35 | import { getServerActor } from '../../helpers/utils' | ||
36 | import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' | 34 | import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' |
37 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' | 35 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' |
38 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 36 | import { VideoPlaylistModel } from '../../models/video/video-playlist' |
39 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 37 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
40 | import { MAccountId, MActorId, MVideo, MVideoAPWithoutCaption } from '@server/typings/models' | 38 | import { MAccountId, MActorId, MVideoAPWithoutCaption, MVideoId } from '@server/typings/models' |
39 | import { getServerActor } from '@server/models/application/application' | ||
40 | import { getRateUrl } from '@server/lib/activitypub/video-rates' | ||
41 | 41 | ||
42 | const activityPubClientRouter = express.Router() | 42 | const activityPubClientRouter = express.Router() |
43 | activityPubClientRouter.use(cors()) | ||
44 | |||
45 | // Intercept ActivityPub client requests | ||
43 | 46 | ||
44 | activityPubClientRouter.get('/accounts?/:name', | 47 | activityPubClientRouter.get('/accounts?/:name', |
45 | executeIfActivityPub, | 48 | executeIfActivityPub, |
@@ -63,13 +66,13 @@ activityPubClientRouter.get('/accounts?/:name/playlists', | |||
63 | ) | 66 | ) |
64 | activityPubClientRouter.get('/accounts?/:name/likes/:videoId', | 67 | activityPubClientRouter.get('/accounts?/:name/likes/:videoId', |
65 | executeIfActivityPub, | 68 | executeIfActivityPub, |
66 | asyncMiddleware(getAccountVideoRateValidator('like')), | 69 | asyncMiddleware(getAccountVideoRateValidatorFactory('like')), |
67 | getAccountVideoRate('like') | 70 | getAccountVideoRateFactory('like') |
68 | ) | 71 | ) |
69 | activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', | 72 | activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', |
70 | executeIfActivityPub, | 73 | executeIfActivityPub, |
71 | asyncMiddleware(getAccountVideoRateValidator('dislike')), | 74 | asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')), |
72 | getAccountVideoRate('dislike') | 75 | getAccountVideoRateFactory('dislike') |
73 | ) | 76 | ) |
74 | 77 | ||
75 | activityPubClientRouter.get('/videos/watch/:id', | 78 | activityPubClientRouter.get('/videos/watch/:id', |
@@ -85,7 +88,7 @@ activityPubClientRouter.get('/videos/watch/:id/activity', | |||
85 | ) | 88 | ) |
86 | activityPubClientRouter.get('/videos/watch/:id/announces', | 89 | activityPubClientRouter.get('/videos/watch/:id/announces', |
87 | executeIfActivityPub, | 90 | executeIfActivityPub, |
88 | asyncMiddleware(videosCustomGetValidator('only-video')), | 91 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), |
89 | asyncMiddleware(videoAnnouncesController) | 92 | asyncMiddleware(videoAnnouncesController) |
90 | ) | 93 | ) |
91 | activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', | 94 | activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', |
@@ -95,17 +98,17 @@ activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', | |||
95 | ) | 98 | ) |
96 | activityPubClientRouter.get('/videos/watch/:id/likes', | 99 | activityPubClientRouter.get('/videos/watch/:id/likes', |
97 | executeIfActivityPub, | 100 | executeIfActivityPub, |
98 | asyncMiddleware(videosCustomGetValidator('only-video')), | 101 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), |
99 | asyncMiddleware(videoLikesController) | 102 | asyncMiddleware(videoLikesController) |
100 | ) | 103 | ) |
101 | activityPubClientRouter.get('/videos/watch/:id/dislikes', | 104 | activityPubClientRouter.get('/videos/watch/:id/dislikes', |
102 | executeIfActivityPub, | 105 | executeIfActivityPub, |
103 | asyncMiddleware(videosCustomGetValidator('only-video')), | 106 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), |
104 | asyncMiddleware(videoDislikesController) | 107 | asyncMiddleware(videoDislikesController) |
105 | ) | 108 | ) |
106 | activityPubClientRouter.get('/videos/watch/:id/comments', | 109 | activityPubClientRouter.get('/videos/watch/:id/comments', |
107 | executeIfActivityPub, | 110 | executeIfActivityPub, |
108 | asyncMiddleware(videosCustomGetValidator('only-video')), | 111 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), |
109 | asyncMiddleware(videoCommentsController) | 112 | asyncMiddleware(videoCommentsController) |
110 | ) | 113 | ) |
111 | activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId', | 114 | activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId', |
@@ -122,7 +125,7 @@ activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity | |||
122 | activityPubClientRouter.get('/video-channels/:name', | 125 | activityPubClientRouter.get('/video-channels/:name', |
123 | executeIfActivityPub, | 126 | executeIfActivityPub, |
124 | asyncMiddleware(localVideoChannelValidator), | 127 | asyncMiddleware(localVideoChannelValidator), |
125 | asyncMiddleware(videoChannelController) | 128 | videoChannelController |
126 | ) | 129 | ) |
127 | activityPubClientRouter.get('/video-channels/:name/followers', | 130 | activityPubClientRouter.get('/video-channels/:name/followers', |
128 | executeIfActivityPub, | 131 | executeIfActivityPub, |
@@ -154,7 +157,7 @@ activityPubClientRouter.get('/video-playlists/:playlistId', | |||
154 | activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', | 157 | activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', |
155 | executeIfActivityPub, | 158 | executeIfActivityPub, |
156 | asyncMiddleware(videoPlaylistElementAPGetValidator), | 159 | asyncMiddleware(videoPlaylistElementAPGetValidator), |
157 | asyncMiddleware(videoPlaylistElementController) | 160 | videoPlaylistElementController |
158 | ) | 161 | ) |
159 | 162 | ||
160 | // --------------------------------------------------------------------------- | 163 | // --------------------------------------------------------------------------- |
@@ -192,7 +195,7 @@ async function accountPlaylistsController (req: express.Request, res: express.Re | |||
192 | return activityPubResponse(activityPubContextify(activityPubResult), res) | 195 | return activityPubResponse(activityPubContextify(activityPubResult), res) |
193 | } | 196 | } |
194 | 197 | ||
195 | function getAccountVideoRate (rateType: VideoRateType) { | 198 | function getAccountVideoRateFactory (rateType: VideoRateType) { |
196 | return (req: express.Request, res: express.Response) => { | 199 | return (req: express.Request, res: express.Response) => { |
197 | const accountVideoRate = res.locals.accountVideoRate | 200 | const accountVideoRate = res.locals.accountVideoRate |
198 | 201 | ||
@@ -234,11 +237,11 @@ async function videoAnnounceController (req: express.Request, res: express.Respo | |||
234 | 237 | ||
235 | const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined) | 238 | const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined) |
236 | 239 | ||
237 | return activityPubResponse(activityPubContextify(activity), res) | 240 | return activityPubResponse(activityPubContextify(activity, 'Announce'), res) |
238 | } | 241 | } |
239 | 242 | ||
240 | async function videoAnnouncesController (req: express.Request, res: express.Response) { | 243 | async function videoAnnouncesController (req: express.Request, res: express.Response) { |
241 | const video = res.locals.onlyVideo | 244 | const video = res.locals.onlyImmutableVideo |
242 | 245 | ||
243 | const handler = async (start: number, count: number) => { | 246 | const handler = async (start: number, count: number) => { |
244 | const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) | 247 | const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) |
@@ -253,21 +256,21 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp | |||
253 | } | 256 | } |
254 | 257 | ||
255 | async function videoLikesController (req: express.Request, res: express.Response) { | 258 | async function videoLikesController (req: express.Request, res: express.Response) { |
256 | const video = res.locals.onlyVideo | 259 | const video = res.locals.onlyImmutableVideo |
257 | const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) | 260 | const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) |
258 | 261 | ||
259 | return activityPubResponse(activityPubContextify(json), res) | 262 | return activityPubResponse(activityPubContextify(json), res) |
260 | } | 263 | } |
261 | 264 | ||
262 | async function videoDislikesController (req: express.Request, res: express.Response) { | 265 | async function videoDislikesController (req: express.Request, res: express.Response) { |
263 | const video = res.locals.onlyVideo | 266 | const video = res.locals.onlyImmutableVideo |
264 | const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) | 267 | const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) |
265 | 268 | ||
266 | return activityPubResponse(activityPubContextify(json), res) | 269 | return activityPubResponse(activityPubContextify(json), res) |
267 | } | 270 | } |
268 | 271 | ||
269 | async function videoCommentsController (req: express.Request, res: express.Response) { | 272 | async function videoCommentsController (req: express.Request, res: express.Response) { |
270 | const video = res.locals.onlyVideo | 273 | const video = res.locals.onlyImmutableVideo |
271 | 274 | ||
272 | const handler = async (start: number, count: number) => { | 275 | const handler = async (start: number, count: number) => { |
273 | const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count) | 276 | const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count) |
@@ -281,7 +284,7 @@ async function videoCommentsController (req: express.Request, res: express.Respo | |||
281 | return activityPubResponse(activityPubContextify(json), res) | 284 | return activityPubResponse(activityPubContextify(json), res) |
282 | } | 285 | } |
283 | 286 | ||
284 | async function videoChannelController (req: express.Request, res: express.Response) { | 287 | function videoChannelController (req: express.Request, res: express.Response) { |
285 | const videoChannel = res.locals.videoChannel | 288 | const videoChannel = res.locals.videoChannel |
286 | 289 | ||
287 | return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject()), res) | 290 | return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject()), res) |
@@ -334,10 +337,10 @@ async function videoRedundancyController (req: express.Request, res: express.Res | |||
334 | 337 | ||
335 | if (req.path.endsWith('/activity')) { | 338 | if (req.path.endsWith('/activity')) { |
336 | const data = buildCreateActivity(videoRedundancy.url, serverActor, object, audience) | 339 | const data = buildCreateActivity(videoRedundancy.url, serverActor, object, audience) |
337 | return activityPubResponse(activityPubContextify(data), res) | 340 | return activityPubResponse(activityPubContextify(data, 'CacheFile'), res) |
338 | } | 341 | } |
339 | 342 | ||
340 | return activityPubResponse(activityPubContextify(object), res) | 343 | return activityPubResponse(activityPubContextify(object, 'CacheFile'), res) |
341 | } | 344 | } |
342 | 345 | ||
343 | async function videoPlaylistController (req: express.Request, res: express.Response) { | 346 | async function videoPlaylistController (req: express.Request, res: express.Response) { |
@@ -353,7 +356,7 @@ async function videoPlaylistController (req: express.Request, res: express.Respo | |||
353 | return activityPubResponse(activityPubContextify(object), res) | 356 | return activityPubResponse(activityPubContextify(object), res) |
354 | } | 357 | } |
355 | 358 | ||
356 | async function videoPlaylistElementController (req: express.Request, res: express.Response) { | 359 | function videoPlaylistElementController (req: express.Request, res: express.Response) { |
357 | const videoPlaylistElement = res.locals.videoPlaylistElementAP | 360 | const videoPlaylistElement = res.locals.videoPlaylistElementAP |
358 | 361 | ||
359 | const json = videoPlaylistElement.toActivityPubObject() | 362 | const json = videoPlaylistElement.toActivityPubObject() |
@@ -386,7 +389,7 @@ async function actorPlaylists (req: express.Request, account: MAccountId) { | |||
386 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) | 389 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) |
387 | } | 390 | } |
388 | 391 | ||
389 | function videoRates (req: express.Request, rateType: VideoRateType, video: MVideo, url: string) { | 392 | function videoRates (req: express.Request, rateType: VideoRateType, video: MVideoId, url: string) { |
390 | const handler = async (start: number, count: number) => { | 393 | const handler = async (start: number, count: number) => { |
391 | const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) | 394 | const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) |
392 | return { | 395 | return { |
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts index ca42106b8..c5edf86b7 100644 --- a/server/controllers/activitypub/inbox.ts +++ b/server/controllers/activitypub/inbox.ts | |||
@@ -46,15 +46,19 @@ const inboxQueue = queue<QueueParam, Error>((task, cb) => { | |||
46 | 46 | ||
47 | processActivities(task.activities, options) | 47 | processActivities(task.activities, options) |
48 | .then(() => cb()) | 48 | .then(() => cb()) |
49 | .catch(err => { | ||
50 | logger.error('Error in process activities.', { err }) | ||
51 | cb() | ||
52 | }) | ||
49 | }) | 53 | }) |
50 | 54 | ||
51 | function inboxController (req: express.Request, res: express.Response) { | 55 | function inboxController (req: express.Request, res: express.Response) { |
52 | const rootActivity: RootActivity = req.body | 56 | const rootActivity: RootActivity = req.body |
53 | let activities: Activity[] = [] | 57 | let activities: Activity[] |
54 | 58 | ||
55 | if ([ 'Collection', 'CollectionPage' ].indexOf(rootActivity.type) !== -1) { | 59 | if ([ 'Collection', 'CollectionPage' ].includes(rootActivity.type)) { |
56 | activities = (rootActivity as ActivityPubCollection).items | 60 | activities = (rootActivity as ActivityPubCollection).items |
57 | } else if ([ 'OrderedCollection', 'OrderedCollectionPage' ].indexOf(rootActivity.type) !== -1) { | 61 | } else if ([ 'OrderedCollection', 'OrderedCollectionPage' ].includes(rootActivity.type)) { |
58 | activities = (rootActivity as ActivityPubOrderedCollection<Activity>).orderedItems | 62 | activities = (rootActivity as ActivityPubOrderedCollection<Activity>).orderedItems |
59 | } else { | 63 | } else { |
60 | activities = [ rootActivity as Activity ] | 64 | activities = [ rootActivity as Activity ] |
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 05740318e..ccdc610a2 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects, getServerActor } from '../../helpers/utils' | 2 | import { getFormattedObjects } from '../../helpers/utils' |
3 | import { | 3 | import { |
4 | asyncMiddleware, | 4 | asyncMiddleware, |
5 | authenticate, | 5 | authenticate, |
@@ -16,21 +16,19 @@ import { | |||
16 | accountNameWithHostGetValidator, | 16 | accountNameWithHostGetValidator, |
17 | accountsSortValidator, | 17 | accountsSortValidator, |
18 | ensureAuthUserOwnsAccountValidator, | 18 | ensureAuthUserOwnsAccountValidator, |
19 | videoChannelsSortValidator, | ||
19 | videosSortValidator, | 20 | videosSortValidator, |
20 | videoChannelsSortValidator | 21 | videoChannelStatsValidator |
21 | } from '../../middlewares/validators' | 22 | } from '../../middlewares/validators' |
22 | import { AccountModel } from '../../models/account/account' | 23 | import { AccountModel } from '../../models/account/account' |
23 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | 24 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' |
24 | import { VideoModel } from '../../models/video/video' | 25 | import { VideoModel } from '../../models/video/video' |
25 | import { buildNSFWFilter, isUserAbleToSearchRemoteURI, getCountVideos } from '../../helpers/express-utils' | 26 | import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
26 | import { VideoChannelModel } from '../../models/video/video-channel' | 27 | import { VideoChannelModel } from '../../models/video/video-channel' |
27 | import { JobQueue } from '../../lib/job-queue' | 28 | import { JobQueue } from '../../lib/job-queue' |
28 | import { logger } from '../../helpers/logger' | ||
29 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 29 | import { VideoPlaylistModel } from '../../models/video/video-playlist' |
30 | import { | 30 | import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists' |
31 | commonVideoPlaylistFiltersValidator, | 31 | import { getServerActor } from '@server/models/application/application' |
32 | videoPlaylistsSearchValidator | ||
33 | } from '../../middlewares/validators/videos/video-playlists' | ||
34 | 32 | ||
35 | const accountsRouter = express.Router() | 33 | const accountsRouter = express.Router() |
36 | 34 | ||
@@ -60,6 +58,7 @@ accountsRouter.get('/:accountName/videos', | |||
60 | 58 | ||
61 | accountsRouter.get('/:accountName/video-channels', | 59 | accountsRouter.get('/:accountName/video-channels', |
62 | asyncMiddleware(accountNameWithHostGetValidator), | 60 | asyncMiddleware(accountNameWithHostGetValidator), |
61 | videoChannelStatsValidator, | ||
63 | paginationValidator, | 62 | paginationValidator, |
64 | videoChannelsSortValidator, | 63 | videoChannelsSortValidator, |
65 | setDefaultSort, | 64 | setDefaultSort, |
@@ -104,7 +103,6 @@ function getAccount (req: express.Request, res: express.Response) { | |||
104 | 103 | ||
105 | if (account.isOutdated()) { | 104 | if (account.isOutdated()) { |
106 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: account.Actor.url } }) | 105 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: account.Actor.url } }) |
107 | .catch(err => logger.error('Cannot create AP refresher job for actor %s.', account.Actor.url, { err })) | ||
108 | } | 106 | } |
109 | 107 | ||
110 | return res.json(account.toFormattedJSON()) | 108 | return res.json(account.toFormattedJSON()) |
@@ -121,7 +119,8 @@ async function listAccountChannels (req: express.Request, res: express.Response) | |||
121 | accountId: res.locals.account.id, | 119 | accountId: res.locals.account.id, |
122 | start: req.query.start, | 120 | start: req.query.start, |
123 | count: req.query.count, | 121 | count: req.query.count, |
124 | sort: req.query.sort | 122 | sort: req.query.sort, |
123 | withStats: req.query.withStats | ||
125 | } | 124 | } |
126 | 125 | ||
127 | const resultList = await VideoChannelModel.listByAccount(options) | 126 | const resultList = await VideoChannelModel.listByAccount(options) |
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index ae4e00248..edcb0b99e 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -1,23 +1,22 @@ | |||
1 | import { Hooks } from '@server/lib/plugins/hooks' | ||
1 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { remove, writeJSON } from 'fs-extra' | ||
2 | import { snakeCase } from 'lodash' | 4 | import { snakeCase } from 'lodash' |
3 | import { ServerConfig, UserRight } from '../../../shared' | 5 | import validator from 'validator' |
6 | import { RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig, UserRight } from '../../../shared' | ||
4 | import { About } from '../../../shared/models/server/about.model' | 7 | import { About } from '../../../shared/models/server/about.model' |
5 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' | 8 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' |
9 | import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' | ||
10 | import { objectConverter } from '../../helpers/core-utils' | ||
6 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' | 11 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' |
12 | import { getServerCommit } from '../../helpers/utils' | ||
13 | import { CONFIG, isEmailEnabled, reloadConfig } from '../../initializers/config' | ||
7 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '../../initializers/constants' | 14 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '../../initializers/constants' |
8 | import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares' | ||
9 | import { customConfigUpdateValidator } from '../../middlewares/validators/config' | ||
10 | import { ClientHtml } from '../../lib/client-html' | 15 | import { ClientHtml } from '../../lib/client-html' |
11 | import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' | ||
12 | import { remove, writeJSON } from 'fs-extra' | ||
13 | import { getServerCommit } from '../../helpers/utils' | ||
14 | import { Emailer } from '../../lib/emailer' | ||
15 | import validator from 'validator' | ||
16 | import { objectConverter } from '../../helpers/core-utils' | ||
17 | import { CONFIG, reloadConfig } from '../../initializers/config' | ||
18 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 16 | import { PluginManager } from '../../lib/plugins/plugin-manager' |
19 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' | 17 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' |
20 | import { Hooks } from '@server/lib/plugins/hooks' | 18 | import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares' |
19 | import { customConfigUpdateValidator } from '../../middlewares/validators/config' | ||
21 | 20 | ||
22 | const configRouter = express.Router() | 21 | const configRouter = express.Router() |
23 | 22 | ||
@@ -31,12 +30,12 @@ configRouter.get('/', | |||
31 | configRouter.get('/custom', | 30 | configRouter.get('/custom', |
32 | authenticate, | 31 | authenticate, |
33 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 32 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
34 | asyncMiddleware(getCustomConfig) | 33 | getCustomConfig |
35 | ) | 34 | ) |
36 | configRouter.put('/custom', | 35 | configRouter.put('/custom', |
37 | authenticate, | 36 | authenticate, |
38 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 37 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
39 | asyncMiddleware(customConfigUpdateValidator), | 38 | customConfigUpdateValidator, |
40 | asyncMiddleware(updateCustomConfig) | 39 | asyncMiddleware(updateCustomConfig) |
41 | ) | 40 | ) |
42 | configRouter.delete('/custom', | 41 | configRouter.delete('/custom', |
@@ -73,15 +72,23 @@ async function getConfig (req: express.Request, res: express.Response) { | |||
73 | css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS | 72 | css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS |
74 | } | 73 | } |
75 | }, | 74 | }, |
75 | search: { | ||
76 | remoteUri: { | ||
77 | users: CONFIG.SEARCH.REMOTE_URI.USERS, | ||
78 | anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS | ||
79 | } | ||
80 | }, | ||
76 | plugin: { | 81 | plugin: { |
77 | registered: getRegisteredPlugins() | 82 | registered: getRegisteredPlugins(), |
83 | registeredExternalAuths: getExternalAuthsPlugins(), | ||
84 | registeredIdAndPassAuths: getIdAndPassAuthPlugins() | ||
78 | }, | 85 | }, |
79 | theme: { | 86 | theme: { |
80 | registered: getRegisteredThemes(), | 87 | registered: getRegisteredThemes(), |
81 | default: defaultTheme | 88 | default: defaultTheme |
82 | }, | 89 | }, |
83 | email: { | 90 | email: { |
84 | enabled: Emailer.isEnabled() | 91 | enabled: isEmailEnabled() |
85 | }, | 92 | }, |
86 | contactForm: { | 93 | contactForm: { |
87 | enabled: CONFIG.CONTACT_FORM.ENABLED | 94 | enabled: CONFIG.CONTACT_FORM.ENABLED |
@@ -196,7 +203,7 @@ function getAbout (req: express.Request, res: express.Response) { | |||
196 | return res.json(about).end() | 203 | return res.json(about).end() |
197 | } | 204 | } |
198 | 205 | ||
199 | async function getCustomConfig (req: express.Request, res: express.Response) { | 206 | function getCustomConfig (req: express.Request, res: express.Response) { |
200 | const data = customConfig() | 207 | const data = customConfig() |
201 | 208 | ||
202 | return res.json(data).end() | 209 | return res.json(data).end() |
@@ -250,7 +257,7 @@ function getRegisteredThemes () { | |||
250 | 257 | ||
251 | function getEnabledResolutions () { | 258 | function getEnabledResolutions () { |
252 | return Object.keys(CONFIG.TRANSCODING.RESOLUTIONS) | 259 | return Object.keys(CONFIG.TRANSCODING.RESOLUTIONS) |
253 | .filter(key => CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.RESOLUTIONS[ key ] === true) | 260 | .filter(key => CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.RESOLUTIONS[key] === true) |
254 | .map(r => parseInt(r, 10)) | 261 | .map(r => parseInt(r, 10)) |
255 | } | 262 | } |
256 | 263 | ||
@@ -264,6 +271,42 @@ function getRegisteredPlugins () { | |||
264 | })) | 271 | })) |
265 | } | 272 | } |
266 | 273 | ||
274 | function getIdAndPassAuthPlugins () { | ||
275 | const result: RegisteredIdAndPassAuthConfig[] = [] | ||
276 | |||
277 | for (const p of PluginManager.Instance.getIdAndPassAuths()) { | ||
278 | for (const auth of p.idAndPassAuths) { | ||
279 | result.push({ | ||
280 | npmName: p.npmName, | ||
281 | name: p.name, | ||
282 | version: p.version, | ||
283 | authName: auth.authName, | ||
284 | weight: auth.getWeight() | ||
285 | }) | ||
286 | } | ||
287 | } | ||
288 | |||
289 | return result | ||
290 | } | ||
291 | |||
292 | function getExternalAuthsPlugins () { | ||
293 | const result: RegisteredExternalAuthConfig[] = [] | ||
294 | |||
295 | for (const p of PluginManager.Instance.getExternalAuths()) { | ||
296 | for (const auth of p.externalAuths) { | ||
297 | result.push({ | ||
298 | npmName: p.npmName, | ||
299 | name: p.name, | ||
300 | version: p.version, | ||
301 | authName: auth.authName, | ||
302 | authDisplayName: auth.authDisplayName() | ||
303 | }) | ||
304 | } | ||
305 | } | ||
306 | |||
307 | return result | ||
308 | } | ||
309 | |||
267 | // --------------------------------------------------------------------------- | 310 | // --------------------------------------------------------------------------- |
268 | 311 | ||
269 | export { | 312 | export { |
@@ -340,13 +383,13 @@ function customConfig (): CustomConfig { | |||
340 | allowAudioFiles: CONFIG.TRANSCODING.ALLOW_AUDIO_FILES, | 383 | allowAudioFiles: CONFIG.TRANSCODING.ALLOW_AUDIO_FILES, |
341 | threads: CONFIG.TRANSCODING.THREADS, | 384 | threads: CONFIG.TRANSCODING.THREADS, |
342 | resolutions: { | 385 | resolutions: { |
343 | '0p': CONFIG.TRANSCODING.RESOLUTIONS[ '0p' ], | 386 | '0p': CONFIG.TRANSCODING.RESOLUTIONS['0p'], |
344 | '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], | 387 | '240p': CONFIG.TRANSCODING.RESOLUTIONS['240p'], |
345 | '360p': CONFIG.TRANSCODING.RESOLUTIONS[ '360p' ], | 388 | '360p': CONFIG.TRANSCODING.RESOLUTIONS['360p'], |
346 | '480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ], | 389 | '480p': CONFIG.TRANSCODING.RESOLUTIONS['480p'], |
347 | '720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ], | 390 | '720p': CONFIG.TRANSCODING.RESOLUTIONS['720p'], |
348 | '1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ], | 391 | '1080p': CONFIG.TRANSCODING.RESOLUTIONS['1080p'], |
349 | '2160p': CONFIG.TRANSCODING.RESOLUTIONS[ '2160p' ] | 392 | '2160p': CONFIG.TRANSCODING.RESOLUTIONS['2160p'] |
350 | }, | 393 | }, |
351 | webtorrent: { | 394 | webtorrent: { |
352 | enabled: CONFIG.TRANSCODING.WEBTORRENT.ENABLED | 395 | enabled: CONFIG.TRANSCODING.WEBTORRENT.ENABLED |
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts index 6138a32de..7bec6c527 100644 --- a/server/controllers/api/index.ts +++ b/server/controllers/api/index.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as RateLimit from 'express-rate-limit' | ||
3 | import { configRouter } from './config' | 2 | import { configRouter } from './config' |
4 | import { jobsRouter } from './jobs' | 3 | import { jobsRouter } from './jobs' |
5 | import { oauthClientsRouter } from './oauth-clients' | 4 | import { oauthClientsRouter } from './oauth-clients' |
@@ -15,6 +14,7 @@ import { overviewsRouter } from './overviews' | |||
15 | import { videoPlaylistRouter } from './video-playlist' | 14 | import { videoPlaylistRouter } from './video-playlist' |
16 | import { CONFIG } from '../../initializers/config' | 15 | import { CONFIG } from '../../initializers/config' |
17 | import { pluginRouter } from './plugins' | 16 | import { pluginRouter } from './plugins' |
17 | import * as RateLimit from 'express-rate-limit' | ||
18 | 18 | ||
19 | const apiRouter = express.Router() | 19 | const apiRouter = express.Router() |
20 | 20 | ||
@@ -24,8 +24,6 @@ apiRouter.use(cors({ | |||
24 | credentials: true | 24 | credentials: true |
25 | })) | 25 | })) |
26 | 26 | ||
27 | // FIXME: https://github.com/nfriedly/express-rate-limit/issues/138 | ||
28 | // @ts-ignore | ||
29 | const apiRateLimiter = RateLimit({ | 27 | const apiRateLimiter = RateLimit({ |
30 | windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS, | 28 | windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS, |
31 | max: CONFIG.RATES_LIMIT.API.MAX | 29 | max: CONFIG.RATES_LIMIT.API.MAX |
diff --git a/server/controllers/api/jobs.ts b/server/controllers/api/jobs.ts index 05320311e..13fc04d18 100644 --- a/server/controllers/api/jobs.ts +++ b/server/controllers/api/jobs.ts | |||
@@ -50,7 +50,7 @@ async function listJobs (req: express.Request, res: express.Response) { | |||
50 | }) | 50 | }) |
51 | const total = await JobQueue.Instance.count(state) | 51 | const total = await JobQueue.Instance.count(state) |
52 | 52 | ||
53 | const result: ResultList<any> = { | 53 | const result: ResultList<Job> = { |
54 | total, | 54 | total, |
55 | data: jobs.map(j => formatJob(j, state)) | 55 | data: jobs.map(j => formatJob(j, state)) |
56 | } | 56 | } |
diff --git a/server/controllers/api/overviews.ts b/server/controllers/api/overviews.ts index 46e76ac6b..fb31932aa 100644 --- a/server/controllers/api/overviews.ts +++ b/server/controllers/api/overviews.ts | |||
@@ -1,17 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { buildNSFWFilter } from '../../helpers/express-utils' | 2 | import { buildNSFWFilter } from '../../helpers/express-utils' |
3 | import { VideoModel } from '../../models/video/video' | 3 | import { VideoModel } from '../../models/video/video' |
4 | import { asyncMiddleware } from '../../middlewares' | 4 | import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares' |
5 | import { TagModel } from '../../models/video/tag' | 5 | import { TagModel } from '../../models/video/tag' |
6 | import { VideosOverview } from '../../../shared/models/overviews' | 6 | import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews' |
7 | import { MEMOIZE_TTL, OVERVIEWS, ROUTE_CACHE_LIFETIME } from '../../initializers/constants' | 7 | import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants' |
8 | import { cacheRoute } from '../../middlewares/cache' | ||
9 | import * as memoizee from 'memoizee' | 8 | import * as memoizee from 'memoizee' |
9 | import { logger } from '@server/helpers/logger' | ||
10 | 10 | ||
11 | const overviewsRouter = express.Router() | 11 | const overviewsRouter = express.Router() |
12 | 12 | ||
13 | overviewsRouter.get('/videos', | 13 | overviewsRouter.get('/videos', |
14 | asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS)), | 14 | videosOverviewValidator, |
15 | optionalAuthenticate, | ||
15 | asyncMiddleware(getVideosOverview) | 16 | asyncMiddleware(getVideosOverview) |
16 | ) | 17 | ) |
17 | 18 | ||
@@ -24,21 +25,32 @@ export { overviewsRouter } | |||
24 | const buildSamples = memoizee(async function () { | 25 | const buildSamples = memoizee(async function () { |
25 | const [ categories, channels, tags ] = await Promise.all([ | 26 | const [ categories, channels, tags ] = await Promise.all([ |
26 | VideoModel.getRandomFieldSamples('category', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT), | 27 | VideoModel.getRandomFieldSamples('category', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT), |
27 | VideoModel.getRandomFieldSamples('channelId', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD ,OVERVIEWS.VIDEOS.SAMPLES_COUNT), | 28 | VideoModel.getRandomFieldSamples('channelId', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT), |
28 | TagModel.getRandomSamples(OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT) | 29 | TagModel.getRandomSamples(OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT) |
29 | ]) | 30 | ]) |
30 | 31 | ||
31 | return { categories, channels, tags } | 32 | const result = { categories, channels, tags } |
33 | |||
34 | logger.debug('Building samples for overview endpoint.', { result }) | ||
35 | |||
36 | return result | ||
32 | }, { maxAge: MEMOIZE_TTL.OVERVIEWS_SAMPLE }) | 37 | }, { maxAge: MEMOIZE_TTL.OVERVIEWS_SAMPLE }) |
33 | 38 | ||
34 | // This endpoint could be quite long, but we cache it | 39 | // This endpoint could be quite long, but we cache it |
35 | async function getVideosOverview (req: express.Request, res: express.Response) { | 40 | async function getVideosOverview (req: express.Request, res: express.Response) { |
36 | const attributes = await buildSamples() | 41 | const attributes = await buildSamples() |
37 | 42 | ||
38 | const [ categories, channels, tags ] = await Promise.all([ | 43 | const page = req.query.page || 1 |
39 | Promise.all(attributes.categories.map(c => getVideosByCategory(c, res))), | 44 | const index = page - 1 |
40 | Promise.all(attributes.channels.map(c => getVideosByChannel(c, res))), | 45 | |
41 | Promise.all(attributes.tags.map(t => getVideosByTag(t, res))) | 46 | const categories: CategoryOverview[] = [] |
47 | const channels: ChannelOverview[] = [] | ||
48 | const tags: TagOverview[] = [] | ||
49 | |||
50 | await Promise.all([ | ||
51 | getVideosByCategory(attributes.categories, index, res, categories), | ||
52 | getVideosByChannel(attributes.channels, index, res, channels), | ||
53 | getVideosByTag(attributes.tags, index, res, tags) | ||
42 | ]) | 54 | ]) |
43 | 55 | ||
44 | const result: VideosOverview = { | 56 | const result: VideosOverview = { |
@@ -47,45 +59,49 @@ async function getVideosOverview (req: express.Request, res: express.Response) { | |||
47 | tags | 59 | tags |
48 | } | 60 | } |
49 | 61 | ||
50 | // Cleanup our object | ||
51 | for (const key of Object.keys(result)) { | ||
52 | result[key] = result[key].filter(v => v !== undefined) | ||
53 | } | ||
54 | |||
55 | return res.json(result) | 62 | return res.json(result) |
56 | } | 63 | } |
57 | 64 | ||
58 | async function getVideosByTag (tag: string, res: express.Response) { | 65 | async function getVideosByTag (tagsSample: string[], index: number, res: express.Response, acc: TagOverview[]) { |
66 | if (tagsSample.length <= index) return | ||
67 | |||
68 | const tag = tagsSample[index] | ||
59 | const videos = await getVideos(res, { tagsOneOf: [ tag ] }) | 69 | const videos = await getVideos(res, { tagsOneOf: [ tag ] }) |
60 | 70 | ||
61 | if (videos.length === 0) return undefined | 71 | if (videos.length === 0) return |
62 | 72 | ||
63 | return { | 73 | acc.push({ |
64 | tag, | 74 | tag, |
65 | videos | 75 | videos |
66 | } | 76 | }) |
67 | } | 77 | } |
68 | 78 | ||
69 | async function getVideosByCategory (category: number, res: express.Response) { | 79 | async function getVideosByCategory (categoriesSample: number[], index: number, res: express.Response, acc: CategoryOverview[]) { |
80 | if (categoriesSample.length <= index) return | ||
81 | |||
82 | const category = categoriesSample[index] | ||
70 | const videos = await getVideos(res, { categoryOneOf: [ category ] }) | 83 | const videos = await getVideos(res, { categoryOneOf: [ category ] }) |
71 | 84 | ||
72 | if (videos.length === 0) return undefined | 85 | if (videos.length === 0) return |
73 | 86 | ||
74 | return { | 87 | acc.push({ |
75 | category: videos[0].category, | 88 | category: videos[0].category, |
76 | videos | 89 | videos |
77 | } | 90 | }) |
78 | } | 91 | } |
79 | 92 | ||
80 | async function getVideosByChannel (channelId: number, res: express.Response) { | 93 | async function getVideosByChannel (channelsSample: number[], index: number, res: express.Response, acc: ChannelOverview[]) { |
94 | if (channelsSample.length <= index) return | ||
95 | |||
96 | const channelId = channelsSample[index] | ||
81 | const videos = await getVideos(res, { videoChannelId: channelId }) | 97 | const videos = await getVideos(res, { videoChannelId: channelId }) |
82 | 98 | ||
83 | if (videos.length === 0) return undefined | 99 | if (videos.length === 0) return |
84 | 100 | ||
85 | return { | 101 | acc.push({ |
86 | channel: videos[0].channel, | 102 | channel: videos[0].channel, |
87 | videos | 103 | videos |
88 | } | 104 | }) |
89 | } | 105 | } |
90 | 106 | ||
91 | async function getVideos ( | 107 | async function getVideos ( |
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts index 6b7562fd3..f8a0d19ca 100644 --- a/server/controllers/api/plugins.ts +++ b/server/controllers/api/plugins.ts | |||
@@ -191,6 +191,8 @@ async function updatePluginSettings (req: express.Request, res: express.Response | |||
191 | plugin.settings = req.body.settings | 191 | plugin.settings = req.body.settings |
192 | await plugin.save() | 192 | await plugin.save() |
193 | 193 | ||
194 | await PluginManager.Instance.onSettingsChanged(plugin.name, plugin.settings) | ||
195 | |||
194 | return res.sendStatus(204) | 196 | return res.sendStatus(204) |
195 | } | 197 | } |
196 | 198 | ||
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index 349650aca..35d94d747 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 2 | import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
3 | import { getFormattedObjects, getServerActor } from '../../helpers/utils' | 3 | import { getFormattedObjects } from '../../helpers/utils' |
4 | import { VideoModel } from '../../models/video/video' | 4 | import { VideoModel } from '../../models/video/video' |
5 | import { | 5 | import { |
6 | asyncMiddleware, | 6 | asyncMiddleware, |
@@ -15,11 +15,13 @@ import { | |||
15 | videosSearchValidator | 15 | videosSearchValidator |
16 | } from '../../middlewares' | 16 | } from '../../middlewares' |
17 | import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search' | 17 | import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search' |
18 | import { getOrCreateActorAndServerAndModel, getOrCreateVideoAndAccountAndChannel } from '../../lib/activitypub' | 18 | import { getOrCreateActorAndServerAndModel } from '../../lib/activitypub/actor' |
19 | import { logger } from '../../helpers/logger' | 19 | import { logger } from '../../helpers/logger' |
20 | import { VideoChannelModel } from '../../models/video/video-channel' | 20 | import { VideoChannelModel } from '../../models/video/video-channel' |
21 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | 21 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' |
22 | import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models' | 22 | import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models' |
23 | import { getServerActor } from '@server/models/application/application' | ||
24 | import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos' | ||
23 | 25 | ||
24 | const searchRouter = express.Router() | 26 | const searchRouter = express.Router() |
25 | 27 | ||
@@ -60,7 +62,7 @@ function searchVideoChannels (req: express.Request, res: express.Response) { | |||
60 | 62 | ||
61 | // Handle strings like @toto@example.com | 63 | // Handle strings like @toto@example.com |
62 | if (parts.length === 3 && parts[0].length === 0) parts.shift() | 64 | if (parts.length === 3 && parts[0].length === 0) parts.shift() |
63 | const isWebfingerSearch = parts.length === 2 && parts.every(p => p && p.indexOf(' ') === -1) | 65 | const isWebfingerSearch = parts.length === 2 && parts.every(p => p && !p.includes(' ')) |
64 | 66 | ||
65 | if (isURISearch || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res) | 67 | if (isURISearch || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res) |
66 | 68 | ||
diff --git a/server/controllers/api/server/debug.ts b/server/controllers/api/server/debug.ts index 4450038f6..e12fc1dd4 100644 --- a/server/controllers/api/server/debug.ts +++ b/server/controllers/api/server/debug.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight } from '../../../../shared/models/users' | 2 | import { UserRight } from '../../../../shared/models/users' |
3 | import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares' | 3 | import { authenticate, ensureUserHasRight } from '../../../middlewares' |
4 | 4 | ||
5 | const debugRouter = express.Router() | 5 | const debugRouter = express.Router() |
6 | 6 | ||
7 | debugRouter.get('/debug', | 7 | debugRouter.get('/debug', |
8 | authenticate, | 8 | authenticate, |
9 | ensureUserHasRight(UserRight.MANAGE_DEBUG), | 9 | ensureUserHasRight(UserRight.MANAGE_DEBUG), |
10 | asyncMiddleware(getDebug) | 10 | getDebug |
11 | ) | 11 | ) |
12 | 12 | ||
13 | // --------------------------------------------------------------------------- | 13 | // --------------------------------------------------------------------------- |
@@ -18,7 +18,7 @@ export { | |||
18 | 18 | ||
19 | // --------------------------------------------------------------------------- | 19 | // --------------------------------------------------------------------------- |
20 | 20 | ||
21 | async function getDebug (req: express.Request, res: express.Response) { | 21 | function getDebug (req: express.Request, res: express.Response) { |
22 | return res.json({ | 22 | return res.json({ |
23 | ip: req.ip | 23 | ip: req.ip |
24 | }).end() | 24 | }).end() |
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index 29a403a43..23823c9fb 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight } from '../../../../shared/models/users' | 2 | import { UserRight } from '../../../../shared/models/users' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 4 | import { getFormattedObjects } from '../../../helpers/utils' |
5 | import { SERVER_ACTOR_NAME } from '../../../initializers/constants' | 5 | import { SERVER_ACTOR_NAME } from '../../../initializers/constants' |
6 | import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send' | 6 | import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send' |
7 | import { | 7 | import { |
@@ -24,9 +24,10 @@ import { | |||
24 | } from '../../../middlewares/validators' | 24 | } from '../../../middlewares/validators' |
25 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 25 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
26 | import { JobQueue } from '../../../lib/job-queue' | 26 | import { JobQueue } from '../../../lib/job-queue' |
27 | import { removeRedundancyOf } from '../../../lib/redundancy' | 27 | import { removeRedundanciesOfServer } from '../../../lib/redundancy' |
28 | import { sequelizeTypescript } from '../../../initializers/database' | 28 | import { sequelizeTypescript } from '../../../initializers/database' |
29 | import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' | 29 | import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' |
30 | import { getServerActor } from '@server/models/application/application' | ||
30 | 31 | ||
31 | const serverFollowsRouter = express.Router() | 32 | const serverFollowsRouter = express.Router() |
32 | serverFollowsRouter.get('/following', | 33 | serverFollowsRouter.get('/following', |
@@ -135,7 +136,6 @@ async function followInstance (req: express.Request, res: express.Response) { | |||
135 | } | 136 | } |
136 | 137 | ||
137 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | 138 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) |
138 | .catch(err => logger.error('Cannot create follow job for %s.', host, err)) | ||
139 | } | 139 | } |
140 | 140 | ||
141 | return res.status(204).end() | 141 | return res.status(204).end() |
@@ -153,7 +153,7 @@ async function removeFollowing (req: express.Request, res: express.Response) { | |||
153 | await server.save({ transaction: t }) | 153 | await server.save({ transaction: t }) |
154 | 154 | ||
155 | // Async, could be long | 155 | // Async, could be long |
156 | removeRedundancyOf(server.id) | 156 | removeRedundanciesOfServer(server.id) |
157 | .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, err)) | 157 | .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, err)) |
158 | 158 | ||
159 | await follow.destroy({ transaction: t }) | 159 | await follow.destroy({ transaction: t }) |
diff --git a/server/controllers/api/server/logs.ts b/server/controllers/api/server/logs.ts index cd1e0f5bf..4b543d686 100644 --- a/server/controllers/api/server/logs.ts +++ b/server/controllers/api/server/logs.ts | |||
@@ -59,9 +59,9 @@ async function getLogs (req: express.Request, res: express.Response) { | |||
59 | } | 59 | } |
60 | 60 | ||
61 | async function generateOutput (options: { | 61 | async function generateOutput (options: { |
62 | startDateQuery: string, | 62 | startDateQuery: string |
63 | endDateQuery?: string, | 63 | endDateQuery?: string |
64 | level: LogLevel, | 64 | level: LogLevel |
65 | nameFilter: RegExp | 65 | nameFilter: RegExp |
66 | }) { | 66 | }) { |
67 | const { startDateQuery, level, nameFilter } = options | 67 | const { startDateQuery, level, nameFilter } = options |
@@ -111,7 +111,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date, | |||
111 | const output: any[] = [] | 111 | const output: any[] = [] |
112 | 112 | ||
113 | for (let i = lines.length - 1; i >= 0; i--) { | 113 | for (let i = lines.length - 1; i >= 0; i--) { |
114 | const line = lines[ i ] | 114 | const line = lines[i] |
115 | let log: any | 115 | let log: any |
116 | 116 | ||
117 | try { | 117 | try { |
@@ -122,7 +122,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date, | |||
122 | } | 122 | } |
123 | 123 | ||
124 | logTime = new Date(log.timestamp).getTime() | 124 | logTime = new Date(log.timestamp).getTime() |
125 | if (logTime >= startTime && logTime <= endTime && logsLevel[ log.level ] >= logsLevel[ level ]) { | 125 | if (logTime >= startTime && logTime <= endTime && logsLevel[log.level] >= logsLevel[level]) { |
126 | output.push(log) | 126 | output.push(log) |
127 | 127 | ||
128 | currentSize += line.length | 128 | currentSize += line.length |
diff --git a/server/controllers/api/server/redundancy.ts b/server/controllers/api/server/redundancy.ts index 4ea6164a3..1ced0759e 100644 --- a/server/controllers/api/server/redundancy.ts +++ b/server/controllers/api/server/redundancy.ts | |||
@@ -1,9 +1,24 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight } from '../../../../shared/models/users' | 2 | import { UserRight } from '../../../../shared/models/users' |
3 | import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares' | 3 | import { |
4 | import { updateServerRedundancyValidator } from '../../../middlewares/validators/redundancy' | 4 | asyncMiddleware, |
5 | import { removeRedundancyOf } from '../../../lib/redundancy' | 5 | authenticate, |
6 | ensureUserHasRight, | ||
7 | paginationValidator, | ||
8 | setDefaultPagination, | ||
9 | setDefaultVideoRedundanciesSort, | ||
10 | videoRedundanciesSortValidator | ||
11 | } from '../../../middlewares' | ||
12 | import { | ||
13 | listVideoRedundanciesValidator, | ||
14 | updateServerRedundancyValidator, | ||
15 | addVideoRedundancyValidator, | ||
16 | removeVideoRedundancyValidator | ||
17 | } from '../../../middlewares/validators/redundancy' | ||
18 | import { removeRedundanciesOfServer, removeVideoRedundancy } from '../../../lib/redundancy' | ||
6 | import { logger } from '../../../helpers/logger' | 19 | import { logger } from '../../../helpers/logger' |
20 | import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy' | ||
21 | import { JobQueue } from '@server/lib/job-queue' | ||
7 | 22 | ||
8 | const serverRedundancyRouter = express.Router() | 23 | const serverRedundancyRouter = express.Router() |
9 | 24 | ||
@@ -14,6 +29,31 @@ serverRedundancyRouter.put('/redundancy/:host', | |||
14 | asyncMiddleware(updateRedundancy) | 29 | asyncMiddleware(updateRedundancy) |
15 | ) | 30 | ) |
16 | 31 | ||
32 | serverRedundancyRouter.get('/redundancy/videos', | ||
33 | authenticate, | ||
34 | ensureUserHasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES), | ||
35 | listVideoRedundanciesValidator, | ||
36 | paginationValidator, | ||
37 | videoRedundanciesSortValidator, | ||
38 | setDefaultVideoRedundanciesSort, | ||
39 | setDefaultPagination, | ||
40 | asyncMiddleware(listVideoRedundancies) | ||
41 | ) | ||
42 | |||
43 | serverRedundancyRouter.post('/redundancy/videos', | ||
44 | authenticate, | ||
45 | ensureUserHasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES), | ||
46 | addVideoRedundancyValidator, | ||
47 | asyncMiddleware(addVideoRedundancy) | ||
48 | ) | ||
49 | |||
50 | serverRedundancyRouter.delete('/redundancy/videos/:redundancyId', | ||
51 | authenticate, | ||
52 | ensureUserHasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES), | ||
53 | removeVideoRedundancyValidator, | ||
54 | asyncMiddleware(removeVideoRedundancyController) | ||
55 | ) | ||
56 | |||
17 | // --------------------------------------------------------------------------- | 57 | // --------------------------------------------------------------------------- |
18 | 58 | ||
19 | export { | 59 | export { |
@@ -22,6 +62,42 @@ export { | |||
22 | 62 | ||
23 | // --------------------------------------------------------------------------- | 63 | // --------------------------------------------------------------------------- |
24 | 64 | ||
65 | async function listVideoRedundancies (req: express.Request, res: express.Response) { | ||
66 | const resultList = await VideoRedundancyModel.listForApi({ | ||
67 | start: req.query.start, | ||
68 | count: req.query.count, | ||
69 | sort: req.query.sort, | ||
70 | target: req.query.target, | ||
71 | strategy: req.query.strategy | ||
72 | }) | ||
73 | |||
74 | const result = { | ||
75 | total: resultList.total, | ||
76 | data: resultList.data.map(r => VideoRedundancyModel.toFormattedJSONStatic(r)) | ||
77 | } | ||
78 | |||
79 | return res.json(result) | ||
80 | } | ||
81 | |||
82 | async function addVideoRedundancy (req: express.Request, res: express.Response) { | ||
83 | const payload = { | ||
84 | videoId: res.locals.onlyVideo.id | ||
85 | } | ||
86 | |||
87 | await JobQueue.Instance.createJobWithPromise({ | ||
88 | type: 'video-redundancy', | ||
89 | payload | ||
90 | }) | ||
91 | |||
92 | return res.sendStatus(204) | ||
93 | } | ||
94 | |||
95 | async function removeVideoRedundancyController (req: express.Request, res: express.Response) { | ||
96 | await removeVideoRedundancy(res.locals.videoRedundancy) | ||
97 | |||
98 | return res.sendStatus(204) | ||
99 | } | ||
100 | |||
25 | async function updateRedundancy (req: express.Request, res: express.Response) { | 101 | async function updateRedundancy (req: express.Request, res: express.Response) { |
26 | const server = res.locals.server | 102 | const server = res.locals.server |
27 | 103 | ||
@@ -30,7 +106,7 @@ async function updateRedundancy (req: express.Request, res: express.Response) { | |||
30 | await server.save() | 106 | await server.save() |
31 | 107 | ||
32 | // Async, could be long | 108 | // Async, could be long |
33 | removeRedundancyOf(server.id) | 109 | removeRedundanciesOfServer(server.id) |
34 | .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, { err })) | 110 | .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, { err })) |
35 | 111 | ||
36 | return res.sendStatus(204) | 112 | return res.sendStatus(204) |
diff --git a/server/controllers/api/server/server-blocklist.ts b/server/controllers/api/server/server-blocklist.ts index d165db191..f849b15c7 100644 --- a/server/controllers/api/server/server-blocklist.ts +++ b/server/controllers/api/server/server-blocklist.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import 'multer' | 2 | import 'multer' |
3 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 3 | import { getFormattedObjects } from '../../../helpers/utils' |
4 | import { | 4 | import { |
5 | asyncMiddleware, | 5 | asyncMiddleware, |
6 | asyncRetryTransactionMiddleware, | 6 | asyncRetryTransactionMiddleware, |
@@ -22,6 +22,7 @@ import { AccountBlocklistModel } from '../../../models/account/account-blocklist | |||
22 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' | 22 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' |
23 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' | 23 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' |
24 | import { UserRight } from '../../../../shared/models/users' | 24 | import { UserRight } from '../../../../shared/models/users' |
25 | import { getServerActor } from '@server/models/application/application' | ||
25 | 26 | ||
26 | const serverBlocklistRouter = express.Router() | 27 | const serverBlocklistRouter = express.Router() |
27 | 28 | ||
@@ -82,7 +83,13 @@ export { | |||
82 | async function listBlockedAccounts (req: express.Request, res: express.Response) { | 83 | async function listBlockedAccounts (req: express.Request, res: express.Response) { |
83 | const serverActor = await getServerActor() | 84 | const serverActor = await getServerActor() |
84 | 85 | ||
85 | const resultList = await AccountBlocklistModel.listForApi(serverActor.Account.id, req.query.start, req.query.count, req.query.sort) | 86 | const resultList = await AccountBlocklistModel.listForApi({ |
87 | start: req.query.start, | ||
88 | count: req.query.count, | ||
89 | sort: req.query.sort, | ||
90 | search: req.query.search, | ||
91 | accountId: serverActor.Account.id | ||
92 | }) | ||
86 | 93 | ||
87 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 94 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
88 | } | 95 | } |
@@ -107,7 +114,13 @@ async function unblockAccount (req: express.Request, res: express.Response) { | |||
107 | async function listBlockedServers (req: express.Request, res: express.Response) { | 114 | async function listBlockedServers (req: express.Request, res: express.Response) { |
108 | const serverActor = await getServerActor() | 115 | const serverActor = await getServerActor() |
109 | 116 | ||
110 | const resultList = await ServerBlocklistModel.listForApi(serverActor.Account.id, req.query.start, req.query.count, req.query.sort) | 117 | const resultList = await ServerBlocklistModel.listForApi({ |
118 | start: req.query.start, | ||
119 | count: req.query.count, | ||
120 | sort: req.query.sort, | ||
121 | search: req.query.search, | ||
122 | accountId: serverActor.Account.id | ||
123 | }) | ||
111 | 124 | ||
112 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 125 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
113 | } | 126 | } |
diff --git a/server/controllers/api/server/stats.ts b/server/controllers/api/server/stats.ts index 3616c074d..f07301a04 100644 --- a/server/controllers/api/server/stats.ts +++ b/server/controllers/api/server/stats.ts | |||
@@ -10,6 +10,7 @@ import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants' | |||
10 | import { cacheRoute } from '../../../middlewares/cache' | 10 | import { cacheRoute } from '../../../middlewares/cache' |
11 | import { VideoFileModel } from '../../../models/video/video-file' | 11 | import { VideoFileModel } from '../../../models/video/video-file' |
12 | import { CONFIG } from '../../../initializers/config' | 12 | import { CONFIG } from '../../../initializers/config' |
13 | import { VideoRedundancyStrategyWithManual } from '@shared/models' | ||
13 | 14 | ||
14 | const statsRouter = express.Router() | 15 | const statsRouter = express.Router() |
15 | 16 | ||
@@ -21,12 +22,20 @@ statsRouter.get('/stats', | |||
21 | async function getStats (req: express.Request, res: express.Response) { | 22 | async function getStats (req: express.Request, res: express.Response) { |
22 | const { totalLocalVideos, totalLocalVideoViews, totalVideos } = await VideoModel.getStats() | 23 | const { totalLocalVideos, totalLocalVideoViews, totalVideos } = await VideoModel.getStats() |
23 | const { totalLocalVideoComments, totalVideoComments } = await VideoCommentModel.getStats() | 24 | const { totalLocalVideoComments, totalVideoComments } = await VideoCommentModel.getStats() |
24 | const { totalUsers } = await UserModel.getStats() | 25 | const { totalUsers, totalDailyActiveUsers, totalWeeklyActiveUsers, totalMonthlyActiveUsers } = await UserModel.getStats() |
25 | const { totalInstanceFollowers, totalInstanceFollowing } = await ActorFollowModel.getStats() | 26 | const { totalInstanceFollowers, totalInstanceFollowing } = await ActorFollowModel.getStats() |
26 | const { totalLocalVideoFilesSize } = await VideoFileModel.getStats() | 27 | const { totalLocalVideoFilesSize } = await VideoFileModel.getStats() |
27 | 28 | ||
29 | const strategies = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES | ||
30 | .map(r => ({ | ||
31 | strategy: r.strategy as VideoRedundancyStrategyWithManual, | ||
32 | size: r.size | ||
33 | })) | ||
34 | |||
35 | strategies.push({ strategy: 'manual', size: null }) | ||
36 | |||
28 | const videosRedundancyStats = await Promise.all( | 37 | const videosRedundancyStats = await Promise.all( |
29 | CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.map(r => { | 38 | strategies.map(r => { |
30 | return VideoRedundancyModel.getStats(r.strategy) | 39 | return VideoRedundancyModel.getStats(r.strategy) |
31 | .then(stats => Object.assign(stats, { strategy: r.strategy, totalSize: r.size })) | 40 | .then(stats => Object.assign(stats, { strategy: r.strategy, totalSize: r.size })) |
32 | }) | 41 | }) |
@@ -39,9 +48,15 @@ async function getStats (req: express.Request, res: express.Response) { | |||
39 | totalLocalVideoComments, | 48 | totalLocalVideoComments, |
40 | totalVideos, | 49 | totalVideos, |
41 | totalVideoComments, | 50 | totalVideoComments, |
51 | |||
42 | totalUsers, | 52 | totalUsers, |
53 | totalDailyActiveUsers, | ||
54 | totalWeeklyActiveUsers, | ||
55 | totalMonthlyActiveUsers, | ||
56 | |||
43 | totalInstanceFollowers, | 57 | totalInstanceFollowers, |
44 | totalInstanceFollowing, | 58 | totalInstanceFollowing, |
59 | |||
45 | videosRedundancy: videosRedundancyStats | 60 | videosRedundancy: videosRedundancyStats |
46 | } | 61 | } |
47 | 62 | ||
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index b960e80c1..c488f720b 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -2,7 +2,7 @@ import * as express from 'express' | |||
2 | import * as RateLimit from 'express-rate-limit' | 2 | import * as RateLimit from 'express-rate-limit' |
3 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' | 3 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { getFormattedObjects } from '../../../helpers/utils' | 5 | import { generateRandomString, getFormattedObjects } from '../../../helpers/utils' |
6 | import { WEBSERVER } from '../../../initializers/constants' | 6 | import { WEBSERVER } from '../../../initializers/constants' |
7 | import { Emailer } from '../../../lib/emailer' | 7 | import { Emailer } from '../../../lib/emailer' |
8 | import { Redis } from '../../../lib/redis' | 8 | import { Redis } from '../../../lib/redis' |
@@ -17,7 +17,6 @@ import { | |||
17 | paginationValidator, | 17 | paginationValidator, |
18 | setDefaultPagination, | 18 | setDefaultPagination, |
19 | setDefaultSort, | 19 | setDefaultSort, |
20 | token, | ||
21 | userAutocompleteValidator, | 20 | userAutocompleteValidator, |
22 | usersAddValidator, | 21 | usersAddValidator, |
23 | usersGetValidator, | 22 | usersGetValidator, |
@@ -27,12 +26,12 @@ import { | |||
27 | usersUpdateValidator | 26 | usersUpdateValidator |
28 | } from '../../../middlewares' | 27 | } from '../../../middlewares' |
29 | import { | 28 | import { |
29 | ensureCanManageUser, | ||
30 | usersAskResetPasswordValidator, | 30 | usersAskResetPasswordValidator, |
31 | usersAskSendVerifyEmailValidator, | 31 | usersAskSendVerifyEmailValidator, |
32 | usersBlockingValidator, | 32 | usersBlockingValidator, |
33 | usersResetPasswordValidator, | 33 | usersResetPasswordValidator, |
34 | usersVerifyEmailValidator, | 34 | usersVerifyEmailValidator |
35 | ensureCanManageUser | ||
36 | } from '../../../middlewares/validators' | 35 | } from '../../../middlewares/validators' |
37 | import { UserModel } from '../../../models/account/user' | 36 | import { UserModel } from '../../../models/account/user' |
38 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' | 37 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' |
@@ -50,16 +49,10 @@ import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | |||
50 | import { UserRegister } from '../../../../shared/models/users/user-register.model' | 49 | import { UserRegister } from '../../../../shared/models/users/user-register.model' |
51 | import { MUser, MUserAccountDefault } from '@server/typings/models' | 50 | import { MUser, MUserAccountDefault } from '@server/typings/models' |
52 | import { Hooks } from '@server/lib/plugins/hooks' | 51 | import { Hooks } from '@server/lib/plugins/hooks' |
52 | import { tokensRouter } from '@server/controllers/api/users/token' | ||
53 | 53 | ||
54 | const auditLogger = auditLoggerFactory('users') | 54 | const auditLogger = auditLoggerFactory('users') |
55 | 55 | ||
56 | // FIXME: https://github.com/nfriedly/express-rate-limit/issues/138 | ||
57 | // @ts-ignore | ||
58 | const loginRateLimiter = RateLimit({ | ||
59 | windowMs: CONFIG.RATES_LIMIT.LOGIN.WINDOW_MS, | ||
60 | max: CONFIG.RATES_LIMIT.LOGIN.MAX | ||
61 | }) | ||
62 | |||
63 | // @ts-ignore | 56 | // @ts-ignore |
64 | const signupRateLimiter = RateLimit({ | 57 | const signupRateLimiter = RateLimit({ |
65 | windowMs: CONFIG.RATES_LIMIT.SIGNUP.WINDOW_MS, | 58 | windowMs: CONFIG.RATES_LIMIT.SIGNUP.WINDOW_MS, |
@@ -74,6 +67,7 @@ const askSendEmailLimiter = new RateLimit({ | |||
74 | }) | 67 | }) |
75 | 68 | ||
76 | const usersRouter = express.Router() | 69 | const usersRouter = express.Router() |
70 | usersRouter.use('/', tokensRouter) | ||
77 | usersRouter.use('/', myNotificationsRouter) | 71 | usersRouter.use('/', myNotificationsRouter) |
78 | usersRouter.use('/', mySubscriptionsRouter) | 72 | usersRouter.use('/', mySubscriptionsRouter) |
79 | usersRouter.use('/', myBlocklistRouter) | 73 | usersRouter.use('/', myBlocklistRouter) |
@@ -170,13 +164,6 @@ usersRouter.post('/:id/verify-email', | |||
170 | asyncMiddleware(verifyUserEmail) | 164 | asyncMiddleware(verifyUserEmail) |
171 | ) | 165 | ) |
172 | 166 | ||
173 | usersRouter.post('/token', | ||
174 | loginRateLimiter, | ||
175 | token, | ||
176 | tokenSuccess | ||
177 | ) | ||
178 | // TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route | ||
179 | |||
180 | // --------------------------------------------------------------------------- | 167 | // --------------------------------------------------------------------------- |
181 | 168 | ||
182 | export { | 169 | export { |
@@ -199,11 +186,25 @@ async function createUser (req: express.Request, res: express.Response) { | |||
199 | adminFlags: body.adminFlags || UserAdminFlag.NONE | 186 | adminFlags: body.adminFlags || UserAdminFlag.NONE |
200 | }) as MUser | 187 | }) as MUser |
201 | 188 | ||
189 | // NB: due to the validator usersAddValidator, password==='' can only be true if we can send the mail. | ||
190 | const createPassword = userToCreate.password === '' | ||
191 | if (createPassword) { | ||
192 | userToCreate.password = await generateRandomString(20) | ||
193 | } | ||
194 | |||
202 | const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) | 195 | const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) |
203 | 196 | ||
204 | auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) | 197 | auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) |
205 | logger.info('User %s with its channel and account created.', body.username) | 198 | logger.info('User %s with its channel and account created.', body.username) |
206 | 199 | ||
200 | if (createPassword) { | ||
201 | // this will send an email for newly created users, so then can set their first password. | ||
202 | logger.info('Sending to user %s a create password email', body.username) | ||
203 | const verificationString = await Redis.Instance.setCreatePasswordVerificationString(user.id) | ||
204 | const url = WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString | ||
205 | await Emailer.Instance.addPasswordCreateEmailJob(userToCreate.username, user.email, url) | ||
206 | } | ||
207 | |||
207 | Hooks.runAction('action:api.user.created', { body, user, account, videoChannel }) | 208 | Hooks.runAction('action:api.user.created', { body, user, account, videoChannel }) |
208 | 209 | ||
209 | return res.json({ | 210 | return res.json({ |
@@ -369,12 +370,6 @@ async function verifyUserEmail (req: express.Request, res: express.Response) { | |||
369 | return res.status(204).end() | 370 | return res.status(204).end() |
370 | } | 371 | } |
371 | 372 | ||
372 | function tokenSuccess (req: express.Request) { | ||
373 | const username = req.body.username | ||
374 | |||
375 | Hooks.runAction('action:api.user.oauth2-got-token', { username, ip: req.ip }) | ||
376 | } | ||
377 | |||
378 | async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) { | 373 | async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) { |
379 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) | 374 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) |
380 | 375 | ||
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index ac7c62aab..23890e20c 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -39,7 +39,7 @@ meRouter.get('/me', | |||
39 | ) | 39 | ) |
40 | meRouter.delete('/me', | 40 | meRouter.delete('/me', |
41 | authenticate, | 41 | authenticate, |
42 | asyncMiddleware(deleteMeValidator), | 42 | deleteMeValidator, |
43 | asyncMiddleware(deleteMe) | 43 | asyncMiddleware(deleteMe) |
44 | ) | 44 | ) |
45 | 45 | ||
@@ -214,7 +214,7 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
214 | } | 214 | } |
215 | 215 | ||
216 | async function updateMyAvatar (req: express.Request, res: express.Response) { | 216 | async function updateMyAvatar (req: express.Request, res: express.Response) { |
217 | const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] | 217 | const avatarPhysicalFile = req.files['avatarfile'][0] |
218 | const user = res.locals.oauth.token.user | 218 | const user = res.locals.oauth.token.user |
219 | 219 | ||
220 | const userAccount = await AccountModel.load(user.Account.id) | 220 | const userAccount = await AccountModel.load(user.Account.id) |
diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts index 713c16022..3a44376f2 100644 --- a/server/controllers/api/users/my-blocklist.ts +++ b/server/controllers/api/users/my-blocklist.ts | |||
@@ -74,7 +74,13 @@ export { | |||
74 | async function listBlockedAccounts (req: express.Request, res: express.Response) { | 74 | async function listBlockedAccounts (req: express.Request, res: express.Response) { |
75 | const user = res.locals.oauth.token.User | 75 | const user = res.locals.oauth.token.User |
76 | 76 | ||
77 | const resultList = await AccountBlocklistModel.listForApi(user.Account.id, req.query.start, req.query.count, req.query.sort) | 77 | const resultList = await AccountBlocklistModel.listForApi({ |
78 | start: req.query.start, | ||
79 | count: req.query.count, | ||
80 | sort: req.query.sort, | ||
81 | search: req.query.search, | ||
82 | accountId: user.Account.id | ||
83 | }) | ||
78 | 84 | ||
79 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 85 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
80 | } | 86 | } |
@@ -99,7 +105,13 @@ async function unblockAccount (req: express.Request, res: express.Response) { | |||
99 | async function listBlockedServers (req: express.Request, res: express.Response) { | 105 | async function listBlockedServers (req: express.Request, res: express.Response) { |
100 | const user = res.locals.oauth.token.User | 106 | const user = res.locals.oauth.token.User |
101 | 107 | ||
102 | const resultList = await ServerBlocklistModel.listForApi(user.Account.id, req.query.start, req.query.count, req.query.sort) | 108 | const resultList = await ServerBlocklistModel.listForApi({ |
109 | start: req.query.start, | ||
110 | count: req.query.count, | ||
111 | sort: req.query.sort, | ||
112 | search: req.query.search, | ||
113 | accountId: user.Account.id | ||
114 | }) | ||
103 | 115 | ||
104 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 116 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
105 | } | 117 | } |
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts index 4da1f3496..77a15e5fc 100644 --- a/server/controllers/api/users/my-history.ts +++ b/server/controllers/api/users/my-history.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | } from '../../../middlewares' | 9 | } from '../../../middlewares' |
10 | import { getFormattedObjects } from '../../../helpers/utils' | 10 | import { getFormattedObjects } from '../../../helpers/utils' |
11 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 11 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' |
12 | import { sequelizeTypescript } from '../../../initializers' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
13 | 13 | ||
14 | const myVideosHistoryRouter = express.Router() | 14 | const myVideosHistoryRouter = express.Router() |
15 | 15 | ||
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts index 43c4c37d8..efe1b9bc3 100644 --- a/server/controllers/api/users/my-subscriptions.ts +++ b/server/controllers/api/users/my-subscriptions.ts | |||
@@ -19,7 +19,6 @@ import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' | |||
19 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | 19 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' |
20 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 20 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
21 | import { JobQueue } from '../../../lib/job-queue' | 21 | import { JobQueue } from '../../../lib/job-queue' |
22 | import { logger } from '../../../helpers/logger' | ||
23 | import { sequelizeTypescript } from '../../../initializers/database' | 22 | import { sequelizeTypescript } from '../../../initializers/database' |
24 | 23 | ||
25 | const mySubscriptionsRouter = express.Router() | 24 | const mySubscriptionsRouter = express.Router() |
@@ -52,7 +51,7 @@ mySubscriptionsRouter.get('/me/subscriptions', | |||
52 | mySubscriptionsRouter.post('/me/subscriptions', | 51 | mySubscriptionsRouter.post('/me/subscriptions', |
53 | authenticate, | 52 | authenticate, |
54 | userSubscriptionAddValidator, | 53 | userSubscriptionAddValidator, |
55 | asyncMiddleware(addUserSubscription) | 54 | addUserSubscription |
56 | ) | 55 | ) |
57 | 56 | ||
58 | mySubscriptionsRouter.get('/me/subscriptions/:uri', | 57 | mySubscriptionsRouter.get('/me/subscriptions/:uri', |
@@ -106,18 +105,18 @@ async function areSubscriptionsExist (req: express.Request, res: express.Respons | |||
106 | return res.json(existObject) | 105 | return res.json(existObject) |
107 | } | 106 | } |
108 | 107 | ||
109 | async function addUserSubscription (req: express.Request, res: express.Response) { | 108 | function addUserSubscription (req: express.Request, res: express.Response) { |
110 | const user = res.locals.oauth.token.User | 109 | const user = res.locals.oauth.token.User |
111 | const [ name, host ] = req.body.uri.split('@') | 110 | const [ name, host ] = req.body.uri.split('@') |
112 | 111 | ||
113 | const payload = { | 112 | const payload = { |
114 | name, | 113 | name, |
115 | host, | 114 | host, |
115 | assertIsChannel: true, | ||
116 | followerActorId: user.Account.Actor.id | 116 | followerActorId: user.Account.Actor.id |
117 | } | 117 | } |
118 | 118 | ||
119 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | 119 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) |
120 | .catch(err => logger.error('Cannot create follow job for subscription %s.', req.body.uri, err)) | ||
121 | 120 | ||
122 | return res.status(204).end() | 121 | return res.status(204).end() |
123 | } | 122 | } |
diff --git a/server/controllers/api/users/token.ts b/server/controllers/api/users/token.ts new file mode 100644 index 000000000..41aa26769 --- /dev/null +++ b/server/controllers/api/users/token.ts | |||
@@ -0,0 +1,37 @@ | |||
1 | import { handleLogin, handleTokenRevocation } from '@server/lib/auth' | ||
2 | import * as RateLimit from 'express-rate-limit' | ||
3 | import { CONFIG } from '@server/initializers/config' | ||
4 | import * as express from 'express' | ||
5 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | import { asyncMiddleware, authenticate } from '@server/middlewares' | ||
7 | |||
8 | const tokensRouter = express.Router() | ||
9 | |||
10 | const loginRateLimiter = RateLimit({ | ||
11 | windowMs: CONFIG.RATES_LIMIT.LOGIN.WINDOW_MS, | ||
12 | max: CONFIG.RATES_LIMIT.LOGIN.MAX | ||
13 | }) | ||
14 | |||
15 | tokensRouter.post('/token', | ||
16 | loginRateLimiter, | ||
17 | handleLogin, | ||
18 | tokenSuccess | ||
19 | ) | ||
20 | |||
21 | tokensRouter.post('/revoke-token', | ||
22 | authenticate, | ||
23 | asyncMiddleware(handleTokenRevocation) | ||
24 | ) | ||
25 | |||
26 | // --------------------------------------------------------------------------- | ||
27 | |||
28 | export { | ||
29 | tokensRouter | ||
30 | } | ||
31 | // --------------------------------------------------------------------------- | ||
32 | |||
33 | function tokenSuccess (req: express.Request) { | ||
34 | const username = req.body.username | ||
35 | |||
36 | Hooks.runAction('action:api.user.oauth2-got-token', { username, ip: req.ip }) | ||
37 | } | ||
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index e1f37a8fb..d779f1aab 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects, getServerActor } from '../../helpers/utils' | 2 | import { getFormattedObjects } from '../../helpers/utils' |
3 | import { | 3 | import { |
4 | asyncMiddleware, | 4 | asyncMiddleware, |
5 | asyncRetryTransactionMiddleware, | 5 | asyncRetryTransactionMiddleware, |
@@ -21,7 +21,7 @@ import { sendUpdateActor } from '../../lib/activitypub/send' | |||
21 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | 21 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' |
22 | import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' | 22 | import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' |
23 | import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 23 | import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
24 | import { setAsyncActorKeys } from '../../lib/activitypub' | 24 | import { setAsyncActorKeys } from '../../lib/activitypub/actor' |
25 | import { AccountModel } from '../../models/account/account' | 25 | import { AccountModel } from '../../models/account/account' |
26 | import { MIMETYPES } from '../../initializers/constants' | 26 | import { MIMETYPES } from '../../initializers/constants' |
27 | import { logger } from '../../helpers/logger' | 27 | import { logger } from '../../helpers/logger' |
@@ -36,6 +36,7 @@ import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validator | |||
36 | import { CONFIG } from '../../initializers/config' | 36 | import { CONFIG } from '../../initializers/config' |
37 | import { sequelizeTypescript } from '../../initializers/database' | 37 | import { sequelizeTypescript } from '../../initializers/database' |
38 | import { MChannelAccountDefault } from '@server/typings/models' | 38 | import { MChannelAccountDefault } from '@server/typings/models' |
39 | import { getServerActor } from '@server/models/application/application' | ||
39 | 40 | ||
40 | const auditLogger = auditLoggerFactory('channels') | 41 | const auditLogger = auditLoggerFactory('channels') |
41 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) | 42 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) |
@@ -119,7 +120,7 @@ async function listVideoChannels (req: express.Request, res: express.Response) { | |||
119 | } | 120 | } |
120 | 121 | ||
121 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { | 122 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { |
122 | const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] | 123 | const avatarPhysicalFile = req.files['avatarfile'][0] |
123 | const videoChannel = res.locals.videoChannel | 124 | const videoChannel = res.locals.videoChannel |
124 | const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) | 125 | const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) |
125 | 126 | ||
@@ -232,7 +233,6 @@ async function getVideoChannel (req: express.Request, res: express.Response) { | |||
232 | 233 | ||
233 | if (videoChannelWithVideos.isOutdated()) { | 234 | if (videoChannelWithVideos.isOutdated()) { |
234 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } }) | 235 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } }) |
235 | .catch(err => logger.error('Cannot create AP refresher job for actor %s.', videoChannelWithVideos.Actor.url, { err })) | ||
236 | } | 236 | } |
237 | 237 | ||
238 | return res.json(videoChannelWithVideos.toFormattedJSON()) | 238 | return res.json(videoChannelWithVideos.toFormattedJSON()) |
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index d9f0ff925..375d711fd 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects, getServerActor } from '../../helpers/utils' | 2 | import { getFormattedObjects } from '../../helpers/utils' |
3 | import { | 3 | import { |
4 | asyncMiddleware, | 4 | asyncMiddleware, |
5 | asyncRetryTransactionMiddleware, | 5 | asyncRetryTransactionMiddleware, |
@@ -41,6 +41,7 @@ import { CONFIG } from '../../initializers/config' | |||
41 | import { sequelizeTypescript } from '../../initializers/database' | 41 | import { sequelizeTypescript } from '../../initializers/database' |
42 | import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail' | 42 | import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail' |
43 | import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/typings/models' | 43 | import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/typings/models' |
44 | import { getServerActor } from '@server/models/application/application' | ||
44 | 45 | ||
45 | const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR }) | 46 | const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR }) |
46 | 47 | ||
@@ -144,7 +145,6 @@ function getVideoPlaylist (req: express.Request, res: express.Response) { | |||
144 | 145 | ||
145 | if (videoPlaylist.isOutdated()) { | 146 | if (videoPlaylist.isOutdated()) { |
146 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } }) | 147 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } }) |
147 | .catch(err => logger.error('Cannot create AP refresher job for playlist %s.', videoPlaylist.url, { err })) | ||
148 | } | 148 | } |
149 | 149 | ||
150 | return res.json(videoPlaylist.toFormattedJSON()) | 150 | return res.json(videoPlaylist.toFormattedJSON()) |
@@ -464,7 +464,13 @@ async function regeneratePlaylistThumbnail (videoPlaylist: MVideoPlaylistThumbna | |||
464 | async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbnail, video: MVideoThumbnail) { | 464 | async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbnail, video: MVideoThumbnail) { |
465 | logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url) | 465 | logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url) |
466 | 466 | ||
467 | const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename) | 467 | const videoMiniature = video.getMiniature() |
468 | if (!videoMiniature) { | ||
469 | logger.info('Cannot generate thumbnail for playlist %s because video %s does not have any.', videoPlaylist.url, video.url) | ||
470 | return | ||
471 | } | ||
472 | |||
473 | const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, videoMiniature.filename) | ||
468 | const thumbnailModel = await createPlaylistMiniatureFromExisting(inputPath, videoPlaylist, true, true) | 474 | const thumbnailModel = await createPlaylistMiniatureFromExisting(inputPath, videoPlaylist, true, true) |
469 | 475 | ||
470 | thumbnailModel.videoPlaylistId = videoPlaylist.id | 476 | thumbnailModel.videoPlaylistId = videoPlaylist.id |
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts index 4ae899b7e..2af7b3864 100644 --- a/server/controllers/api/videos/abuse.ts +++ b/server/controllers/api/videos/abuse.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared' | 2 | import { UserRight, VideoAbuseCreate, VideoAbuseState, VideoAbuse } from '../../../../shared' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 4 | import { getFormattedObjects } from '../../../helpers/utils' |
5 | import { sequelizeTypescript } from '../../../initializers' | 5 | import { sequelizeTypescript } from '../../../initializers/database' |
6 | import { | 6 | import { |
7 | asyncMiddleware, | 7 | asyncMiddleware, |
8 | asyncRetryTransactionMiddleware, | 8 | asyncRetryTransactionMiddleware, |
@@ -14,7 +14,8 @@ import { | |||
14 | videoAbuseGetValidator, | 14 | videoAbuseGetValidator, |
15 | videoAbuseReportValidator, | 15 | videoAbuseReportValidator, |
16 | videoAbusesSortValidator, | 16 | videoAbusesSortValidator, |
17 | videoAbuseUpdateValidator | 17 | videoAbuseUpdateValidator, |
18 | videoAbuseListValidator | ||
18 | } from '../../../middlewares' | 19 | } from '../../../middlewares' |
19 | import { AccountModel } from '../../../models/account/account' | 20 | import { AccountModel } from '../../../models/account/account' |
20 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | 21 | import { VideoAbuseModel } from '../../../models/video/video-abuse' |
@@ -22,6 +23,8 @@ import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit- | |||
22 | import { Notifier } from '../../../lib/notifier' | 23 | import { Notifier } from '../../../lib/notifier' |
23 | import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag' | 24 | import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag' |
24 | import { MVideoAbuseAccountVideo } from '../../../typings/models/video' | 25 | import { MVideoAbuseAccountVideo } from '../../../typings/models/video' |
26 | import { getServerActor } from '@server/models/application/application' | ||
27 | import { MAccountDefault } from '@server/typings/models' | ||
25 | 28 | ||
26 | const auditLogger = auditLoggerFactory('abuse') | 29 | const auditLogger = auditLoggerFactory('abuse') |
27 | const abuseVideoRouter = express.Router() | 30 | const abuseVideoRouter = express.Router() |
@@ -33,6 +36,7 @@ abuseVideoRouter.get('/abuse', | |||
33 | videoAbusesSortValidator, | 36 | videoAbusesSortValidator, |
34 | setDefaultSort, | 37 | setDefaultSort, |
35 | setDefaultPagination, | 38 | setDefaultPagination, |
39 | videoAbuseListValidator, | ||
36 | asyncMiddleware(listVideoAbuses) | 40 | asyncMiddleware(listVideoAbuses) |
37 | ) | 41 | ) |
38 | abuseVideoRouter.put('/:videoId/abuse/:id', | 42 | abuseVideoRouter.put('/:videoId/abuse/:id', |
@@ -69,6 +73,14 @@ async function listVideoAbuses (req: express.Request, res: express.Response) { | |||
69 | start: req.query.start, | 73 | start: req.query.start, |
70 | count: req.query.count, | 74 | count: req.query.count, |
71 | sort: req.query.sort, | 75 | sort: req.query.sort, |
76 | id: req.query.id, | ||
77 | search: req.query.search, | ||
78 | state: req.query.state, | ||
79 | videoIs: req.query.videoIs, | ||
80 | searchReporter: req.query.searchReporter, | ||
81 | searchReportee: req.query.searchReportee, | ||
82 | searchVideo: req.query.searchVideo, | ||
83 | searchVideoChannel: req.query.searchVideoChannel, | ||
72 | serverAccountId: serverActor.Account.id, | 84 | serverAccountId: serverActor.Account.id, |
73 | user | 85 | user |
74 | }) | 86 | }) |
@@ -106,9 +118,11 @@ async function deleteVideoAbuse (req: express.Request, res: express.Response) { | |||
106 | async function reportVideoAbuse (req: express.Request, res: express.Response) { | 118 | async function reportVideoAbuse (req: express.Request, res: express.Response) { |
107 | const videoInstance = res.locals.videoAll | 119 | const videoInstance = res.locals.videoAll |
108 | const body: VideoAbuseCreate = req.body | 120 | const body: VideoAbuseCreate = req.body |
121 | let reporterAccount: MAccountDefault | ||
122 | let videoAbuseJSON: VideoAbuse | ||
109 | 123 | ||
110 | const videoAbuse = await sequelizeTypescript.transaction(async t => { | 124 | const videoAbuseInstance = await sequelizeTypescript.transaction(async t => { |
111 | const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) | 125 | reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) |
112 | 126 | ||
113 | const abuseToCreate = { | 127 | const abuseToCreate = { |
114 | reporterAccountId: reporterAccount.id, | 128 | reporterAccountId: reporterAccount.id, |
@@ -126,14 +140,19 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) { | |||
126 | await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance, t) | 140 | await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance, t) |
127 | } | 141 | } |
128 | 142 | ||
129 | auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseInstance.toFormattedJSON())) | 143 | videoAbuseJSON = videoAbuseInstance.toFormattedJSON() |
144 | auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseJSON)) | ||
130 | 145 | ||
131 | return videoAbuseInstance | 146 | return videoAbuseInstance |
132 | }) | 147 | }) |
133 | 148 | ||
134 | Notifier.Instance.notifyOnNewVideoAbuse(videoAbuse) | 149 | Notifier.Instance.notifyOnNewVideoAbuse({ |
150 | videoAbuse: videoAbuseJSON, | ||
151 | videoAbuseInstance, | ||
152 | reporter: reporterAccount.Actor.getIdentifier() | ||
153 | }) | ||
135 | 154 | ||
136 | logger.info('Abuse report for video %s created.', videoInstance.name) | 155 | logger.info('Abuse report for video %s created.', videoInstance.name) |
137 | 156 | ||
138 | return res.json({ videoAbuse: videoAbuse.toFormattedJSON() }).end() | 157 | return res.json({ videoAbuse: videoAbuseJSON }).end() |
139 | } | 158 | } |
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts index 2a667480d..3b25ceea2 100644 --- a/server/controllers/api/videos/blacklist.ts +++ b/server/controllers/api/videos/blacklist.ts | |||
@@ -1,7 +1,9 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared' | 2 | import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist' |
3 | import { UserRight, VideoBlacklistCreate } from '../../../../shared' | ||
3 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects } from '../../../helpers/utils' | 5 | import { getFormattedObjects } from '../../../helpers/utils' |
6 | import { sequelizeTypescript } from '../../../initializers/database' | ||
5 | import { | 7 | import { |
6 | asyncMiddleware, | 8 | asyncMiddleware, |
7 | authenticate, | 9 | authenticate, |
@@ -16,11 +18,6 @@ import { | |||
16 | videosBlacklistUpdateValidator | 18 | videosBlacklistUpdateValidator |
17 | } from '../../../middlewares' | 19 | } from '../../../middlewares' |
18 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | 20 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' |
19 | import { sequelizeTypescript } from '../../../initializers' | ||
20 | import { Notifier } from '../../../lib/notifier' | ||
21 | import { sendDeleteVideo } from '../../../lib/activitypub/send' | ||
22 | import { federateVideoIfNeeded } from '../../../lib/activitypub' | ||
23 | import { MVideoBlacklistVideo } from '@server/typings/models' | ||
24 | 21 | ||
25 | const blacklistRouter = express.Router() | 22 | const blacklistRouter = express.Router() |
26 | 23 | ||
@@ -28,7 +25,7 @@ blacklistRouter.post('/:videoId/blacklist', | |||
28 | authenticate, | 25 | authenticate, |
29 | ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), | 26 | ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), |
30 | asyncMiddleware(videosBlacklistAddValidator), | 27 | asyncMiddleware(videosBlacklistAddValidator), |
31 | asyncMiddleware(addVideoToBlacklist) | 28 | asyncMiddleware(addVideoToBlacklistController) |
32 | ) | 29 | ) |
33 | 30 | ||
34 | blacklistRouter.get('/blacklist', | 31 | blacklistRouter.get('/blacklist', |
@@ -64,29 +61,15 @@ export { | |||
64 | 61 | ||
65 | // --------------------------------------------------------------------------- | 62 | // --------------------------------------------------------------------------- |
66 | 63 | ||
67 | async function addVideoToBlacklist (req: express.Request, res: express.Response) { | 64 | async function addVideoToBlacklistController (req: express.Request, res: express.Response) { |
68 | const videoInstance = res.locals.videoAll | 65 | const videoInstance = res.locals.videoAll |
69 | const body: VideoBlacklistCreate = req.body | 66 | const body: VideoBlacklistCreate = req.body |
70 | 67 | ||
71 | const toCreate = { | 68 | await blacklistVideo(videoInstance, body) |
72 | videoId: videoInstance.id, | ||
73 | unfederated: body.unfederate === true, | ||
74 | reason: body.reason, | ||
75 | type: VideoBlacklistType.MANUAL | ||
76 | } | ||
77 | |||
78 | const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate) | ||
79 | blacklist.Video = videoInstance | ||
80 | |||
81 | if (body.unfederate === true) { | ||
82 | await sendDeleteVideo(videoInstance, undefined) | ||
83 | } | ||
84 | |||
85 | Notifier.Instance.notifyOnVideoBlacklist(blacklist) | ||
86 | 69 | ||
87 | logger.info('Video %s blacklisted.', videoInstance.uuid) | 70 | logger.info('Video %s blacklisted.', videoInstance.uuid) |
88 | 71 | ||
89 | return res.type('json').status(204).end() | 72 | return res.type('json').sendStatus(204) |
90 | } | 73 | } |
91 | 74 | ||
92 | async function updateVideoBlacklistController (req: express.Request, res: express.Response) { | 75 | async function updateVideoBlacklistController (req: express.Request, res: express.Response) { |
@@ -98,11 +81,17 @@ async function updateVideoBlacklistController (req: express.Request, res: expres | |||
98 | return videoBlacklist.save({ transaction: t }) | 81 | return videoBlacklist.save({ transaction: t }) |
99 | }) | 82 | }) |
100 | 83 | ||
101 | return res.type('json').status(204).end() | 84 | return res.type('json').sendStatus(204) |
102 | } | 85 | } |
103 | 86 | ||
104 | async function listBlacklist (req: express.Request, res: express.Response) { | 87 | async function listBlacklist (req: express.Request, res: express.Response) { |
105 | const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.type) | 88 | const resultList = await VideoBlacklistModel.listForApi({ |
89 | start: req.query.start, | ||
90 | count: req.query.count, | ||
91 | sort: req.query.sort, | ||
92 | search: req.query.search, | ||
93 | type: req.query.type | ||
94 | }) | ||
106 | 95 | ||
107 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 96 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
108 | } | 97 | } |
@@ -111,32 +100,9 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex | |||
111 | const videoBlacklist = res.locals.videoBlacklist | 100 | const videoBlacklist = res.locals.videoBlacklist |
112 | const video = res.locals.videoAll | 101 | const video = res.locals.videoAll |
113 | 102 | ||
114 | const videoBlacklistType = await sequelizeTypescript.transaction(async t => { | 103 | await unblacklistVideo(videoBlacklist, video) |
115 | const unfederated = videoBlacklist.unfederated | ||
116 | const videoBlacklistType = videoBlacklist.type | ||
117 | |||
118 | await videoBlacklist.destroy({ transaction: t }) | ||
119 | video.VideoBlacklist = undefined | ||
120 | |||
121 | // Re federate the video | ||
122 | if (unfederated === true) { | ||
123 | await federateVideoIfNeeded(video, true, t) | ||
124 | } | ||
125 | |||
126 | return videoBlacklistType | ||
127 | }) | ||
128 | |||
129 | Notifier.Instance.notifyOnVideoUnblacklist(video) | ||
130 | |||
131 | if (videoBlacklistType === VideoBlacklistType.AUTO_BEFORE_PUBLISHED) { | ||
132 | Notifier.Instance.notifyOnVideoPublishedAfterRemovedFromAutoBlacklist(video) | ||
133 | |||
134 | // Delete on object so new video notifications will send | ||
135 | delete video.VideoBlacklist | ||
136 | Notifier.Instance.notifyOnNewVideoIfNeeded(video) | ||
137 | } | ||
138 | 104 | ||
139 | logger.info('Video %s removed from blacklist.', video.uuid) | 105 | logger.info('Video %s removed from blacklist.', video.uuid) |
140 | 106 | ||
141 | return res.type('json').status(204).end() | 107 | return res.type('json').sendStatus(204) |
142 | } | 108 | } |
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts index 37481d12f..8c1d12ca8 100644 --- a/server/controllers/api/videos/captions.ts +++ b/server/controllers/api/videos/captions.ts | |||
@@ -6,7 +6,7 @@ import { MIMETYPES } from '../../../initializers/constants' | |||
6 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
7 | import { VideoCaptionModel } from '../../../models/video/video-caption' | 7 | import { VideoCaptionModel } from '../../../models/video/video-caption' |
8 | import { logger } from '../../../helpers/logger' | 8 | import { logger } from '../../../helpers/logger' |
9 | import { federateVideoIfNeeded } from '../../../lib/activitypub' | 9 | import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' |
10 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | 10 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' |
11 | import { CONFIG } from '../../../initializers/config' | 11 | import { CONFIG } from '../../../initializers/config' |
12 | import { sequelizeTypescript } from '../../../initializers/database' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
@@ -66,7 +66,7 @@ async function addVideoCaption (req: express.Request, res: express.Response) { | |||
66 | await moveAndProcessCaptionFile(videoCaptionPhysicalFile, videoCaption) | 66 | await moveAndProcessCaptionFile(videoCaptionPhysicalFile, videoCaption) |
67 | 67 | ||
68 | await sequelizeTypescript.transaction(async t => { | 68 | await sequelizeTypescript.transaction(async t => { |
69 | await VideoCaptionModel.insertOrReplaceLanguage(video.id, req.params.captionLanguage, t) | 69 | await VideoCaptionModel.insertOrReplaceLanguage(video.id, req.params.captionLanguage, null, t) |
70 | 70 | ||
71 | // Update video update | 71 | // Update video update |
72 | await federateVideoIfNeeded(video, false, t) | 72 | await federateVideoIfNeeded(video, false, t) |
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index 5f3fed5c0..5070bb3c0 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts | |||
@@ -4,7 +4,7 @@ import { ResultList } from '../../../../shared/models' | |||
4 | import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model' | 4 | import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
7 | import { sequelizeTypescript } from '../../../initializers' | 7 | import { sequelizeTypescript } from '../../../initializers/database' |
8 | import { buildFormattedCommentTree, createVideoComment, markCommentAsDeleted } from '../../../lib/video-comment' | 8 | import { buildFormattedCommentTree, createVideoComment, markCommentAsDeleted } from '../../../lib/video-comment' |
9 | import { | 9 | import { |
10 | asyncMiddleware, | 10 | asyncMiddleware, |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 28ced5836..b4f70a086 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -3,12 +3,14 @@ import * as magnetUtil from 'magnet-uri' | |||
3 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' | 3 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' |
4 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' | 4 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' |
5 | import { MIMETYPES } from '../../../initializers/constants' | 5 | import { MIMETYPES } from '../../../initializers/constants' |
6 | import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl' | 6 | import { getYoutubeDLInfo, YoutubeDLInfo, getYoutubeDLSubs } from '../../../helpers/youtube-dl' |
7 | import { createReqFiles } from '../../../helpers/express-utils' | 7 | import { createReqFiles } from '../../../helpers/express-utils' |
8 | import { logger } from '../../../helpers/logger' | 8 | import { logger } from '../../../helpers/logger' |
9 | import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' | 9 | import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' |
10 | import { VideoModel } from '../../../models/video/video' | 10 | import { VideoModel } from '../../../models/video/video' |
11 | import { getVideoActivityPubUrl } from '../../../lib/activitypub' | 11 | import { VideoCaptionModel } from '../../../models/video/video-caption' |
12 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | ||
13 | import { getVideoActivityPubUrl } from '../../../lib/activitypub/url' | ||
12 | import { TagModel } from '../../../models/video/tag' | 14 | import { TagModel } from '../../../models/video/tag' |
13 | import { VideoImportModel } from '../../../models/video/video-import' | 15 | import { VideoImportModel } from '../../../models/video/video-import' |
14 | import { JobQueue } from '../../../lib/job-queue/job-queue' | 16 | import { JobQueue } from '../../../lib/job-queue/job-queue' |
@@ -21,13 +23,14 @@ import { move, readFile } from 'fs-extra' | |||
21 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | 23 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' |
22 | import { CONFIG } from '../../../initializers/config' | 24 | import { CONFIG } from '../../../initializers/config' |
23 | import { sequelizeTypescript } from '../../../initializers/database' | 25 | import { sequelizeTypescript } from '../../../initializers/database' |
24 | import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail' | 26 | import { createVideoMiniatureFromExisting, createVideoMiniatureFromUrl } from '../../../lib/thumbnail' |
25 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 27 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
26 | import { | 28 | import { |
27 | MChannelAccountDefault, | 29 | MChannelAccountDefault, |
28 | MThumbnail, | 30 | MThumbnail, |
29 | MUser, | 31 | MUser, |
30 | MVideoAccountDefault, | 32 | MVideoAccountDefault, |
33 | MVideoCaptionVideo, | ||
31 | MVideoTag, | 34 | MVideoTag, |
32 | MVideoThumbnailAccountDefault, | 35 | MVideoThumbnailAccountDefault, |
33 | MVideoWithBlacklistLight | 36 | MVideoWithBlacklistLight |
@@ -88,12 +91,12 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
88 | const buf = await readFile(torrentfile.path) | 91 | const buf = await readFile(torrentfile.path) |
89 | const parsedTorrent = parseTorrent(buf) | 92 | const parsedTorrent = parseTorrent(buf) |
90 | 93 | ||
91 | videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[ 0 ] : parsedTorrent.name as string | 94 | videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[0] : parsedTorrent.name as string |
92 | } else { | 95 | } else { |
93 | magnetUri = body.magnetUri | 96 | magnetUri = body.magnetUri |
94 | 97 | ||
95 | const parsed = magnetUtil.decode(magnetUri) | 98 | const parsed = magnetUtil.decode(magnetUri) |
96 | videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string | 99 | videoName = isArray(parsed.name) ? parsed.name[0] : parsed.name as string |
97 | } | 100 | } |
98 | 101 | ||
99 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) | 102 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) |
@@ -124,7 +127,7 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
124 | videoImportId: videoImport.id, | 127 | videoImportId: videoImport.id, |
125 | magnetUri | 128 | magnetUri |
126 | } | 129 | } |
127 | await JobQueue.Instance.createJob({ type: 'video-import', payload }) | 130 | await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload }) |
128 | 131 | ||
129 | auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) | 132 | auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) |
130 | 133 | ||
@@ -136,6 +139,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
136 | const targetUrl = body.targetUrl | 139 | const targetUrl = body.targetUrl |
137 | const user = res.locals.oauth.token.User | 140 | const user = res.locals.oauth.token.User |
138 | 141 | ||
142 | // Get video infos | ||
139 | let youtubeDLInfo: YoutubeDLInfo | 143 | let youtubeDLInfo: YoutubeDLInfo |
140 | try { | 144 | try { |
141 | youtubeDLInfo = await getYoutubeDLInfo(targetUrl) | 145 | youtubeDLInfo = await getYoutubeDLInfo(targetUrl) |
@@ -149,8 +153,25 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
149 | 153 | ||
150 | const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo) | 154 | const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo) |
151 | 155 | ||
152 | const thumbnailModel = await processThumbnail(req, video) | 156 | let thumbnailModel: MThumbnail |
153 | const previewModel = await processPreview(req, video) | 157 | |
158 | // Process video thumbnail from request.files | ||
159 | thumbnailModel = await processThumbnail(req, video) | ||
160 | |||
161 | // Process video thumbnail from url if processing from request.files failed | ||
162 | if (!thumbnailModel && youtubeDLInfo.thumbnailUrl) { | ||
163 | thumbnailModel = await processThumbnailFromUrl(youtubeDLInfo.thumbnailUrl, video) | ||
164 | } | ||
165 | |||
166 | let previewModel: MThumbnail | ||
167 | |||
168 | // Process video preview from request.files | ||
169 | previewModel = await processPreview(req, video) | ||
170 | |||
171 | // Process video preview from url if processing from request.files failed | ||
172 | if (!previewModel && youtubeDLInfo.thumbnailUrl) { | ||
173 | previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) | ||
174 | } | ||
154 | 175 | ||
155 | const tags = body.tags || youtubeDLInfo.tags | 176 | const tags = body.tags || youtubeDLInfo.tags |
156 | const videoImportAttributes = { | 177 | const videoImportAttributes = { |
@@ -168,15 +189,41 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
168 | user | 189 | user |
169 | }) | 190 | }) |
170 | 191 | ||
192 | // Get video subtitles | ||
193 | try { | ||
194 | const subtitles = await getYoutubeDLSubs(targetUrl) | ||
195 | |||
196 | logger.info('Will create %s subtitles from youtube import %s.', subtitles.length, targetUrl) | ||
197 | |||
198 | for (const subtitle of subtitles) { | ||
199 | const videoCaption = new VideoCaptionModel({ | ||
200 | videoId: video.id, | ||
201 | language: subtitle.language | ||
202 | }) as MVideoCaptionVideo | ||
203 | videoCaption.Video = video | ||
204 | |||
205 | // Move physical file | ||
206 | await moveAndProcessCaptionFile(subtitle, videoCaption) | ||
207 | |||
208 | await sequelizeTypescript.transaction(async t => { | ||
209 | await VideoCaptionModel.insertOrReplaceLanguage(video.id, subtitle.language, null, t) | ||
210 | }) | ||
211 | } | ||
212 | } catch (err) { | ||
213 | logger.warn('Cannot get video subtitles.', { err }) | ||
214 | } | ||
215 | |||
171 | // Create job to import the video | 216 | // Create job to import the video |
172 | const payload = { | 217 | const payload = { |
173 | type: 'youtube-dl' as 'youtube-dl', | 218 | type: 'youtube-dl' as 'youtube-dl', |
174 | videoImportId: videoImport.id, | 219 | videoImportId: videoImport.id, |
175 | thumbnailUrl: youtubeDLInfo.thumbnailUrl, | 220 | generateThumbnail: !thumbnailModel, |
176 | downloadThumbnail: !thumbnailModel, | 221 | generatePreview: !previewModel, |
177 | downloadPreview: !previewModel | 222 | fileExt: youtubeDLInfo.fileExt |
223 | ? `.${youtubeDLInfo.fileExt}` | ||
224 | : '.mp4' | ||
178 | } | 225 | } |
179 | await JobQueue.Instance.createJob({ type: 'video-import', payload }) | 226 | await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload }) |
180 | 227 | ||
181 | auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) | 228 | auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) |
182 | 229 | ||
@@ -189,7 +236,7 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
189 | remote: false, | 236 | remote: false, |
190 | category: body.category || importData.category, | 237 | category: body.category || importData.category, |
191 | licence: body.licence || importData.licence, | 238 | licence: body.licence || importData.licence, |
192 | language: body.language || undefined, | 239 | language: body.language || importData.language, |
193 | commentsEnabled: body.commentsEnabled !== false, // If the value is not "false", the default is "true" | 240 | commentsEnabled: body.commentsEnabled !== false, // If the value is not "false", the default is "true" |
194 | downloadEnabled: body.downloadEnabled !== false, | 241 | downloadEnabled: body.downloadEnabled !== false, |
195 | waitTranscoding: body.waitTranscoding || false, | 242 | waitTranscoding: body.waitTranscoding || false, |
@@ -200,7 +247,7 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
200 | privacy: body.privacy || VideoPrivacy.PRIVATE, | 247 | privacy: body.privacy || VideoPrivacy.PRIVATE, |
201 | duration: 0, // duration will be set by the import job | 248 | duration: 0, // duration will be set by the import job |
202 | channelId: channelId, | 249 | channelId: channelId, |
203 | originallyPublishedAt: importData.originallyPublishedAt | 250 | originallyPublishedAt: body.originallyPublishedAt || importData.originallyPublishedAt |
204 | } | 251 | } |
205 | const video = new VideoModel(videoData) | 252 | const video = new VideoModel(videoData) |
206 | video.url = getVideoActivityPubUrl(video) | 253 | video.url = getVideoActivityPubUrl(video) |
@@ -211,7 +258,7 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
211 | async function processThumbnail (req: express.Request, video: VideoModel) { | 258 | async function processThumbnail (req: express.Request, video: VideoModel) { |
212 | const thumbnailField = req.files ? req.files['thumbnailfile'] : undefined | 259 | const thumbnailField = req.files ? req.files['thumbnailfile'] : undefined |
213 | if (thumbnailField) { | 260 | if (thumbnailField) { |
214 | const thumbnailPhysicalFile = thumbnailField[ 0 ] | 261 | const thumbnailPhysicalFile = thumbnailField[0] |
215 | 262 | ||
216 | return createVideoMiniatureFromExisting(thumbnailPhysicalFile.path, video, ThumbnailType.MINIATURE, false) | 263 | return createVideoMiniatureFromExisting(thumbnailPhysicalFile.path, video, ThumbnailType.MINIATURE, false) |
217 | } | 264 | } |
@@ -230,13 +277,31 @@ async function processPreview (req: express.Request, video: VideoModel) { | |||
230 | return undefined | 277 | return undefined |
231 | } | 278 | } |
232 | 279 | ||
280 | async function processThumbnailFromUrl (url: string, video: VideoModel) { | ||
281 | try { | ||
282 | return createVideoMiniatureFromUrl(url, video, ThumbnailType.MINIATURE) | ||
283 | } catch (err) { | ||
284 | logger.warn('Cannot generate video thumbnail %s for %s.', url, video.url, { err }) | ||
285 | return undefined | ||
286 | } | ||
287 | } | ||
288 | |||
289 | async function processPreviewFromUrl (url: string, video: VideoModel) { | ||
290 | try { | ||
291 | return createVideoMiniatureFromUrl(url, video, ThumbnailType.PREVIEW) | ||
292 | } catch (err) { | ||
293 | logger.warn('Cannot generate video preview %s for %s.', url, video.url, { err }) | ||
294 | return undefined | ||
295 | } | ||
296 | } | ||
297 | |||
233 | function insertIntoDB (parameters: { | 298 | function insertIntoDB (parameters: { |
234 | video: MVideoThumbnailAccountDefault, | 299 | video: MVideoThumbnailAccountDefault |
235 | thumbnailModel: MThumbnail, | 300 | thumbnailModel: MThumbnail |
236 | previewModel: MThumbnail, | 301 | previewModel: MThumbnail |
237 | videoChannel: MChannelAccountDefault, | 302 | videoChannel: MChannelAccountDefault |
238 | tags: string[], | 303 | tags: string[] |
239 | videoImportAttributes: Partial<MVideoImport>, | 304 | videoImportAttributes: Partial<MVideoImport> |
240 | user: MUser | 305 | user: MUser |
241 | }): Bluebird<MVideoImportFormattable> { | 306 | }): Bluebird<MVideoImportFormattable> { |
242 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters | 307 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 8d4ff07eb..8048c568c 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { extname } from 'path' | 2 | import { extname } from 'path' |
3 | import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared' | 3 | import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared' |
4 | import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' | 4 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 6 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
7 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 7 | import { getFormattedObjects } from '../../../helpers/utils' |
8 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | 8 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' |
9 | import { | 9 | import { |
10 | DEFAULT_AUDIO_RESOLUTION, | 10 | DEFAULT_AUDIO_RESOLUTION, |
@@ -14,12 +14,7 @@ import { | |||
14 | VIDEO_LICENCES, | 14 | VIDEO_LICENCES, |
15 | VIDEO_PRIVACIES | 15 | VIDEO_PRIVACIES |
16 | } from '../../../initializers/constants' | 16 | } from '../../../initializers/constants' |
17 | import { | 17 | import { federateVideoIfNeeded, fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' |
18 | changeVideoChannelShare, | ||
19 | federateVideoIfNeeded, | ||
20 | fetchRemoteVideoDescription, | ||
21 | getVideoActivityPubUrl | ||
22 | } from '../../../lib/activitypub' | ||
23 | import { JobQueue } from '../../../lib/job-queue' | 18 | import { JobQueue } from '../../../lib/job-queue' |
24 | import { Redis } from '../../../lib/redis' | 19 | import { Redis } from '../../../lib/redis' |
25 | import { | 20 | import { |
@@ -32,6 +27,7 @@ import { | |||
32 | paginationValidator, | 27 | paginationValidator, |
33 | setDefaultPagination, | 28 | setDefaultPagination, |
34 | setDefaultSort, | 29 | setDefaultSort, |
30 | videoFileMetadataGetValidator, | ||
35 | videosAddValidator, | 31 | videosAddValidator, |
36 | videosCustomGetValidator, | 32 | videosCustomGetValidator, |
37 | videosGetValidator, | 33 | videosGetValidator, |
@@ -61,11 +57,15 @@ import { CONFIG } from '../../../initializers/config' | |||
61 | import { sequelizeTypescript } from '../../../initializers/database' | 57 | import { sequelizeTypescript } from '../../../initializers/database' |
62 | import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' | 58 | import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' |
63 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 59 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
64 | import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding' | ||
65 | import { Hooks } from '../../../lib/plugins/hooks' | 60 | import { Hooks } from '../../../lib/plugins/hooks' |
66 | import { MVideoDetails, MVideoFullLight } from '@server/typings/models' | 61 | import { MVideoDetails, MVideoFullLight } from '@server/typings/models' |
67 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 62 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
68 | import { getVideoFilePath } from '@server/lib/video-paths' | 63 | import { getVideoFilePath } from '@server/lib/video-paths' |
64 | import toInt from 'validator/lib/toInt' | ||
65 | import { addOptimizeOrMergeAudioJob } from '@server/helpers/video' | ||
66 | import { getServerActor } from '@server/models/application/application' | ||
67 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | ||
68 | import { getVideoActivityPubUrl } from '@server/lib/activitypub/url' | ||
69 | 69 | ||
70 | const auditLogger = auditLoggerFactory('videos') | 70 | const auditLogger = auditLoggerFactory('videos') |
71 | const videosRouter = express.Router() | 71 | const videosRouter = express.Router() |
@@ -128,6 +128,10 @@ videosRouter.get('/:id/description', | |||
128 | asyncMiddleware(videosGetValidator), | 128 | asyncMiddleware(videosGetValidator), |
129 | asyncMiddleware(getVideoDescription) | 129 | asyncMiddleware(getVideoDescription) |
130 | ) | 130 | ) |
131 | videosRouter.get('/:id/metadata/:videoFileId', | ||
132 | asyncMiddleware(videoFileMetadataGetValidator), | ||
133 | asyncMiddleware(getVideoFileMetadata) | ||
134 | ) | ||
131 | videosRouter.get('/:id', | 135 | videosRouter.get('/:id', |
132 | optionalAuthenticate, | 136 | optionalAuthenticate, |
133 | asyncMiddleware(videosCustomGetValidator('only-video-with-rights')), | 137 | asyncMiddleware(videosCustomGetValidator('only-video-with-rights')), |
@@ -135,7 +139,7 @@ videosRouter.get('/:id', | |||
135 | asyncMiddleware(getVideo) | 139 | asyncMiddleware(getVideo) |
136 | ) | 140 | ) |
137 | videosRouter.post('/:id/views', | 141 | videosRouter.post('/:id/views', |
138 | asyncMiddleware(videosGetValidator), | 142 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), |
139 | asyncMiddleware(viewVideo) | 143 | asyncMiddleware(viewVideo) |
140 | ) | 144 | ) |
141 | 145 | ||
@@ -206,7 +210,8 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
206 | const videoFile = new VideoFileModel({ | 210 | const videoFile = new VideoFileModel({ |
207 | extname: extname(videoPhysicalFile.filename), | 211 | extname: extname(videoPhysicalFile.filename), |
208 | size: videoPhysicalFile.size, | 212 | size: videoPhysicalFile.size, |
209 | videoStreamingPlaylistId: null | 213 | videoStreamingPlaylistId: null, |
214 | metadata: await getMetadataFromFile<any>(videoPhysicalFile.path) | ||
210 | }) | 215 | }) |
211 | 216 | ||
212 | if (videoFile.isAudio()) { | 217 | if (videoFile.isAudio()) { |
@@ -289,25 +294,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
289 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoCreated) | 294 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoCreated) |
290 | 295 | ||
291 | if (video.state === VideoState.TO_TRANSCODE) { | 296 | if (video.state === VideoState.TO_TRANSCODE) { |
292 | // Put uuid because we don't have id auto incremented for now | 297 | await addOptimizeOrMergeAudioJob(videoCreated, videoFile) |
293 | let dataInput: VideoTranscodingPayload | ||
294 | |||
295 | if (videoFile.isAudio()) { | ||
296 | dataInput = { | ||
297 | type: 'merge-audio' as 'merge-audio', | ||
298 | resolution: DEFAULT_AUDIO_RESOLUTION, | ||
299 | videoUUID: videoCreated.uuid, | ||
300 | isNewVideo: true | ||
301 | } | ||
302 | } else { | ||
303 | dataInput = { | ||
304 | type: 'optimize' as 'optimize', | ||
305 | videoUUID: videoCreated.uuid, | ||
306 | isNewVideo: true | ||
307 | } | ||
308 | } | ||
309 | |||
310 | await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) | ||
311 | } | 298 | } |
312 | 299 | ||
313 | Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) | 300 | Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) |
@@ -452,14 +439,13 @@ async function getVideo (req: express.Request, res: express.Response) { | |||
452 | 439 | ||
453 | if (video.isOutdated()) { | 440 | if (video.isOutdated()) { |
454 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) | 441 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) |
455 | .catch(err => logger.error('Cannot create AP refresher job for video %s.', video.url, { err })) | ||
456 | } | 442 | } |
457 | 443 | ||
458 | return res.json(video.toFormattedDetailsJSON()) | 444 | return res.json(video.toFormattedDetailsJSON()) |
459 | } | 445 | } |
460 | 446 | ||
461 | async function viewVideo (req: express.Request, res: express.Response) { | 447 | async function viewVideo (req: express.Request, res: express.Response) { |
462 | const videoInstance = res.locals.videoAll | 448 | const videoInstance = res.locals.onlyImmutableVideo |
463 | 449 | ||
464 | const ip = req.ip | 450 | const ip = req.ip |
465 | const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid) | 451 | const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid) |
@@ -494,6 +480,11 @@ async function getVideoDescription (req: express.Request, res: express.Response) | |||
494 | return res.json({ description }) | 480 | return res.json({ description }) |
495 | } | 481 | } |
496 | 482 | ||
483 | async function getVideoFileMetadata (req: express.Request, res: express.Response) { | ||
484 | const videoFile = await VideoFileModel.loadWithMetadata(toInt(req.params.videoFileId)) | ||
485 | return res.json(videoFile.metadata) | ||
486 | } | ||
487 | |||
497 | async function listVideos (req: express.Request, res: express.Response) { | 488 | async function listVideos (req: express.Request, res: express.Response) { |
498 | const countVideos = getCountVideos(req) | 489 | const countVideos = getCountVideos(req) |
499 | 490 | ||
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index 41d7cdc43..540a49010 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { logger } from '../../../helpers/logger' | 2 | import { logger } from '../../../helpers/logger' |
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers/database' |
4 | import { | 4 | import { |
5 | asyncMiddleware, | 5 | asyncMiddleware, |
6 | asyncRetryTransactionMiddleware, | 6 | asyncRetryTransactionMiddleware, |
@@ -15,7 +15,7 @@ import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ow | |||
15 | import { VideoChangeOwnershipStatus, VideoState } from '../../../../shared/models/videos' | 15 | import { VideoChangeOwnershipStatus, VideoState } from '../../../../shared/models/videos' |
16 | import { VideoChannelModel } from '../../../models/video/video-channel' | 16 | import { VideoChannelModel } from '../../../models/video/video-channel' |
17 | import { getFormattedObjects } from '../../../helpers/utils' | 17 | import { getFormattedObjects } from '../../../helpers/utils' |
18 | import { changeVideoChannelShare } from '../../../lib/activitypub' | 18 | import { changeVideoChannelShare } from '../../../lib/activitypub/share' |
19 | import { sendUpdateVideo } from '../../../lib/activitypub/send' | 19 | import { sendUpdateVideo } from '../../../lib/activitypub/send' |
20 | import { VideoModel } from '../../../models/video/video' | 20 | import { VideoModel } from '../../../models/video/video' |
21 | import { MVideoFullLight } from '@server/typings/models' | 21 | import { MVideoFullLight } from '@server/typings/models' |
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts index 3d2f3d728..3ee365289 100644 --- a/server/controllers/api/videos/rate.ts +++ b/server/controllers/api/videos/rate.ts | |||
@@ -2,7 +2,7 @@ import * as express from 'express' | |||
2 | import { UserVideoRateUpdate } from '../../../../shared' | 2 | import { UserVideoRateUpdate } from '../../../../shared' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { VIDEO_RATE_TYPES } from '../../../initializers/constants' | 4 | import { VIDEO_RATE_TYPES } from '../../../initializers/constants' |
5 | import { getRateUrl, sendVideoRateChange } from '../../../lib/activitypub' | 5 | import { getRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates' |
6 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares' | 6 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares' |
7 | import { AccountModel } from '../../../models/account/account' | 7 | import { AccountModel } from '../../../models/account/account' |
8 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 8 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
diff --git a/server/controllers/bots.ts b/server/controllers/bots.ts index 63280dabb..8d1fa72f3 100644 --- a/server/controllers/bots.ts +++ b/server/controllers/bots.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { asyncMiddleware } from '../middlewares' | 2 | import { asyncMiddleware } from '../middlewares' |
3 | import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants' | 3 | import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants' |
4 | import * as sitemapModule from 'sitemap' | 4 | import { SitemapStream, streamToPromise } from 'sitemap' |
5 | import { VideoModel } from '../models/video/video' | 5 | import { VideoModel } from '../models/video/video' |
6 | import { VideoChannelModel } from '../models/video/video-channel' | 6 | import { VideoChannelModel } from '../models/video/video-channel' |
7 | import { AccountModel } from '../models/account/account' | 7 | import { AccountModel } from '../models/account/account' |
@@ -33,12 +33,14 @@ async function getSitemap (req: express.Request, res: express.Response) { | |||
33 | urls = urls.concat(await getSitemapVideoChannelUrls()) | 33 | urls = urls.concat(await getSitemapVideoChannelUrls()) |
34 | urls = urls.concat(await getSitemapAccountUrls()) | 34 | urls = urls.concat(await getSitemapAccountUrls()) |
35 | 35 | ||
36 | const sitemap = sitemapModule.createSitemap({ | 36 | const sitemapStream = new SitemapStream({ hostname: WEBSERVER.URL }) |
37 | hostname: WEBSERVER.URL, | 37 | |
38 | urls: urls | 38 | for (const urlObj of urls) { |
39 | }) | 39 | sitemapStream.write(urlObj) |
40 | } | ||
41 | sitemapStream.end() | ||
40 | 42 | ||
41 | const xml = sitemap.toXML() | 43 | const xml = await streamToPromise(sitemapStream) |
42 | 44 | ||
43 | res.header('Content-Type', 'application/xml') | 45 | res.header('Content-Type', 'application/xml') |
44 | res.send(xml) | 46 | res.send(xml) |
diff --git a/server/controllers/client.ts b/server/controllers/client.ts index 56685f102..08c0f1fa6 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts | |||
@@ -72,11 +72,11 @@ export { | |||
72 | 72 | ||
73 | // --------------------------------------------------------------------------- | 73 | // --------------------------------------------------------------------------- |
74 | 74 | ||
75 | async function serveServerTranslations (req: express.Request, res: express.Response) { | 75 | function serveServerTranslations (req: express.Request, res: express.Response) { |
76 | const locale = req.params.locale | 76 | const locale = req.params.locale |
77 | const file = req.params.file | 77 | const file = req.params.file |
78 | 78 | ||
79 | if (is18nLocale(locale) && LOCALE_FILES.indexOf(file) !== -1) { | 79 | if (is18nLocale(locale) && LOCALE_FILES.includes(file)) { |
80 | const completeLocale = getCompleteLocale(locale) | 80 | const completeLocale = getCompleteLocale(locale) |
81 | const completeFileLocale = buildFileLocale(completeLocale) | 81 | const completeFileLocale = buildFileLocale(completeLocale) |
82 | 82 | ||
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index 72628dffb..cb82bfc6d 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts | |||
@@ -67,7 +67,7 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res | |||
67 | const feed = initFeed(name, description) | 67 | const feed = initFeed(name, description) |
68 | 68 | ||
69 | // Adding video items to the feed, one at a time | 69 | // Adding video items to the feed, one at a time |
70 | comments.forEach(comment => { | 70 | for (const comment of comments) { |
71 | const link = WEBSERVER.URL + comment.getCommentStaticPath() | 71 | const link = WEBSERVER.URL + comment.getCommentStaticPath() |
72 | 72 | ||
73 | let title = comment.Video.name | 73 | let title = comment.Video.name |
@@ -89,7 +89,7 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res | |||
89 | author, | 89 | author, |
90 | date: comment.createdAt | 90 | date: comment.createdAt |
91 | }) | 91 | }) |
92 | }) | 92 | } |
93 | 93 | ||
94 | // Now the feed generation is done, let's send it! | 94 | // Now the feed generation is done, let's send it! |
95 | return sendFeed(feed, req, res) | 95 | return sendFeed(feed, req, res) |
diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts index 1caee9a29..f88a1632d 100644 --- a/server/controllers/plugins.ts +++ b/server/controllers/plugins.ts | |||
@@ -2,11 +2,12 @@ import * as express from 'express' | |||
2 | import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' | 2 | import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import { PluginManager, RegisteredPlugin } from '../lib/plugins/plugin-manager' | 4 | import { PluginManager, RegisteredPlugin } from '../lib/plugins/plugin-manager' |
5 | import { servePluginStaticDirectoryValidator } from '../middlewares/validators/plugins' | 5 | import { getPluginValidator, pluginStaticDirectoryValidator, getExternalAuthValidator } from '../middlewares/validators/plugins' |
6 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' | 6 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' |
7 | import { PluginType } from '../../shared/models/plugins/plugin.type' | 7 | import { PluginType } from '../../shared/models/plugins/plugin.type' |
8 | import { isTestInstance } from '../helpers/core-utils' | 8 | import { isTestInstance } from '../helpers/core-utils' |
9 | import { getCompleteLocale, is18nLocale } from '../../shared/models/i18n' | 9 | import { getCompleteLocale, is18nLocale } from '../../shared/models/i18n' |
10 | import { logger } from '@server/helpers/logger' | ||
10 | 11 | ||
11 | const sendFileOptions = { | 12 | const sendFileOptions = { |
12 | maxAge: '30 days', | 13 | maxAge: '30 days', |
@@ -23,23 +24,43 @@ pluginsRouter.get('/plugins/translations/:locale.json', | |||
23 | getPluginTranslations | 24 | getPluginTranslations |
24 | ) | 25 | ) |
25 | 26 | ||
27 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/auth/:authName', | ||
28 | getPluginValidator(PluginType.PLUGIN), | ||
29 | getExternalAuthValidator, | ||
30 | handleAuthInPlugin | ||
31 | ) | ||
32 | |||
26 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)', | 33 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)', |
27 | servePluginStaticDirectoryValidator(PluginType.PLUGIN), | 34 | getPluginValidator(PluginType.PLUGIN), |
35 | pluginStaticDirectoryValidator, | ||
28 | servePluginStaticDirectory | 36 | servePluginStaticDirectory |
29 | ) | 37 | ) |
30 | 38 | ||
31 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)', | 39 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)', |
32 | servePluginStaticDirectoryValidator(PluginType.PLUGIN), | 40 | getPluginValidator(PluginType.PLUGIN), |
41 | pluginStaticDirectoryValidator, | ||
33 | servePluginClientScripts | 42 | servePluginClientScripts |
34 | ) | 43 | ) |
35 | 44 | ||
45 | pluginsRouter.use('/plugins/:pluginName/router', | ||
46 | getPluginValidator(PluginType.PLUGIN, false), | ||
47 | servePluginCustomRoutes | ||
48 | ) | ||
49 | |||
50 | pluginsRouter.use('/plugins/:pluginName/:pluginVersion/router', | ||
51 | getPluginValidator(PluginType.PLUGIN), | ||
52 | servePluginCustomRoutes | ||
53 | ) | ||
54 | |||
36 | pluginsRouter.get('/themes/:pluginName/:pluginVersion/static/:staticEndpoint(*)', | 55 | pluginsRouter.get('/themes/:pluginName/:pluginVersion/static/:staticEndpoint(*)', |
37 | servePluginStaticDirectoryValidator(PluginType.THEME), | 56 | getPluginValidator(PluginType.THEME), |
57 | pluginStaticDirectoryValidator, | ||
38 | servePluginStaticDirectory | 58 | servePluginStaticDirectory |
39 | ) | 59 | ) |
40 | 60 | ||
41 | pluginsRouter.get('/themes/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)', | 61 | pluginsRouter.get('/themes/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)', |
42 | servePluginStaticDirectoryValidator(PluginType.THEME), | 62 | getPluginValidator(PluginType.THEME), |
63 | pluginStaticDirectoryValidator, | ||
43 | servePluginClientScripts | 64 | servePluginClientScripts |
44 | ) | 65 | ) |
45 | 66 | ||
@@ -85,22 +106,27 @@ function servePluginStaticDirectory (req: express.Request, res: express.Response | |||
85 | const [ directory, ...file ] = staticEndpoint.split('/') | 106 | const [ directory, ...file ] = staticEndpoint.split('/') |
86 | 107 | ||
87 | const staticPath = plugin.staticDirs[directory] | 108 | const staticPath = plugin.staticDirs[directory] |
88 | if (!staticPath) { | 109 | if (!staticPath) return res.sendStatus(404) |
89 | return res.sendStatus(404) | ||
90 | } | ||
91 | 110 | ||
92 | const filepath = file.join('/') | 111 | const filepath = file.join('/') |
93 | return res.sendFile(join(plugin.path, staticPath, filepath), sendFileOptions) | 112 | return res.sendFile(join(plugin.path, staticPath, filepath), sendFileOptions) |
94 | } | 113 | } |
95 | 114 | ||
115 | function servePluginCustomRoutes (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
116 | const plugin: RegisteredPlugin = res.locals.registeredPlugin | ||
117 | const router = PluginManager.Instance.getRouter(plugin.npmName) | ||
118 | |||
119 | if (!router) return res.sendStatus(404) | ||
120 | |||
121 | return router(req, res, next) | ||
122 | } | ||
123 | |||
96 | function servePluginClientScripts (req: express.Request, res: express.Response) { | 124 | function servePluginClientScripts (req: express.Request, res: express.Response) { |
97 | const plugin: RegisteredPlugin = res.locals.registeredPlugin | 125 | const plugin: RegisteredPlugin = res.locals.registeredPlugin |
98 | const staticEndpoint = req.params.staticEndpoint | 126 | const staticEndpoint = req.params.staticEndpoint |
99 | 127 | ||
100 | const file = plugin.clientScripts[staticEndpoint] | 128 | const file = plugin.clientScripts[staticEndpoint] |
101 | if (!file) { | 129 | if (!file) return res.sendStatus(404) |
102 | return res.sendStatus(404) | ||
103 | } | ||
104 | 130 | ||
105 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) | 131 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) |
106 | } | 132 | } |
@@ -115,3 +141,14 @@ function serveThemeCSSDirectory (req: express.Request, res: express.Response) { | |||
115 | 141 | ||
116 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) | 142 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) |
117 | } | 143 | } |
144 | |||
145 | function handleAuthInPlugin (req: express.Request, res: express.Response) { | ||
146 | const authOptions = res.locals.externalAuth | ||
147 | |||
148 | try { | ||
149 | logger.debug('Forwarding auth plugin request in %s of plugin %s.', authOptions.authName, res.locals.registeredPlugin.npmName) | ||
150 | authOptions.onAuthRequest(req, res) | ||
151 | } catch (err) { | ||
152 | logger.error('Forward request error in auth %s of plugin %s.', authOptions.authName, res.locals.registeredPlugin.npmName, { err }) | ||
153 | } | ||
154 | } | ||
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index a4bb3a4d9..271b788f6 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -1,15 +1,15 @@ | |||
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 { | 3 | import { |
4 | CONSTRAINTS_FIELDS, | ||
5 | DEFAULT_THEME_NAME, | ||
4 | HLS_STREAMING_PLAYLIST_DIRECTORY, | 6 | HLS_STREAMING_PLAYLIST_DIRECTORY, |
5 | PEERTUBE_VERSION, | 7 | PEERTUBE_VERSION, |
6 | ROUTE_CACHE_LIFETIME, | 8 | ROUTE_CACHE_LIFETIME, |
7 | STATIC_DOWNLOAD_PATHS, | 9 | STATIC_DOWNLOAD_PATHS, |
8 | STATIC_MAX_AGE, | 10 | STATIC_MAX_AGE, |
9 | STATIC_PATHS, | 11 | STATIC_PATHS, |
10 | WEBSERVER, | 12 | WEBSERVER |
11 | CONSTRAINTS_FIELDS, | ||
12 | DEFAULT_THEME_NAME | ||
13 | } from '../initializers/constants' | 13 | } from '../initializers/constants' |
14 | import { cacheRoute } from '../middlewares/cache' | 14 | import { cacheRoute } from '../middlewares/cache' |
15 | import { asyncMiddleware, videosDownloadValidator } from '../middlewares' | 15 | import { asyncMiddleware, videosDownloadValidator } from '../middlewares' |
@@ -19,8 +19,7 @@ import { VideoCommentModel } from '../models/video/video-comment' | |||
19 | import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo' | 19 | import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo' |
20 | import { join } from 'path' | 20 | import { join } from 'path' |
21 | import { root } from '../helpers/core-utils' | 21 | import { root } from '../helpers/core-utils' |
22 | import { CONFIG } from '../initializers/config' | 22 | import { CONFIG, isEmailEnabled } from '../initializers/config' |
23 | import { Emailer } from '../lib/emailer' | ||
24 | import { getPreview, getVideoCaption } from './lazy-static' | 23 | import { getPreview, getVideoCaption } from './lazy-static' |
25 | import { VideoStreamingPlaylistType } from '@shared/models/videos/video-streaming-playlist.type' | 24 | import { VideoStreamingPlaylistType } from '@shared/models/videos/video-streaming-playlist.type' |
26 | import { MVideoFile, MVideoFullLight } from '@server/typings/models' | 25 | import { MVideoFile, MVideoFullLight } from '@server/typings/models' |
@@ -45,12 +44,12 @@ staticRouter.use( | |||
45 | staticRouter.use( | 44 | staticRouter.use( |
46 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':id-:resolution([0-9]+).torrent', | 45 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':id-:resolution([0-9]+).torrent', |
47 | asyncMiddleware(videosDownloadValidator), | 46 | asyncMiddleware(videosDownloadValidator), |
48 | asyncMiddleware(downloadTorrent) | 47 | downloadTorrent |
49 | ) | 48 | ) |
50 | staticRouter.use( | 49 | staticRouter.use( |
51 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':id-:resolution([0-9]+)-hls.torrent', | 50 | STATIC_DOWNLOAD_PATHS.TORRENTS + ':id-:resolution([0-9]+)-hls.torrent', |
52 | asyncMiddleware(videosDownloadValidator), | 51 | asyncMiddleware(videosDownloadValidator), |
53 | asyncMiddleware(downloadHLSVideoFileTorrent) | 52 | downloadHLSVideoFileTorrent |
54 | ) | 53 | ) |
55 | 54 | ||
56 | // Videos path for webseeding | 55 | // Videos path for webseeding |
@@ -68,13 +67,13 @@ staticRouter.use( | |||
68 | staticRouter.use( | 67 | staticRouter.use( |
69 | STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', | 68 | STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', |
70 | asyncMiddleware(videosDownloadValidator), | 69 | asyncMiddleware(videosDownloadValidator), |
71 | asyncMiddleware(downloadVideoFile) | 70 | downloadVideoFile |
72 | ) | 71 | ) |
73 | 72 | ||
74 | staticRouter.use( | 73 | staticRouter.use( |
75 | STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension', | 74 | STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension', |
76 | asyncMiddleware(videosDownloadValidator), | 75 | asyncMiddleware(videosDownloadValidator), |
77 | asyncMiddleware(downloadHLSVideoFile) | 76 | downloadHLSVideoFile |
78 | ) | 77 | ) |
79 | 78 | ||
80 | // HLS | 79 | // HLS |
@@ -235,6 +234,12 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
235 | nodeName: CONFIG.INSTANCE.NAME, | 234 | nodeName: CONFIG.INSTANCE.NAME, |
236 | nodeDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, | 235 | nodeDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, |
237 | nodeConfig: { | 236 | nodeConfig: { |
237 | search: { | ||
238 | remoteUri: { | ||
239 | users: CONFIG.SEARCH.REMOTE_URI.USERS, | ||
240 | anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS | ||
241 | } | ||
242 | }, | ||
238 | plugin: { | 243 | plugin: { |
239 | registered: getRegisteredPlugins() | 244 | registered: getRegisteredPlugins() |
240 | }, | 245 | }, |
@@ -243,7 +248,7 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
243 | default: getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) | 248 | default: getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) |
244 | }, | 249 | }, |
245 | email: { | 250 | email: { |
246 | enabled: Emailer.isEnabled() | 251 | enabled: isEmailEnabled() |
247 | }, | 252 | }, |
248 | contactForm: { | 253 | contactForm: { |
249 | enabled: CONFIG.CONTACT_FORM.ENABLED | 254 | enabled: CONFIG.CONTACT_FORM.ENABLED |
@@ -325,7 +330,7 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
325 | return res.send(json).end() | 330 | return res.send(json).end() |
326 | } | 331 | } |
327 | 332 | ||
328 | async function downloadTorrent (req: express.Request, res: express.Response) { | 333 | function downloadTorrent (req: express.Request, res: express.Response) { |
329 | const video = res.locals.videoAll | 334 | const video = res.locals.videoAll |
330 | 335 | ||
331 | const videoFile = getVideoFile(req, video.VideoFiles) | 336 | const videoFile = getVideoFile(req, video.VideoFiles) |
@@ -334,7 +339,7 @@ async function downloadTorrent (req: express.Request, res: express.Response) { | |||
334 | return res.download(getTorrentFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p.torrent`) | 339 | return res.download(getTorrentFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p.torrent`) |
335 | } | 340 | } |
336 | 341 | ||
337 | async function downloadHLSVideoFileTorrent (req: express.Request, res: express.Response) { | 342 | function downloadHLSVideoFileTorrent (req: express.Request, res: express.Response) { |
338 | const video = res.locals.videoAll | 343 | const video = res.locals.videoAll |
339 | 344 | ||
340 | const playlist = getHLSPlaylist(video) | 345 | const playlist = getHLSPlaylist(video) |
@@ -346,7 +351,7 @@ async function downloadHLSVideoFileTorrent (req: express.Request, res: express.R | |||
346 | return res.download(getTorrentFilePath(playlist, videoFile), `${video.name}-${videoFile.resolution}p-hls.torrent`) | 351 | return res.download(getTorrentFilePath(playlist, videoFile), `${video.name}-${videoFile.resolution}p-hls.torrent`) |
347 | } | 352 | } |
348 | 353 | ||
349 | async function downloadVideoFile (req: express.Request, res: express.Response) { | 354 | function downloadVideoFile (req: express.Request, res: express.Response) { |
350 | const video = res.locals.videoAll | 355 | const video = res.locals.videoAll |
351 | 356 | ||
352 | const videoFile = getVideoFile(req, video.VideoFiles) | 357 | const videoFile = getVideoFile(req, video.VideoFiles) |
@@ -355,7 +360,7 @@ async function downloadVideoFile (req: express.Request, res: express.Response) { | |||
355 | return res.download(getVideoFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`) | 360 | return res.download(getVideoFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`) |
356 | } | 361 | } |
357 | 362 | ||
358 | async function downloadHLSVideoFile (req: express.Request, res: express.Response) { | 363 | function downloadHLSVideoFile (req: express.Request, res: express.Response) { |
359 | const video = res.locals.videoAll | 364 | const video = res.locals.videoAll |
360 | const playlist = getHLSPlaylist(video) | 365 | const playlist = getHLSPlaylist(video) |
361 | if (!playlist) return res.status(404).end | 366 | if (!playlist) return res.status(404).end |
diff --git a/server/controllers/tracker.ts b/server/controllers/tracker.ts index 2ae1cf86c..4f756fc0a 100644 --- a/server/controllers/tracker.ts +++ b/server/controllers/tracker.ts | |||
@@ -6,7 +6,6 @@ import * as proxyAddr from 'proxy-addr' | |||
6 | import { Server as WebSocketServer } from 'ws' | 6 | import { Server as WebSocketServer } from 'ws' |
7 | import { TRACKER_RATE_LIMITS } from '../initializers/constants' | 7 | import { 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' | ||
10 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 9 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' |
11 | import { CONFIG } from '../initializers/config' | 10 | import { CONFIG } from '../initializers/config' |
12 | 11 | ||
@@ -38,11 +37,11 @@ const trackerServer = new TrackerServer({ | |||
38 | 37 | ||
39 | const key = ip + '-' + infoHash | 38 | const key = ip + '-' + infoHash |
40 | 39 | ||
41 | peersIps[ ip ] = peersIps[ ip ] ? peersIps[ ip ] + 1 : 1 | 40 | peersIps[ip] = peersIps[ip] ? peersIps[ip] + 1 : 1 |
42 | peersIpInfoHash[ key ] = peersIpInfoHash[ key ] ? peersIpInfoHash[ key ] + 1 : 1 | 41 | peersIpInfoHash[key] = peersIpInfoHash[key] ? peersIpInfoHash[key] + 1 : 1 |
43 | 42 | ||
44 | if (CONFIG.TRACKER.REJECT_TOO_MANY_ANNOUNCES && peersIpInfoHash[ key ] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { | 43 | if (CONFIG.TRACKER.REJECT_TOO_MANY_ANNOUNCES && peersIpInfoHash[key] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { |
45 | return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`)) | 44 | return cb(new Error(`Too many requests (${peersIpInfoHash[key]} of ip ${ip} for torrent ${infoHash}`)) |
46 | } | 45 | } |
47 | 46 | ||
48 | try { | 47 | try { |
@@ -87,10 +86,8 @@ function createWebsocketTrackerServer (app: express.Application) { | |||
87 | trackerServer.onWebSocketConnection(ws) | 86 | trackerServer.onWebSocketConnection(ws) |
88 | }) | 87 | }) |
89 | 88 | ||
90 | server.on('upgrade', (request, socket, head) => { | 89 | server.on('upgrade', (request: express.Request, socket, head) => { |
91 | const pathname = parse(request.url).pathname | 90 | if (request.url === '/tracker/socket') { |
92 | |||
93 | if (pathname === '/tracker/socket') { | ||
94 | wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws, request)) | 91 | wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws, request)) |
95 | } | 92 | } |
96 | 93 | ||
diff --git a/server/controllers/webfinger.ts b/server/controllers/webfinger.ts index fc9575160..5c308d9ad 100644 --- a/server/controllers/webfinger.ts +++ b/server/controllers/webfinger.ts | |||
@@ -1,9 +1,12 @@ | |||
1 | import * as cors from 'cors' | ||
1 | import * as express from 'express' | 2 | import * as express from 'express' |
2 | import { asyncMiddleware } from '../middlewares' | 3 | import { asyncMiddleware } from '../middlewares' |
3 | import { webfingerValidator } from '../middlewares/validators' | 4 | import { webfingerValidator } from '../middlewares/validators' |
4 | 5 | ||
5 | const webfingerRouter = express.Router() | 6 | const webfingerRouter = express.Router() |
6 | 7 | ||
8 | webfingerRouter.use(cors()) | ||
9 | |||
7 | webfingerRouter.get('/.well-known/webfinger', | 10 | webfingerRouter.get('/.well-known/webfinger', |
8 | asyncMiddleware(webfingerValidator), | 11 | asyncMiddleware(webfingerValidator), |
9 | webfingerController | 12 | webfingerController |
@@ -18,7 +21,7 @@ export { | |||
18 | // --------------------------------------------------------------------------- | 21 | // --------------------------------------------------------------------------- |
19 | 22 | ||
20 | function webfingerController (req: express.Request, res: express.Response) { | 23 | function webfingerController (req: express.Request, res: express.Response) { |
21 | const actor = res.locals.actorFull | 24 | const actor = res.locals.actorUrl |
22 | 25 | ||
23 | const json = { | 26 | const json = { |
24 | subject: req.query.resource, | 27 | subject: req.query.resource, |
@@ -32,5 +35,5 @@ function webfingerController (req: express.Request, res: express.Response) { | |||
32 | ] | 35 | ] |
33 | } | 36 | } |
34 | 37 | ||
35 | return res.json(json).end() | 38 | return res.json(json) |
36 | } | 39 | } |