diff options
author | Chocobozzz <me@florianbigard.com> | 2020-05-29 16:16:24 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-06-10 14:02:41 +0200 |
commit | 5fb2e2888ce032c638e4b75d07458642f0833e52 (patch) | |
tree | 8830d873569316889b8134027e9a43b198cca38f /server/controllers/api | |
parent | 62e7be634bc189f942ae51cb4b080079ab503ff0 (diff) | |
download | PeerTube-5fb2e2888ce032c638e4b75d07458642f0833e52.tar.gz PeerTube-5fb2e2888ce032c638e4b75d07458642f0833e52.tar.zst PeerTube-5fb2e2888ce032c638e4b75d07458642f0833e52.zip |
First implem global search
Diffstat (limited to 'server/controllers/api')
-rw-r--r-- | server/controllers/api/config.ts | 20 | ||||
-rw-r--r-- | server/controllers/api/search.ts | 103 |
2 files changed, 115 insertions, 8 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 | } | ||