.oO SearXNG Developer Documentation Oo.
Loading...
Searching...
No Matches
searx.webapp Namespace Reference

Functions

 get_locale ()
 _get_browser_language (req, lang_list)
 _get_locale_rfc5646 (locale)
 code_highlighter (codelines, language=None)
 get_result_template (str theme_name, str template_name)
 custom_url_for (str endpoint, **values)
 image_proxify (str url)
 get_translations ()
 get_enabled_categories (typing.Iterable[str] category_names)
 get_pretty_url (urllib.parse.ParseResult parsed_url)
 get_client_settings ()
 render (str template_name, **kwargs)
 pre_request ()
 add_default_headers (flask.Response response)
 post_request (flask.Response response)
 index_error (str output_format, str error_message)
 index ()
 health ()
 client_token (token=None)
 rss_xsl ()
 search ()
 about ()
 info (pagename, locale)
 autocompleter ()
 preferences ()
 image_proxy ()
 engine_descriptions ()
 stats ()
 stats_errors ()
 stats_checker ()
 stats_open_metrics ()
 robots ()
 opensearch ()
 favicon ()
 clear_cookies ()
 config ()
 page_not_found (_e)
 run ()
 init ()
None static_headers (Headers headers, str _path, str _url)

Variables

 logger = logger.getChild('webapp')
 default_theme = settings['ui']['default_theme']
 templates_path = settings['ui']['templates_path']
 themes = get_themes(templates_path)
 result_templates = get_result_templates(templates_path)
dict STATS_SORT_PARAMETERS
 app = Flask(__name__, static_folder=None, template_folder=templates_path)
 trim_blocks
 lstrip_blocks
 secret_key
 babel = Babel(app, locale_selector=get_locale)
list _STATIC_FILES = []
 methods
 endpoint
 view_func
 wsgi_app
 root
 prefix
 max_age
 allow_all_origins
 add_headers_function
 application = app

Detailed Description

WebApp

Function Documentation

◆ _get_browser_language()

searx.webapp._get_browser_language ( req,
lang_list )
protected

Definition at line 166 of file webapp.py.

166def _get_browser_language(req, lang_list):
167 client = ClientPref.from_http_request(req)
168 locale = match_locale(client.locale_tag, lang_list, fallback='en')
169 return locale
170
171

Referenced by pre_request().

Here is the caller graph for this function:

◆ _get_locale_rfc5646()

searx.webapp._get_locale_rfc5646 ( locale)
protected
Get locale name for <html lang="...">
Chrom* browsers don't detect the language when there is a subtag (ie a territory).
For example "zh-TW" is detected but not "zh-Hant-TW".
This function returns a locale without the subtag.

Definition at line 172 of file webapp.py.

172def _get_locale_rfc5646(locale):
173 """Get locale name for <html lang="...">
174 Chrom* browsers don't detect the language when there is a subtag (ie a territory).
175 For example "zh-TW" is detected but not "zh-Hant-TW".
176 This function returns a locale without the subtag.
177 """
178 parts = locale.split('-')
179 return parts[0].lower() + '-' + parts[-1].upper()
180
181
182# code-highlighter
183@app.template_filter('code_highlighter')

Referenced by render().

Here is the caller graph for this function:

◆ about()

searx.webapp.about ( )
Redirect to about page

Definition at line 775 of file webapp.py.

775def about():
776 """Redirect to about page"""
777 # custom_url_for is going to add the locale
778 return redirect(custom_url_for('info', pagename='about'))
779
780
781@app.route('/info/<locale>/<pagename>', methods=['GET'])

References custom_url_for().

Here is the call graph for this function:

◆ add_default_headers()

searx.webapp.add_default_headers ( flask.Response response)

Definition at line 508 of file webapp.py.

508def add_default_headers(response: flask.Response):
509 # set default http headers
510 for header, value in settings['server']['default_http_headers'].items():
511 if header in response.headers:
512 continue
513 response.headers[header] = value
514 return response
515
516
517@app.after_request

◆ autocompleter()

searx.webapp.autocompleter ( )
Return autocompleter results

Definition at line 798 of file webapp.py.

798def autocompleter():
799 """Return autocompleter results"""
800
801 # run autocompleter
802 results = []
803
804 # set blocked engines
805 disabled_engines = sxng_request.preferences.engines.get_disabled()
806
807 # parse query
808 raw_text_query = RawTextQuery(sxng_request.form.get('q', ''), disabled_engines)
809 sug_prefix = raw_text_query.getQuery()
810
811 for obj in searx.answerers.STORAGE.ask(sug_prefix):
812 if isinstance(obj, Answer):
813 results.append(obj.answer)
814
815 # normal autocompletion results only appear if no inner results returned
816 # and there is a query part
817 if len(raw_text_query.autocomplete_list) == 0 and len(sug_prefix) > 0:
818
819 # get SearXNG's locale and autocomplete backend from cookie
820 sxng_locale = sxng_request.preferences.get_value('language')
821 backend_name = sxng_request.preferences.get_value('autocomplete')
822
823 for result in search_autocomplete(backend_name, sug_prefix, sxng_locale):
824 # attention: this loop will change raw_text_query object and this is
825 # the reason why the sug_prefix was stored before (see above)
826 if result != sug_prefix:
827 results.append(raw_text_query.changeQuery(result).getFullQuery())
828
829 if len(raw_text_query.autocomplete_list) > 0:
830 for autocomplete_text in raw_text_query.autocomplete_list:
831 results.append(raw_text_query.get_autocomplete_full_query(autocomplete_text))
832
833 if sxng_request.headers.get('X-Requested-With') == 'XMLHttpRequest':
834 # the suggestion request comes from the searx search form
835 suggestions = json.dumps(results)
836 mimetype = 'application/json'
837 else:
838 # the suggestion request comes from browser's URL bar
839 suggestions = json.dumps([sug_prefix, results])
840 mimetype = 'application/x-suggestions+json'
841
842 suggestions = escape(suggestions, False)
843 return Response(suggestions, mimetype=mimetype)
844
845
846@app.route('/preferences', methods=['GET', 'POST'])

◆ clear_cookies()

searx.webapp.clear_cookies ( )

Definition at line 1252 of file webapp.py.

1252def clear_cookies():
1253 resp = make_response(redirect(url_for('index', _external=True)))
1254 for cookie_name in sxng_request.cookies:
1255 resp.delete_cookie(cookie_name)
1256 return resp
1257
1258
1259@app.route('/config')

◆ client_token()

searx.webapp.client_token ( token = None)

Definition at line 592 of file webapp.py.

592def client_token(token=None):
593 link_token.ping(sxng_request, token)
594 return Response('', mimetype='text/css', headers={"Cache-Control": "no-store, max-age=0"})
595
596
597@app.route('/rss.xsl', methods=['GET', 'POST'])

◆ code_highlighter()

searx.webapp.code_highlighter ( codelines,
language = None )

Definition at line 184 of file webapp.py.

