diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/config.ts | 20 | ||||
-rw-r--r-- | server/controllers/api/search.ts | 103 | ||||
-rw-r--r-- | server/initializers/checker-after-init.ts | 7 | ||||
-rw-r--r-- | server/initializers/checker-before-init.ts | 4 | ||||
-rw-r--r-- | server/initializers/config.ts | 18 | ||||
-rw-r--r-- | server/initializers/constants.ts | 11 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 17 | ||||
-rw-r--r-- | server/lib/plugins/plugin-index.ts | 3 | ||||
-rw-r--r-- | server/middlewares/validators/config.ts | 9 | ||||
-rw-r--r-- | server/models/account/account-blocklist.ts | 38 | ||||
-rw-r--r-- | server/models/server/server-blocklist.ts | 21 | ||||
-rw-r--r-- | server/tests/api/check-params/config.ts | 12 | ||||
-rw-r--r-- | server/tests/api/server/config.ts | 12 |
13 files changed, 255 insertions, 20 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 41e5027b9..1d48b4b26 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -76,6 +76,12 @@ async function getConfig (req: express.Request, res: express.Response) { | |||
76 | remoteUri: { | 76 | remoteUri: { |
77 | users: CONFIG.SEARCH.REMOTE_URI.USERS, | 77 | users: CONFIG.SEARCH.REMOTE_URI.USERS, |
78 | anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS | 78 | anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS |
79 | }, | ||
80 | searchIndex: { | ||
81 | enabled: CONFIG.SEARCH.SEARCH_INDEX.ENABLED, | ||
82 | url: CONFIG.SEARCH.SEARCH_INDEX.URL, | ||
83 | disableLocalSearch: CONFIG.SEARCH.SEARCH_INDEX.DISABLE_LOCAL_SEARCH, | ||
84 | isDefaultSearch: CONFIG.SEARCH.SEARCH_INDEX.IS_DEFAULT_SEARCH | ||
79 | } | 85 | } |
80 | }, | 86 | }, |
81 | plugin: { | 87 | plugin: { |
@@ -445,7 +451,19 @@ function customConfig (): CustomConfig { | |||
445 | message: CONFIG.BROADCAST_MESSAGE.MESSAGE, | 451 | message: CONFIG.BROADCAST_MESSAGE.MESSAGE, |
446 | level: CONFIG.BROADCAST_MESSAGE.LEVEL, | 452 | level: CONFIG.BROADCAST_MESSAGE.LEVEL, |
447 | dismissable: CONFIG.BROADCAST_MESSAGE.DISMISSABLE | 453 | dismissable: CONFIG.BROADCAST_MESSAGE.DISMISSABLE |
448 | } | 454 | }, |
455 | search: { | ||
456 | remoteUri: { | ||
457 | users: CONFIG.SEARCH.REMOTE_URI.USERS, | ||
458 | anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS | ||
459 | }, | ||
460 | searchIndex: { | ||
461 | enabled: CONFIG.SEARCH.SEARCH_INDEX.ENABLED, | ||
462 | url: CONFIG.SEARCH.SEARCH_INDEX.URL, | ||
463 | disableLocalSearch: CONFIG.SEARCH.SEARCH_INDEX.DISABLE_LOCAL_SEARCH, | ||
464 | isDefaultSearch: CONFIG.SEARCH.SEARCH_INDEX.IS_DEFAULT_SEARCH | ||
465 | } | ||
466 | }, | ||
449 | } | 467 | } |
450 | } | 468 | } |
451 | 469 | ||
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index 35d94d747..e08e1d79f 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts | |||
@@ -1,7 +1,19 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { sanitizeUrl } from '@server/helpers/core-utils' | ||
3 | import { doRequest } from '@server/helpers/requests' | ||
4 | import { CONFIG } from '@server/initializers/config' | ||
5 | import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos' | ||
6 | import { AccountBlocklistModel } from '@server/models/account/account-blocklist' | ||
7 | import { getServerActor } from '@server/models/application/application' | ||
8 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' | ||
9 | import { ResultList, Video, VideoChannel } from '@shared/models' | ||
10 | import { SearchTargetQuery } from '@shared/models/search/search-target-query.model' | ||
11 | import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search' | ||
2 | import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 12 | import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
13 | import { logger } from '../../helpers/logger' | ||
3 | import { getFormattedObjects } from '../../helpers/utils' | 14 | import { getFormattedObjects } from '../../helpers/utils' |
4 | import { VideoModel } from '../../models/video/video' | 15 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' |
16 | import { getOrCreateActorAndServerAndModel } from '../../lib/activitypub/actor' | ||
5 | import { | 17 | import { |
6 | asyncMiddleware, | 18 | asyncMiddleware, |
7 | commonVideosFiltersValidator, | 19 | commonVideosFiltersValidator, |
@@ -14,14 +26,9 @@ import { | |||
14 | videosSearchSortValidator, | 26 | videosSearchSortValidator, |
15 | videosSearchValidator | 27 | videosSearchValidator |
16 | } from '../../middlewares' | 28 | } from '../../middlewares' |
17 | import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search' | 29 | import { VideoModel } from '../../models/video/video' |
18 | import { getOrCreateActorAndServerAndModel } from '../../lib/activitypub/actor' | ||
19 | import { logger } from '../../helpers/logger' | ||
20 | import { VideoChannelModel } from '../../models/video/video-channel' | 30 | import { VideoChannelModel } from '../../models/video/video-channel' |
21 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | ||
22 | import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models' | 31 | import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models' |
23 | import { getServerActor } from '@server/models/application/application' | ||
24 | import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos' | ||
25 | 32 | ||
26 | const searchRouter = express.Router() | 33 | const searchRouter = express.Router() |
27 | 34 | ||
@@ -68,9 +75,34 @@ function searchVideoChannels (req: express.Request, res: express.Response) { | |||
68 | 75 | ||
69 | // @username -> username to search in DB | 76 | // @username -> username to search in DB |
70 | if (query.search.startsWith('@')) query.search = query.search.replace(/^@/, '') | 77 | if (query.search.startsWith('@')) query.search = query.search.replace(/^@/, '') |
78 | |||
79 | if (isSearchIndexEnabled(query)) { | ||
80 | return searchVideoChannelsIndex(query, res) | ||
81 | } | ||
82 | |||
71 | return searchVideoChannelsDB(query, res) | 83 | return searchVideoChannelsDB(query, res) |
72 | } | 84 | } |
73 | 85 | ||
86 | async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: express.Response) { | ||
87 | logger.debug('Doing channels search on search index.') | ||
88 | |||
89 | const result = await buildMutedForSearchIndex(res) | ||
90 | |||
91 | const body = Object.assign(query, result) | ||
92 | |||
93 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/video-channels' | ||
94 | |||
95 | try { | ||
96 | const searchIndexResult = await doRequest<ResultList<VideoChannel>>({ uri: url, body, json: true }) | ||
97 | |||
98 | return res.json(searchIndexResult.body) | ||
99 | } catch (err) { | ||
100 | logger.warn('Cannot use search index to make video channels search.', { err }) | ||
101 | |||
102 | return res.sendStatus(500) | ||
103 | } | ||
104 | } | ||
105 | |||
74 | async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { | 106 | async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { |
75 | const serverActor = await getServerActor() | 107 | const serverActor = await getServerActor() |
76 | 108 | ||
@@ -120,13 +152,38 @@ async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean | |||
120 | function searchVideos (req: express.Request, res: express.Response) { | 152 | function searchVideos (req: express.Request, res: express.Response) { |
121 | const query: VideosSearchQuery = req.query | 153 | const query: VideosSearchQuery = req.query |
122 | const search = query.search | 154 | const search = query.search |
155 | |||
123 | if (search && (search.startsWith('http://') || search.startsWith('https://'))) { | 156 | if (search && (search.startsWith('http://') || search.startsWith('https://'))) { |
124 | return searchVideoURI(search, res) | 157 | return searchVideoURI(search, res) |
125 | } | 158 | } |
126 | 159 | ||
160 | if (isSearchIndexEnabled(query)) { | ||
161 | return searchVideosIndex(query, res) | ||
162 | } | ||
163 | |||
127 | return searchVideosDB(query, res) | 164 | return searchVideosDB(query, res) |
128 | } | 165 | } |
129 | 166 | ||
167 | async function searchVideosIndex (query: VideosSearchQuery, res: express.Response) { | ||
168 | logger.debug('Doing videos search on search index.') | ||
169 | |||
170 | const result = await buildMutedForSearchIndex(res) | ||
171 | |||
172 | const body = Object.assign(query, result) | ||
173 | |||
174 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/videos' | ||
175 | |||
176 | try { | ||
177 | const searchIndexResult = await doRequest<ResultList<Video>>({ uri: url, body, json: true }) | ||
178 | |||
179 | return res.json(searchIndexResult.body) | ||
180 | } catch (err) { | ||
181 | logger.warn('Cannot use search index to make video search.', { err }) | ||
182 | |||
183 | return res.sendStatus(500) | ||
184 | } | ||
185 | } | ||
186 | |||
130 | async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { | 187 | async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { |
131 | const options = Object.assign(query, { | 188 | const options = Object.assign(query, { |
132 | includeLocalVideos: true, | 189 | includeLocalVideos: true, |
@@ -168,3 +225,35 @@ async function searchVideoURI (url: string, res: express.Response) { | |||
168 | data: video ? [ video.toFormattedJSON() ] : [] | 225 | data: video ? [ video.toFormattedJSON() ] : [] |
169 | }) | 226 | }) |
170 | } | 227 | } |
228 | |||
229 | function isSearchIndexEnabled (query: SearchTargetQuery) { | ||
230 | if (query.searchTarget === 'search-index') return true | ||
231 | |||
232 | const searchIndexConfig = CONFIG.SEARCH.SEARCH_INDEX | ||
233 | |||
234 | if (searchIndexConfig.ENABLED !== true) return false | ||
235 | |||
236 | if (searchIndexConfig.DISABLE_LOCAL_SEARCH) return true | ||
237 | if (searchIndexConfig.IS_DEFAULT_SEARCH && !query.searchTarget) return true | ||
238 | |||
239 | return false | ||
240 | } | ||
241 | |||
242 | async function buildMutedForSearchIndex (res: express.Response) { | ||
243 | const serverActor = await getServerActor() | ||
244 | const accountIds = [ serverActor.Account.id ] | ||
245 | |||
246 | if (res.locals.oauth) { | ||
247 | accountIds.push(res.locals.oauth.token.User.Account.id) | ||
248 | } | ||
249 | |||
250 | const [ blockedHosts, blockedAccounts ] = await Promise.all([ | ||
251 | ServerBlocklistModel.listHostsBlockedBy(accountIds), | ||
252 | AccountBlocklistModel.listHandlesBlockedBy(accountIds) | ||
253 | ]) | ||
254 | |||
255 | return { | ||
256 | blockedHosts, | ||
257 | blockedAccounts | ||
258 | } | ||
259 | } | ||
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts index b5b854137..b49ab6bca 100644 --- a/server/initializers/checker-after-init.ts +++ b/server/initializers/checker-after-init.ts | |||
@@ -128,6 +128,13 @@ function checkConfig () { | |||
128 | } | 128 | } |
129 | } | 129 | } |
130 | 130 | ||
131 | // Search index | ||
132 | if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) { | ||
133 | if (CONFIG.SEARCH.REMOTE_URI.USERS === false) { | ||
134 | return 'You cannot enable search index without enabling remote URI search for users.' | ||
135 | } | ||
136 | } | ||
137 | |||
131 | return null | 138 | return null |
132 | } | 139 | } |
133 | 140 | ||
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts index bd8f02bc0..e0819c4aa 100644 --- a/server/initializers/checker-before-init.ts +++ b/server/initializers/checker-before-init.ts | |||
@@ -35,7 +35,9 @@ function checkMissedConfig () { | |||
35 | 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max', | 35 | 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max', |
36 | 'theme.default', | 36 | 'theme.default', |
37 | 'remote_redundancy.videos.accept_from', | 37 | 'remote_redundancy.videos.accept_from', |
38 | 'federation.videos.federate_unlisted' | 38 | 'federation.videos.federate_unlisted', |
39 | 'search.remote_uri.users', 'search.remote_uri.anonymous', 'search.search_index.enabled', 'search.search_index.url', | ||
40 | 'search.search_index.disable_local_search', 'search.search_index.is_default_search' | ||
39 | ] | 41 | ] |
40 | const requiredAlternatives = [ | 42 | const requiredAlternatives = [ |
41 | [ // set | 43 | [ // set |
diff --git a/server/initializers/config.ts b/server/initializers/config.ts index 44fd9045b..5b402dd74 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts | |||
@@ -104,12 +104,6 @@ const CONFIG = { | |||
104 | }, | 104 | }, |
105 | ANONYMIZE_IP: config.get<boolean>('log.anonymizeIP') | 105 | ANONYMIZE_IP: config.get<boolean>('log.anonymizeIP') |
106 | }, | 106 | }, |
107 | SEARCH: { | ||
108 | REMOTE_URI: { | ||
109 | USERS: config.get<boolean>('search.remote_uri.users'), | ||
110 | ANONYMOUS: config.get<boolean>('search.remote_uri.anonymous') | ||
111 | } | ||
112 | }, | ||
113 | TRENDING: { | 107 | TRENDING: { |
114 | VIDEOS: { | 108 | VIDEOS: { |
115 | INTERVAL_DAYS: config.get<number>('trending.videos.interval_days') | 109 | INTERVAL_DAYS: config.get<number>('trending.videos.interval_days') |
@@ -297,6 +291,18 @@ const CONFIG = { | |||
297 | get MESSAGE () { return config.get<string>('broadcast_message.message') }, | 291 | get MESSAGE () { return config.get<string>('broadcast_message.message') }, |
298 | get LEVEL () { return config.get<BroadcastMessageLevel>('broadcast_message.level') }, | 292 | get LEVEL () { return config.get<BroadcastMessageLevel>('broadcast_message.level') }, |
299 | get DISMISSABLE () { return config.get<boolean>('broadcast_message.dismissable') } | 293 | get DISMISSABLE () { return config.get<boolean>('broadcast_message.dismissable') } |
294 | }, | ||
295 | SEARCH: { | ||
296 | REMOTE_URI: { | ||
297 | USERS: config.get<boolean>('search.remote_uri.users'), | ||
298 | ANONYMOUS: config.get<boolean>('search.remote_uri.anonymous') | ||
299 | }, | ||
300 | SEARCH_INDEX: { | ||
301 | get ENABLED () { return config.get<boolean>('search.search_index.enabled') }, | ||
302 | get URL () { return config.get<string>('search.search_index.url') }, | ||
303 | get DISABLE_LOCAL_SEARCH () { return config.get<boolean>('search.search_index.disable_local_search') }, | ||
304 | get IS_DEFAULT_SEARCH () { return config.get<boolean>('search.search_index.is_default_search') } | ||
305 | } | ||
300 | } | 306 | } |
301 | } | 307 | } |
302 | 308 | ||
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index d201df3d8..314f094b3 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -61,6 +61,7 @@ const SORTABLE_COLUMNS = { | |||
61 | 61 | ||
62 | VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'originallyPublishedAt', 'views', 'likes', 'trending' ], | 62 | VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'originallyPublishedAt', 'views', 'likes', 'trending' ], |
63 | 63 | ||
64 | // Don't forget to update peertube-search-index with the same values | ||
64 | VIDEOS_SEARCH: [ 'name', 'duration', 'createdAt', 'publishedAt', 'originallyPublishedAt', 'views', 'likes', 'match' ], | 65 | VIDEOS_SEARCH: [ 'name', 'duration', 'createdAt', 'publishedAt', 'originallyPublishedAt', 'views', 'likes', 'match' ], |
65 | VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName', 'createdAt' ], | 66 | VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName', 'createdAt' ], |
66 | 67 | ||
@@ -649,6 +650,15 @@ const DEFAULT_USER_THEME_NAME = 'instance-default' | |||
649 | 650 | ||
650 | // --------------------------------------------------------------------------- | 651 | // --------------------------------------------------------------------------- |
651 | 652 | ||
653 | const SEARCH_INDEX = { | ||
654 | ROUTES: { | ||
655 | VIDEOS: '/api/v1/search/videos', | ||
656 | VIDEO_CHANNELS: '/api/v1/search/video-channels' | ||
657 | } | ||
658 | } | ||
659 | |||
660 | // --------------------------------------------------------------------------- | ||
661 | |||
652 | // Special constants for a test instance | 662 | // Special constants for a test instance |
653 | if (isTestInstance() === true) { | 663 | if (isTestInstance() === true) { |
654 | PRIVATE_RSA_KEY_SIZE = 1024 | 664 | PRIVATE_RSA_KEY_SIZE = 1024 |
@@ -704,6 +714,7 @@ export { | |||
704 | API_VERSION, | 714 | API_VERSION, |
705 | PEERTUBE_VERSION, | 715 | PEERTUBE_VERSION, |
706 | LAZY_STATIC_PATHS, | 716 | LAZY_STATIC_PATHS, |
717 | SEARCH_INDEX, | ||
707 | HLS_REDUNDANCY_DIRECTORY, | 718 | HLS_REDUNDANCY_DIRECTORY, |
708 | P2P_MEDIA_LOADER_PEER_VERSION, | 719 | P2P_MEDIA_LOADER_PEER_VERSION, |
709 | AVATARS_SIZE, | 720 | AVATARS_SIZE, |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 7d16bd390..6d20e0e65 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -272,11 +272,22 @@ async function getOrCreateVideoAndAccountAndChannel ( | |||
272 | 272 | ||
273 | const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) | 273 | const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) |
274 | const videoChannel = actor.VideoChannel | 274 | const videoChannel = actor.VideoChannel |
275 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail) | ||
276 | 275 | ||
277 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) | 276 | try { |
277 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail) | ||
278 | |||
279 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) | ||
278 | 280 | ||
279 | return { video: videoCreated, created: true, autoBlacklisted } | 281 | return { video: videoCreated, created: true, autoBlacklisted } |
282 | } catch (err) { | ||
283 | // Maybe a concurrent getOrCreateVideoAndAccountAndChannel call created this video | ||
284 | if (err.name === 'SequelizeUniqueConstraintError') { | ||
285 | const fallbackVideo = await fetchVideoByUrl(videoUrl, fetchType) | ||
286 | if (fallbackVideo) return { video: fallbackVideo, created: false } | ||
287 | } | ||
288 | |||
289 | throw err | ||
290 | } | ||
280 | } | 291 | } |
281 | 292 | ||
282 | async function updateVideoFromAP (options: { | 293 | async function updateVideoFromAP (options: { |
diff --git a/server/lib/plugins/plugin-index.ts b/server/lib/plugins/plugin-index.ts index 170f0c7e2..7bcb6ed4c 100644 --- a/server/lib/plugins/plugin-index.ts +++ b/server/lib/plugins/plugin-index.ts | |||
@@ -11,6 +11,7 @@ import { PluginModel } from '../../models/server/plugin' | |||
11 | import { PluginManager } from './plugin-manager' | 11 | import { PluginManager } from './plugin-manager' |
12 | import { logger } from '../../helpers/logger' | 12 | import { logger } from '../../helpers/logger' |
13 | import { PEERTUBE_VERSION } from '../../initializers/constants' | 13 | import { PEERTUBE_VERSION } from '../../initializers/constants' |
14 | import { sanitizeUrl } from '@server/helpers/core-utils' | ||
14 | 15 | ||
15 | async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList) { | 16 | async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList) { |
16 | const { start = 0, count = 20, search, sort = 'npmName', pluginType } = options | 17 | const { start = 0, count = 20, search, sort = 'npmName', pluginType } = options |
@@ -55,7 +56,7 @@ async function getLatestPluginsVersion (npmNames: string[]): Promise<PeertubePlu | |||
55 | currentPeerTubeEngine: PEERTUBE_VERSION | 56 | currentPeerTubeEngine: PEERTUBE_VERSION |
56 | } | 57 | } |
57 | 58 | ||
58 | const uri = CONFIG.PLUGINS.INDEX.URL + '/api/v1/plugins/latest-version' | 59 | const uri = sanitizeUrl(CONFIG.PLUGINS.INDEX.URL) + '/api/v1/plugins/latest-version' |
59 | 60 | ||
60 | const { body } = await doRequest<any>({ uri, body: bodyRequest, json: true, method: 'POST' }) | 61 | const { body } = await doRequest<any>({ uri, body: bodyRequest, json: true, method: 'POST' }) |
61 | 62 | ||
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts index 6905ac762..d3669f6be 100644 --- a/server/middlewares/validators/config.ts +++ b/server/middlewares/validators/config.ts | |||
@@ -58,7 +58,14 @@ const customConfigUpdateValidator = [ | |||
58 | body('broadcastMessage.enabled').isBoolean().withMessage('Should have a valid broadcast message enabled boolean'), | 58 | body('broadcastMessage.enabled').isBoolean().withMessage('Should have a valid broadcast message enabled boolean'), |
59 | body('broadcastMessage.message').exists().withMessage('Should have a valid broadcast message'), | 59 | body('broadcastMessage.message').exists().withMessage('Should have a valid broadcast message'), |
60 | body('broadcastMessage.level').exists().withMessage('Should have a valid broadcast level'), | 60 | body('broadcastMessage.level').exists().withMessage('Should have a valid broadcast level'), |
61 | body('broadcastMessage.dismissable').exists().withMessage('Should have a valid broadcast dismissable boolean'), | 61 | body('broadcastMessage.dismissable').isBoolean().withMessage('Should have a valid broadcast dismissable boolean'), |
62 | |||
63 | body('search.remoteUri.users').isBoolean().withMessage('Should have a remote URI search for users boolean'), | ||
64 | body('search.remoteUri.anonymous').isBoolean().withMessage('Should have a valid remote URI search for anonymous boolean'), | ||
65 | body('search.searchIndex.enabled').isBoolean().withMessage('Should have a valid search index enabled boolean'), | ||
66 | body('search.searchIndex.url').exists().withMessage('Should have a valid search index URL'), | ||
67 | body('search.searchIndex.disableLocalSearch').isBoolean().withMessage('Should have a valid search index disable local search boolean'), | ||
68 | body('search.searchIndex.isDefaultSearch').isBoolean().withMessage('Should have a valid search index default enabled boolean'), | ||
62 | 69 | ||
63 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 70 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
64 | logger.debug('Checking customConfigUpdateValidator parameters', { parameters: req.body }) | 71 | logger.debug('Checking customConfigUpdateValidator parameters', { parameters: req.body }) |
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts index d8a7ce4b4..2c6b756d2 100644 --- a/server/models/account/account-blocklist.ts +++ b/server/models/account/account-blocklist.ts | |||
@@ -5,6 +5,8 @@ import { AccountBlock } from '../../../shared/models/blocklist' | |||
5 | import { Op } from 'sequelize' | 5 | import { Op } from 'sequelize' |
6 | import * as Bluebird from 'bluebird' | 6 | import * as Bluebird from 'bluebird' |
7 | import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/typings/models' | 7 | import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/typings/models' |
8 | import { ActorModel } from '../activitypub/actor' | ||
9 | import { ServerModel } from '../server/server' | ||
8 | 10 | ||
9 | enum ScopeNames { | 11 | enum ScopeNames { |
10 | WITH_ACCOUNTS = 'WITH_ACCOUNTS' | 12 | WITH_ACCOUNTS = 'WITH_ACCOUNTS' |
@@ -149,6 +151,42 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> { | |||
149 | }) | 151 | }) |
150 | } | 152 | } |
151 | 153 | ||
154 | static listHandlesBlockedBy (accountIds: number[]): Bluebird<string[]> { | ||
155 | const query = { | ||
156 | attributes: [], | ||
157 | where: { | ||
158 | accountId: { | ||
159 | [Op.in]: accountIds | ||
160 | } | ||
161 | }, | ||
162 | include: [ | ||
163 | { | ||
164 | attributes: [ 'id' ], | ||
165 | model: AccountModel.unscoped(), | ||
166 | required: true, | ||
167 | as: 'BlockedAccount', | ||
168 | include: [ | ||
169 | { | ||
170 | attributes: [ 'preferredUsername' ], | ||
171 | model: ActorModel.unscoped(), | ||
172 | required: true, | ||
173 | include: [ | ||
174 | { | ||
175 | attributes: [ 'host' ], | ||
176 | model: ServerModel.unscoped(), | ||
177 | required: true | ||
178 | } | ||
179 | ] | ||
180 | } | ||
181 | ] | ||
182 | } | ||
183 | ] | ||
184 | } | ||
185 | |||
186 | return AccountBlocklistModel.findAll(query) | ||
187 | .then(entries => entries.map(e => `${e.BlockedAccount.Actor.preferredUsername}@${e.BlockedAccount.Actor.Server.host}`)) | ||
188 | } | ||
189 | |||
152 | toFormattedJSON (this: MAccountBlocklistFormattable): AccountBlock { | 190 | toFormattedJSON (this: MAccountBlocklistFormattable): AccountBlock { |
153 | return { | 191 | return { |
154 | byAccount: this.ByAccount.toFormattedJSON(), | 192 | byAccount: this.ByAccount.toFormattedJSON(), |
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts index 892024c04..ad8e3d1e8 100644 --- a/server/models/server/server-blocklist.ts +++ b/server/models/server/server-blocklist.ts | |||
@@ -120,6 +120,27 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> { | |||
120 | return ServerBlocklistModel.findOne(query) | 120 | return ServerBlocklistModel.findOne(query) |
121 | } | 121 | } |
122 | 122 | ||
123 | static listHostsBlockedBy (accountIds: number[]): Bluebird<string[]> { | ||
124 | const query = { | ||
125 | attributes: [ ], | ||
126 | where: { | ||
127 | accountId: { | ||
128 | [Op.in]: accountIds | ||
129 | } | ||
130 | }, | ||
131 | include: [ | ||
132 | { | ||
133 | attributes: [ 'host' ], | ||
134 | model: ServerModel.unscoped(), | ||
135 | required: true | ||
136 | } | ||
137 | ] | ||
138 | } | ||
139 | |||
140 | return ServerBlocklistModel.findAll(query) | ||
141 | .then(entries => entries.map(e => e.BlockedServer.host)) | ||
142 | } | ||
143 | |||
123 | static listForApi (parameters: { | 144 | static listForApi (parameters: { |
124 | start: number | 145 | start: number |
125 | count: number | 146 | count: number |
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index 7c96fa762..3f2708f94 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts | |||
@@ -139,6 +139,18 @@ describe('Test config API validators', function () { | |||
139 | dismissable: true, | 139 | dismissable: true, |
140 | message: 'super message', | 140 | message: 'super message', |
141 | level: 'warning' | 141 | level: 'warning' |
142 | }, | ||
143 | search: { | ||
144 | remoteUri: { | ||
145 | users: true, | ||
146 | anonymous: true | ||
147 | }, | ||
148 | searchIndex: { | ||
149 | enabled: true, | ||
150 | url: 'https://search.joinpeertube.org', | ||
151 | disableLocalSearch: true, | ||
152 | isDefaultSearch: true | ||
153 | } | ||
142 | } | 154 | } |
143 | } | 155 | } |
144 | 156 | ||
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index d18a93082..597233588 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts | |||
@@ -340,6 +340,18 @@ describe('Test config', function () { | |||
340 | level: 'error', | 340 | level: 'error', |
341 | message: 'super bad message', | 341 | message: 'super bad message', |
342 | dismissable: true | 342 | dismissable: true |
343 | }, | ||
344 | search: { | ||
345 | remoteUri: { | ||
346 | anonymous: true, | ||
347 | users: true | ||
348 | }, | ||
349 | searchIndex: { | ||
350 | enabled: true, | ||
351 | url: 'https://search.joinpeertube.org', | ||
352 | disableLocalSearch: true, | ||
353 | isDefaultSearch: true | ||
354 | } | ||
343 | } | 355 | } |
344 | } | 356 | } |
345 | await updateCustomConfig(server.url, server.accessToken, newCustomConfig) | 357 | await updateCustomConfig(server.url, server.accessToken, newCustomConfig) |