aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/config.ts20
-rw-r--r--server/controllers/api/search.ts103
-rw-r--r--server/initializers/checker-after-init.ts7
-rw-r--r--server/initializers/checker-before-init.ts4
-rw-r--r--server/initializers/config.ts18
-rw-r--r--server/initializers/constants.ts11
-rw-r--r--server/lib/activitypub/videos.ts17
-rw-r--r--server/lib/plugins/plugin-index.ts3
-rw-r--r--server/middlewares/validators/config.ts9
-rw-r--r--server/models/account/account-blocklist.ts38
-rw-r--r--server/models/server/server-blocklist.ts21
-rw-r--r--server/tests/api/check-params/config.ts12
-rw-r--r--server/tests/api/server/config.ts12
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 @@
1import * as express from 'express' 1import * as express from 'express'
2import { sanitizeUrl } from '@server/helpers/core-utils'
3import { doRequest } from '@server/helpers/requests'
4import { CONFIG } from '@server/initializers/config'
5import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos'
6import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
7import { getServerActor } from '@server/models/application/application'
8import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
9import { ResultList, Video, VideoChannel } from '@shared/models'
10import { SearchTargetQuery } from '@shared/models/search/search-target-query.model'
11import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search'
2import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 12import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
13import { logger } from '../../helpers/logger'
3import { getFormattedObjects } from '../../helpers/utils' 14import { getFormattedObjects } from '../../helpers/utils'
4import { VideoModel } from '../../models/video/video' 15import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
16import { getOrCreateActorAndServerAndModel } from '../../lib/activitypub/actor'
5import { 17import {
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'
17import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search' 29import { VideoModel } from '../../models/video/video'
18import { getOrCreateActorAndServerAndModel } from '../../lib/activitypub/actor'
19import { logger } from '../../helpers/logger'
20import { VideoChannelModel } from '../../models/video/video-channel' 30import { VideoChannelModel } from '../../models/video/video-channel'
21import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
22import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models' 31import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models'
23import { getServerActor } from '@server/models/application/application'
24import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos'
25 32
26const searchRouter = express.Router() 33const 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
86async 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
74async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { 106async 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
120function searchVideos (req: express.Request, res: express.Response) { 152function 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
167async 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
130async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { 187async 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
229function 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
242async 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
653const 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
653if (isTestInstance() === true) { 663if (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
282async function updateVideoFromAP (options: { 293async 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'
11import { PluginManager } from './plugin-manager' 11import { PluginManager } from './plugin-manager'
12import { logger } from '../../helpers/logger' 12import { logger } from '../../helpers/logger'
13import { PEERTUBE_VERSION } from '../../initializers/constants' 13import { PEERTUBE_VERSION } from '../../initializers/constants'
14import { sanitizeUrl } from '@server/helpers/core-utils'
14 15
15async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList) { 16async 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'
5import { Op } from 'sequelize' 5import { Op } from 'sequelize'
6import * as Bluebird from 'bluebird' 6import * as Bluebird from 'bluebird'
7import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/typings/models' 7import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/typings/models'
8import { ActorModel } from '../activitypub/actor'
9import { ServerModel } from '../server/server'
8 10
9enum ScopeNames { 11enum 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)