diff options
-rw-r--r-- | server/controllers/api/search.ts | 37 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test/main.js | 24 | ||||
-rw-r--r-- | server/tests/plugins/filter-hooks.ts | 62 | ||||
-rw-r--r-- | shared/models/plugins/server-hook.model.ts | 10 |
4 files changed, 121 insertions, 12 deletions
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index 57357b1e8..f0cdf3a89 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts | |||
@@ -3,6 +3,7 @@ import { sanitizeUrl } from '@server/helpers/core-utils' | |||
3 | import { doJSONRequest } from '@server/helpers/requests' | 3 | import { doJSONRequest } from '@server/helpers/requests' |
4 | import { CONFIG } from '@server/initializers/config' | 4 | import { CONFIG } from '@server/initializers/config' |
5 | import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos' | 5 | import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos' |
6 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | import { AccountBlocklistModel } from '@server/models/account/account-blocklist' | 7 | import { AccountBlocklistModel } from '@server/models/account/account-blocklist' |
7 | import { getServerActor } from '@server/models/application/application' | 8 | import { getServerActor } from '@server/models/application/application' |
8 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' | 9 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' |
@@ -22,8 +23,8 @@ import { | |||
22 | paginationValidator, | 23 | paginationValidator, |
23 | setDefaultPagination, | 24 | setDefaultPagination, |
24 | setDefaultSearchSort, | 25 | setDefaultSearchSort, |
25 | videoChannelsSearchSortValidator, | ||
26 | videoChannelsListSearchValidator, | 26 | videoChannelsListSearchValidator, |
27 | videoChannelsSearchSortValidator, | ||
27 | videosSearchSortValidator, | 28 | videosSearchSortValidator, |
28 | videosSearchValidator | 29 | videosSearchValidator |
29 | } from '../../middlewares' | 30 | } from '../../middlewares' |
@@ -87,7 +88,7 @@ function searchVideoChannels (req: express.Request, res: express.Response) { | |||
87 | async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: express.Response) { | 88 | async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: express.Response) { |
88 | const result = await buildMutedForSearchIndex(res) | 89 | const result = await buildMutedForSearchIndex(res) |
89 | 90 | ||
90 | const body = Object.assign(query, result) | 91 | const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-channels.index.list.params') |
91 | 92 | ||
92 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/video-channels' | 93 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/video-channels' |
93 | 94 | ||
@@ -95,8 +96,9 @@ async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: e | |||
95 | logger.debug('Doing video channels search index request on %s.', url, { body }) | 96 | logger.debug('Doing video channels search index request on %s.', url, { body }) |
96 | 97 | ||
97 | const { body: searchIndexResult } = await doJSONRequest<ResultList<VideoChannel>>(url, { method: 'POST', json: body }) | 98 | const { body: searchIndexResult } = await doJSONRequest<ResultList<VideoChannel>>(url, { method: 'POST', json: body }) |
99 | const jsonResult = await Hooks.wrapObject(searchIndexResult, 'filter:api.search.video-channels.index.list.result') | ||
98 | 100 | ||
99 | return res.json(searchIndexResult) | 101 | return res.json(jsonResult) |
100 | } catch (err) { | 102 | } catch (err) { |
101 | logger.warn('Cannot use search index to make video channels search.', { err }) | 103 | logger.warn('Cannot use search index to make video channels search.', { err }) |
102 | 104 | ||
@@ -107,14 +109,19 @@ async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: e | |||
107 | async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { | 109 | async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { |
108 | const serverActor = await getServerActor() | 110 | const serverActor = await getServerActor() |
109 | 111 | ||
110 | const options = { | 112 | const apiOptions = await Hooks.wrapObject({ |
111 | actorId: serverActor.id, | 113 | actorId: serverActor.id, |
112 | search: query.search, | 114 | search: query.search, |
113 | start: query.start, | 115 | start: query.start, |
114 | count: query.count, | 116 | count: query.count, |
115 | sort: query.sort | 117 | sort: query.sort |
116 | } | 118 | }, 'filter:api.search.video-channels.local.list.params') |
117 | const resultList = await VideoChannelModel.searchForApi(options) | 119 | |
120 | const resultList = await Hooks.wrapPromiseFun( | ||
121 | VideoChannelModel.searchForApi, | ||
122 | apiOptions, | ||
123 | 'filter:api.search.video-channels.local.list.result' | ||
124 | ) | ||
118 | 125 | ||
119 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 126 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
120 | } | 127 | } |
@@ -168,7 +175,7 @@ function searchVideos (req: express.Request, res: express.Response) { | |||
168 | async function searchVideosIndex (query: VideosSearchQuery, res: express.Response) { | 175 | async function searchVideosIndex (query: VideosSearchQuery, res: express.Response) { |
169 | const result = await buildMutedForSearchIndex(res) | 176 | const result = await buildMutedForSearchIndex(res) |
170 | 177 | ||
171 | const body: VideosSearchQuery = Object.assign(query, result) | 178 | let body: VideosSearchQuery = Object.assign(query, result) |
172 | 179 | ||
173 | // Use the default instance NSFW policy if not specified | 180 | // Use the default instance NSFW policy if not specified |
174 | if (!body.nsfw) { | 181 | if (!body.nsfw) { |
@@ -181,14 +188,17 @@ async function searchVideosIndex (query: VideosSearchQuery, res: express.Respons | |||
181 | : 'both' | 188 | : 'both' |
182 | } | 189 | } |
183 | 190 | ||
191 | body = await Hooks.wrapObject(body, 'filter:api.search.videos.index.list.params') | ||
192 | |||
184 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/videos' | 193 | const url = sanitizeUrl(CONFIG.SEARCH.SEARCH_INDEX.URL) + '/api/v1/search/videos' |
185 | 194 | ||
186 | try { | 195 | try { |
187 | logger.debug('Doing videos search index request on %s.', url, { body }) | 196 | logger.debug('Doing videos search index request on %s.', url, { body }) |
188 | 197 | ||
189 | const { body: searchIndexResult } = await doJSONRequest<ResultList<Video>>(url, { method: 'POST', json: body }) | 198 | const { body: searchIndexResult } = await doJSONRequest<ResultList<Video>>(url, { method: 'POST', json: body }) |
199 | const jsonResult = await Hooks.wrapObject(searchIndexResult, 'filter:api.search.videos.index.list.result') | ||
190 | 200 | ||
191 | return res.json(searchIndexResult) | 201 | return res.json(jsonResult) |
192 | } catch (err) { | 202 | } catch (err) { |
193 | logger.warn('Cannot use search index to make video search.', { err }) | 203 | logger.warn('Cannot use search index to make video search.', { err }) |
194 | 204 | ||
@@ -197,13 +207,18 @@ async function searchVideosIndex (query: VideosSearchQuery, res: express.Respons | |||
197 | } | 207 | } |
198 | 208 | ||
199 | async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { | 209 | async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { |
200 | const options = Object.assign(query, { | 210 | const apiOptions = await Hooks.wrapObject(Object.assign(query, { |
201 | includeLocalVideos: true, | 211 | includeLocalVideos: true, |
202 | nsfw: buildNSFWFilter(res, query.nsfw), | 212 | nsfw: buildNSFWFilter(res, query.nsfw), |
203 | filter: query.filter, | 213 | filter: query.filter, |
204 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined | 214 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined |
205 | }) | 215 | }), 'filter:api.search.videos.local.list.params') |
206 | const resultList = await VideoModel.searchAndPopulateAccountAndServer(options) | 216 | |
217 | const resultList = await Hooks.wrapPromiseFun( | ||
218 | VideoModel.searchAndPopulateAccountAndServer, | ||
219 | apiOptions, | ||
220 | 'filter:api.search.videos.local.list.result' | ||
221 | ) | ||
207 | 222 | ||
208 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 223 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
209 | } | 224 | } |
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js index dfcc874d4..ee0bc39f3 100644 --- a/server/tests/fixtures/peertube-plugin-test/main.js +++ b/server/tests/fixtures/peertube-plugin-test/main.js | |||
@@ -230,6 +230,30 @@ async function register ({ registerHook, registerSetting, settingsManager, stora | |||
230 | } | 230 | } |
231 | } | 231 | } |
232 | }) | 232 | }) |
233 | |||
234 | { | ||
235 | const searchHooks = [ | ||
236 | 'filter:api.search.videos.local.list.params', | ||
237 | 'filter:api.search.videos.local.list.result', | ||
238 | 'filter:api.search.videos.index.list.params', | ||
239 | 'filter:api.search.videos.index.list.result', | ||
240 | 'filter:api.search.video-channels.local.list.params', | ||
241 | 'filter:api.search.video-channels.local.list.result', | ||
242 | 'filter:api.search.video-channels.index.list.params', | ||
243 | 'filter:api.search.video-channels.index.list.result', | ||
244 | ] | ||
245 | |||
246 | for (const h of searchHooks) { | ||
247 | registerHook({ | ||
248 | target: h, | ||
249 | handler: (obj) => { | ||
250 | peertubeHelpers.logger.debug('Run hook %s.', h) | ||
251 | |||
252 | return obj | ||
253 | } | ||
254 | }) | ||
255 | } | ||
256 | } | ||
233 | } | 257 | } |
234 | 258 | ||
235 | async function unregister () { | 259 | async function unregister () { |
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts index be47b20ba..6f7fec767 100644 --- a/server/tests/plugins/filter-hooks.ts +++ b/server/tests/plugins/filter-hooks.ts | |||
@@ -7,6 +7,7 @@ import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-code | |||
7 | import { | 7 | import { |
8 | addVideoCommentReply, | 8 | addVideoCommentReply, |
9 | addVideoCommentThread, | 9 | addVideoCommentThread, |
10 | advancedVideosSearch, | ||
10 | createLive, | 11 | createLive, |
11 | createVideoPlaylist, | 12 | createVideoPlaylist, |
12 | doubleFollow, | 13 | doubleFollow, |
@@ -25,6 +26,7 @@ import { | |||
25 | installPlugin, | 26 | installPlugin, |
26 | makeRawRequest, | 27 | makeRawRequest, |
27 | registerUser, | 28 | registerUser, |
29 | searchVideo, | ||
28 | setAccessTokensToServers, | 30 | setAccessTokensToServers, |
29 | setDefaultVideoChannel, | 31 | setDefaultVideoChannel, |
30 | updateCustomSubConfig, | 32 | updateCustomSubConfig, |
@@ -33,7 +35,7 @@ import { | |||
33 | uploadVideoAndGetId, | 35 | uploadVideoAndGetId, |
34 | waitJobs | 36 | waitJobs |
35 | } from '../../../shared/extra-utils' | 37 | } from '../../../shared/extra-utils' |
36 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo } from '../../../shared/extra-utils/server/servers' | 38 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' |
37 | import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' | 39 | import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' |
38 | import { | 40 | import { |
39 | VideoDetails, | 41 | VideoDetails, |
@@ -44,6 +46,7 @@ import { | |||
44 | VideoPrivacy | 46 | VideoPrivacy |
45 | } from '../../../shared/models/videos' | 47 | } from '../../../shared/models/videos' |
46 | import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' | 48 | import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' |
49 | import { advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels' | ||
47 | 50 | ||
48 | const expect = chai.expect | 51 | const expect = chai.expect |
49 | 52 | ||
@@ -468,6 +471,63 @@ describe('Test plugin filter hooks', function () { | |||
468 | }) | 471 | }) |
469 | }) | 472 | }) |
470 | 473 | ||
474 | describe('Search filters', function () { | ||
475 | |||
476 | before(async function () { | ||
477 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | ||
478 | search: { | ||
479 | searchIndex: { | ||
480 | enabled: true, | ||
481 | isDefaultSearch: false, | ||
482 | disableLocalSearch: false | ||
483 | } | ||
484 | } | ||
485 | }) | ||
486 | }) | ||
487 | |||
488 | it('Should run filter:api.search.videos.local.list.{params,result}', async function () { | ||
489 | await advancedVideosSearch(servers[0].url, { | ||
490 | search: 'Sun Quan' | ||
491 | }) | ||
492 | |||
493 | await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) | ||
494 | await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1) | ||
495 | }) | ||
496 | |||
497 | it('Should run filter:api.search.videos.index.list.{params,result}', async function () { | ||
498 | await advancedVideosSearch(servers[0].url, { | ||
499 | search: 'Sun Quan', | ||
500 | searchTarget: 'search-index' | ||
501 | }) | ||
502 | |||
503 | await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) | ||
504 | await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1) | ||
505 | await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.params', 1) | ||
506 | await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.result', 1) | ||
507 | }) | ||
508 | |||
509 | it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () { | ||
510 | await advancedVideoChannelSearch(servers[0].url, { | ||
511 | search: 'Sun Ce' | ||
512 | }) | ||
513 | |||
514 | await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) | ||
515 | await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1) | ||
516 | }) | ||
517 | |||
518 | it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () { | ||
519 | await advancedVideoChannelSearch(servers[0].url, { | ||
520 | search: 'Sun Ce', | ||
521 | searchTarget: 'search-index' | ||
522 | }) | ||
523 | |||
524 | await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) | ||
525 | await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1) | ||
526 | await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.params', 1) | ||
527 | await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.result', 1) | ||
528 | }) | ||
529 | }) | ||
530 | |||
471 | after(async function () { | 531 | after(async function () { |
472 | await cleanupTests(servers) | 532 | await cleanupTests(servers) |
473 | }) | 533 | }) |
diff --git a/shared/models/plugins/server-hook.model.ts b/shared/models/plugins/server-hook.model.ts index d28f76dfe..88277af5a 100644 --- a/shared/models/plugins/server-hook.model.ts +++ b/shared/models/plugins/server-hook.model.ts | |||
@@ -18,6 +18,16 @@ export const serverFilterHookObject = { | |||
18 | 'filter:api.user.me.videos.list.params': true, | 18 | 'filter:api.user.me.videos.list.params': true, |
19 | 'filter:api.user.me.videos.list.result': true, | 19 | 'filter:api.user.me.videos.list.result': true, |
20 | 20 | ||
21 | // Filter params/results to search videos/channels in the DB or on the remote index | ||
22 | 'filter:api.search.videos.local.list.params': true, | ||
23 | 'filter:api.search.videos.local.list.result': true, | ||
24 | 'filter:api.search.videos.index.list.params': true, | ||
25 | 'filter:api.search.videos.index.list.result': true, | ||
26 | 'filter:api.search.video-channels.local.list.params': true, | ||
27 | 'filter:api.search.video-channels.local.list.result': true, | ||
28 | 'filter:api.search.video-channels.index.list.params': true, | ||
29 | 'filter:api.search.video-channels.index.list.result': true, | ||
30 | |||
21 | // Filter the result of the get function | 31 | // Filter the result of the get function |
22 | // Used to get detailed video information (video watch page for example) | 32 | // Used to get detailed video information (video watch page for example) |
23 | 'filter:api.video.get.result': true, | 33 | 'filter:api.video.get.result': true, |