184def code_highlighter(codelines, language=None):
185 if not language:
186 language = 'text'
187
188 try:
189 # find lexer by programming language
190 lexer = get_lexer_by_name(language, stripall=True)
191
192 except Exception as e: # pylint: disable=broad-except
193 logger.warning("pygments lexer: %s " % e)
194 # if lexer is not found, using default one
195 lexer = get_lexer_by_name('text', stripall=True)
196
197 html_code = ''
198 tmp_code = ''
199 last_line = None
200 line_code_start = None
201
202 # parse lines
203 for line, code in codelines:
204 if not last_line:
205 line_code_start = line
206
207 # new codeblock is detected
208 if last_line is not None and last_line + 1 != line:
209
210 # highlight last codepart
211 formatter = HtmlFormatter(linenos='inline', linenostart=line_code_start, cssclass="code-highlight")
212 html_code = html_code + highlight(tmp_code, lexer, formatter)
213
214 # reset conditions for next codepart
215 tmp_code = ''
216 line_code_start = line
217
218 # add codepart
219 tmp_code += code + '\n'
220
221 # update line
222 last_line = line
223
224 # highlight last codepart
225 formatter = HtmlFormatter(linenos='inline', linenostart=line_code_start, cssclass="code-highlight")
226 html_code = html_code + highlight(tmp_code, lexer, formatter)
227
228 return html_code
229
230

◆ config()

searx.webapp.config ( )
Return configuration in JSON format.

Definition at line 1260 of file webapp.py.

1260def config():
1261 """Return configuration in JSON format."""
1262 _engines = []
1263 for name, engine in engines.items():
1264 if not sxng_request.preferences.validate_token(engine):
1265 continue
1266
1267 _languages = engine.traits.languages.keys()
1268 _engines.append(
1269 {
1270 'name': name,
1271 'categories': engine.categories,
1272 'shortcut': engine.shortcut,
1273 'enabled': not engine.disabled,
1274 'paging': engine.paging,
1275 'language_support': engine.language_support,
1276 'languages': list(_languages),
1277 'regions': list(engine.traits.regions.keys()),
1278 'safesearch': engine.safesearch,
1279 'time_range_support': engine.time_range_support,
1280 'timeout': engine.timeout,
1281 }
1282 )
1283
1284 _plugins = []
1285 for _ in searx.plugins.STORAGE:
1286 _plugins.append({'name': _.id, 'enabled': _.active})
1287
1288 _limiter_cfg = limiter.get_cfg()
1289
1290 return jsonify(
1291 {
1292 'categories': list(categories.keys()),
1293 'engines': _engines,
1294 'plugins': _plugins,
1295 'instance_name': settings['general']['instance_name'],
1296 'locales': LOCALE_NAMES,
1297 'default_locale': settings['ui']['default_locale'],
1298 'autocomplete': settings['search']['autocomplete'],
1299 'safe_search': settings['search']['safe_search'],
1300 'default_theme': settings['ui']['default_theme'],
1301 'version': VERSION_STRING,
1302 'brand': {
1303 'PRIVACYPOLICY_URL': get_setting('general.privacypolicy_url'),
1304 'CONTACT_URL': get_setting('general.contact_url'),
1305 'GIT_URL': GIT_URL,
1306 'GIT_BRANCH': GIT_BRANCH,
1307 'DOCS_URL': get_setting('brand.docs_url'),
1308 },
1309 'limiter': {
1310 'enabled': limiter.is_installed(),
1311 'botdetection.ip_limit.link_token': _limiter_cfg.get('botdetection.ip_limit.link_token'),
1312 'botdetection.ip_lists.pass_searxng_org': _limiter_cfg.get('botdetection.ip_lists.pass_searxng_org'),
1313 },
1314 'doi_resolvers': list(settings['doi_resolvers'].keys()),
1315 'default_doi_resolver': settings['default_doi_resolver'],
1316 'public_instance': settings['server']['public_instance'],
1317 }
1318 )
1319
1320
1321@app.errorhandler(404)

References searx.get_setting().

Here is the call graph for this function:

◆ custom_url_for()

searx.webapp.custom_url_for ( str endpoint,
** values )

Definition at line 241 of file webapp.py.

241def custom_url_for(endpoint: str, **values):
242 global _STATIC_FILES # pylint: disable=global-statement
243 if not _STATIC_FILES:
244 _STATIC_FILES = webutils.get_static_file_list()
245
246 # handled by WhiteNoise
247 if endpoint == "static" and values.get("filename"):
248
249 # We need to verify the "filename" argument: in the jinja templates
250 # there could be call like:
251 # url_for('static', filename='img/favicon.png')
252 # which should map to:
253 # static/themes/<theme_name>/img/favicon.png
254
255 arg_filename = values["filename"]
256 if arg_filename not in _STATIC_FILES:
257 # try file in the current theme
258 theme_name = sxng_request.preferences.get_value("theme")
259 theme_filename = f"themes/{theme_name}/{arg_filename}"
260 if theme_filename in _STATIC_FILES:
261 values["filename"] = theme_filename
262
263 app_prefix = url_for("index")
264 return f"{app_prefix}static/{values['filename']}"
265
266 if endpoint == "info" and "locale" not in values:
267
268 # We need to verify the "locale" argument: in the jinja templates there
269 # could be call like:
270 # url_for('info', pagename='about')
271 # which should map to:
272 # info/<locale>/about
273
274 locale = sxng_request.preferences.get_value("locale")
275 if infopage.INFO_PAGES.get_page(values["pagename"], locale) is None:
276 locale = infopage.INFO_PAGES.locale_default
277 values["locale"] = locale
278
279 return url_for(endpoint, **values)
280
281

Referenced by about(), get_client_settings(), and render().

Here is the caller graph for this function:

◆ engine_descriptions()

searx.webapp.engine_descriptions ( )

Definition at line 1081 of file webapp.py.

1081def engine_descriptions():
1082 sxng_ui_lang_tag = get_locale().replace("_", "-")
1083 sxng_ui_lang_tag = LOCALE_BEST_MATCH.get(sxng_ui_lang_tag, sxng_ui_lang_tag)
1084
1085 result = ENGINE_DESCRIPTIONS['en'].copy()
1086 if sxng_ui_lang_tag != 'en':
1087 for engine, description in ENGINE_DESCRIPTIONS.get(sxng_ui_lang_tag, {}).items():
1088 result[engine] = description
1089 for engine, description in result.items():
1090 if len(description) == 2 and description[1] == 'ref':
1091 ref_engine, ref_lang = description[0].split(':')
1092 description = ENGINE_DESCRIPTIONS[ref_lang][ref_engine]
1093 if isinstance(description, str):
1094 description = [description, 'wikipedia']
1095 result[engine] = description
1096
1097 # overwrite by about:description (from settings)
1098 for engine_name, engine_mod in engines.items():
1099 descr = getattr(engine_mod, 'about', {}).get('description', None)
1100 if descr is not None:
1101 result[engine_name] = [descr, "SearXNG config"]
1102
1103 return jsonify(result)
1104
1105
1106@app.route('/stats', methods=['GET'])

References get_locale().

Here is the call graph for this function:

◆ favicon()

searx.webapp.favicon ( )

Definition at line 1242 of file webapp.py.

1242def favicon():
1243 theme = sxng_request.preferences.get_value("theme")
1244 return send_from_directory(
1245 os.path.join(app.root_path, settings['ui']['static_path'], 'themes', theme, 'img'), # type: ignore
1246 'favicon.png',
1247 mimetype='image/vnd.microsoft.icon',
1248 )
1249
1250
1251@app.route('/clear_cookies')

◆ get_client_settings()

searx.webapp.get_client_settings ( )

