diff options
-rw-r--r-- | client/src/app/header/search-typeahead.component.ts | 6 | ||||
-rw-r--r-- | config/test.yaml | 2 | ||||
-rw-r--r-- | server/helpers/custom-validators/search.ts | 19 | ||||
-rw-r--r-- | server/middlewares/validators/search.ts | 4 | ||||
-rw-r--r-- | server/tests/api/check-params/search.ts | 79 | ||||
-rw-r--r-- | shared/models/search/videos-search-query.model.ts | 4 |
6 files changed, 103 insertions, 11 deletions
diff --git a/client/src/app/header/search-typeahead.component.ts b/client/src/app/header/search-typeahead.component.ts index 6c8b8efee..7d04e0f6d 100644 --- a/client/src/app/header/search-typeahead.component.ts +++ b/client/src/app/header/search-typeahead.component.ts | |||
@@ -112,10 +112,10 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewInit, AfterVie | |||
112 | const searchIndexConfig = this.serverConfig.search.searchIndex | 112 | const searchIndexConfig = this.serverConfig.search.searchIndex |
113 | 113 | ||
114 | if (!this.activeSearch) { | 114 | if (!this.activeSearch) { |
115 | if (searchIndexConfig.enabled && searchIndexConfig.isDefaultSearch) { | 115 | if (searchIndexConfig.enabled && (searchIndexConfig.isDefaultSearch || searchIndexConfig.disableLocalSearch)) { |
116 | this.activeSearch = 'search-instance' | ||
117 | } else { | ||
118 | this.activeSearch = 'search-index' | 116 | this.activeSearch = 'search-index' |
117 | } else { | ||
118 | this.activeSearch = 'search-instance' | ||
119 | } | 119 | } |
120 | } | 120 | } |
121 | 121 | ||
diff --git a/config/test.yaml b/config/test.yaml index da34ccd03..fb37ff8c7 100644 --- a/config/test.yaml +++ b/config/test.yaml | |||
@@ -110,7 +110,7 @@ search: | |||
110 | # Use a third party index instead of your local index, only for search results | 110 | # Use a third party index instead of your local index, only for search results |
111 | # Useful to discover content outside of your instance | 111 | # Useful to discover content outside of your instance |
112 | search_index: | 112 | search_index: |
113 | enabled: true | 113 | enabled: false |
114 | # URL of the search index, that should use the same search API and routes | 114 | # URL of the search index, that should use the same search API and routes |
115 | # than PeerTube: https://docs.joinpeertube.org/api-rest-reference.html | 115 | # than PeerTube: https://docs.joinpeertube.org/api-rest-reference.html |
116 | # You should deploy your own with https://framagit.org/framasoft/peertube/search-index, | 116 | # You should deploy your own with https://framagit.org/framasoft/peertube/search-index, |
diff --git a/server/helpers/custom-validators/search.ts b/server/helpers/custom-validators/search.ts index bb17134c3..429fcafcf 100644 --- a/server/helpers/custom-validators/search.ts +++ b/server/helpers/custom-validators/search.ts | |||
@@ -1,5 +1,7 @@ | |||
1 | import validator from 'validator' | 1 | import validator from 'validator' |
2 | import { isArray } from './misc' | 2 | import { SearchTargetType } from '@shared/models/search/search-target-query.model' |
3 | import { isArray, exists } from './misc' | ||
4 | import { CONFIG } from '@server/initializers/config' | ||
3 | 5 | ||
4 | function isNumberArray (value: any) { | 6 | function isNumberArray (value: any) { |
5 | return isArray(value) && value.every(v => validator.isInt('' + v)) | 7 | return isArray(value) && value.every(v => validator.isInt('' + v)) |
@@ -13,10 +15,23 @@ function isNSFWQueryValid (value: any) { | |||
13 | return value === 'true' || value === 'false' || value === 'both' | 15 | return value === 'true' || value === 'false' || value === 'both' |
14 | } | 16 | } |
15 | 17 | ||
18 | function isSearchTargetValid (value: SearchTargetType) { | ||
19 | if (!exists(value)) return true | ||
20 | |||
21 | const searchIndexConfig = CONFIG.SEARCH.SEARCH_INDEX | ||
22 | |||
23 | if (value === 'local' && (!searchIndexConfig.ENABLED || !searchIndexConfig.DISABLE_LOCAL_SEARCH)) return true | ||
24 | |||
25 | if (value === 'search-index' && searchIndexConfig.ENABLED) return true | ||
26 | |||
27 | return false | ||
28 | } | ||
29 | |||
16 | // --------------------------------------------------------------------------- | 30 | // --------------------------------------------------------------------------- |
17 | 31 | ||
18 | export { | 32 | export { |
19 | isNumberArray, | 33 | isNumberArray, |
20 | isStringArray, | 34 | isStringArray, |
21 | isNSFWQueryValid | 35 | isNSFWQueryValid, |
36 | isSearchTargetValid | ||
22 | } | 37 | } |
diff --git a/server/middlewares/validators/search.ts b/server/middlewares/validators/search.ts index 5a3c83f2c..b4faa8894 100644 --- a/server/middlewares/validators/search.ts +++ b/server/middlewares/validators/search.ts | |||
@@ -3,6 +3,7 @@ import { areValidationErrors } from './utils' | |||
3 | import { logger } from '../../helpers/logger' | 3 | import { logger } from '../../helpers/logger' |
4 | import { query } from 'express-validator' | 4 | import { query } from 'express-validator' |
5 | import { isDateValid } from '../../helpers/custom-validators/misc' | 5 | import { isDateValid } from '../../helpers/custom-validators/misc' |
6 | import { isSearchTargetValid } from '@server/helpers/custom-validators/search' | ||
6 | 7 | ||
7 | const videosSearchValidator = [ | 8 | const videosSearchValidator = [ |
8 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), | 9 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), |
@@ -16,6 +17,8 @@ const videosSearchValidator = [ | |||
16 | query('durationMin').optional().isInt().withMessage('Should have a valid min duration'), | 17 | query('durationMin').optional().isInt().withMessage('Should have a valid min duration'), |
17 | query('durationMax').optional().isInt().withMessage('Should have a valid max duration'), | 18 | query('durationMax').optional().isInt().withMessage('Should have a valid max duration'), |
18 | 19 | ||
20 | query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), | ||
21 | |||
19 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 22 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
20 | logger.debug('Checking videos search query', { parameters: req.query }) | 23 | logger.debug('Checking videos search query', { parameters: req.query }) |
21 | 24 | ||
@@ -27,6 +30,7 @@ const videosSearchValidator = [ | |||
27 | 30 | ||
28 | const videoChannelsSearchValidator = [ | 31 | const videoChannelsSearchValidator = [ |
29 | query('search').not().isEmpty().withMessage('Should have a valid search'), | 32 | query('search').not().isEmpty().withMessage('Should have a valid search'), |
33 | query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), | ||
30 | 34 | ||
31 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 35 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
32 | logger.debug('Checking video channels search query', { parameters: req.query }) | 36 | logger.debug('Checking video channels search query', { parameters: req.query }) |
diff --git a/server/tests/api/check-params/search.ts b/server/tests/api/check-params/search.ts index f8d0cd4ec..1a8a7235e 100644 --- a/server/tests/api/check-params/search.ts +++ b/server/tests/api/check-params/search.ts | |||
@@ -1,14 +1,32 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | 4 | import { | |
5 | import { cleanupTests, flushAndRunServer, immutableAssign, makeGetRequest, ServerInfo } from '../../../../shared/extra-utils' | 5 | cleanupTests, |
6 | flushAndRunServer, | ||
7 | immutableAssign, | ||
8 | makeGetRequest, | ||
9 | ServerInfo, | ||
10 | updateCustomSubConfig, | ||
11 | setAccessTokensToServers | ||
12 | } from '../../../../shared/extra-utils' | ||
6 | import { | 13 | import { |
7 | checkBadCountPagination, | 14 | checkBadCountPagination, |
8 | checkBadSortPagination, | 15 | checkBadSortPagination, |
9 | checkBadStartPagination | 16 | checkBadStartPagination |
10 | } from '../../../../shared/extra-utils/requests/check-api-params' | 17 | } from '../../../../shared/extra-utils/requests/check-api-params' |
11 | 18 | ||
19 | function updateSearchIndex (server: ServerInfo, enabled: boolean, disableLocalSearch = false) { | ||
20 | return updateCustomSubConfig(server.url, server.accessToken, { | ||
21 | search: { | ||
22 | searchIndex: { | ||
23 | enabled, | ||
24 | disableLocalSearch | ||
25 | } | ||
26 | } | ||
27 | }) | ||
28 | } | ||
29 | |||
12 | describe('Test videos API validator', function () { | 30 | describe('Test videos API validator', function () { |
13 | let server: ServerInfo | 31 | let server: ServerInfo |
14 | 32 | ||
@@ -18,6 +36,7 @@ describe('Test videos API validator', function () { | |||
18 | this.timeout(30000) | 36 | this.timeout(30000) |
19 | 37 | ||
20 | server = await flushAndRunServer(1) | 38 | server = await flushAndRunServer(1) |
39 | await setAccessTokensToServers([ server ]) | ||
21 | }) | 40 | }) |
22 | 41 | ||
23 | describe('When searching videos', function () { | 42 | describe('When searching videos', function () { |
@@ -144,6 +163,62 @@ describe('Test videos API validator', function () { | |||
144 | }) | 163 | }) |
145 | }) | 164 | }) |
146 | 165 | ||
166 | describe('Search target', function () { | ||
167 | |||
168 | it('Should fail/succeed depending on the search target', async function () { | ||
169 | this.timeout(10000) | ||
170 | |||
171 | const query = { search: 'coucou' } | ||
172 | const paths = [ | ||
173 | '/api/v1/search/video-channels/', | ||
174 | '/api/v1/search/videos/' | ||
175 | ] | ||
176 | |||
177 | for (const path of paths) { | ||
178 | { | ||
179 | const customQuery = immutableAssign(query, { searchTarget: 'hello' }) | ||
180 | await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: 400 }) | ||
181 | } | ||
182 | |||
183 | { | ||
184 | const customQuery = immutableAssign(query, { searchTarget: undefined }) | ||
185 | await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: 200 }) | ||
186 | } | ||
187 | |||
188 | { | ||
189 | const customQuery = immutableAssign(query, { searchTarget: 'local' }) | ||
190 | await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: 200 }) | ||
191 | } | ||
192 | |||
193 | { | ||
194 | const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) | ||
195 | await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: 400 }) | ||
196 | } | ||
197 | |||
198 | await updateSearchIndex(server, true, true) | ||
199 | |||
200 | { | ||
201 | const customQuery = immutableAssign(query, { searchTarget: 'local' }) | ||
202 | await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: 400 }) | ||
203 | } | ||
204 | |||
205 | { | ||
206 | const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) | ||
207 | await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: 200 }) | ||
208 | } | ||
209 | |||
210 | await updateSearchIndex(server, true, false) | ||
211 | |||
212 | { | ||
213 | const customQuery = immutableAssign(query, { searchTarget: 'local' }) | ||
214 | await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: 200 }) | ||
215 | } | ||
216 | |||
217 | await updateSearchIndex(server, false, false) | ||
218 | } | ||
219 | }) | ||
220 | }) | ||
221 | |||
147 | after(async function () { | 222 | after(async function () { |
148 | await cleanupTests([ server ]) | 223 | await cleanupTests([ server ]) |
149 | }) | 224 | }) |
diff --git a/shared/models/search/videos-search-query.model.ts b/shared/models/search/videos-search-query.model.ts index bd6bb5bc1..3ce4ff73e 100644 --- a/shared/models/search/videos-search-query.model.ts +++ b/shared/models/search/videos-search-query.model.ts | |||
@@ -1,10 +1,8 @@ | |||
1 | import { NSFWQuery } from './nsfw-query.model' | ||
2 | import { VideoFilter } from '../videos' | 1 | import { VideoFilter } from '../videos' |
2 | import { NSFWQuery } from './nsfw-query.model' | ||
3 | import { SearchTargetQuery } from './search-target-query.model' | 3 | import { SearchTargetQuery } from './search-target-query.model' |
4 | 4 | ||
5 | export interface VideosSearchQuery extends SearchTargetQuery { | 5 | export interface VideosSearchQuery extends SearchTargetQuery { |
6 | forceLocalSearch?: boolean | ||
7 | |||
8 | search?: string | 6 | search?: string |
9 | 7 | ||
10 | start?: number | 8 | start?: number |