aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--config/default.yaml20
-rw-r--r--config/production.yaml.example20
-rw-r--r--server.ts8
-rw-r--r--server/controllers/activitypub/client.ts25
-rw-r--r--server/controllers/activitypub/inbox.ts5
-rw-r--r--server/controllers/activitypub/index.ts1
-rw-r--r--server/controllers/activitypub/outbox.ts10
-rw-r--r--server/controllers/client.ts51
-rw-r--r--server/controllers/feeds/comment-feeds.ts2
-rw-r--r--server/controllers/feeds/index.ts15
-rw-r--r--server/controllers/feeds/video-feeds.ts4
-rw-r--r--server/controllers/feeds/video-podcast-feeds.ts2
-rw-r--r--server/controllers/index.ts2
-rw-r--r--server/controllers/misc.ts6
-rw-r--r--server/controllers/plugins.ts17
-rw-r--r--server/controllers/services.ts4
-rw-r--r--server/controllers/sitemap.ts (renamed from server/controllers/bots.ts)11
-rw-r--r--server/controllers/well-known.ts15
-rw-r--r--server/initializers/checker-before-init.ts6
-rw-r--r--server/initializers/config.ts20
-rw-r--r--server/middlewares/rate-limiter.ts5
-rw-r--r--server/tests/api/check-params/video-source.ts2
-rw-r--r--server/tests/api/search/search-activitypub-video-playlists.ts2
23 files changed, 216 insertions, 37 deletions
diff --git a/config/default.yaml b/config/default.yaml
index 10d3f79e7..53d8c45de 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -36,6 +36,26 @@ rates_limit:
36 # 10 attempts in 10 min 36 # 10 attempts in 10 min
37 window: 10 minutes 37 window: 10 minutes
38 max: 10 38 max: 10
39 plugins:
40 # 500 attempts in 10 seconds (we also serve plugin static files)
41 window: 10 seconds
42 max: 500
43 well_known:
44 # 200 attempts in 10 seconds
45 window: 10 seconds
46 max: 200
47 feeds:
48 # 50 attempts in 10 seconds
49 window: 10 seconds
50 max: 50
51 activity_pub:
52 # 500 attempts in 10 seconds (we can have many AP requests)
53 window: 10 seconds
54 max: 500
55 client: # HTML files generated by PeerTube
56 # 500 attempts in 10 seconds (to not break crawlers)
57 window: 10 seconds
58 max: 500
39 59
40oauth2: 60oauth2:
41 token_lifetime: 61 token_lifetime:
diff --git a/config/production.yaml.example b/config/production.yaml.example
index a829b46f9..87ef2b676 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -34,6 +34,26 @@ rates_limit:
34 # 10 attempts in 10 min 34 # 10 attempts in 10 min
35 window: 10 minutes 35 window: 10 minutes
36 max: 10 36 max: 10
37 plugins:
38 # 500 attempts in 10 seconds (we also serve plugin static files)
39 window: 10 seconds
40 max: 500
41 well_known:
42 # 200 attempts in 10 seconds
43 window: 10 seconds
44 max: 200
45 feeds:
46 # 50 attempts in 10 seconds
47 window: 10 seconds
48 max: 50
49 activity_pub:
50 # 500 attempts in 10 seconds (we can have many AP requests)
51 window: 10 seconds
52 max: 500
53 client: # HTML files generated by PeerTube
54 # 500 attempts in 10 seconds (to not break crawlers)
55 window: 10 seconds
56 max: 500
37 57
38oauth2: 58oauth2:
39 token_lifetime: 59 token_lifetime:
diff --git a/server.ts b/server.ts
index e25322b66..8e358716e 100644
--- a/server.ts
+++ b/server.ts
@@ -115,7 +115,7 @@ import {
115 pluginsRouter, 115 pluginsRouter,
116 trackerRouter, 116 trackerRouter,
117 createWebsocketTrackerServer, 117 createWebsocketTrackerServer,
118 botsRouter, 118 sitemapRouter,
119 downloadRouter 119 downloadRouter
120} from './server/controllers' 120} from './server/controllers'
121import { advertiseDoNotTrack } from './server/middlewares/dnt' 121import { advertiseDoNotTrack } from './server/middlewares/dnt'
@@ -222,9 +222,7 @@ OpenTelemetryMetrics.Instance.init(app)
222 222
223// ----------- Views, routes and static files ----------- 223// ----------- Views, routes and static files -----------
224 224
225// API 225app.use('/api/' + API_VERSION, apiRouter)
226const apiRoute = '/api/' + API_VERSION
227app.use(apiRoute, apiRouter)
228 226
229// Services (oembed...) 227// Services (oembed...)
230app.use('/services', servicesRouter) 228app.use('/services', servicesRouter)
@@ -235,7 +233,7 @@ app.use('/', pluginsRouter)
235app.use('/', activityPubRouter) 233app.use('/', activityPubRouter)
236app.use('/', feedsRouter) 234app.use('/', feedsRouter)
237app.use('/', trackerRouter) 235app.use('/', trackerRouter)
238app.use('/', botsRouter) 236app.use('/', sitemapRouter)
239 237
240// Static files 238// Static files
241app.use('/', staticRouter) 239app.use('/', staticRouter)
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index c47c61f52..be52f1662 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -19,6 +19,7 @@ import {
19 getLocalVideoSharesActivityPubUrl 19 getLocalVideoSharesActivityPubUrl
20} from '../../lib/activitypub/url' 20} from '../../lib/activitypub/url'
21import { 21import {
22 activityPubRateLimiter,
22 asyncMiddleware, 23 asyncMiddleware,
23 ensureIsLocalChannel, 24 ensureIsLocalChannel,
24 executeIfActivityPub, 25 executeIfActivityPub,
@@ -47,32 +48,38 @@ activityPubClientRouter.use(cors())
47activityPubClientRouter.get( 48activityPubClientRouter.get(
48 [ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ], 49 [ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ],
49 executeIfActivityPub, 50 executeIfActivityPub,
51 activityPubRateLimiter,
50 asyncMiddleware(localAccountValidator), 52 asyncMiddleware(localAccountValidator),
51 asyncMiddleware(accountController) 53 asyncMiddleware(accountController)
52) 54)
53activityPubClientRouter.get('/accounts?/:name/followers', 55activityPubClientRouter.get('/accounts?/:name/followers',
54 executeIfActivityPub, 56 executeIfActivityPub,
57 activityPubRateLimiter,
55 asyncMiddleware(localAccountValidator), 58 asyncMiddleware(localAccountValidator),
56 asyncMiddleware(accountFollowersController) 59 asyncMiddleware(accountFollowersController)
57) 60)
58activityPubClientRouter.get('/accounts?/:name/following', 61activityPubClientRouter.get('/accounts?/:name/following',
59 executeIfActivityPub, 62 executeIfActivityPub,
63 activityPubRateLimiter,
60 asyncMiddleware(localAccountValidator), 64 asyncMiddleware(localAccountValidator),
61 asyncMiddleware(accountFollowingController) 65 asyncMiddleware(accountFollowingController)
62) 66)
63activityPubClientRouter.get('/accounts?/:name/playlists', 67activityPubClientRouter.get('/accounts?/:name/playlists',
64 executeIfActivityPub, 68 executeIfActivityPub,
69 activityPubRateLimiter,
65 asyncMiddleware(localAccountValidator), 70 asyncMiddleware(localAccountValidator),
66 asyncMiddleware(accountPlaylistsController) 71 asyncMiddleware(accountPlaylistsController)
67) 72)
68activityPubClientRouter.get('/accounts?/:name/likes/:videoId', 73activityPubClientRouter.get('/accounts?/:name/likes/:videoId',
69 executeIfActivityPub, 74 executeIfActivityPub,
75 activityPubRateLimiter,
70 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), 76 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
71 asyncMiddleware(getAccountVideoRateValidatorFactory('like')), 77 asyncMiddleware(getAccountVideoRateValidatorFactory('like')),
72 asyncMiddleware(getAccountVideoRateFactory('like')) 78 asyncMiddleware(getAccountVideoRateFactory('like'))
73) 79)
74activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', 80activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
75 executeIfActivityPub, 81 executeIfActivityPub,
82 activityPubRateLimiter,
76 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), 83 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
77 asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')), 84 asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')),
78 asyncMiddleware(getAccountVideoRateFactory('dislike')) 85 asyncMiddleware(getAccountVideoRateFactory('dislike'))
@@ -81,47 +88,56 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
81activityPubClientRouter.get( 88activityPubClientRouter.get(
82 [ '/videos/watch/:id', '/w/:id' ], 89 [ '/videos/watch/:id', '/w/:id' ],
83 executeIfActivityPub, 90 executeIfActivityPub,
91 activityPubRateLimiter,
84 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), 92 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
85 asyncMiddleware(videosCustomGetValidator('all')), 93 asyncMiddleware(videosCustomGetValidator('all')),
86 asyncMiddleware(videoController) 94 asyncMiddleware(videoController)
87) 95)
88activityPubClientRouter.get('/videos/watch/:id/activity', 96activityPubClientRouter.get('/videos/watch/:id/activity',
89 executeIfActivityPub, 97 executeIfActivityPub,
98 activityPubRateLimiter,
90 asyncMiddleware(videosCustomGetValidator('all')), 99 asyncMiddleware(videosCustomGetValidator('all')),
91 asyncMiddleware(videoController) 100 asyncMiddleware(videoController)
92) 101)
93activityPubClientRouter.get('/videos/watch/:id/announces', 102activityPubClientRouter.get('/videos/watch/:id/announces',
94 executeIfActivityPub, 103 executeIfActivityPub,
104 activityPubRateLimiter,
95 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), 105 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
96 asyncMiddleware(videoAnnouncesController) 106 asyncMiddleware(videoAnnouncesController)
97) 107)
98activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', 108activityPubClientRouter.get('/videos/watch/:id/announces/:actorId',
99 executeIfActivityPub, 109 executeIfActivityPub,
110 activityPubRateLimiter,
100 asyncMiddleware(videosShareValidator), 111 asyncMiddleware(videosShareValidator),
101 asyncMiddleware(videoAnnounceController) 112 asyncMiddleware(videoAnnounceController)
102) 113)
103activityPubClientRouter.get('/videos/watch/:id/likes', 114activityPubClientRouter.get('/videos/watch/:id/likes',
104 executeIfActivityPub, 115 executeIfActivityPub,
116 activityPubRateLimiter,
105 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), 117 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
106 asyncMiddleware(videoLikesController) 118 asyncMiddleware(videoLikesController)
107) 119)
108activityPubClientRouter.get('/videos/watch/:id/dislikes', 120activityPubClientRouter.get('/videos/watch/:id/dislikes',
109 executeIfActivityPub, 121 executeIfActivityPub,
122 activityPubRateLimiter,
110 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), 123 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
111 asyncMiddleware(videoDislikesController) 124 asyncMiddleware(videoDislikesController)
112) 125)
113activityPubClientRouter.get('/videos/watch/:id/comments', 126activityPubClientRouter.get('/videos/watch/:id/comments',
114 executeIfActivityPub, 127 executeIfActivityPub,
128 activityPubRateLimiter,
115 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), 129 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
116 asyncMiddleware(videoCommentsController) 130 asyncMiddleware(videoCommentsController)
117) 131)
118activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId', 132activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId',
119 executeIfActivityPub, 133 executeIfActivityPub,
134 activityPubRateLimiter,
120 asyncMiddleware(videoCommentGetValidator), 135 asyncMiddleware(videoCommentGetValidator),
121 asyncMiddleware(videoCommentController) 136 asyncMiddleware(videoCommentController)
122) 137)
123activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity', 138activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity',
124 executeIfActivityPub, 139 executeIfActivityPub,
140 activityPubRateLimiter,
125 asyncMiddleware(videoCommentGetValidator), 141 asyncMiddleware(videoCommentGetValidator),
126 asyncMiddleware(videoCommentController) 142 asyncMiddleware(videoCommentController)
127) 143)
@@ -129,24 +145,28 @@ activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity
129activityPubClientRouter.get( 145activityPubClientRouter.get(
130 [ '/video-channels/:nameWithHost', '/video-channels/:nameWithHost/videos', '/c/:nameWithHost', '/c/:nameWithHost/videos' ], 146 [ '/video-channels/:nameWithHost', '/video-channels/:nameWithHost/videos', '/c/:nameWithHost', '/c/:nameWithHost/videos' ],
131 executeIfActivityPub, 147 executeIfActivityPub,
148 activityPubRateLimiter,
132 asyncMiddleware(videoChannelsNameWithHostValidator), 149 asyncMiddleware(videoChannelsNameWithHostValidator),
133 ensureIsLocalChannel, 150 ensureIsLocalChannel,
134 asyncMiddleware(videoChannelController) 151 asyncMiddleware(videoChannelController)
135) 152)
136activityPubClientRouter.get('/video-channels/:nameWithHost/followers', 153activityPubClientRouter.get('/video-channels/:nameWithHost/followers',
137 executeIfActivityPub, 154 executeIfActivityPub,
155 activityPubRateLimiter,
138 asyncMiddleware(videoChannelsNameWithHostValidator), 156 asyncMiddleware(videoChannelsNameWithHostValidator),
139 ensureIsLocalChannel, 157 ensureIsLocalChannel,
140 asyncMiddleware(videoChannelFollowersController) 158 asyncMiddleware(videoChannelFollowersController)
141) 159)
142activityPubClientRouter.get('/video-channels/:nameWithHost/following', 160activityPubClientRouter.get('/video-channels/:nameWithHost/following',
143 executeIfActivityPub, 161 executeIfActivityPub,
162 activityPubRateLimiter,
144 asyncMiddleware(videoChannelsNameWithHostValidator), 163 asyncMiddleware(videoChannelsNameWithHostValidator),
145 ensureIsLocalChannel, 164 ensureIsLocalChannel,
146 asyncMiddleware(videoChannelFollowingController) 165 asyncMiddleware(videoChannelFollowingController)
147) 166)
148activityPubClientRouter.get('/video-channels/:nameWithHost/playlists', 167activityPubClientRouter.get('/video-channels/:nameWithHost/playlists',
149 executeIfActivityPub, 168 executeIfActivityPub,
169 activityPubRateLimiter,
150 asyncMiddleware(videoChannelsNameWithHostValidator), 170 asyncMiddleware(videoChannelsNameWithHostValidator),
151 ensureIsLocalChannel, 171 ensureIsLocalChannel,
152 asyncMiddleware(videoChannelPlaylistsController) 172 asyncMiddleware(videoChannelPlaylistsController)
@@ -154,11 +174,13 @@ activityPubClientRouter.get('/video-channels/:nameWithHost/playlists',
154 174
155activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?', 175activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?',
156 executeIfActivityPub, 176 executeIfActivityPub,
177 activityPubRateLimiter,
157 asyncMiddleware(videoFileRedundancyGetValidator), 178 asyncMiddleware(videoFileRedundancyGetValidator),
158 asyncMiddleware(videoRedundancyController) 179 asyncMiddleware(videoRedundancyController)
159) 180)
160activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistType/:videoId', 181activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistType/:videoId',
161 executeIfActivityPub, 182 executeIfActivityPub,
183 activityPubRateLimiter,
162 asyncMiddleware(videoPlaylistRedundancyGetValidator), 184 asyncMiddleware(videoPlaylistRedundancyGetValidator),
163 asyncMiddleware(videoRedundancyController) 185 asyncMiddleware(videoRedundancyController)
164) 186)
@@ -166,17 +188,20 @@ activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistT
166activityPubClientRouter.get( 188activityPubClientRouter.get(
167 [ '/video-playlists/:playlistId', '/videos/watch/playlist/:playlistId', '/w/p/:playlistId' ], 189 [ '/video-playlists/:playlistId', '/videos/watch/playlist/:playlistId', '/w/p/:playlistId' ],
168 executeIfActivityPub, 190 executeIfActivityPub,
191 activityPubRateLimiter,
169 asyncMiddleware(videoPlaylistsGetValidator('all')), 192 asyncMiddleware(videoPlaylistsGetValidator('all')),
170 asyncMiddleware(videoPlaylistController) 193 asyncMiddleware(videoPlaylistController)
171) 194)
172activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId', 195activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId',
173 executeIfActivityPub, 196 executeIfActivityPub,
197 activityPubRateLimiter,
174 asyncMiddleware(videoPlaylistElementAPGetValidator), 198 asyncMiddleware(videoPlaylistElementAPGetValidator),
175 asyncMiddleware(videoPlaylistElementController) 199 asyncMiddleware(videoPlaylistElementController)
176) 200)
177 201
178activityPubClientRouter.get('/videos/local-viewer/:localViewerId', 202activityPubClientRouter.get('/videos/local-viewer/:localViewerId',
179 executeIfActivityPub, 203 executeIfActivityPub,
204 activityPubRateLimiter,
180 asyncMiddleware(getVideoLocalViewerValidator), 205 asyncMiddleware(getVideoLocalViewerValidator),
181 asyncMiddleware(getVideoLocalViewerController) 206 asyncMiddleware(getVideoLocalViewerController)
182) 207)
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts
index 66a38e055..862c7baf1 100644
--- a/server/controllers/activitypub/inbox.ts
+++ b/server/controllers/activitypub/inbox.ts
@@ -5,6 +5,7 @@ import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity' 5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { 7import {
8 activityPubRateLimiter,
8 asyncMiddleware, 9 asyncMiddleware,
9 checkSignature, 10 checkSignature,
10 ensureIsLocalChannel, 11 ensureIsLocalChannel,
@@ -17,6 +18,7 @@ import { activityPubValidator } from '../../middlewares/validators/activitypub/a
17const inboxRouter = express.Router() 18const inboxRouter = express.Router()
18 19
19inboxRouter.post('/inbox', 20inboxRouter.post('/inbox',
21 activityPubRateLimiter,
20 signatureValidator, 22 signatureValidator,
21 asyncMiddleware(checkSignature), 23 asyncMiddleware(checkSignature),
22 asyncMiddleware(activityPubValidator), 24 asyncMiddleware(activityPubValidator),
@@ -24,13 +26,16 @@ inboxRouter.post('/inbox',
24) 26)
25 27
26inboxRouter.post('/accounts/:name/inbox', 28inboxRouter.post('/accounts/:name/inbox',
29 activityPubRateLimiter,
27 signatureValidator, 30 signatureValidator,
28 asyncMiddleware(checkSignature), 31 asyncMiddleware(checkSignature),
29 asyncMiddleware(localAccountValidator), 32 asyncMiddleware(localAccountValidator),
30 asyncMiddleware(activityPubValidator), 33 asyncMiddleware(activityPubValidator),
31 inboxController 34 inboxController
32) 35)
36
33inboxRouter.post('/video-channels/:nameWithHost/inbox', 37inboxRouter.post('/video-channels/:nameWithHost/inbox',
38 activityPubRateLimiter,
34 signatureValidator, 39 signatureValidator,
35 asyncMiddleware(checkSignature), 40 asyncMiddleware(checkSignature),
36 asyncMiddleware(videoChannelsNameWithHostValidator), 41 asyncMiddleware(videoChannelsNameWithHostValidator),
diff --git a/server/controllers/activitypub/index.ts b/server/controllers/activitypub/index.ts
index 8c681820a..c14d95108 100644
--- a/server/controllers/activitypub/index.ts
+++ b/server/controllers/activitypub/index.ts
@@ -1,4 +1,5 @@
1import express from 'express' 1import express from 'express'
2
2import { activityPubClientRouter } from './client' 3import { activityPubClientRouter } from './client'
3import { inboxRouter } from './inbox' 4import { inboxRouter } from './inbox'
4import { outboxRouter } from './outbox' 5import { outboxRouter } from './outbox'
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts
index 4175cf276..8c88b6971 100644
--- a/server/controllers/activitypub/outbox.ts
+++ b/server/controllers/activitypub/outbox.ts
@@ -7,7 +7,13 @@ import { VideoPrivacy } from '../../../shared/models/videos'
7import { logger } from '../../helpers/logger' 7import { logger } from '../../helpers/logger'
8import { buildAudience } from '../../lib/activitypub/audience' 8import { buildAudience } from '../../lib/activitypub/audience'
9import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send' 9import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
10import { asyncMiddleware, ensureIsLocalChannel, localAccountValidator, videoChannelsNameWithHostValidator } from '../../middlewares' 10import {
11 activityPubRateLimiter,
12 asyncMiddleware,
13 ensureIsLocalChannel,
14 localAccountValidator,
15 videoChannelsNameWithHostValidator
16} from '../../middlewares'
11import { apPaginationValidator } from '../../middlewares/validators/activitypub' 17import { apPaginationValidator } from '../../middlewares/validators/activitypub'
12import { VideoModel } from '../../models/video/video' 18import { VideoModel } from '../../models/video/video'
13import { activityPubResponse } from './utils' 19import { activityPubResponse } from './utils'
@@ -15,12 +21,14 @@ import { activityPubResponse } from './utils'
15const outboxRouter = express.Router() 21const outboxRouter = express.Router()
16 22
17outboxRouter.get('/accounts/:name/outbox', 23outboxRouter.get('/accounts/:name/outbox',
24 activityPubRateLimiter,
18 apPaginationValidator, 25 apPaginationValidator,
19 localAccountValidator, 26 localAccountValidator,
20 asyncMiddleware(outboxController) 27 asyncMiddleware(outboxController)
21) 28)
22 29
23outboxRouter.get('/video-channels/:nameWithHost/outbox', 30outboxRouter.get('/video-channels/:nameWithHost/outbox',
31 activityPubRateLimiter,
24 apPaginationValidator, 32 apPaginationValidator,
25 asyncMiddleware(videoChannelsNameWithHostValidator), 33 asyncMiddleware(videoChannelsNameWithHostValidator),
26 ensureIsLocalChannel, 34 ensureIsLocalChannel,
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index a85c10720..2d0c49904 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -5,27 +5,53 @@ import { join } from 'path'
5import { logger } from '@server/helpers/logger' 5import { logger } from '@server/helpers/logger'
6import { CONFIG } from '@server/initializers/config' 6import { CONFIG } from '@server/initializers/config'
7import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
8import { root } from '@shared/core-utils'
8import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' 9import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
9import { HttpStatusCode } from '@shared/models' 10import { HttpStatusCode } from '@shared/models'
10import { root } from '@shared/core-utils'
11import { STATIC_MAX_AGE } from '../initializers/constants' 11import { STATIC_MAX_AGE } from '../initializers/constants'
12import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html' 12import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html'
13import { asyncMiddleware, embedCSP } from '../middlewares' 13import { asyncMiddleware, buildRateLimiter, embedCSP } from '../middlewares'
14 14
15const clientsRouter = express.Router() 15const clientsRouter = express.Router()
16 16
17const clientsRateLimiter = buildRateLimiter({
18 windowMs: CONFIG.RATES_LIMIT.CLIENT.WINDOW_MS,
19 max: CONFIG.RATES_LIMIT.CLIENT.MAX
20})
21
17const distPath = join(root(), 'client', 'dist') 22const distPath = join(root(), 'client', 'dist')
18const testEmbedPath = join(distPath, 'standalone', 'videos', 'test-embed.html') 23const testEmbedPath = join(distPath, 'standalone', 'videos', 'test-embed.html')
19 24
20// Special route that add OpenGraph and oEmbed tags 25// Special route that add OpenGraph and oEmbed tags
21// Do not use a template engine for a so little thing 26// Do not use a template engine for a so little thing
22clientsRouter.use([ '/w/p/:id', '/videos/watch/playlist/:id' ], asyncMiddleware(generateWatchPlaylistHtmlPage)) 27clientsRouter.use([ '/w/p/:id', '/videos/watch/playlist/:id' ],
23clientsRouter.use([ '/w/:id', '/videos/watch/:id' ], asyncMiddleware(generateWatchHtmlPage)) 28 clientsRateLimiter,
24clientsRouter.use([ '/accounts/:nameWithHost', '/a/:nameWithHost' ], asyncMiddleware(generateAccountHtmlPage)) 29 asyncMiddleware(generateWatchPlaylistHtmlPage)
25clientsRouter.use([ '/video-channels/:nameWithHost', '/c/:nameWithHost' ], asyncMiddleware(generateVideoChannelHtmlPage)) 30)
26clientsRouter.use('/@:nameWithHost', asyncMiddleware(generateActorHtmlPage)) 31
32clientsRouter.use([ '/w/:id', '/videos/watch/:id' ],
33 clientsRateLimiter,
34 asyncMiddleware(generateWatchHtmlPage)
35)
36
37clientsRouter.use([ '/accounts/:nameWithHost', '/a/:nameWithHost' ],
38 clientsRateLimiter,
39 asyncMiddleware(generateAccountHtmlPage)
40)
41
42clientsRouter.use([ '/video-channels/:nameWithHost', '/c/:nameWithHost' ],
43 clientsRateLimiter,
44 asyncMiddleware(generateVideoChannelHtmlPage)
45)
46
47clientsRouter.use('/@:nameWithHost',
48 clientsRateLimiter,
49 asyncMiddleware(generateActorHtmlPage)
50)
27 51
28const embedMiddlewares = [ 52const embedMiddlewares = [
53 clientsRateLimiter,
54
29 CONFIG.CSP.ENABLED 55 CONFIG.CSP.ENABLED
30 ? embedCSP 56 ? embedCSP
31 : (req: express.Request, res: express.Response, next: express.NextFunction) => next(), 57 : (req: express.Request, res: express.Response, next: express.NextFunction) => next(),
@@ -48,11 +74,11 @@ clientsRouter.use('/video-playlists/embed', ...embedMiddlewares)
48 74
49const testEmbedController = (req: express.Request, res: express.Response) => res.sendFile(testEmbedPath) 75const testEmbedController = (req: express.Request, res: express.Response) => res.sendFile(testEmbedPath)
50 76
51clientsRouter.use('/videos/test-embed', testEmbedController) 77clientsRouter.use('/videos/test-embed', clientsRateLimiter, testEmbedController)
52clientsRouter.use('/video-playlists/test-embed', testEmbedController) 78clientsRouter.use('/video-playlists/test-embed', clientsRateLimiter, testEmbedController)
53 79
54// Dynamic PWA manifest 80// Dynamic PWA manifest
55clientsRouter.get('/manifest.webmanifest', asyncMiddleware(generateManifest)) 81clientsRouter.get('/manifest.webmanifest', clientsRateLimiter, asyncMiddleware(generateManifest))
56 82
57// Static client overrides 83// Static client overrides
58// Must be consistent with static client overrides redirections in /support/nginx/peertube 84// Must be consistent with static client overrides redirections in /support/nginx/peertube
@@ -88,7 +114,10 @@ clientsRouter.use('/client/*', (req: express.Request, res: express.Response) =>
88 114
89// Always serve index client page (the client is a single page application, let it handle routing) 115// Always serve index client page (the client is a single page application, let it handle routing)
90// Try to provide the right language index.html 116// Try to provide the right language index.html
91clientsRouter.use('/(:language)?', asyncMiddleware(serveIndexHTML)) 117clientsRouter.use('/(:language)?',
118 clientsRateLimiter,
119 asyncMiddleware(serveIndexHTML)
120)
92 121
93// --------------------------------------------------------------------------- 122// ---------------------------------------------------------------------------
94 123
diff --git a/server/controllers/feeds/comment-feeds.ts b/server/controllers/feeds/comment-feeds.ts
index bdc53b51f..68dc9ef90 100644
--- a/server/controllers/feeds/comment-feeds.ts
+++ b/server/controllers/feeds/comment-feeds.ts
@@ -23,7 +23,7 @@ const { middleware: cacheRouteMiddleware } = cacheRouteFactory({
23 23
24// --------------------------------------------------------------------------- 24// ---------------------------------------------------------------------------
25 25
26commentFeedsRouter.get('/feeds/video-comments.:format', 26commentFeedsRouter.get('/video-comments.:format',
27 feedsFormatValidator, 27 feedsFormatValidator,
28 setFeedFormatContentType, 28 setFeedFormatContentType,
29 cacheRouteMiddleware(ROUTE_CACHE_LIFETIME.FEEDS), 29 cacheRouteMiddleware(ROUTE_CACHE_LIFETIME.FEEDS),
diff --git a/server/controllers/feeds/index.ts b/server/controllers/feeds/index.ts
index e344a1448..19352318d 100644
--- a/server/controllers/feeds/index.ts
+++ b/server/controllers/feeds/index.ts
@@ -1,13 +1,22 @@
1import express from 'express' 1import express from 'express'
2import { CONFIG } from '@server/initializers/config'
3import { buildRateLimiter } from '@server/middlewares'
2import { commentFeedsRouter } from './comment-feeds' 4import { commentFeedsRouter } from './comment-feeds'
3import { videoFeedsRouter } from './video-feeds' 5import { videoFeedsRouter } from './video-feeds'
4import { videoPodcastFeedsRouter } from './video-podcast-feeds' 6import { videoPodcastFeedsRouter } from './video-podcast-feeds'
5 7
6const feedsRouter = express.Router() 8const feedsRouter = express.Router()
7 9
8feedsRouter.use('/', commentFeedsRouter) 10const feedsRateLimiter = buildRateLimiter({
9feedsRouter.use('/', videoFeedsRouter) 11 windowMs: CONFIG.RATES_LIMIT.FEEDS.WINDOW_MS,
10feedsRouter.use('/', videoPodcastFeedsRouter) 12 max: CONFIG.RATES_LIMIT.FEEDS.MAX
13})
14
15feedsRouter.use('/feeds', feedsRateLimiter)
16
17feedsRouter.use('/feeds', commentFeedsRouter)
18feedsRouter.use('/feeds', videoFeedsRouter)
19feedsRouter.use('/feeds', videoPodcastFeedsRouter)
11 20
12// --------------------------------------------------------------------------- 21// ---------------------------------------------------------------------------
13 22
diff --git a/server/controllers/feeds/video-feeds.ts b/server/controllers/feeds/video-feeds.ts
index b6e0663eb..97ac594ec 100644
--- a/server/controllers/feeds/video-feeds.ts
+++ b/server/controllers/feeds/video-feeds.ts
@@ -26,7 +26,7 @@ const { middleware: cacheRouteMiddleware } = cacheRouteFactory({
26 26
27// --------------------------------------------------------------------------- 27// ---------------------------------------------------------------------------
28 28
29videoFeedsRouter.get('/feeds/videos.:format', 29videoFeedsRouter.get('/videos.:format',
30 videosSortValidator, 30 videosSortValidator,
31 setDefaultVideosSort, 31 setDefaultVideosSort,
32 feedsFormatValidator, 32 feedsFormatValidator,
@@ -37,7 +37,7 @@ videoFeedsRouter.get('/feeds/videos.:format',
37 asyncMiddleware(generateVideoFeed) 37 asyncMiddleware(generateVideoFeed)
38) 38)
39 39
40videoFeedsRouter.get('/feeds/subscriptions.:format', 40videoFeedsRouter.get('/subscriptions.:format',
41 videosSortValidator, 41 videosSortValidator,
42 setDefaultVideosSort, 42 setDefaultVideosSort,
43 feedsFormatValidator, 43 feedsFormatValidator,
diff --git a/server/controllers/feeds/video-podcast-feeds.ts b/server/controllers/feeds/video-podcast-feeds.ts
index bd399580e..fca82ba68 100644
--- a/server/controllers/feeds/video-podcast-feeds.ts
+++ b/server/controllers/feeds/video-podcast-feeds.ts
@@ -40,7 +40,7 @@ for (const event of ([ 'channel-updated', 'channel-deleted' ] as const)) {
40 40
41// --------------------------------------------------------------------------- 41// ---------------------------------------------------------------------------
42 42
43videoPodcastFeedsRouter.get('/feeds/podcast/videos.xml', 43videoPodcastFeedsRouter.get('/podcast/videos.xml',
44 setFeedPodcastContentType, 44 setFeedPodcastContentType,
45 videoFeedsPodcastSetCacheKey, 45 videoFeedsPodcastSetCacheKey,
46 podcastCacheRouteMiddleware(ROUTE_CACHE_LIFETIME.FEEDS), 46 podcastCacheRouteMiddleware(ROUTE_CACHE_LIFETIME.FEEDS),
diff --git a/server/controllers/index.ts b/server/controllers/index.ts
index 09d657aca..8a647aff1 100644
--- a/server/controllers/index.ts
+++ b/server/controllers/index.ts
@@ -1,6 +1,6 @@
1export * from './activitypub' 1export * from './activitypub'
2export * from './api' 2export * from './api'
3export * from './bots' 3export * from './sitemap'
4export * from './client' 4export * from './client'
5export * from './download' 5export * from './download'
6export * from './feeds' 6export * from './feeds'
diff --git a/server/controllers/misc.ts b/server/controllers/misc.ts
index 163352ac5..a7dfc7867 100644
--- a/server/controllers/misc.ts
+++ b/server/controllers/misc.ts
@@ -7,7 +7,7 @@ import { HttpStatusCode } from '@shared/models'
7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model' 7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model'
8import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION, ROUTE_CACHE_LIFETIME } from '../initializers/constants' 8import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION, ROUTE_CACHE_LIFETIME } from '../initializers/constants'
9import { getThemeOrDefault } from '../lib/plugins/theme-utils' 9import { getThemeOrDefault } from '../lib/plugins/theme-utils'
10import { asyncMiddleware } from '../middlewares' 10import { apiRateLimiter, asyncMiddleware } from '../middlewares'
11import { cacheRoute } from '../middlewares/cache/cache' 11import { cacheRoute } from '../middlewares/cache/cache'
12import { UserModel } from '../models/user/user' 12import { UserModel } from '../models/user/user'
13import { VideoModel } from '../models/video/video' 13import { VideoModel } from '../models/video/video'
@@ -18,12 +18,14 @@ const miscRouter = express.Router()
18miscRouter.use(cors()) 18miscRouter.use(cors())
19 19
20miscRouter.use('/nodeinfo/:version.json', 20miscRouter.use('/nodeinfo/:version.json',
21 apiRateLimiter,
21 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO), 22 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
22 asyncMiddleware(generateNodeinfo) 23 asyncMiddleware(generateNodeinfo)
23) 24)
24 25
25// robots.txt service 26// robots.txt service
26miscRouter.get('/robots.txt', 27miscRouter.get('/robots.txt',
28 apiRateLimiter,
27 cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS), 29 cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS),
28 (_, res: express.Response) => { 30 (_, res: express.Response) => {
29 res.type('text/plain') 31 res.type('text/plain')
@@ -33,12 +35,14 @@ miscRouter.get('/robots.txt',
33) 35)
34 36
35miscRouter.all('/teapot', 37miscRouter.all('/teapot',
38 apiRateLimiter,
36 getCup, 39 getCup,
37 asyncMiddleware(serveIndexHTML) 40 asyncMiddleware(serveIndexHTML)
38) 41)
39 42
40// security.txt service 43// security.txt service
41miscRouter.get('/security.txt', 44miscRouter.get('/security.txt',
45 apiRateLimiter,
42 (_, res: express.Response) => { 46 (_, res: express.Response) => {
43 return res.redirect(HttpStatusCode.MOVED_PERMANENTLY_301, '/.well-known/security.txt') 47 return res.redirect(HttpStatusCode.MOVED_PERMANENTLY_301, '/.well-known/security.txt')
44 } 48 }
diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts
index 51db1ad89..f0491b16a 100644
--- a/server/controllers/plugins.ts
+++ b/server/controllers/plugins.ts
@@ -1,6 +1,8 @@
1import express from 'express' 1import express from 'express'
2import { join } from 'path' 2import { join } from 'path'
3import { logger } from '@server/helpers/logger' 3import { logger } from '@server/helpers/logger'
4import { CONFIG } from '@server/initializers/config'
5import { buildRateLimiter } from '@server/middlewares'
4import { optionalAuthenticate } from '@server/middlewares/auth' 6import { optionalAuthenticate } from '@server/middlewares/auth'
5import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n' 7import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n'
6import { HttpStatusCode } from '../../shared/models/http/http-error-codes' 8import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
@@ -18,57 +20,72 @@ const sendFileOptions = {
18 20
19const pluginsRouter = express.Router() 21const pluginsRouter = express.Router()
20 22
23const pluginsRateLimiter = buildRateLimiter({
24 windowMs: CONFIG.RATES_LIMIT.PLUGINS.WINDOW_MS,
25 max: CONFIG.RATES_LIMIT.PLUGINS.MAX
26})
27
21pluginsRouter.get('/plugins/global.css', 28pluginsRouter.get('/plugins/global.css',
29 pluginsRateLimiter,
22 servePluginGlobalCSS 30 servePluginGlobalCSS
23) 31)
24 32
25pluginsRouter.get('/plugins/translations/:locale.json', 33pluginsRouter.get('/plugins/translations/:locale.json',
34 pluginsRateLimiter,
26 getPluginTranslations 35 getPluginTranslations
27) 36)
28 37
29pluginsRouter.get('/plugins/:pluginName/:pluginVersion/auth/:authName', 38pluginsRouter.get('/plugins/:pluginName/:pluginVersion/auth/:authName',
39 pluginsRateLimiter,
30 getPluginValidator(PluginType.PLUGIN), 40 getPluginValidator(PluginType.PLUGIN),
31 getExternalAuthValidator, 41 getExternalAuthValidator,
32 handleAuthInPlugin 42 handleAuthInPlugin
33) 43)
34 44
35pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)', 45pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)',
46 pluginsRateLimiter,
36 getPluginValidator(PluginType.PLUGIN), 47 getPluginValidator(PluginType.PLUGIN),
37 pluginStaticDirectoryValidator, 48 pluginStaticDirectoryValidator,
38 servePluginStaticDirectory 49 servePluginStaticDirectory
39) 50)
40 51
41pluginsRouter.get('/plugins/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)', 52pluginsRouter.get('/plugins/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)',
53 pluginsRateLimiter,
42 getPluginValidator(PluginType.PLUGIN), 54 getPluginValidator(PluginType.PLUGIN),
43 pluginStaticDirectoryValidator, 55 pluginStaticDirectoryValidator,
44 servePluginClientScripts 56 servePluginClientScripts
45) 57)
46 58
47pluginsRouter.use('/plugins/:pluginName/router', 59pluginsRouter.use('/plugins/:pluginName/router',
60 pluginsRateLimiter,
48 getPluginValidator(PluginType.PLUGIN, false), 61 getPluginValidator(PluginType.PLUGIN, false),
49 optionalAuthenticate, 62 optionalAuthenticate,
50 servePluginCustomRoutes 63 servePluginCustomRoutes
51) 64)
52 65
53pluginsRouter.use('/plugins/:pluginName/:pluginVersion/router', 66pluginsRouter.use('/plugins/:pluginName/:pluginVersion/router',
67 pluginsRateLimiter,
54 getPluginValidator(PluginType.PLUGIN), 68 getPluginValidator(PluginType.PLUGIN),
55 optionalAuthenticate, 69 optionalAuthenticate,
56 servePluginCustomRoutes 70 servePluginCustomRoutes
57) 71)
58 72
59pluginsRouter.get('/themes/:pluginName/:pluginVersion/static/:staticEndpoint(*)', 73pluginsRouter.get('/themes/:pluginName/:pluginVersion/static/:staticEndpoint(*)',
74 pluginsRateLimiter,
60 getPluginValidator(PluginType.THEME), 75 getPluginValidator(PluginType.THEME),
61 pluginStaticDirectoryValidator, 76 pluginStaticDirectoryValidator,
62 servePluginStaticDirectory 77 servePluginStaticDirectory
63) 78)
64 79
65pluginsRouter.get('/themes/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)', 80pluginsRouter.get('/themes/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)',
81 pluginsRateLimiter,
66 getPluginValidator(PluginType.THEME), 82 getPluginValidator(PluginType.THEME),
67 pluginStaticDirectoryValidator, 83 pluginStaticDirectoryValidator,
68 servePluginClientScripts 84 servePluginClientScripts
69) 85)
70 86
71pluginsRouter.get('/themes/:themeName/:themeVersion/css/:staticEndpoint(*)', 87pluginsRouter.get('/themes/:themeName/:themeVersion/css/:staticEndpoint(*)',
88 pluginsRateLimiter,
72 serveThemeCSSValidator, 89 serveThemeCSSValidator,
73 serveThemeCSSDirectory 90 serveThemeCSSDirectory
74) 91)
diff --git a/server/controllers/services.ts b/server/controllers/services.ts
index 7c7ca1ff3..0fd63a30f 100644
--- a/server/controllers/services.ts
+++ b/server/controllers/services.ts
@@ -2,17 +2,19 @@ import express from 'express'
2import { MChannelSummary } from '@server/types/models' 2import { MChannelSummary } from '@server/types/models'
3import { escapeHTML } from '@shared/core-utils/renderer' 3import { escapeHTML } from '@shared/core-utils/renderer'
4import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants' 4import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants'
5import { asyncMiddleware, oembedValidator } from '../middlewares' 5import { apiRateLimiter, asyncMiddleware, oembedValidator } from '../middlewares'
6import { accountNameWithHostGetValidator } from '../middlewares/validators' 6import { accountNameWithHostGetValidator } from '../middlewares/validators'
7import { forceNumber } from '@shared/core-utils' 7import { forceNumber } from '@shared/core-utils'
8 8
9const servicesRouter = express.Router() 9const servicesRouter = express.Router()
10 10
11servicesRouter.use('/oembed', 11servicesRouter.use('/oembed',
12 apiRateLimiter,
12 asyncMiddleware(oembedValidator), 13 asyncMiddleware(oembedValidator),
13 generateOEmbed 14 generateOEmbed
14) 15)
15servicesRouter.use('/redirect/accounts/:accountName', 16servicesRouter.use('/redirect/accounts/:accountName',
17 apiRateLimiter,
16 asyncMiddleware(accountNameWithHostGetValidator), 18 asyncMiddleware(accountNameWithHostGetValidator),
17 redirectToAccountUrl 19 redirectToAccountUrl
18) 20)
diff --git a/server/controllers/bots.ts b/server/controllers/sitemap.ts
index 2b825a730..07f4c554e 100644
--- a/server/controllers/bots.ts
+++ b/server/controllers/sitemap.ts
@@ -5,17 +5,16 @@ import { logger } from '@server/helpers/logger'
5import { getServerActor } from '@server/models/application/application' 5import { getServerActor } from '@server/models/application/application'
6import { buildNSFWFilter } from '../helpers/express-utils' 6import { buildNSFWFilter } from '../helpers/express-utils'
7import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants' 7import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
8import { asyncMiddleware } from '../middlewares' 8import { apiRateLimiter, asyncMiddleware } from '../middlewares'
9import { cacheRoute } from '../middlewares/cache/cache' 9import { cacheRoute } from '../middlewares/cache/cache'
10import { AccountModel } from '../models/account/account' 10import { AccountModel } from '../models/account/account'
11import { VideoModel } from '../models/video/video' 11import { VideoModel } from '../models/video/video'
12import { VideoChannelModel } from '../models/video/video-channel' 12import { VideoChannelModel } from '../models/video/video-channel'
13 13
14const botsRouter = express.Router() 14const sitemapRouter = express.Router()
15 15
16// Special route that add OpenGraph and oEmbed tags 16sitemapRouter.use('/sitemap.xml',
17// Do not use a template engine for a so little thing 17 apiRateLimiter,
18botsRouter.use('/sitemap.xml',
19 cacheRoute(ROUTE_CACHE_LIFETIME.SITEMAP), 18 cacheRoute(ROUTE_CACHE_LIFETIME.SITEMAP),
20 asyncMiddleware(getSitemap) 19 asyncMiddleware(getSitemap)
21) 20)
@@ -23,7 +22,7 @@ botsRouter.use('/sitemap.xml',
23// --------------------------------------------------------------------------- 22// ---------------------------------------------------------------------------
24 23
25export { 24export {
26 botsRouter 25 sitemapRouter
27} 26}
28 27
29// --------------------------------------------------------------------------- 28// ---------------------------------------------------------------------------
diff --git a/server/controllers/well-known.ts b/server/controllers/well-known.ts
index bb9acfb37..322cf6ea2 100644
--- a/server/controllers/well-known.ts
+++ b/server/controllers/well-known.ts
@@ -1,7 +1,7 @@
1import cors from 'cors' 1import cors from 'cors'
2import express from 'express' 2import express from 'express'
3import { join } from 'path' 3import { join } from 'path'
4import { asyncMiddleware, handleStaticError, webfingerValidator } from '@server/middlewares' 4import { asyncMiddleware, buildRateLimiter, handleStaticError, webfingerValidator } from '@server/middlewares'
5import { root } from '@shared/core-utils' 5import { root } from '@shared/core-utils'
6import { CONFIG } from '../initializers/config' 6import { CONFIG } from '../initializers/config'
7import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants' 7import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
@@ -9,14 +9,21 @@ import { cacheRoute } from '../middlewares/cache/cache'
9 9
10const wellKnownRouter = express.Router() 10const wellKnownRouter = express.Router()
11 11
12const wellKnownRateLimiter = buildRateLimiter({
13 windowMs: CONFIG.RATES_LIMIT.WELL_KNOWN.WINDOW_MS,
14 max: CONFIG.RATES_LIMIT.WELL_KNOWN.MAX
15})
16
12wellKnownRouter.use(cors()) 17wellKnownRouter.use(cors())
13 18
14wellKnownRouter.get('/.well-known/webfinger', 19wellKnownRouter.get('/.well-known/webfinger',
20 wellKnownRateLimiter,
15 asyncMiddleware(webfingerValidator), 21 asyncMiddleware(webfingerValidator),
16 webfingerController 22 webfingerController
17) 23)
18 24
19wellKnownRouter.get('/.well-known/security.txt', 25wellKnownRouter.get('/.well-known/security.txt',
26 wellKnownRateLimiter,
20 cacheRoute(ROUTE_CACHE_LIFETIME.SECURITYTXT), 27 cacheRoute(ROUTE_CACHE_LIFETIME.SECURITYTXT),
21 (_, res: express.Response) => { 28 (_, res: express.Response) => {
22 res.type('text/plain') 29 res.type('text/plain')
@@ -26,6 +33,7 @@ wellKnownRouter.get('/.well-known/security.txt',
26 33
27// nodeinfo service 34// nodeinfo service
28wellKnownRouter.use('/.well-known/nodeinfo', 35wellKnownRouter.use('/.well-known/nodeinfo',
36 wellKnownRateLimiter,
29 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO), 37 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
30 (_, res: express.Response) => { 38 (_, res: express.Response) => {
31 return res.json({ 39 return res.json({
@@ -41,6 +49,7 @@ wellKnownRouter.use('/.well-known/nodeinfo',
41 49
42// dnt-policy.txt service (see https://www.eff.org/dnt-policy) 50// dnt-policy.txt service (see https://www.eff.org/dnt-policy)
43wellKnownRouter.use('/.well-known/dnt-policy.txt', 51wellKnownRouter.use('/.well-known/dnt-policy.txt',
52 wellKnownRateLimiter,
44 cacheRoute(ROUTE_CACHE_LIFETIME.DNT_POLICY), 53 cacheRoute(ROUTE_CACHE_LIFETIME.DNT_POLICY),
45 (_, res: express.Response) => { 54 (_, res: express.Response) => {
46 res.type('text/plain') 55 res.type('text/plain')
@@ -51,18 +60,21 @@ wellKnownRouter.use('/.well-known/dnt-policy.txt',
51 60
52// dnt service (see https://www.w3.org/TR/tracking-dnt/#status-resource) 61// dnt service (see https://www.w3.org/TR/tracking-dnt/#status-resource)
53wellKnownRouter.use('/.well-known/dnt/', 62wellKnownRouter.use('/.well-known/dnt/',
63 wellKnownRateLimiter,
54 (_, res: express.Response) => { 64 (_, res: express.Response) => {
55 res.json({ tracking: 'N' }) 65 res.json({ tracking: 'N' })
56 } 66 }
57) 67)
58 68
59wellKnownRouter.use('/.well-known/change-password', 69wellKnownRouter.use('/.well-known/change-password',
70 wellKnownRateLimiter,
60 (_, res: express.Response) => { 71 (_, res: express.Response) => {
61 res.redirect('/my-account/settings') 72 res.redirect('/my-account/settings')
62 } 73 }
63) 74)
64 75
65wellKnownRouter.use('/.well-known/host-meta', 76wellKnownRouter.use('/.well-known/host-meta',
77 wellKnownRateLimiter,
66 (_, res: express.Response) => { 78 (_, res: express.Response) => {
67 res.type('application/xml') 79 res.type('application/xml')
68 80
@@ -76,6 +88,7 @@ wellKnownRouter.use('/.well-known/host-meta',
76) 88)
77 89
78wellKnownRouter.use('/.well-known/', 90wellKnownRouter.use('/.well-known/',
91 wellKnownRateLimiter,
79 cacheRoute(ROUTE_CACHE_LIFETIME.WELL_KNOWN), 92 cacheRoute(ROUTE_CACHE_LIFETIME.WELL_KNOWN),
80 express.static(CONFIG.STORAGE.WELL_KNOWN_DIR, { fallthrough: false }), 93 express.static(CONFIG.STORAGE.WELL_KNOWN_DIR, { fallthrough: false }),
81 handleStaticError 94 handleStaticError
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index f77b0defb..0139ded4f 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -56,7 +56,11 @@ function checkMissedConfig () {
56 'followers.instance.enabled', 'followers.instance.manual_approval', 56 'followers.instance.enabled', 'followers.instance.manual_approval',
57 'tracker.enabled', 'tracker.private', 'tracker.reject_too_many_announces', 57 'tracker.enabled', 'tracker.private', 'tracker.reject_too_many_announces',
58 'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration', 58 'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration',
59 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max', 59 'rates_limit.api.window', 'rates_limit.api.max', 'rates_limit.login.window', 'rates_limit.login.max',
60 'rates_limit.signup.window', 'rates_limit.signup.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max',
61 'rates_limit.receive_client_log.window', 'rates_limit.receive_client_log.max', 'rates_limit.plugins.window', 'rates_limit.plugins.max',
62 'rates_limit.well_known.window', 'rates_limit.well_known.max', 'rates_limit.feeds.window', 'rates_limit.feeds.max',
63 'rates_limit.activity_pub.window', 'rates_limit.activity_pub.max', 'rates_limit.client.window', 'rates_limit.client.max',
60 'static_files.private_files_require_auth', 64 'static_files.private_files_require_auth',
61 'object_storage.enabled', 'object_storage.endpoint', 'object_storage.region', 'object_storage.upload_acl.public', 65 'object_storage.enabled', 'object_storage.endpoint', 'object_storage.region', 'object_storage.upload_acl.public',
62 'object_storage.upload_acl.private', 'object_storage.proxy.proxify_private_files', 'object_storage.credentials.access_key_id', 66 'object_storage.upload_acl.private', 'object_storage.proxy.proxify_private_files', 'object_storage.credentials.access_key_id',
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index f12d9b85a..2724990c1 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -183,6 +183,26 @@ const CONFIG = {
183 ASK_SEND_EMAIL: { 183 ASK_SEND_EMAIL: {
184 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.ask_send_email.window')), 184 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.ask_send_email.window')),
185 MAX: config.get<number>('rates_limit.ask_send_email.max') 185 MAX: config.get<number>('rates_limit.ask_send_email.max')
186 },
187 PLUGINS: {
188 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.plugins.window')),
189 MAX: config.get<number>('rates_limit.plugins.max')
190 },
191 WELL_KNOWN: {
192 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.well_known.window')),
193 MAX: config.get<number>('rates_limit.well_known.max')
194 },
195 FEEDS: {
196 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.feeds.window')),
197 MAX: config.get<number>('rates_limit.feeds.max')
198 },
199 ACTIVITY_PUB: {
200 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.activity_pub.window')),
201 MAX: config.get<number>('rates_limit.activity_pub.max')
202 },
203 CLIENT: {
204 WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.client.window')),
205 MAX: config.get<number>('rates_limit.client.max')
186 } 206 }
187 }, 207 },
188 TRUST_PROXY: config.get<string[]>('trust_proxy'), 208 TRUST_PROXY: config.get<string[]>('trust_proxy'),
diff --git a/server/middlewares/rate-limiter.ts b/server/middlewares/rate-limiter.ts
index 8257965dd..143d43632 100644
--- a/server/middlewares/rate-limiter.ts
+++ b/server/middlewares/rate-limiter.ts
@@ -45,6 +45,11 @@ export const apiRateLimiter = buildRateLimiter({
45 max: CONFIG.RATES_LIMIT.API.MAX 45 max: CONFIG.RATES_LIMIT.API.MAX
46}) 46})
47 47
48export const activityPubRateLimiter = buildRateLimiter({
49 windowMs: CONFIG.RATES_LIMIT.ACTIVITY_PUB.WINDOW_MS,
50 max: CONFIG.RATES_LIMIT.ACTIVITY_PUB.MAX
51})
52
48// --------------------------------------------------------------------------- 53// ---------------------------------------------------------------------------
49// Private 54// Private
50// --------------------------------------------------------------------------- 55// ---------------------------------------------------------------------------
diff --git a/server/tests/api/check-params/video-source.ts b/server/tests/api/check-params/video-source.ts
index 3c641ccd3..767590d5e 100644
--- a/server/tests/api/check-params/video-source.ts
+++ b/server/tests/api/check-params/video-source.ts
@@ -114,7 +114,7 @@ describe('Test video sources API validator', function () {
114 await server.videos.replaceSourceFile({ 114 await server.videos.replaceSourceFile({
115 fixture: 'video_short_fake.webm', 115 fixture: 'video_short_fake.webm',
116 videoId, 116 videoId,
117 expectedStatus: HttpStatusCode.UNPROCESSABLE_ENTITY_422 117 completedExpectedStatus: HttpStatusCode.UNPROCESSABLE_ENTITY_422
118 }) 118 })
119 119
120 await server.videos.replaceSourceFile({ 120 await server.videos.replaceSourceFile({
diff --git a/server/tests/api/search/search-activitypub-video-playlists.ts b/server/tests/api/search/search-activitypub-video-playlists.ts
index 25b162074..2bb5d869a 100644
--- a/server/tests/api/search/search-activitypub-video-playlists.ts
+++ b/server/tests/api/search/search-activitypub-video-playlists.ts
@@ -23,7 +23,7 @@ describe('Test ActivityPub playlists search', function () {
23 let command: SearchCommand 23 let command: SearchCommand
24 24
25 before(async function () { 25 before(async function () {
26 this.timeout(120000) 26 this.timeout(240000)
27 27
28 servers = await createMultipleServers(2) 28 servers = await createMultipleServers(2)
29 29