Definition at line 345 of file webapp.py.

345def get_client_settings():
346 req_pref = sxng_request.preferences
347 return {
348 'autocomplete': req_pref.get_value('autocomplete'),
349 'autocomplete_min': get_setting('search.autocomplete_min'),
350 'method': req_pref.get_value('method'),
351 'infinite_scroll': req_pref.get_value('infinite_scroll'),
352 'translations': get_translations(),
353 'search_on_category_select': req_pref.get_value('search_on_category_select'),
354 'hotkeys': req_pref.get_value('hotkeys'),
355 'url_formatting': req_pref.get_value('url_formatting'),
356 'theme_static_path': custom_url_for('static', filename='themes/simple'),
357 'results_on_new_tab': req_pref.get_value('results_on_new_tab'),
358 'favicon_resolver': req_pref.get_value('favicon_resolver'),
359 'advanced_search': req_pref.get_value('advanced_search'),
360 'query_in_title': req_pref.get_value('query_in_title'),
361 'safesearch': str(req_pref.get_value('safesearch')),
362 'theme': req_pref.get_value('theme'),
363 'doi_resolver': get_doi_resolver(),
364 }
365
366

References custom_url_for(), searx.get_setting(), and get_translations().

Referenced by render().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_enabled_categories()

searx.webapp.get_enabled_categories ( typing.Iterable[str] category_names)
The categories in ``category_names```for which there is no active engine
are filtered out and a reduced list is returned.

Definition at line 319 of file webapp.py.

319def get_enabled_categories(category_names: typing.Iterable[str]):
320 """The categories in ``category_names```for which there is no active engine
321 are filtered out and a reduced list is returned."""
322
323 enabled_engines = [item[0] for item in sxng_request.preferences.engines.get_enabled()]
324 enabled_categories = set()
325 for engine_name in enabled_engines:
326 enabled_categories.update(engines[engine_name].categories)
327 return [x for x in category_names if x in enabled_categories]
328
329

Referenced by render().

Here is the caller graph for this function:

◆ get_locale()

searx.webapp.get_locale ( )

Definition at line 157 of file webapp.py.

157def get_locale():
158 locale = localeselector()
159 logger.debug("%s uses locale `%s`", urllib.parse.quote(sxng_request.url), locale)
160 return locale
161
162

Referenced by engine_descriptions().

Here is the caller graph for this function:

◆ get_pretty_url()

searx.webapp.get_pretty_url ( urllib.parse.ParseResult parsed_url)

Definition at line 330 of file webapp.py.

330def get_pretty_url(parsed_url: urllib.parse.ParseResult):
331 url_formatting_pref = sxng_request.preferences.get_value('url_formatting')
332
333 if url_formatting_pref == 'full':
334 return [parsed_url.geturl()]
335
336 if url_formatting_pref == 'host':
337 return [parsed_url.netloc]
338
339 path = parsed_url.path
340 path = path[:-1] if len(path) > 0 and path[-1] == '/' else path
341 path = unquote(path.replace("/", " › "))
342 return [parsed_url.scheme + "://" + parsed_url.netloc, path]
343
344

◆ get_result_template()

searx.webapp.get_result_template ( str theme_name,
str template_name )

Definition at line 231 of file webapp.py.

231def get_result_template(theme_name: str, template_name: str):
232 themed_path = theme_name + '/result_templates/' + template_name
233 if themed_path in result_templates:
234 return themed_path
235 return 'result_templates/' + template_name
236
237

◆ get_translations()

searx.webapp.get_translations ( )

Definition at line 308 of file webapp.py.

308def get_translations():
309 return {
310 # when there is autocompletion
311 'no_item_found': gettext('No item found'),
312 # /preferences: the source of the engine description (wikipedata, wikidata, website)
313 'Source': gettext('Source'),
314 # infinite scroll
315 'error_loading_next_page': gettext('Error loading the next page'),
316 }
317
318

Referenced by get_client_settings().

Here is the caller graph for this function:

◆ health()

searx.webapp.health ( )

Definition at line 587 of file webapp.py.

587def health():
588 return Response('OK', mimetype='text/plain')
589
590
591@app.route('/client<token>.css', methods=['GET', 'POST'])

◆ image_proxify()

searx.webapp.image_proxify ( str url)

Definition at line 282 of file webapp.py.

282def image_proxify(url: str):
283 if not url:
284 return url
285
286 if url.startswith('//'):
287 url = 'https:' + url
288
289 if not sxng_request.preferences.get_value('image_proxy'):
290 return url
291
292 if url.startswith('data:image/'):
293 # 50 is an arbitrary number to get only the beginning of the image.
294 partial_base64 = url[len('data:image/') : 50].split(';')
295 if (
296 len(partial_base64) == 2
297 and partial_base64[0] in ['gif', 'png', 'jpeg', 'pjpeg', 'webp', 'tiff', 'bmp']
298 and partial_base64[1].startswith('base64,')
299 ):
300 return url
301 return None
302
303 h = new_hmac(settings['server']['secret_key'], url.encode())
304
305 return '{0}?{1}'.format(url_for('image_proxy'), urlencode(dict(url=url.encode(), h=h)))
306
307

◆ image_proxy()

searx.webapp.image_proxy ( )

Definition at line 1008 of file webapp.py.

1008def image_proxy():
1009 # pylint: disable=too-many-return-statements, too-many-branches
1010
1011 url = sxng_request.args.get('url')
1012 if not url:
1013 return '', 400
1014
1015 if not is_hmac_of(settings['server']['secret_key'], url.encode(), sxng_request.args.get('h', '')):
1016 return '', 400
1017
1018 maximum_size = 5 * 1024 * 1024
1019 forward_resp = False
1020 resp = None
1021 try:
1022 request_headers = {
1023 'User-Agent': gen_useragent(),
1024 'Accept': 'image/webp,*/*',
1025 'Accept-Encoding': 'gzip, deflate',
1026 'Sec-GPC': '1',
1027 'DNT': '1',
1028 }
1029 set_context_network_name('image_proxy')
1030 resp, stream = http_stream(method='GET', url=url, headers=request_headers, allow_redirects=True)
1031 content_length = resp.headers.get('Content-Length')
1032 if content_length and content_length.isdigit() and int(content_length) > maximum_size:
1033 return 'Max size', 400
1034
1035 if resp.status_code != 200:
1036 logger.debug('image-proxy: wrong response code: %i', resp.status_code)
1037 if resp.status_code >= 400:
1038 return '', resp.status_code
1039 return '', 400
1040
1041 if not resp.headers.get('Content-Type', '').startswith('image/') and not resp.headers.get(
1042 'Content-Type', ''
1043 ).startswith('binary/octet-stream'):
1044 logger.debug('image-proxy: wrong content-type: %s', resp.headers.get('Content-Type', ''))
1045 return '', 400
1046
1047 forward_resp = True
1048 except httpx.HTTPError:
1049 logger.exception('HTTP error')
1050 return '', 400
1051 finally:
1052 if resp and not forward_resp:
1053 # the code is about to return an HTTP 400 error to the browser
1054 # we make sure to close the response between searxng and the HTTP server
1055 try:
1056 resp.close()
1057 except httpx.HTTPError:
1058 logger.exception('HTTP error on closing')
1059
1060 def close_stream():
1061 nonlocal resp, stream
1062 try:
1063 if resp:
1064 resp.close()
1065 del resp
1066 del stream
1067 except httpx.HTTPError as e:
1068 logger.debug('Exception while closing response', e)
1069
1070 try:
1071 headers = dict_subset(resp.headers, {'Content-Type', 'Content-Encoding', 'Content-Length', 'Length'})
1072 response = Response(stream, mimetype=resp.headers['Content-Type'], headers=headers, direct_passthrough=True)
1073 response.call_on_close(close_stream)
1074 return response
1075 except httpx.HTTPError:
1076 close_stream()
1077 return '', 400
1078
1079
1080@app.route('/engine_descriptions.json', methods=['GET'])

◆ index()

searx.webapp.index ( )
Render index page.

Definition at line 569 of file webapp.py.

569def index():
570 """Render index page."""
571
572 # redirect to search if there's a query in the request
573 if sxng_request.form.get('q'):
574 query = ('?' + sxng_request.query_string.decode()) if sxng_request.query_string else ''
575 return redirect(url_for('search') + query, 308)
576
577 return render(
578 # fmt: off
579 'index.html',
580 selected_categories=get_selected_categories(sxng_request.preferences, sxng_request.form),
581 current_locale = sxng_request.preferences.get_value("locale"),
582 # fmt: on
583 )
584
585
586@app.route('/healthz', methods=['GET'])

References render().

Here is the call graph for this function:

◆ index_error()

searx.webapp.index_error ( str output_format,
str error_message )

Definition at line 539 of file webapp.py.

539def index_error(output_format: str, error_message: str):
540 if output_format == 'json':
541 return Response(json.dumps({'error': error_message}), mimetype='application/json')
542 if output_format == 'csv':
543 response = Response('', mimetype='application/csv')
544 cont_disp = 'attachment;Filename=searx.csv'
545 response.headers.add('Content-Disposition', cont_disp)
546 return response
547
548 if output_format == 'rss':
549 response_rss = render(
550 'opensearch_response_rss.xml',
551 results=[],
552 q=sxng_request.form['q'] if 'q' in sxng_request.form else '',
553 number_of_results=0,
554 error_message=error_message,
555 )
556 return Response(response_rss, mimetype='text/xml')
557
558 # html
559 sxng_request.errors.append(gettext('search error'))
560 return render(
561 # fmt: off
562 'index.html',
563 selected_categories=get_selected_categories(sxng_request.preferences, sxng_request.form),
564 # fmt: on
565 )
566
567
568@app.route('/', methods=['GET', 'POST'])

References render().

Referenced by search().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ info()

searx.webapp.info ( pagename,
locale )
Render page of online user documentation

Definition at line 782 of file webapp.py.

782def info(pagename, locale):
783 """Render page of online user documentation"""
784 page = infopage.INFO_PAGES.get_page(pagename, locale)
785 if page is None:
786 flask.abort(404)
787
788 user_locale = sxng_request.preferences.get_value('locale')
789 return render(
790 'info.html',
791 all_pages=infopage.INFO_PAGES.iter_pages(user_locale, fallback_to_default=True),
792 active_page=page,
793 active_pagename=pagename,
794 )
795
796
797@app.route('/autocompleter', methods=['GET', 'POST'])

References render().

Here is the call graph for this function:

◆ init()

searx.webapp.init ( )

Definition at line 1363 of file webapp.py.

1363def init():
1364
1365 if searx.sxng_debug or app.debug:
1366 app.debug = True
1367 searx.sxng_debug = True
1368
1369 # check secret_key in production
1370
1371 if not app.debug and get_setting("server.secret_key") == 'ultrasecretkey':
1372 logger.error("server.secret_key is not changed. Please use something else instead of ultrasecretkey.")
1373 sys.exit(1)
1374
1375 locales_initialize()
1376 valkey_initialize()
1378
1379 metrics: bool = get_setting("general.enable_metrics") # type: ignore
1380 searx.search.initialize(enable_checker=True, check_network=True, enable_metrics=metrics)
1381
1382 limiter.initialize(app, settings)
1383 favicons.init()
1384
1385
initialize(app)
Definition __init__.py:108
initialize(settings_engines=None, enable_checker=False, check_network=False, enable_metrics=True)
Definition __init__.py:32

References searx.get_setting(), searx.plugins.initialize(), and searx.search.initialize().

Here is the call graph for this function:

◆ opensearch()

searx.webapp.opensearch ( )

Definition at line 1225 of file webapp.py.

1225def opensearch():
1226 method = sxng_request.preferences.get_value('method')
1227 autocomplete = sxng_request.preferences.get_value('autocomplete')
1228
1229 # chrome/chromium only supports HTTP GET....
1230 if sxng_request.headers.get('User-Agent', '').lower().find('webkit') >= 0:
1231 method = 'GET'
1232
1233 if method not in ('POST', 'GET'):
1234 method = 'POST'
1235
1236 ret = render('opensearch.xml', opensearch_method=method, autocomplete=autocomplete)
1237 resp = Response(response=ret, status=200, mimetype="application/opensearchdescription+xml")
1238 return resp
1239
1240
1241@app.route('/favicon.ico')

References render().

Here is the call graph for this function:

◆ page_not_found()

searx.webapp.page_not_found ( _e)

Definition at line 1322 of file webapp.py.

1322def page_not_found(_e):
1323 return render('404.html'), 404
1324
1325

References render().

Here is the call graph for this function:

◆ post_request()

searx.webapp.post_request ( flask.Response response)

Definition at line 518 of file webapp.py.

518def post_request(response: flask.Response):
519 total_time = default_timer() - sxng_request.start_time
520 timings_all = [
521 'total;dur=' + str(round(total_time * 1000, 3)),
522 'render;dur=' + str(round(sxng_request.render_time * 1000, 3)),
523 ]
524 if len(sxng_request.timings) > 0:
525 timings = sorted(sxng_request.timings, key=lambda t: t.total)
526 timings_total = [
527 'total_' + str(i) + '_' + t.engine + ';dur=' + str(round(t.total * 1000, 3)) for i, t in enumerate(timings)
528 ]
529 timings_load = [
530 'load_' + str(i) + '_' + t.engine + ';dur=' + str(round(t.load * 1000, 3))
531 for i, t in enumerate(timings)
532 if t.load
533 ]
534 timings_all = timings_all + timings_total + timings_load
535 response.headers.add('Server-Timing', ', '.join(timings_all))
536 return response
537
538

◆ pre_request()

searx.webapp.pre_request ( )

Definition at line 446 of file webapp.py.

446def pre_request():
447 sxng_request.start_time = default_timer() # pylint: disable=assigning-non-slot
448 sxng_request.render_time = 0 # pylint: disable=assigning-non-slot
449 sxng_request.timings = [] # pylint: disable=assigning-non-slot
450 sxng_request.errors = [] # pylint: disable=assigning-non-slot
451
452 client_pref = ClientPref.from_http_request(sxng_request)
453 # pylint: disable=redefined-outer-name
454 preferences = Preferences(themes, list(categories.keys()), engines, searx.plugins.STORAGE, client_pref)
455
456 user_agent = sxng_request.headers.get('User-Agent', '').lower()
457 if 'webkit' in user_agent and 'android' in user_agent:
458 preferences.key_value_settings['method'].value = 'GET'
459 sxng_request.preferences = preferences # pylint: disable=assigning-non-slot
460
461 try:
462 preferences.parse_dict(sxng_request.cookies)
463
464 except Exception as e: # pylint: disable=broad-except
465 logger.exception(e, exc_info=True)
466 sxng_request.errors.append(gettext('Invalid settings, please edit your preferences'))
467
468 # merge GET, POST vars
469 # HINT request.form is of type werkzeug.datastructures.ImmutableMultiDict
470 sxng_request.form = dict(sxng_request.form.items()) # type: ignore
471 for k, v in sxng_request.args.items():
472 if k not in sxng_request.form:
473 sxng_request.form[k] = v
474
475 if sxng_request.form.get('preferences'):
476 preferences.parse_encoded_data(sxng_request.form['preferences'])
477 else:
478 try:
479 preferences.parse_dict(sxng_request.form)
480 except Exception as e: # pylint: disable=broad-except
481 logger.exception(e, exc_info=True)
482 sxng_request.errors.append(gettext('Invalid settings'))
483
484 # language is defined neither in settings nor in preferences
485 # use browser headers
486 if not preferences.get_value("language"):
487 language = _get_browser_language(sxng_request, settings['search']['languages'])
488 preferences.parse_dict({"language": language})
489 logger.debug('set language %s (from browser)', preferences.get_value("language"))
490
491 # UI locale is defined neither in settings nor in preferences
492 # use browser headers
493 if not preferences.get_value("locale"):
494 locale = _get_browser_language(sxng_request, LOCALE_NAMES.keys())
495 preferences.parse_dict({"locale": locale})
496 logger.debug('set locale %s (from browser)', preferences.get_value("locale"))
497
498 # request.user_plugins
499 sxng_request.user_plugins = [] # pylint: disable=assigning-non-slot
500 allowed_plugins = preferences.plugins.get_enabled()
501 disabled_plugins = preferences.plugins.get_disabled()
502 for plugin in searx.plugins.STORAGE:
503 if (plugin.id not in disabled_plugins) or plugin.id in allowed_plugins:
504 sxng_request.user_plugins.append(plugin.id)
505
506
507@app.after_request

References _get_browser_language().

Here is the call graph for this function:

◆ preferences()

searx.webapp.preferences ( )
Render preferences page && save user preferences

Definition at line 847 of file webapp.py.

847def preferences():
848 """Render preferences page && save user preferences"""
849
850 # pylint: disable=too-many-locals, too-many-return-statements, too-many-branches
851 # pylint: disable=too-many-statements
852
853 # save preferences using the link the /preferences?preferences=...
854 if sxng_request.args.get('preferences') or sxng_request.form.get('preferences'):
855 resp = make_response(redirect(url_for('index', _external=True)))
856 return sxng_request.preferences.save(resp)
857
858 # save preferences
859 if sxng_request.method == 'POST':
860 resp = make_response(redirect(url_for('index', _external=True)))
861 try:
862 sxng_request.preferences.parse_form(sxng_request.form)
863 except ValidationException:
864 sxng_request.errors.append(gettext('Invalid settings, please edit your preferences'))
865 return resp
866 return sxng_request.preferences.save(resp)
867
868 # render preferences
869 image_proxy = sxng_request.preferences.get_value('image_proxy') # pylint: disable=redefined-outer-name
870 disabled_engines = sxng_request.preferences.engines.get_disabled()
871 allowed_plugins = sxng_request.preferences.plugins.get_enabled()
872
873 # stats for preferences page
874 filtered_engines = dict(filter(lambda kv: sxng_request.preferences.validate_token(kv[1]), engines.items()))
875
876 engines_by_category = {}
877
878 for c in categories: # pylint: disable=consider-using-dict-items
879 engines_by_category[c] = [e for e in categories[c] if e.name in filtered_engines]
880 # sort the engines alphabetically since the order in settings.yml is meaningless.
881 list.sort(engines_by_category[c], key=lambda e: e.name)
882
883 # get first element [0], the engine time,
884 # and then the second element [1] : the time (the first one is the label)
885 stats = {} # pylint: disable=redefined-outer-name
886 max_rate95 = 0
887 for _, e in filtered_engines.items():
888 h = histogram('engine', e.name, 'time', 'total')
889 median = round(h.percentage(50), 1) if h.count > 0 else None
890 rate80 = round(h.percentage(80), 1) if h.count > 0 else None
891 rate95 = round(h.percentage(95), 1) if h.count > 0 else None
892
893 max_rate95 = max(max_rate95, rate95 or 0)
894
895 result_count_sum = histogram('engine', e.name, 'result', 'count').sum
896 successful_count = counter('engine', e.name, 'search', 'count', 'successful')
897 result_count = int(result_count_sum / float(successful_count)) if successful_count else 0
898
899 stats[e.name] = {
900 'time': median,
901 'rate80': rate80,
902 'rate95': rate95,
903 'warn_timeout': e.timeout > settings['outgoing']['request_timeout'],
904 'supports_selected_language': e.traits.is_locale_supported(
905 str(sxng_request.preferences.get_value('language') or 'all')
906 ),
907 'result_count': result_count,
908 }
909 # end of stats
910
911 # reliabilities
912 reliabilities = {}
913 engine_errors = get_engine_errors(filtered_engines)
914 checker_results = checker_get_result()
915 checker_results = (
916 checker_results['engines'] if checker_results['status'] == 'ok' and 'engines' in checker_results else {}
917 )
918 for _, e in filtered_engines.items():
919 checker_result = checker_results.get(e.name, {})
920 checker_success = checker_result.get('success', True)
921 errors = engine_errors.get(e.name) or []
922 if counter('engine', e.name, 'search', 'count', 'sent') == 0:
923 # no request
924 reliability = None
925 elif checker_success and not errors:
926 reliability = 100
927 elif 'simple' in checker_result.get('errors', {}):
928 # the basic (simple) test doesn't work: the engine is broken according to the checker
929 # even if there is no exception
930 reliability = 0
931 else:
932 # pylint: disable=consider-using-generator
933 reliability = 100 - sum([error['percentage'] for error in errors if not error.get('secondary')])
934
935 reliabilities[e.name] = {
936 'reliability': reliability,
937 'errors': [],
938 'checker': checker_results.get(e.name, {}).get('errors', {}).keys(),
939 }
940 # keep the order of the list checker_results[e.name]['errors'] and deduplicate.
941 # the first element has the highest percentage rate.
942 reliabilities_errors = []
943 for error in errors:
944 error_user_text = None
945 if error.get('secondary') or 'exception_classname' not in error:
946 continue
947 error_user_text = exception_classname_to_text.get(error.get('exception_classname'))
948 if not error:
949 error_user_text = exception_classname_to_text[None]
950 if error_user_text not in reliabilities_errors:
951 reliabilities_errors.append(error_user_text)
952 reliabilities[e.name]['errors'] = reliabilities_errors
953
954 # supports
955 supports = {}
956 for _, e in filtered_engines.items():
957 supports_selected_language = e.traits.is_locale_supported(
958 str(sxng_request.preferences.get_value('language') or 'all')
959 )
960 safesearch = e.safesearch
961 time_range_support = e.time_range_support
962 for checker_test_name in checker_results.get(e.name, {}).get('errors', {}):
963 if supports_selected_language and checker_test_name.startswith('lang_'):
964 supports_selected_language = '?'
965 elif safesearch and checker_test_name == 'safesearch':
966 safesearch = '?'
967 elif time_range_support and checker_test_name == 'time_range':
968 time_range_support = '?'
969 supports[e.name] = {
970 'supports_selected_language': supports_selected_language,
971 'safesearch': safesearch,
972 'time_range_support': time_range_support,
973 }
974
975 return render(
976 # fmt: off
977 'preferences.html',
978 preferences = True,
979 selected_categories = get_selected_categories(sxng_request.preferences, sxng_request.form),
980 locales = LOCALE_NAMES,
981 current_locale = sxng_request.preferences.get_value("locale"),
982 image_proxy = image_proxy,
983 engines_by_category = engines_by_category,
984 stats = stats,
985 max_rate95 = max_rate95,
986 reliabilities = reliabilities,
987 supports = supports,
988 answer_storage = searx.answerers.STORAGE.info,
989 disabled_engines = disabled_engines,
990 autocomplete_backends = autocomplete_backends,
991 favicon_resolver_names = favicons.proxy.CFG.resolver_map.keys(),
992 shortcuts = {y: x for x, y in engine_shortcuts.items()},
993 themes = themes,
994 plugins_storage = searx.plugins.STORAGE.info,
995 current_doi_resolver = get_doi_resolver(),
996 allowed_plugins = allowed_plugins,
997 preferences_url_params = sxng_request.preferences.get_as_url_params(),
998 locked_preferences = get_setting("preferences.lock", []),
999 doi_resolvers = get_setting("doi_resolvers", {}),
1000 # fmt: on
1001 )
1002
1003

References searx.get_setting(), and render().

Here is the call graph for this function:

◆ render()

searx.webapp.render ( str template_name,
** kwargs )

Definition at line 367 of file webapp.py.

367def render(template_name: str, **kwargs):
368 # values from the preferences
369 # pylint: disable=too-many-statements
370 client_settings = get_client_settings()
371 kwargs['client_settings'] = str(
372 base64.b64encode(
373 bytes(
374 json.dumps(client_settings),
375 encoding='utf-8',
376 )
377 ),
378 encoding='utf-8',
379 )
380 kwargs['preferences'] = sxng_request.preferences
381 kwargs.update(client_settings)
382
383 # values from the HTTP requests
384 kwargs['endpoint'] = 'results' if 'q' in kwargs else sxng_request.endpoint
385 kwargs['cookies'] = sxng_request.cookies
386 kwargs['errors'] = sxng_request.errors
387 kwargs['link_token'] = link_token.get_token()
388
389 kwargs['categories_as_tabs'] = list(settings['categories_as_tabs'].keys())
390 kwargs['categories'] = get_enabled_categories(settings['categories_as_tabs'].keys())
391 kwargs['DEFAULT_CATEGORY'] = DEFAULT_CATEGORY
392
393 # i18n
394 kwargs['sxng_locales'] = [l for l in sxng_locales if l[0] in settings['search']['languages']]
395
396 locale = sxng_request.preferences.get_value('locale')
397 kwargs['locale_rfc5646'] = _get_locale_rfc5646(locale)
398
399 if locale in RTL_LOCALES and 'rtl' not in kwargs:
400 kwargs['rtl'] = True
401
402 if 'current_language' not in kwargs:
403 kwargs['current_language'] = parse_lang(sxng_request.preferences, {}, RawTextQuery('', []))
404
405 # values from settings
406 kwargs['search_formats'] = [x for x in settings['search']['formats'] if x != 'html']
407 kwargs['instance_name'] = get_setting('general.instance_name')
408 kwargs['searx_version'] = VERSION_STRING
409 kwargs['searx_git_url'] = GIT_URL
410 kwargs['enable_metrics'] = get_setting('general.enable_metrics')
411 kwargs['get_setting'] = get_setting
412 kwargs['get_pretty_url'] = get_pretty_url
413
414 # values from settings: donation_url
415 donation_url = get_setting('general.donation_url')
416 if donation_url is True:
417 donation_url = custom_url_for('info', pagename='donate')
418 kwargs['donation_url'] = donation_url
419
420 # helpers to create links to other pages
421 kwargs['url_for'] = custom_url_for # override url_for function in templates
422 kwargs['image_proxify'] = image_proxify
423 kwargs['favicon_url'] = favicons.favicon_url
424 kwargs['cache_url'] = settings['ui']['cache_url']
425 kwargs['get_result_template'] = get_result_template
426 kwargs['opensearch_url'] = (
427 url_for('opensearch')
428 + '?'
429 + urlencode(
430 {
431 'method': sxng_request.preferences.get_value('method'),
432 'autocomplete': sxng_request.preferences.get_value('autocomplete'),
433 }
434 )
435 )
436 kwargs['urlparse'] = urlparse
437
438 start_time = default_timer()
439 result = render_template('{}/{}'.format(kwargs['theme'], template_name), **kwargs)
440 sxng_request.render_time += default_timer() - start_time # pylint: disable=assigning-non-slot
441
442 return result
443
444
445@app.before_request

References _get_locale_rfc5646(), custom_url_for(), get_client_settings(), get_enabled_categories(), and searx.get_setting().

Referenced by index(), index_error(), info(), opensearch(), page_not_found(), preferences(), search(), and stats().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ robots()

searx.webapp.robots ( )

Definition at line 1211 of file webapp.py.

1211def robots():
1212 return Response(
1213 """User-agent: *
1214Allow: /info/en/about
1215Disallow: /stats
1216Disallow: /image_proxy
1217Disallow: /preferences
1218Disallow: /*?*q=*
1219""",
1220 mimetype='text/plain',
1221 )
1222
1223
1224@app.route('/opensearch.xml', methods=['GET'])

◆ rss_xsl()

searx.webapp.rss_xsl ( )

Definition at line 598 of file webapp.py.

598def rss_xsl():
599 return render_template(
600 f"{sxng_request.preferences.get_value('theme')}/rss.xsl",
601 url_for=custom_url_for,
602 )
603
604
605@app.route('/search', methods=['GET', 'POST'])

◆ run()

searx.webapp.run ( )
Runs the application on a local development server.

This run method is only called when SearXNG is started via ``__main__``::

    python -m searx.webapp

Do not use :ref:`run() <flask.Flask.run>` in a production setting.  It is
not intended to meet security and performance requirements for a production
server.

It is not recommended to use this function for development with automatic
reloading as this is badly supported.  Instead you should be using the flask
command line script’s run support::

    flask --app searx.webapp run --debug --reload --host 127.0.0.1 --port 8888

.. _Flask.run: https://flask.palletsprojects.com/en/stable/api/#flask.Flask.run

Definition at line 1326 of file webapp.py.

1326def run():
1327 """Runs the application on a local development server.
1328
1329 This run method is only called when SearXNG is started via ``__main__``::
1330
1331 python -m searx.webapp
1332
1333 Do not use :ref:`run() <flask.Flask.run>` in a production setting. It is
1334 not intended to meet security and performance requirements for a production
1335 server.
1336
1337 It is not recommended to use this function for development with automatic
1338 reloading as this is badly supported. Instead you should be using the flask
1339 command line script’s run support::
1340
1341 flask --app searx.webapp run --debug --reload --host 127.0.0.1 --port 8888
1342
1343 .. _Flask.run: https://flask.palletsprojects.com/en/stable/api/#flask.Flask.run
1344 """
1345
1346 host: str = get_setting("server.bind_address") # type: ignore
1347 port: int = get_setting("server.port") # type: ignore
1348
1349 if searx.sxng_debug:
1350 logger.debug("run local development server (DEBUG) on %s:%s", host, port)
1351 app.run(
1352 debug=True,
1353 port=port,
1354 host=host,
1355 threaded=True,
1356 extra_files=[DEFAULT_SETTINGS_FILE],
1357 )
1358 else:
1359 logger.debug("run local development server on %s:%s", host, port)
1360 app.run(port=port, host=host, threaded=True)
1361
1362

References searx.get_setting().

Here is the call graph for this function:

◆ search()

searx.webapp.search ( )
Search query in q and return results.

Supported outputs: html, json, csv, rss.

Definition at line 606 of file webapp.py.

606def search():
607 """Search query in q and return results.
608
609 Supported outputs: html, json, csv, rss.
610 """
611 # pylint: disable=too-many-locals, too-many-return-statements, too-many-branches
612 # pylint: disable=too-many-statements
613
614 # output_format
615 output_format = sxng_request.form.get('format', 'html')
616 if output_format not in OUTPUT_FORMATS:
617 output_format = 'html'
618
619 if output_format not in settings['search']['formats']:
620 flask.abort(403)
621
622 # check if there is query (not None and not an empty string)
623 if not sxng_request.form.get('q'):
624 if output_format == 'html':
625 return render(
626 # fmt: off
627 'index.html',
628 selected_categories=get_selected_categories(sxng_request.preferences, sxng_request.form),
629 # fmt: on
630 )
631 return index_error(output_format, 'No query'), 400
632
633 # search
634 search_query = None
635 raw_text_query = None
636 result_container = None
637 try:
638 search_query, raw_text_query, _, _, selected_locale = get_search_query_from_webapp(
639 sxng_request.preferences, sxng_request.form
640 )
641 search_obj = searx.search.SearchWithPlugins(search_query, sxng_request, sxng_request.user_plugins)
642 result_container = search_obj.search()
643
644 except SearxParameterException as e:
645 logger.exception('search error: SearxParameterException')
646 return index_error(output_format, e.message), 400
647 except Exception as e: # pylint: disable=broad-except
648 logger.exception(e, exc_info=True)
649 return index_error(output_format, gettext('search error')), 500
650
651 # 1. check if the result is a redirect for an external bang
652 if result_container.redirect_url:
653 return redirect(result_container.redirect_url)
654
655 # 2. add Server-Timing header for measuring performance characteristics of
656 # web applications
657 sxng_request.timings = result_container.get_timings() # pylint: disable=assigning-non-slot
658
659 # 3. formats without a template
660
661 if output_format == 'json':
662
663 response = webutils.get_json_response(search_query, result_container)
664 return Response(response, mimetype='application/json')
665
666 if output_format == 'csv':
667
668 csv = webutils.CSVWriter(StringIO())
669 webutils.write_csv_response(csv, result_container)
670 csv.stream.seek(0)
671
672 response = Response(csv.stream.read(), mimetype='application/csv')
673 cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query)
674 response.headers.add('Content-Disposition', cont_disp)
675 return response
676
677 # 4. formats rendered by a template / RSS & HTML
678
679 current_template = None
680 previous_result = None
681
682 results = result_container.get_ordered_results()
683
684 if search_query.redirect_to_first_result and results:
685 return redirect(results[0]['url'], 302)
686
687 for result in results:
688 if output_format == 'html':
689 if 'content' in result and result['content']:
690 result['content'] = highlight_content(escape(result['content'][:1024]), search_query.query)
691 if 'title' in result and result['title']:
692 result['title'] = highlight_content(escape(result['title'] or ''), search_query.query)
693
694 # set result['open_group'] = True when the template changes from the previous result
695 # set result['close_group'] = True when the template changes on the next result
696 if current_template != result.template:
697 result.open_group = True
698 if previous_result:
699 previous_result.close_group = True # pylint: disable=unsupported-assignment-operation
700 current_template = result.template
701 previous_result = result
702
703 if previous_result:
704 previous_result.close_group = True
705
706 # 4.a RSS
707
708 if output_format == 'rss':
709 response_rss = render(
710 'opensearch_response_rss.xml',
711 results=results,
712 q=sxng_request.form['q'],
713 number_of_results=result_container.number_of_results,
714 )
715 return Response(response_rss, mimetype='text/xml')
716
717 # 4.b HTML
718
719 # suggestions: use RawTextQuery to get the suggestion URLs with the same bang
720 suggestion_urls = list(
721 map(
722 lambda suggestion: {'url': raw_text_query.changeQuery(suggestion).getFullQuery(), 'title': suggestion},
723 result_container.suggestions,
724 )
725 )
726
727 correction_urls = list(
728 map(
729 lambda correction: {'url': raw_text_query.changeQuery(correction).getFullQuery(), 'title': correction},
730 result_container.corrections,
731 )
732 )
733
734 # engine_timings: get engine response times sorted from slowest to fastest
735 engine_timings = sorted(result_container.get_timings(), reverse=True, key=lambda e: e.total)
736 max_response_time = engine_timings[0].total if engine_timings else None
737 engine_timings_pairs = [(timing.engine, timing.total) for timing in engine_timings]
738
739 # search_query.lang contains the user choice (all, auto, en, ...)
740 # when the user choice is "auto", search.search_query.lang contains the detected language
741 # otherwise it is equals to search_query.lang
742 return render(
743 # fmt: off
744 'results.html',
745 results = results,
746 q=sxng_request.form['q'],
747 selected_categories = search_query.categories,
748 pageno = search_query.pageno,
749 time_range = search_query.time_range or '',
750 number_of_results = format_decimal(result_container.number_of_results),
751 suggestions = suggestion_urls,
752 answers = result_container.answers,
753 corrections = correction_urls,
754 infoboxes = result_container.infoboxes,
755 engine_data = result_container.engine_data,
756 paging = result_container.paging,
757 unresponsive_engines = webutils.get_translated_errors(
758 result_container.unresponsive_engines
759 ),
760 current_locale = sxng_request.preferences.get_value("locale"),
761 current_language = selected_locale,
762 search_language = match_locale(
763 search_obj.search_query.lang,
764 settings['search']['languages'],
765 fallback=sxng_request.preferences.get_value("language")
766 ),
767 timeout_limit = sxng_request.form.get('timeout_limit', None),
768 timings = engine_timings_pairs,
769 max_response_time = max_response_time
770 # fmt: on
771 )
772
773
774@app.route('/about', methods=['GET'])

References index_error(), and render().

Here is the call graph for this function:

◆ static_headers()

None searx.webapp.static_headers ( Headers headers,
str _path,
str _url )

Definition at line 1386 of file webapp.py.

1386def static_headers(headers: Headers, _path: str, _url: str) -> None:
1387 headers['Cache-Control'] = 'public, max-age=30, stale-while-revalidate=60'
1388
1389 for header, value in settings['server']['default_http_headers'].items():
1390 # cast value to string, as WhiteNoise requires header values to be strings
1391 headers[header] = str(value)
1392
1393

◆ stats()

searx.webapp.stats ( )
Render engine statistics page.

Definition at line 1107 of file webapp.py.

1107def stats():
1108 """Render engine statistics page."""
1109 sort_order = sxng_request.args.get('sort', default='name', type=str)
1110 selected_engine_name = sxng_request.args.get('engine', default=None, type=str)
1111
1112 filtered_engines = dict(filter(lambda kv: sxng_request.preferences.validate_token(kv[1]), engines.items()))
1113 if selected_engine_name:
1114 if selected_engine_name not in filtered_engines:
1115 selected_engine_name = None
1116 else:
1117 filtered_engines = [selected_engine_name]
1118
1119 checker_results = checker_get_result()
1120 checker_results = (
1121 checker_results['engines'] if checker_results['status'] == 'ok' and 'engines' in checker_results else {}
1122 )
1123
1124 engine_stats = get_engines_stats(filtered_engines)
1125 engine_reliabilities = get_reliabilities(filtered_engines, checker_results)
1126
1127 if sort_order not in STATS_SORT_PARAMETERS:
1128 sort_order = 'name'
1129
1130 reverse, key_name, default_value = STATS_SORT_PARAMETERS[sort_order]
1131
1132 def get_key(engine_stat):
1133 reliability = engine_reliabilities.get(engine_stat['name'], {}).get('reliability', 0)
1134 reliability_order = 0 if reliability else 1
1135 if key_name == 'reliability':
1136 key = reliability
1137 reliability_order = 0
1138 else:
1139 key = engine_stat.get(key_name) or default_value
1140 if reverse:
1141 reliability_order = 1 - reliability_order
1142 return (reliability_order, key, engine_stat['name'])
1143
1144 technical_report = []
1145 for error in engine_reliabilities.get(selected_engine_name, {}).get('errors', []):
1146 technical_report.append(
1147 f"\
1148 Error: {error['exception_classname'] or error['log_message']} \
1149 Parameters: {error['log_parameters']} \
1150 File name: {error['filename'] }:{ error['line_no'] } \
1151 Error Function: {error['function']} \
1152 Code: {error['code']} \
1153 ".replace(
1154 ' ' * 12, ''
1155 ).strip()
1156 )
1157 technical_report = ' '.join(technical_report)
1158
1159 engine_stats['time'] = sorted(engine_stats['time'], reverse=reverse, key=get_key)
1160 return render(
1161 # fmt: off
1162 'stats.html',
1163 sort_order = sort_order,
1164 engine_stats = engine_stats,
1165 engine_reliabilities = engine_reliabilities,
1166 selected_engine_name = selected_engine_name,
1167 searx_git_branch = GIT_BRANCH,
1168 technical_report = technical_report,
1169 # fmt: on
1170 )
1171
1172
1173@app.route('/stats/errors', methods=['GET'])

References render().

Here is the call graph for this function:

◆ stats_checker()

searx.webapp.stats_checker ( )

Definition at line 1181 of file webapp.py.

1181def stats_checker():
1182 result = checker_get_result()
1183 return jsonify(result)
1184
1185
1186@app.route('/metrics')

◆ stats_errors()

searx.webapp.stats_errors ( )

Definition at line 1174 of file webapp.py.

1174def stats_errors():
1175 filtered_engines = dict(filter(lambda kv: sxng_request.preferences.validate_token(kv[1]), engines.items()))
1176 result = get_engine_errors(filtered_engines)
1177 return jsonify(result)
1178
1179
1180@app.route('/stats/checker', methods=['GET'])

◆ stats_open_metrics()

searx.webapp.stats_open_metrics ( )

Definition at line 1187 of file webapp.py.

1187def stats_open_metrics():
1188 password = settings['general'].get("open_metrics")
1189
1190 if not (settings['general'].get("enable_metrics") and password):
1191 return Response('open metrics is disabled', status=404, mimetype='text/plain')
1192
1193 if not sxng_request.authorization or sxng_request.authorization.password != password:
1194 return Response('access forbidden', status=401, mimetype='text/plain')
1195
1196 filtered_engines = dict(filter(lambda kv: sxng_request.preferences.validate_token(kv[1]), engines.items()))
1197
1198 checker_results = checker_get_result()
1199 checker_results = (
1200 checker_results['engines'] if checker_results['status'] == 'ok' and 'engines' in checker_results else {}
1201 )
1202
1203 engine_stats = get_engines_stats(filtered_engines)
1204 engine_reliabilities = get_reliabilities(filtered_engines, checker_results)
1205 metrics_text = openmetrics(engine_stats, engine_reliabilities)
1206
1207 return Response(metrics_text, mimetype='text/plain')
1208
1209
1210@app.route('/robots.txt', methods=['GET'])

Variable Documentation

◆ _STATIC_FILES

list searx.webapp._STATIC_FILES = []
protected

Definition at line 238 of file webapp.py.

◆ add_headers_function

searx.webapp.add_headers_function

Definition at line 1401 of file webapp.py.

◆ allow_all_origins

searx.webapp.allow_all_origins

Definition at line 1400 of file webapp.py.

◆ app

searx.webapp.app = Flask(__name__, static_folder=None, template_folder=templates_path)

Definition at line 148 of file webapp.py.

◆ application

searx.webapp.application = app

Definition at line 1407 of file webapp.py.

◆ babel

searx.webapp.babel = Babel(app, locale_selector=get_locale)

Definition at line 163 of file webapp.py.

◆ default_theme

searx.webapp.default_theme = settings['ui']['default_theme']

Definition at line 134 of file webapp.py.

◆ endpoint

searx.webapp.endpoint

Definition at line 1004 of file webapp.py.

◆ logger

searx.webapp.logger = logger.getChild('webapp')

Definition at line 125 of file webapp.py.

◆ lstrip_blocks

searx.webapp.lstrip_blocks

Definition at line 151 of file webapp.py.

◆ max_age

searx.webapp.max_age

Definition at line 1399 of file webapp.py.

◆ methods

searx.webapp.methods

Definition at line 1004 of file webapp.py.

◆ prefix

searx.webapp.prefix

Definition at line 1398 of file webapp.py.

◆ result_templates

searx.webapp.result_templates = get_result_templates(templates_path)

Definition at line 137 of file webapp.py.

◆ root

searx.webapp.root

Definition at line 1397 of file webapp.py.

◆ secret_key

searx.webapp.secret_key

Definition at line 154 of file webapp.py.

◆ STATS_SORT_PARAMETERS

dict searx.webapp.STATS_SORT_PARAMETERS
Initial value:
1= {
2 'name': (False, 'name', ''),
3 'score': (True, 'score_per_result', 0),
4 'result_count': (True, 'result_count', 0),
5 'time': (False, 'total', 0),
6 'reliability': (False, 'reliability', 100),
7}

Definition at line 139 of file webapp.py.

◆ templates_path

searx.webapp.templates_path = settings['ui']['templates_path']

Definition at line 135 of file webapp.py.

◆ themes

searx.webapp.themes = get_themes(templates_path)

Definition at line 136 of file webapp.py.

◆ trim_blocks

searx.webapp.trim_blocks

Definition at line 150 of file webapp.py.

◆ view_func

searx.webapp.view_func

Definition at line 1004 of file webapp.py.

◆ wsgi_app

searx.webapp.wsgi_app

Definition at line 1394 of file webapp.py.