summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.config.sh55
-rw-r--r--.gitignore1
-rw-r--r--Makefile65
-rw-r--r--docs/_themes/searx/static/searx.css41
-rw-r--r--docs/admin/arch_public.dot8
-rw-r--r--docs/admin/architecture.rst14
-rw-r--r--docs/admin/buildhosts.rst74
-rw-r--r--docs/admin/filtron.rst247
-rw-r--r--docs/admin/index.rst9
-rw-r--r--docs/admin/installation-apache.rst514
-rw-r--r--docs/admin/installation-docker.rst28
-rw-r--r--docs/admin/installation-nginx.rst381
-rw-r--r--docs/admin/installation-searx.rst92
-rw-r--r--docs/admin/installation-uwsgi.rst149
-rw-r--r--docs/admin/installation.rst357
-rw-r--r--docs/admin/morty.rst7
-rw-r--r--docs/admin/settings.rst8
-rw-r--r--docs/admin/update-searx.rst23
-rw-r--r--docs/blog/index.rst3
-rw-r--r--docs/blog/private-engines.rst2
-rw-r--r--docs/build-templates/filtron.rst52
-rw-r--r--docs/build-templates/morty.rst52
-rw-r--r--docs/build-templates/searx.rst192
-rw-r--r--docs/conf.py14
-rw-r--r--docs/dev/contribution_guide.rst5
-rw-r--r--docs/dev/index.rst3
-rw-r--r--docs/dev/makefile.rst48
-rw-r--r--docs/dev/quickstart.rst2
-rw-r--r--docs/dev/reST.rst10
-rw-r--r--docs/dev/search_api.rst4
-rw-r--r--docs/index.rst18
-rw-r--r--docs/user/conf.py19
-rw-r--r--docs/user/index.rst3
-rw-r--r--docs/user/own-instance.rst10
-rw-r--r--docs/user/public_instances.rst3
-rw-r--r--docs/utils/filtron.sh.rst80
-rw-r--r--docs/utils/index.rst53
-rw-r--r--docs/utils/lxc.sh.rst148
-rw-r--r--docs/utils/morty.sh.rst80
-rw-r--r--docs/utils/searx.sh.rst39
-rw-r--r--requirements-dev.txt2
-rw-r--r--searx/brand.py1
-rw-r--r--searx/engines/flickr_noapi.py12
-rw-r--r--searx/engines/yahoo.py2
-rw-r--r--searx/static/plugins/js/search_on_category_select.js18
-rw-r--r--searx/static/themes/oscar/css/logicodev-dark.css36
-rw-r--r--searx/static/themes/oscar/css/logicodev-dark.min.css2
-rw-r--r--searx/static/themes/oscar/css/logicodev.css36
-rw-r--r--searx/static/themes/oscar/css/logicodev.min.css2
-rw-r--r--searx/static/themes/oscar/js/searx.js2
-rw-r--r--searx/static/themes/oscar/js/searx.min.js4
-rw-r--r--searx/static/themes/oscar/js/searx_src/leaflet_map.js2
-rw-r--r--searx/static/themes/oscar/less/logicodev/advanced.less4
-rw-r--r--searx/static/themes/oscar/less/logicodev/onoff.less21
-rw-r--r--searx/static/themes/oscar/less/logicodev/search.less12
-rw-r--r--searx/static/themes/simple/js/searx.head.min.js2
-rw-r--r--searx/static/themes/simple/js/searx.js2
-rw-r--r--searx/static/themes/simple/js/searx.min.js4
-rw-r--r--searx/static/themes/simple/js/searx.min.js.map2
-rw-r--r--searx/static/themes/simple/js/searx_src/searx_mapresult.js2
-rw-r--r--searx/templates/__common__/about.html14
-rw-r--r--searx/templates/__common__/opensearch.xml20
-rw-r--r--searx/templates/__common__/translations.js.tpl1
-rw-r--r--searx/templates/oscar/advanced.html2
-rw-r--r--searx/templates/oscar/base.html6
-rw-r--r--searx/templates/oscar/index.html4
-rw-r--r--searx/templates/oscar/infobox.html2
-rw-r--r--searx/templates/oscar/languages.html1
-rw-r--r--searx/templates/oscar/macros.html7
-rw-r--r--searx/templates/oscar/preferences.html94
-rw-r--r--searx/templates/oscar/result_templates/default.html2
-rw-r--r--searx/templates/oscar/result_templates/videos.html2
-rw-r--r--searx/templates/oscar/time-range.html1
-rw-r--r--searx/templates/simple/base.html1
-rwxr-xr-xsearx/webapp.py21
-rw-r--r--utils/brand.env1
-rwxr-xr-xutils/filtron.sh561
-rwxr-xr-xutils/lib.sh1519
-rw-r--r--utils/lxc-searx.env95
-rwxr-xr-xutils/lxc.sh552
-rw-r--r--utils/makefile.include16
-rw-r--r--utils/makefile.lxc29
-rw-r--r--utils/makefile.python8
-rw-r--r--utils/makefile.sphinx26
-rwxr-xr-xutils/morty.sh546
-rwxr-xr-xutils/searx.sh869
-rw-r--r--utils/site-python/sphinx_build_tools.py48
l---------utils/templates/etc/apache21
-rw-r--r--utils/templates/etc/filtron/rules.json129
-rw-r--r--utils/templates/etc/httpd/sites-available/morty.conf28
-rw-r--r--utils/templates/etc/httpd/sites-available/searx.conf:filtron33
-rw-r--r--utils/templates/etc/httpd/sites-available/searx.conf:uwsgi27
-rw-r--r--utils/templates/etc/nginx/default.apps-available/morty.conf11
-rw-r--r--utils/templates/etc/nginx/default.apps-available/searx.conf:filtron16
-rw-r--r--utils/templates/etc/uwsgi/apps-archlinux/searx.ini80
-rw-r--r--utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket80
-rw-r--r--utils/templates/etc/uwsgi/apps-available/searx.ini79
-rw-r--r--utils/templates/etc/uwsgi/apps-available/searx.ini:socket79
-rw-r--r--utils/templates/lib/systemd/system/filtron.service29
-rw-r--r--utils/templates/lib/systemd/system/morty.service29
100 files changed, 7508 insertions, 642 deletions
diff --git a/.config.sh b/.config.sh
new file mode 100644
index 000000000..4eff5f4c6
--- /dev/null
+++ b/.config.sh
@@ -0,0 +1,55 @@
+# -*- coding: utf-8; mode: sh -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck shell=bash disable=SC2034
+#
+# This environment is used by ./utils scripts like filtron.sh or searx.sh. The
+# default values are *most flexible* and *best maintained*, you normally not
+# need to change the defaults (except PUBLIC_URL).
+#
+# Before you change any value here you have to uninstall any previous
+# installation. Further is it recommended to backup your changes simply by
+# adding them to you local brand (git branch)::
+#
+# git add .config
+
+# The public URL of the searx instance: PUBLIC_URL="https://mydomain.xy/searx"
+# The default is taken from ./utils/brand.env.
+
+PUBLIC_URL="${SEARX_URL}"
+
+if [[ ${PUBLIC_URL} == "https://searx.me" ]]; then
+ # hint: Linux containers do not have DNS entries, lets use IPs
+ PUBLIC_URL="http://$(primary_ip)/searx"
+fi
+
+# searx.sh
+# ---------
+
+# SEARX_INTERNAL_URL="127.0.0.1:8888"
+
+# Only change, if you maintain a searx brand in your searx fork.
+# GIT_BRANCH="${GIT_BRANCH:-master}"
+
+# filtron.sh
+# ----------
+
+# FILTRON_API="127.0.0.1:4005"
+# FILTRON_LISTEN="127.0.0.1:4004"
+# FILTRON_TARGET="127.0.0.1:8888"
+
+# morty.sh
+# --------
+
+# morty listen address
+# MORTY_LISTEN="127.0.0.1:3000"
+# PUBLIC_URL_PATH_MORTY="/morty/"
+
+# system services
+# ---------------
+
+# Common $HOME folder of the service accounts
+# SERVICE_HOME_BASE="/usr/local"
+
+# **experimental**: Set SERVICE_USER to run all services by one account, but be
+# aware that removing discrete components might conflict!
+# SERVICE_USER=searx
diff --git a/.gitignore b/.gitignore
index 1c82be41b..e56a575ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# to sync with .dockerignore
.coverage
coverage/
+cache/
.installed.cfg
engines.cfg
env
diff --git a/Makefile b/Makefile
index abd0eddb8..d10bd9dbf 100644
--- a/Makefile
+++ b/Makefile
@@ -1,24 +1,31 @@
# -*- coding: utf-8; mode: makefile-gmake -*-
+.DEFAULT_GOAL=help
+# START Makefile setup
export GIT_URL=https://github.com/asciimoo/searx
+export GIT_BRANCH=master
export SEARX_URL=https://searx.me
export DOCS_URL=https://asciimoo.github.io/searx
+# END Makefile setup
+
+include utils/makefile.include
PYOBJECTS = searx
DOC = docs
PY_SETUP_EXTRAS ?= \[test\]
-PYDIST=./dist/py
-PYBUILD=./build/py
-
-include utils/makefile.include
include utils/makefile.python
include utils/makefile.sphinx
all: clean install
-PHONY += help
-help:
+PHONY += help-min help-all help
+
+help: help-min
+ @echo ''
+ @echo 'to get more help: make help-all'
+
+help-min:
@echo ' test - run developer tests'
@echo ' docs - build documentation'
@echo ' docs-live - autobuild HTML documentation while editing'
@@ -33,9 +40,18 @@ help:
@echo ' docker - build Docker image'
@echo ' node.env - download & install npm dependencies locally'
@echo ''
- @$(MAKE) -s -f utils/makefile.include make-help
+ @echo 'environment'
+ @echo ' SEARX_URL = $(SEARX_URL)'
+ @echo ' GIT_URL = $(GIT_URL)'
+ @echo ' DOCS_URL = $(DOCS_URL)'
+ @echo ''
+ @$(MAKE) -e -s make-help
+
+help-all: help-min
+ @echo ''
+ @$(MAKE) -e -s python-help
@echo ''
- @$(MAKE) -s -f utils/makefile.python python-help
+ @$(MAKE) -e -s docs-help
PHONY += install
install: buildenv pyenvinstall
@@ -44,7 +60,7 @@ PHONY += uninstall
uninstall: pyenvuninstall
PHONY += clean
-clean: pyclean node.clean test.clean
+clean: pyclean docs-clean node.clean test.clean
$(call cmd,common_clean)
PHONY += run
@@ -61,14 +77,24 @@ run: buildenv pyenvinstall
# docs
# ----
+sphinx-doc-prebuilds:: buildenv pyenvinstall prebuild-includes
+
PHONY += docs
-docs: buildenv pyenvinstall sphinx-doc
+docs: sphinx-doc-prebuilds sphinx-doc
$(call cmd,sphinx,html,docs,docs)
PHONY += docs-live
-docs-live: buildenv pyenvinstall sphinx-live
+docs-live: sphinx-doc-prebuilds sphinx-live
$(call cmd,sphinx_autobuild,html,docs,docs)
+PHONY += prebuild-includes
+prebuild-includes:
+ $(Q)mkdir -p $(DOCS_BUILD)/includes
+ $(Q)./utils/searx.sh doc | cat > $(DOCS_BUILD)/includes/searx.rst
+ $(Q)./utils/filtron.sh doc | cat > $(DOCS_BUILD)/includes/filtron.rst
+ $(Q)./utils/morty.sh doc | cat > $(DOCS_BUILD)/includes/morty.rst
+
+
$(GH_PAGES)::
@echo "doc available at --> $(DOCS_URL)"
@@ -94,12 +120,14 @@ useragents.update: pyenvinstall
buildenv:
$(Q)echo "build searx/brand.py"
$(Q)echo "GIT_URL = '$(GIT_URL)'" > searx/brand.py
+ $(Q)echo "GIT_BRANCH = '$(GIT_BRANCH)'" >> searx/brand.py
$(Q)echo "ISSUE_URL = 'https://github.com/asciimoo/searx/issues'" >> searx/brand.py
$(Q)echo "SEARX_URL = '$(SEARX_URL)'" >> searx/brand.py
$(Q)echo "DOCS_URL = '$(DOCS_URL)'" >> searx/brand.py
$(Q)echo "PUBLIC_INSTANCES = 'https://searx.space'" >> searx/brand.py
$(Q)echo "build utils/brand.env"
$(Q)echo "export GIT_URL='$(GIT_URL)'" > utils/brand.env
+ $(Q)echo "export GIT_BRANCH='$(GIT_BRANCH)'" >> utils/brand.env
$(Q)echo "export ISSUE_URL='https://github.com/asciimoo/searx/issues'" >> utils/brand.env
$(Q)echo "export SEARX_URL='$(SEARX_URL)'" >> utils/brand.env
$(Q)echo "export DOCS_URL='$(DOCS_URL)'" >> utils/brand.env
@@ -182,8 +210,7 @@ gecko.driver:
# test
# ----
-PHONY += test test.pylint test.pep8 test.unit test.coverage test.robot
-
+PHONY += test test.sh test.pylint test.pep8 test.unit test.coverage test.robot
test: buildenv test.pylint test.pep8 test.unit gecko.driver test.robot
ifeq ($(PY),2)
@@ -191,6 +218,7 @@ test.pylint:
@echo "LINT skip liniting py2"
else
# TODO: balance linting with pylint
+
test.pylint: pyenvinstall
$(call cmd,pylint,\
searx/preferences.py \
@@ -202,6 +230,17 @@ endif
# E402 module level import not at top of file
# W503 line break before binary operator
+# ubu1604: uses shellcheck v0.3.7 (from 04/2015), no longer supported!
+test.sh:
+ shellcheck -x -s bash utils/brand.env
+ shellcheck -x utils/lib.sh
+ shellcheck -x utils/filtron.sh
+ shellcheck -x utils/searx.sh
+ shellcheck -x utils/morty.sh
+ shellcheck -x utils/lxc.sh
+ shellcheck -x utils/lxc-searx.env
+ shellcheck -x .config.sh
+
test.pep8: pyenvinstall
@echo "TEST pep8"
$(Q)$(PY_ENV_ACT); pep8 --exclude=searx/static --max-line-length=120 --ignore "E402,W503" searx tests
diff --git a/docs/_themes/searx/static/searx.css b/docs/_themes/searx/static/searx.css
index d6a664f0f..0f2eff728 100644
--- a/docs/_themes/searx/static/searx.css
+++ b/docs/_themes/searx/static/searx.css
@@ -33,7 +33,7 @@ p.sidebar-title, .sidebar p {
/* admonitions
*/
-div.admonition, div.topic {
+div.admonition, div.topic, div.toctree-wrapper {
background-color: #fafafa;
margin: 8px 0px;
padding: 1em;
@@ -42,6 +42,16 @@ div.admonition, div.topic {
border-right: none;
border-bottom: none;
border-left: 5pt solid #ccc;
+ list-style-type: disclosure-closed;
+}
+
+div.toctree-wrapper p.caption {
+ font-weight: normal;
+ font-size: 24px;
+ margin: 0 0 10px 0;
+ padding: 0;
+ line-height: 1;
+ display: inline;
}
p.admonition-title:after {
@@ -128,3 +138,32 @@ caption {
caption-side: top;
text-align: left;
}
+
+/* bugs since sphinx 3.1
+
+See sphinx-doc project, PR 7838 & 7484 with elementary patch to the basic CSS:
+
+- https://github.com/sphinx-doc/sphinx/issues/7838#issuecomment-646009605
+- https://github.com/sphinx-doc/sphinx/pull/7484#issuecomment-646058972
+
+*/
+
+li > p:first-child {
+ margin-top: 0;
+}
+
+li > p:last-child {
+ margin-bottom: 0;
+}
+
+div.admonition dl {
+ margin-bottom: 0;
+}
+
+div.sidebar {
+ clear: none;
+}
+
+div.admonition, div.topic, pre {
+ clear: none;
+}
diff --git a/docs/admin/arch_public.dot b/docs/admin/arch_public.dot
index a46c96de3..5018225c3 100644
--- a/docs/admin/arch_public.dot
+++ b/docs/admin/arch_public.dot
@@ -4,11 +4,11 @@ digraph G {
edge [fontname="Sans"];
browser [label="Browser", shape=Mdiamond];
- rp [label="Reverse Proxy", href="url to configure reverse proxy"];
- filtron [label="Filtron", href="https://github.com/asciimoo/filtron"];
- morty [label="Morty", href="https://github.com/asciimoo/morty"];
+ rp [label="Reverse Proxy", href="https://asciimoo.github.io/searx/utils/filtron.sh.html#public-reverse-proxy"];
+ filtron [label="Filtron", href="https://asciimoo.github.io/searx/utils/filtron.sh.html"];
+ morty [label="Morty", href="https://asciimoo.github.io/searx/utils/morty.sh.html"];
static [label="Static files", href="url to configure static files"];
- uwsgi [label="uwsgi", href="url to configure uwsgi"]
+ uwsgi [label="uwsgi", href="https://asciimoo.github.io/searx/utils/searx.sh.html"]
searx1 [label="Searx #1"];
searx2 [label="Searx #2"];
searx3 [label="Searx #3"];
diff --git a/docs/admin/architecture.rst b/docs/admin/architecture.rst
index 7064a294b..464e765eb 100644
--- a/docs/admin/architecture.rst
+++ b/docs/admin/architecture.rst
@@ -4,17 +4,21 @@
Architecture
============
-.. sidebar:: Needs work!
+.. sidebar:: Further reading
- This article needs some work / Searx is a collaborative effort. If you have
- any contribution, feel welcome to send us your :pull:`PR <../pulls>`, see
- :ref:`how to contribute`.
+ - Reverse Proxy: :ref:`Apache <apache searx site>` & :ref:`nginx <nginx searx
+ site>`
+ - Filtron: :ref:`searx filtron`
+ - Morty: :ref:`searx morty`
+ - uWSGI: :ref:`searx uwsgi`
+ - Searx: :ref:`installation basic`
Herein you will find some hints and suggestions about typical architectures of
searx infrastructures.
We start with a contribution from :pull:`@dalf <1776#issuecomment-567917320>`.
-It shows a *reference* setup for public searx instances.
+It shows a *reference* setup for public searx instances which can build up and
+maintained by the scripts from our :ref:`toolboxing`.
.. _arch public:
diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index 5260da033..a727d25b9 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -9,8 +9,27 @@ Buildhosts
If you have any contribution send us your :pull:`PR <../pulls>`, see
:ref:`how to contribute`.
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
To get best results from build, its recommend to install additional packages
-on build hosts.
+on build hosts (see :ref:`searx.sh`).::
+
+ sudo -H ./utils/searx.sh install buildhost
+
+This will install packages needed by searx:
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START distro-packages
+ :end-before: END distro-packages
+
+and packages needed to build docuemtation and run tests:
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START build-packages
+ :end-before: END build-packages
.. _docs build:
@@ -35,8 +54,17 @@ processing additional packages are needed. The XeTeX_ needed not only for PDF
creation, its also needed for :ref:`math` when HTML output is build.
To be able to do :ref:`sphinx:math-support` without CDNs, the math are rendered
-as images (``sphinx.ext.imgmath`` extension). If your docs build (``make
-docs``) shows warnings like this::
+as images (``sphinx.ext.imgmath`` extension).
+
+Here is the extract from the :origin:`docs/conf.py` file, setting math renderer
+to ``imgmath``:
+
+.. literalinclude:: ../conf.py
+ :language: python
+ :start-after: # sphinx.ext.imgmath setup
+ :end-before: # sphinx.ext.imgmath setup END
+
+If your docs build (``make docs``) shows warnings like this::
WARNING: dot(1) not found, for better output quality install \
graphviz from http://www.graphviz.org
@@ -47,8 +75,6 @@ docs``) shows warnings like this::
you need to install additional packages on your build host, to get better HTML
output.
-.. _system requirements:
-
.. tabs::
.. group-tab:: Ubuntu / debian
@@ -92,12 +118,38 @@ For PDF output you also need:
$ sudo dnf install \
texlive-collection-fontsrecommended texlive-collection-latex \
- dejavu-sans-fonts dejavu-serif-fonts dejavu-sans-mono-fonts
+ dejavu-sans-fonts dejavu-serif-fonts dejavu-sans-mono-fonts \
+ ImageMagick
-.. _system requirements END:
+.. _sh lint:
-.. literalinclude:: ../conf.py
- :language: python
- :start-after: # sphinx.ext.imgmath setup
- :end-before: # sphinx.ext.imgmath setup END
+Lint shell scripts
+==================
+
+.. _ShellCheck: https://github.com/koalaman/shellcheck
+
+To lint shell scripts, we use ShellCheck_ - A shell script static analysis tool.
+
+.. SNIP sh lint requirements
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code-block:: sh
+
+ $ sudo apt install shellcheck
+
+ .. group-tab:: Arch Linux
+
+ .. code-block:: sh
+
+ $ sudo pacman -S shellcheck
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code-block:: sh
+
+ $ sudo dnf install ShellCheck
+.. SNAP sh lint requirements
diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 07dcb9bc5..93e430b1f 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -1,18 +1,51 @@
+
+.. _searx filtron:
+
==========================
How to protect an instance
==========================
-Searx depens on external search services. To avoid the abuse of these services
+.. sidebar:: further reading
+
+ - :ref:`filtron.sh`
+ - :ref:`nginx searx site`
+
+
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
+.. _filtron: https://github.com/asciimoo/filtron
+
+Searx depends on external search services. To avoid the abuse of these services
it is advised to limit the number of requests processed by searx.
-An application firewall, ``filtron`` solves exactly this problem. Information
-on how to install it can be found at the `project page of filtron
-<https://github.com/asciimoo/filtron>`__.
+An application firewall, filtron_ solves exactly this problem. Filtron is just
+a middleware between your web server (nginx, apache, ...) and searx, we describe
+such infratructures in chapter: :ref:`architecture`.
+filtron & go
+============
+
+.. _Go: https://golang.org/
+.. _filtron README: https://github.com/asciimoo/filtron/blob/master/README.md
+
+Filtron needs Go_ installed. If Go_ is preinstalled, filtron_ is simply
+installed by ``go get`` package management (see `filtron README`_). If you use
+filtron as middleware, a more isolated setup is recommended. To simplify such
+an installation and the maintenance of, use our script :ref:`filtron.sh`.
+
+.. _Sample configuration of filtron:
+
Sample configuration of filtron
===============================
+.. sidebar:: Tooling box
+
+ - :origin:`/etc/filtron/rules.json <utils/templates/etc/filtron/rules.json>`
+
An example configuration can be find below. This configuration limits the access
of:
@@ -24,105 +57,104 @@ of:
.. code:: json
- [{
- "name":"search request",
- "filters":[
- "Param:q",
- "Path=^(/|/search)$"
- ],
- "interval":"<time-interval-in-sec (int)>",
- "limit":"<max-request-number-in-interval (int)>",
- "subrules":[
- {
- "name":"roboagent limit",
- "interval":"<time-interval-in-sec (int)>",
- "limit":"<max-request-number-in-interval (int)>",
- "filters":[
- "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
- ],
- "actions":[
- {
- "name":"block",
- "params":{
- "message":"Rate limit exceeded"
- }
- }
- ]
- },
- {
- "name":"botlimit",
- "limit":0,
- "stop":true,
- "filters":[
- "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
- ],
- "actions":[
- {
- "name":"block",
- "params":{
- "message":"Rate limit exceeded"
- }
- }
- ]
- },
- {
- "name":"IP limit",
- "interval":"<time-interval-in-sec (int)>",
- "limit":"<max-request-number-in-interval (int)>",
- "stop":true,
- "aggregations":[
- "Header:X-Forwarded-For"
- ],
- "actions":[
- {
- "name":"block",
- "params":{
- "message":"Rate limit exceeded"
- }
- }
- ]
- },
- {
- "name":"rss/json limit",
- "interval":"<time-interval-in-sec (int)>",
- "limit":"<max-request-number-in-interval (int)>",
- "stop":true,
- "filters":[
- "Param:format=(csv|json|rss)"
- ],
- "actions":[
- {
- "name":"block",
- "params":{
- "message":"Rate limit exceeded"
- }
- }
- ]
- },
- {
- "name":"useragent limit",
- "interval":"<time-interval-in-sec (int)>",
- "limit":"<max-request-number-in-interval (int)>",
- "aggregations":[
- "Header:User-Agent"
+ [
+ {
+ "name": "search request",
+ "filters": [
+ "Param:q",
+ "Path=^(/|/search)$"
],
- "actions":[
- {
- "name":"block",
- "params":{
- "message":"Rate limit exceeded"
- }
- }
+ "interval": "<time-interval-in-sec (int)>"
+ "limit": "<max-request-number-in-interval (int)>",
+ "subrules": [
+ {
+ "name": "missing Accept-Language",
+ "filters": ["!Header:Accept-Language"],
+ "limit": "<max-request-number-in-interval (int)>",
+ "stop": true,
+ "actions": [
+ {"name":"log"},
+ {"name": "block",
+ "params": {"message": "Rate limit exceeded"}}
+ ]
+ },
+ {
+ "name": "suspiciously Connection=close header",
+ "filters": ["Header:Connection=close"],
+ "limit": "<max-request-number-in-interval (int)>",
+ "stop": true,
+ "actions": [
+ {"name":"log"},
+ {"name": "block",
+ "params": {"message": "Rate limit exceeded"}}
+ ]
+ },
+ {
+ "name": "IP limit",
+ "interval": "<time-interval-in-sec (int)>"
+ "limit": "<max-request-number-in-interval (int)>",
+ "stop": true,
+ "aggregations": [
+ "Header:X-Forwarded-For"
+ ],
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ },
+ {
+ "name": "rss/json limit",
+ "filters": [
+ "Param:format=(csv|json|rss)"
+ ],
+ "interval": "<time-interval-in-sec (int)>"
+ "limit": "<max-request-number-in-interval (int)>",
+ "stop": true,
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ },
+ {
+ "name": "useragent limit",
+ "interval": "<time-interval-in-sec (int)>"
+ "limit": "<max-request-number-in-interval (int)>",
+ "aggregations": [
+ "Header:User-Agent"
+ ],
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ }
]
- }
- ]
- }]
+ }
+ ]
+.. _filtron route request:
Route request through filtron
=============================
+.. sidebar:: further reading
+
+ - :ref:`filtron.sh overview`
+ - :ref:`installation nginx`
+ - :ref:`installation apache`
+
Filtron can be started using the following command:
.. code:: sh
@@ -136,13 +168,24 @@ Use it along with ``nginx`` with the following example configuration.
.. code:: nginx
- location / {
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Scheme $scheme;
- proxy_pass http://127.0.0.1:4004/;
+ # https://example.org/searx
+
+ location /searx {
+ proxy_pass http://127.0.0.1:4004/;
+
+ proxy_set_header Host $http_host;
+ proxy_set_header Connection $http_connection;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header X-Script-Name /searx;
}
+ location /searx/static {
+ /usr/local/searx/searx-src/searx/static;
+ }
+
+
Requests are coming from port 4004 going through filtron and then forwarded to
-port 8888 where a searx is being run.
+port 8888 where a searx is being run. For a complete setup see: :ref:`nginx
+searx site`.
diff --git a/docs/admin/index.rst b/docs/admin/index.rst
index b3c7f5119..c708c4ffa 100644
--- a/docs/admin/index.rst
+++ b/docs/admin/index.rst
@@ -3,9 +3,16 @@ Administrator documentation
===========================
.. toctree::
- :maxdepth: 1
+ :maxdepth: 2
+ :caption: Contents
installation
+ installation-searx
+ installation-uwsgi
+ installation-nginx
+ installation-apache
+ installation-docker
+ update-searx
settings
api
architecture
diff --git a/docs/admin/installation-apache.rst b/docs/admin/installation-apache.rst
new file mode 100644
index 000000000..217e57718
--- /dev/null
+++ b/docs/admin/installation-apache.rst
@@ -0,0 +1,514 @@
+.. _installation apache:
+
+===================
+Install with apache
+===================
+
+.. _Apache: https://httpd.apache.org/
+.. _Apache Debian:
+ https://cwiki.apache.org/confluence/display/HTTPD/DistrosDefaultLayout#DistrosDefaultLayout-Debian,Ubuntu(Apachehttpd2.x):
+.. _README.Debian:
+ https://salsa.debian.org/apache-team/apache2/raw/master/debian/apache2.README.Debian
+.. _Apache Arch Linux:
+ https://wiki.archlinux.org/index.php/Apache_HTTP_Server
+.. _Apache Fedora:
+ https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-apache-http-server/index.html
+.. _Apache directives:
+ https://httpd.apache.org/docs/trunk/mod/directives.html
+.. _Getting Started:
+ https://httpd.apache.org/docs/current/en/getting-started.html
+.. _Terms Used to Describe Directives:
+ https://httpd.apache.org/docs/current/en/mod/directive-dict.html
+.. _Configuration Files:
+ https://httpd.apache.org/docs/current/en/configuring.html
+.. _ProxyPreserveHost: https://httpd.apache.org/docs/trunk/mod/mod_proxy.html#proxypreservehost
+.. _LoadModule:
+ https://httpd.apache.org/docs/2.4/mod/mod_so.html#loadmodule
+.. _DocumentRoot:
+ https://httpd.apache.org/docs/trunk/mod/core.html#documentroot
+.. _Location:
+ https://httpd.apache.org/docs/trunk/mod/core.html#location
+.. _uWSGI Apache support:
+ https://uwsgi-docs.readthedocs.io/en/latest/Apache.html
+.. _mod_proxy_uwsgi:
+ https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
+
+.. sidebar:: further read
+
+ - `Apache Arch Linux`_
+ - `Apache Debian`_ and `README.Debian`_
+ - `Apache Fedora`_
+ - `Apache directives`_
+
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
+----
+
+**Install** :ref:`apache searx site` using :ref:`filtron.sh <filtron.sh overview>`
+
+.. code:: bash
+
+ $ sudo -H ./utils/filtron.sh apache install
+
+**Install** :ref:`apache searx site` using :ref:`morty.sh <morty.sh overview>`
+
+.. code:: bash
+
+ $ sudo -H ./utils/morty.sh apache install
+
+----
+
+The apache HTTP server
+======================
+
+If Apache_ is not installed, install it now. If apache_ is new to you, the
+`Getting Started`_, `Configuration Files`_ and `Terms Used to Describe
+Directives`_ documentation gives first orientation. There is also a list of
+`Apache directives`_ *to keep in the pocket*.
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ sudo -H apt-get install apache2
+
+ .. group-tab:: Arch Linux
+
+ .. code:: sh
+
+ sudo -H pacman -S apache
+ sudo -H systemctl enable httpd
+ sudo -H systemctl start http
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code:: sh
+
+ sudo -H dnf install httpd
+ sudo -H systemctl enable httpd
+ sudo -H systemctl start httpd
+
+Now at http://localhost you should see any kind of *Welcome* or *Test* page.
+How this default intro site is configured, depends on the linux distribution
+(compare `Apache directives`_).
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ less /etc/apache2/sites-enabled/000-default.conf
+
+ In this file, there is a line setting the `DocumentRoot`_ directive:
+
+ .. code:: apache
+
+ DocumentRoot /var/www/html
+
+ And the *welcome* page is the HTML file at ``/var/www/html/index.html``.
+
+ .. group-tab:: Arch Linux
+
+ .. code:: sh
+
+ less /etc/httpd/conf/httpd.conf
+
+ In this file, there is a line setting the `DocumentRoot`_ directive:
+
+ .. code:: apache
+
+ DocumentRoot "/srv/http"
+ <Directory "/srv/http">
+ Options Indexes FollowSymLinks
+ AllowOverride None
+ Require all granted
+ </Directory>
+
+ The *welcome* page of Arch Linux is a page showing directory located at
+ ``DocumentRoot``. This is *directory* page is generated by the Module
+ `mod_autoindex <https://httpd.apache.org/docs/2.4/mod/mod_autoindex.html>`_:
+
+ .. code:: apache
+
+ LoadModule autoindex_module modules/mod_autoindex.so
+ ...
+ Include conf/extra/httpd-autoindex.conf
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code:: sh
+
+ less /etc/httpd/conf/httpd.conf
+
+ In this file, there is a line setting the ``DocumentRoot`` directive:
+
+ .. code:: apache
+
+ DocumentRoot "/var/www/html"
+ ...
+ <Directory "/var/www">
+ AllowOverride None
+ # Allow open access:
+ Require all granted
+ </Directory>
+
+ On fresh installations, the ``/var/www`` is empty and the *default
+ welcome page* is shown, the configuration is located at::
+
+ less /etc/httpd/conf.d/welcome.conf
+
+.. _apache searx site:
+
+Apache Reverse Proxy
+====================
+
+.. sidebar:: public to the internet?
+
+ If your searx instance is public, stop here and first install :ref:`filtron
+ reverse proxy <filtron.sh>` and :ref:`result proxy morty <morty.sh>`, see
+ :ref:`installation scripts`. If already done, follow setup: *searx via
+ filtron plus morty*.
+
+To setup a Apache revers proxy you have to enable the *headers* and *proxy*
+modules and create a `Location`_ configuration for the searx site. In most
+distributions you have to un-comment the lines in the main configuration file,
+except in :ref:`The Debian Layout`.
+
+To pass the HTTP HOST header
+With ProxyPreserveHost_ the incoming Host HTTP request header is passed to the
+proxied host.
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ In the Apache setup, enable headers and proxy modules:
+
+ .. code:: sh
+
+ sudo -H a2enmod headers
+ sudo -H a2enmod proxy
+ sudo -H a2enmod proxy_http
+
+ In :ref:`The Debian Layout` you create a ``searx.conf`` with the
+ ``<Location /searx >`` directive and save this file in the *sites
+ available* folder at ``/etc/apache2/sites-available``. To enable the
+ ``searx.conf`` use :man:`a2ensite`:
+
+ .. code:: sh
+
+ sudo -H a2ensite searx.conf
+
+ .. group-tab:: Arch Linux
+
+ In the ``/etc/httpd/conf/httpd.conf`` file, activate headers and proxy
+ modules (LoadModule_):
+
+ .. code:: apache
+
+ FIXME needs test
+
+ LoadModule headers_module modules/mod_headers.so
+ LoadModule proxy_module modules/mod_proxy.so
+ LoadModule proxy_http_module modules/mod_proxy_http.so
+
+ .. group-tab:: Fedora / RHEL
+
+ In the ``/etc/httpd/conf/httpd.conf`` file, activate headers and proxy
+ modules (LoadModule_):
+
+ .. code:: apache
+
+ FIXME needs test
+
+ LoadModule headers_module modules/mod_headers.so
+ LoadModule proxy_module modules/mod_proxy.so
+ LoadModule proxy_http_module modules/mod_proxy_http.so
+
+.. tabs::
+
+ .. group-tab:: searx via filtron plus morty
+
+ Use this setup, if your instance is public to the internet, compare
+ figure: :ref:`architecture <arch public>` and :ref:`installation scripts`.
+
+ 1. Configure a reverse proxy for :ref:`filtron <filtron.sh>`, listening on
+ *localhost 4004* (:ref:`filtron route request`):
+
+ .. code:: apache
+
+ <Location /searx >
+
+ # SetEnvIf Request_URI "/searx" dontlog
+ # CustomLog /dev/null combined env=dontlog
+
+ Require all granted
+
+ Order deny,allow
+ Deny from all
+ #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPreserveHost On
+ ProxyPass http://127.0.0.1:4004
+ RequestHeader set X-Script-Name /searx
+
+ </Location>
+
+ 2. Configure reverse proxy for :ref:`morty <searx morty>`, listening on
+ *localhost 3000*
+
+ .. code:: apache
+
+ ProxyPreserveHost On
+
+ <Location /morty >
+
+ # SetEnvIf Request_URI "/morty" dontlog
+ # CustomLog /dev/null combined env=dontlog
+
+ Require all granted
+
+ Order deny,allow
+ Deny from all
+ #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPass http://127.0.0.1:3000
+ RequestHeader set X-Script-Name /morty
+
+ </Location>
+
+ Note that reverse proxy advised to be used in case of single-user or
+ low-traffic instances. For a fully result proxification add :ref:`morty's
+ <searx morty>` **public URL** to your :origin:`searx/settings.yml`:
+
+ .. code:: yaml
+
+ result_proxy:
+ # replace example.org with your server's public name
+ url : https://example.org/morty
+
+ server:
+ image_proxy : True
+
+uWSGI support
+=============
+
+Be warned, with this setup, your instance isn't :ref:`protected <searx
+filtron>`, nevertheless it is good enough for intranet usage. In modern Linux
+distributions, the `mod_proxy_uwsgi`_ is compiled into the *normal* apache
+package and you need to install only the :ref:`uWSGI <searx uwsgi>` package:
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ sudo -H apt-get install uwsgi
+
+ # Ubuntu =< 18.04
+ sudo -H apt-get install libapache2-mod-proxy-uwsgi
+
+ .. group-tab:: Arch Linux
+
+ .. code:: sh
+
+ sudo -H pacman -S uwsgi
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code:: sh
+
+ sudo -H dnf install uwsgi
+
+The next example shows a configuration using the `uWSGI Apache support`_ via
+unix sockets and `mod_proxy_uwsgi`_.
+
+For socket communication, you have to activate ``socket =
+/run/uwsgi/app/searx/socket`` and comment out the ``http = 127.0.0.1:8888``
+configuration in your :ref:`uwsgi ini file <uwsgi configuration>`. If not
+already exists, create a folder for the unix sockets, which can be used by the
+searx account (see :ref:`create searx user`):
+
+.. code:: bash
+
+ sudo -H mkdir -p /run/uwsgi/app/searx/
+ sudo -H chown -R searx:searx /run/uwsgi/app/searx/
+
+If the server is public; to limit access to your intranet replace ``Allow from
+all`` directive and replace ``192.168.0.0/16`` with your subnet IP/class.
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: apache
+
+ LoadModule headers_module /usr/lib/apache2/mod_headers.so
+ LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
+ LoadModule proxy_uwsgi_module /usr/lib/apache2/modules/mod_proxy_uwsgi.so
+
+ # SetEnvIf Request_URI /searx dontlog
+ # CustomLog /dev/null combined env=dontlog
+
+ <Location /searx>
+
+ Require all granted
+ Order deny,allow
+ Deny from all
+ # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPreserveHost On
+ ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+ </Location>
+
+ .. group-tab:: Arch Linux
+
+ .. code:: apache
+
+ FIXME needs test
+
+ LoadModule proxy_module modules/mod_proxy.so
+ LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
+
+ # SetEnvIf Request_URI /searx dontlog
+ # CustomLog /dev/null combined env=dontlog
+
+ <Location /searx>
+
+ Require all granted
+ Order deny,allow
+ Deny from all
+ # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPreserveHost On
+ ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+ </Location>
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code:: apache
+
+ FIXME needs test
+
+ LoadModule proxy_module modules/mod_proxy.so
+ LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
+ <IfModule proxy_uwsgi_module>
+
+ # SetEnvIf Request_URI /searx dontlog
+ # CustomLog /dev/null combined env=dontlog
+
+ <Location /searx>
+
+ Require all granted
+ Order deny,allow
+ Deny from all
+ # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPreserveHost On
+ ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+ </Location>
+
+ </IfModule>
+
+ .. group-tab:: old mod_wsgi
+
+ We show this only for historical reasons, DON'T USE `mod_uwsgi
+ <https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-uwsgi>`_.
+ ANYMORE!
+
+ .. code:: apache
+
+ <IfModule mod_uwsgi.c>
+
+ # SetEnvIf Request_URI "/searx" dontlog
+ # CustomLog /dev/null combined env=dontlog
+
+ <Location /searx >
+
+ Require all granted
+
+ Options FollowSymLinks Indexes
+ SetHandler uwsgi-handler
+ uWSGISocket /run/uwsgi/app/searx/socket
+
+ Order deny,allow
+ Deny from all
+ # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ </Location>
+
+ </IfModule>
+
+.. _restart apache:
+
+Restart service
+===============
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ sudo -H systemctl restart apache2
+ sudo -H service uwsgi restart searx
+
+ .. group-tab:: Arch Linux
+
+ .. code:: sh
+
+ sudo -H systemctl restart httpd
+ sudo -H systemctl restart uwsgi@searx
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code:: sh
+
+ sudo -H systemctl restart httpd
+ sudo -H touch /etc/uwsgi.d/searx.ini
+
+
+disable logs
+============
+
+For better privacy you can disable Apache logs. In the examples above activate
+one of the lines and `restart apache`_::
+
+
+ # SetEnvIf Request_URI "/searx" dontlog
+ # CustomLog /dev/null combined env=dontlog
+
+The ``CustomLog`` directive disable logs for the whole (virtual) server, use it
+when the URL of the service does not have a path component (``/searx``) / is
+located at root (``/``).
+
+.. _The Debian Layout:
+
+The Debian Layout
+=================
+
+Be aware that the Debian layout is quite different from the standard Apache
+configuration. For details look at the README.Debian_
+(``/usr/share/doc/apache2/README.Debian.gz``). Some commands you should know on
+Debian:
+
+* :man:`apache2ctl`: Apache HTTP server control interface
+* :man:`a2enmod`, :man:`a2dismod`: switch on/off modules
+* :man:`a2enconf`, :man:`a2disconf`: switch on/off configurations
+* :man:`a2ensite`, :man:`a2dissite`: switch on/off sites
diff --git a/docs/admin/installation-docker.rst b/docs/admin/installation-docker.rst
new file mode 100644
index 000000000..340e66319
--- /dev/null
+++ b/docs/admin/installation-docker.rst
@@ -0,0 +1,28 @@
+.. _installation docker:
+
+===================
+Docker installation
+===================
+
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
+Make sure you have installed Docker. For instance, you can deploy searx like this:
+
+.. code:: sh
+
+ docker pull wonderfall/searx
+ docker run -d --name searx -p $PORT:8888 wonderfall/searx
+
+Go to ``http://localhost:$PORT``.
+
+See https://hub.docker.com/r/wonderfall/searx/ for more informations. It's also
+possible to build searx from the embedded Dockerfile.
+
+.. code:: sh
+
+ git clone https://github.com/asciimoo/searx.git
+ cd searx
+ docker build -t whatever/searx .
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
new file mode 100644
index 000000000..5e32d9684
--- /dev/null
+++ b/docs/admin/installation-nginx.rst
@@ -0,0 +1,381 @@
+.. _installation nginx:
+
+==================
+Install with nginx
+==================
+
+.. _nginx:
+ https://docs.nginx.com/nginx/admin-guide/
+.. _nginx server configuration:
+ https://docs.nginx.com/nginx/admin-guide/web-server/web-server/#setting-up-virtual-servers
+.. _nginx beginners guide:
+ http://nginx.org/en/docs/beginners_guide.html
+.. _Getting Started wiki:
+ https://www.nginx.com/resources/wiki/start/
+.. _uWSGI support from nginx:
+ https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
+.. _uwsgi_params:
+ https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html#configuring-nginx
+.. _SCRIPT_NAME:
+ https://werkzeug.palletsprojects.com/en/1.0.x/wsgi/#werkzeug.wsgi.get_script_name
+
+.. sidebar:: further reading
+
+ - nginx_
+ - `nginx beginners guide`_
+ - `nginx server configuration`_
+ - `Getting Started wiki`_
+ - `uWSGI support from nginx`_
+
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
+----
+
+**Install** :ref:`nginx searx site` using :ref:`filtron.sh <filtron.sh overview>`
+
+.. code:: bash
+
+ $ sudo -H ./utils/filtron.sh nginx install
+
+**Install** :ref:`nginx searx site` using :ref:`morty.sh <morty.sh overview>`
+
+.. code:: bash
+
+ $ sudo -H ./utils/morty.sh nginx install
+
+----
+
+
+The nginx HTTP server
+=====================
+
+If nginx_ is not installed (uwsgi will not work with the package nginx-light),
+install it now.
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ sudo -H apt-get install nginx
+
+ .. group-tab:: Arch Linux
+
+ .. code-block:: sh
+
+ sudo -H pacman -S nginx-mainline
+ sudo -H systemctl enable nginx
+ sudo -H systemctl start nginx
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code-block:: sh
+
+ sudo -H dnf install nginx
+ sudo -H systemctl enable nginx
+ sudo -H systemctl start nginx
+
+Now at http://localhost you should see a *Welcome to nginx!* page, on Fedora you
+see a *Fedora Webserver - Test Page*. The test page comes from the default
+`nginx server configuration`_. How this default intro site is configured,
+depends on the linux distribution:
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ less /etc/nginx/nginx.conf
+
+ there is a line including site configurations from:
+
+ .. code:: nginx
+
+ include /etc/nginx/sites-enabled/*;
+
+ .. group-tab:: Arch Linux
+
+ .. code-block:: sh
+
+ less /etc/nginx/nginx.conf
+
+ in there is a configuration section named ``server``:
+
+ .. code-block:: nginx
+
+ server {
+ listen 80;
+ server_name localhost;
+ # ...
+ }
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code-block:: sh
+
+ less /etc/nginx/nginx.conf
+
+ there is a line including site configurations from:
+
+ .. code:: nginx
+
+ include /etc/nginx/conf.d/*.conf;
+
+.. _nginx searx site:
+
+A nginx searx site
+==================
+
+.. sidebar:: public to the internet?
+
+ If your searx instance is public, stop here and first install :ref:`filtron
+ reverse proxy <filtron.sh>` and :ref:`result proxy morty <morty.sh>`, see
+ :ref:`installation scripts`. If already done, follow setup: *searx via
+ filtron plus morty*.
+
+Now you have to create a configuration for the searx site. If nginx_ is new to
+you, the `nginx beginners guide`_ is a good starting point and the `Getting
+Started wiki`_ is always a good resource *to keep in the pocket*.
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ Create configuration at ``/etc/nginx/sites-available/searx`` and place a
+ symlink to sites-enabled:
+
+ .. code:: sh
+
+ sudo -H ln -s /etc/nginx/sites-available/searx /etc/nginx/sites-enabled/searx
+
+ .. group-tab:: Arch Linux
+
+ In the ``/etc/nginx/nginx.conf`` file, replace the configuration section
+ named ``server``.
+
+ .. group-tab:: Fedora / RHEL
+
+ Create configuration at ``/etc/nginx/conf.d/searx`` and place a
+ symlink to sites-enabled:
+
+.. tabs::
+
+ .. group-tab:: searx via filtron plus morty
+
+ Use this setup, if your instance is public to the internet, compare
+ figure: :ref:`architecture <arch public>` and :ref:`installation scripts`.
+
+ 1. Configure a reverse proxy for :ref:`filtron <filtron.sh>`, listening on
+ *localhost 4004* (:ref:`filtron route request`):
+
+ .. code:: nginx
+
+ # https://example.org/searx
+
+ location /searx {
+ proxy_pass http://127.0.0.1:4004/;
+
+ proxy_set_header Host $http_host;
+ proxy_set_header Connection $http_connection;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header X-Script-Name /searx;
+ }
+
+ location /searx/static {
+ /usr/local/searx/searx-src/searx/static;
+ }
+
+
+ 2. Configure reverse proxy for :ref:`morty <searx morty>`, listening on
+ *localhost 3000*:
+
+ .. code:: nginx
+
+ # https://example.org/morty
+
+ location /morty {
+ proxy_pass http://127.0.0.1:3000/;
+
+ proxy_set_header Host $http_host;
+ proxy_set_header Connection $http_connection;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ }
+
+ Note that reverse proxy advised to be used in case of single-user or
+ low-traffic instances. For a fully result proxification add :ref:`morty's
+ <searx morty>` **public URL** to your :origin:`searx/settings.yml`:
+
+ .. code:: yaml
+
+ result_proxy:
+ # replace example.org with your server's public name
+ url : https://example.org/morty
+
+ server:
+ image_proxy : True
+
+
+ .. group-tab:: proxy or uWSGI
+
+ Be warned, with this setup, your instance isn't :ref:`protected <searx
+ filtron>`. Nevertheless it is good enough for intranet usage and it is a
+ excellent example of; *how different services can be set up*. The next
+ example shows a reverse proxy configuration wrapping the :ref:`searx-uWSGI
+ application <uwsgi configuration>`, listening on ``http =
+ 127.0.0.1:8888``.
+
+ .. code:: nginx
+
+ # https://hostname.local/
+
+ location / {
+ proxy_pass http://127.0.0.1:8888;
+
+ proxy_set_header Host $host;
+ proxy_set_header Connection $http_connection;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ proxy_buffering off;
+ }
+
+ Alternatively you can use the `uWSGI support from nginx`_ via unix
+ sockets. For socket communication, you have to activate ``socket =
+ /run/uwsgi/app/searx/socket`` and comment out the ``http =
+ 127.0.0.1:8888`` configuration in your :ref:`uwsgi ini file <uwsgi
+ configuration>`.
+
+ The example shows a nginx virtual ``server`` configuration, listening on
+ port 80 (IPv4 and IPv6 http://[::]:80). The uWSGI app is configured at
+ location ``/`` by importing the `uwsgi_params`_ and passing requests to
+ the uWSGI socket (``uwsgi_pass``). The ``server``\'s root points to the
+ :ref:`searx-src clone <searx-src>` and wraps directly the
+ :origin:`searx/static/` content at ``location /static``.
+
+ .. code:: nginx
+
+ server {
+ # replace hostname.local with your server's name
+ server_name hostname.local;
+
+ listen 80;
+ listen [::]:80;
+
+ location / {
+ include uwsgi_params;
+ uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+ }
+
+ root /usr/local/searx/searx-src/searx;
+ location /static { }
+ }
+
+ If not already exists, create a folder for the unix sockets, which can be
+ used by the searx account:
+
+ .. code:: bash
+
+ mkdir -p /run/uwsgi/app/searx/
+ sudo -H chown -R searx:searx /run/uwsgi/app/searx/
+
+ .. group-tab:: \.\. at subdir URL
+
+ Be warned, with these setups, your instance isn't :ref:`protected <searx
+ filtron>`. The examples are just here to demonstrate how to export the
+ searx application from a subdirectory URL ``https://example.org/searx/``.
+
+ .. code:: nginx
+
+ # https://hostname.local/searx
+
+ location /searx {
+ proxy_pass http://127.0.0.1:8888;
+
+ proxy_set_header Host $host;
+ proxy_set_header Connection $http_connection;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header X-Script-Name /searx;
+ proxy_buffering off;
+ }
+
+ location /searx/static {
+ alias /usr/local/searx/searx-src/searx/static;
+ }
+
+ The ``X-Script-Name /searx`` is needed by the searx implementation to
+ calculate relative URLs correct. The next example shows a uWSGI
+ configuration. Since there are no HTTP headers in a (u)WSGI protocol, the
+ value is shipped via the SCRIPT_NAME_ in the WSGI environment.
+
+ .. code:: nginx
+
+ # https://hostname.local/searx
+
+ location /searx {
+ uwsgi_param SCRIPT_NAME /searx;
+ include uwsgi_params;
+ uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+ }
+
+ location /searx/static {
+ alias /usr/local/searx/searx-src/searx;
+ }
+
+ For searx to work correctly the ``base_url`` must be set in the
+ :origin:`searx/settings.yml`.
+
+ .. code:: yaml
+
+ server:
+ # replace example.org with your server's public name
+ base_url : https://example.org/searx/
+
+
+Restart service:
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ sudo -H systemctl restart nginx
+ sudo -H service uwsgi restart searx
+
+ .. group-tab:: Arch Linux
+
+ .. code:: sh
+
+ sudo -H systemctl restart nginx
+ sudo -H systemctl restart uwsgi@searx
+
+ .. group-tab:: Fedora
+
+ .. code:: sh
+
+ sudo -H systemctl restart nginx
+ sudo -H touch /etc/uwsgi.d/searx.ini
+
+
+Disable logs
+============
+
+For better privacy you can disable nginx logs in ``/etc/nginx/nginx.conf``.
+
+.. code:: nginx
+
+ http {
+ # ...
+ access_log /dev/null;
+ error_log /dev/null;
+ # ...
+ }
diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
new file mode 100644
index 000000000..f1d486021
--- /dev/null
+++ b/docs/admin/installation-searx.rst
@@ -0,0 +1,92 @@
+.. _installation basic:
+
+=========================
+Step by step installation
+=========================
+
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
+Step by step installation with virtualenv. For Ubuntu, be sure to have enable
+universe repository.
+
+.. _install packages:
+
+Install packages
+================
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START distro-packages
+ :end-before: END distro-packages
+
+.. hint::
+
+ This installs also the packages needed by :ref:`searx uwsgi`
+
+.. _create searx user:
+
+Create user
+===========
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START create user
+ :end-before: END create user
+
+.. _searx-src:
+
+install searx & dependencies
+============================
+
+Start a interactive shell from new created user and clone searx:
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START clone searx
+ :end-before: END clone searx
+
+In the same shell create *virtualenv*:
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START create virtualenv
+ :end-before: END create virtualenv
+
+To install searx's dependencies, exit the searx *bash* session you opened above
+and restart a new. Before install, first check if your *virualenv* was sourced
+from the login (*~/.profile*):
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START manage.sh update_packages
+ :end-before: END manage.sh update_packages
+
+.. tip::
+
+ Open a second terminal for the configuration tasks and left the ``(searx)$``
+ terminal open for the tasks below.
+
+Configuration
+==============
+
+Create a copy of the :origin:`searx/settings.yml` configuration file in system's
+*/etc* folder. Configure like shown below -- replace ``searx@\$(uname -n)`` with
+a name of your choice -- *and/or* edit ``/etc/searx/settings.yml`` if necessary.
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START searx config
+ :end-before: END searx config
+
+Check
+=====
+
+To check your searx setup, optional enable debugging and start the *webapp*.
+Searx looks at the exported environment ``$SEARX_SETTINGS_PATH`` for a
+configuration file.
+
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START check searx installation
+ :end-before: END check searx installation
+
+If everything works fine, hit ``[CTRL-C]`` to stop the *webapp* and disable the
+debug option in ``settings.yml``. You can now exit searx user bash (enter exit
+command twice). At this point searx is not demonized; uwsgi allows this.
+
diff --git a/docs/admin/installation-uwsgi.rst b/docs/admin/installation-uwsgi.rst
new file mode 100644
index 000000000..ac4c463b9
--- /dev/null
+++ b/docs/admin/installation-uwsgi.rst
@@ -0,0 +1,149 @@
+.. _searx uwsgi:
+
+=====
+uwsgi
+=====
+
+.. sidebar:: further reading
+
+ - `systemd.unit`_
+ - `uWSGI Emperor`_
+
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
+
+.. _systemd.unit: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
+.. _One service per app in systemd:
+ https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+.. _uWSGI Emperor:
+ https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+.. _uwsgi ini file:
+ https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html#ini-files
+.. _systemd unit template:
+ http://0pointer.de/blog/projects/instances.html
+
+
+Origin uWSGI
+============
+
+How uWSGI is implemented by distributors is different. uWSGI itself
+recommend two methods
+
+`systemd.unit`_ template files as described here `One service per app in systemd`_.
+
+ There is one `systemd unit template`_ and one `uwsgi ini file`_ per uWSGI-app
+ placed at dedicated locations. Take archlinux and a searx.ini as example::
+
+ unit template --> /usr/lib/systemd/system/uwsgi@.service
+ uwsgi ini files --> /etc/uwsgi/searx.ini
+
+ The searx app can be maintained as know from common systemd units::
+
+ systemctl enable uwsgi@searx
+ systemctl start uwsgi@searx
+ systemctl restart uwsgi@searx
+ systemctl stop uwsgi@searx
+
+The `uWSGI Emperor`_ mode which fits for maintaining a large range of uwsgi apps.
+
+ The Emperor mode is a special uWSGI instance that will monitor specific
+ events. The Emperor mode (service) is started by a (common, not template)
+ systemd unit. The Emperor service will scan specific directories for `uwsgi
+ ini file`_\s (also know as *vassals*). If a *vassal* is added, removed or the
+ timestamp is modified, a corresponding action takes place: a new uWSGI
+ instance is started, reload or stopped. Take Fedora and a searx.ini as
+ example::
+
+ to start a new searx instance create --> /etc/uwsgi.d/searx.ini
+ to reload the instance edit timestamp --> touch /etc/uwsgi.d/searx.ini
+ to stop instance remove ini --> rm /etc/uwsgi.d/searx.ini
+
+Distributors
+============
+
+The `uWSGI Emperor`_ mode and `systemd unit template`_ is what the distributors
+mostly offer their users, even if they differ in the way they implement both
+modes and their defaults. Another point they might differ is the packaging of
+plugins (if so, compare :ref:`install packages`) and what the default python
+interpreter is (python2 vs. python3).
+
+Fedora starts a Emperor by default, while archlinux does not start any uwsgi
+service by default. Worth to know; debian (ubuntu) follow a complete different
+approach. *debian*: your are familiar with the apache infrastructure? .. they
+do similar for the uWSGI infrastructure (with less comfort), the folders are::
+
+ /etc/uwsgi/apps-available/
+ /etc/uwsgi/apps-enabled/
+
+The `uwsgi ini file`_ is enabled by a symbolic link::
+
+ ln -s /etc/uwsgi/apps-available/searx.ini /etc/uwsgi/apps-enabled/
+
+From debian's documentation (``/usr/share/doc/uwsgi/README.Debian.gz``): You
+could control specific instance(s) by issuing::
+
+ service uwsgi <command> <confname> <confname> ...
+
+ sudo -H service uwsgi start searx
+ sudo -H service uwsgi stop searx
+
+My experience is, that this command is a bit buggy.
+
+.. _uwsgi configuration:
+
+Alltogether
+===========
+
+Create the configuration ini-file according to your distribution (see below) and
+restart the uwsgi application.
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START searx uwsgi-description ubuntu-20.04
+ :end-before: END searx uwsgi-description ubuntu-20.04
+
+
+ .. group-tab:: Arch Linux
+
+ .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START searx uwsgi-description arch
+ :end-before: END searx uwsgi-description arch
+
+
+ .. group-tab:: Fedora / RHEL
+
+ .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :start-after: START searx uwsgi-description fedora
+ :end-before: END searx uwsgi-description fedora
+
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :code: ini
+ :start-after: START searx uwsgi-appini ubuntu-20.04
+ :end-before: END searx uwsgi-appini ubuntu-20.04
+
+ .. group-tab:: Arch Linux
+
+ .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :code: ini
+ :start-after: START searx uwsgi-appini arch
+ :end-before: END searx uwsgi-appini arch
+
+ .. group-tab:: Fedora / RHEL
+
+ .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+ :code: ini
+ :start-after: START searx uwsgi-appini fedora
+ :end-before: END searx uwsgi-appini fedora
+
+
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index 15800fc01..167c300fe 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -4,346 +4,63 @@
Installation
============
-.. contents::
- :depth: 3
+*You're spoilt for choice*, choose your preferred method of installation.
-Basic installation
-==================
+- :ref:`installation docker`
+- :ref:`installation scripts`
+- :ref:`installation basic`
-Step by step installation for Debian/Ubuntu with virtualenv. For Ubuntu, be sure
-to have enable universe repository.
+The :ref:`installation basic` is good enough for intranet usage and it is a
+excellent illustration of *how a searx instance is build up*. If you place your
+instance public to the internet you should really consider to install a
+:ref:`filtron reverse proxy <filtron.sh>` and for privacy a :ref:`result proxy
+<morty.sh>` is mandatory.
-Install packages:
+Therefore, if you do not have any special preferences, its recommend to use the
+:ref:`installation docker` or the `Installation scripts`_ from our :ref:`tooling
+box <toolboxing>` as described below.
-.. code:: sh
+.. _installation scripts:
- $ sudo -H apt-get install \
- git build-essential libxslt-dev \
- python-dev python-virtualenv python-babel \
- zlib1g-dev libffi-dev libssl-dev
+Installation scripts
+====================
-Install searx:
+.. sidebar:: Update OS first!
-.. code:: sh
+ To avoid unwanted side effects, update your OS before installing searx.
- cd /usr/local
- sudo -H git clone https://github.com/asciimoo/searx.git
- sudo -H useradd searx -d /usr/local/searx
- sudo -H chown searx:searx -R /usr/local/searx
+The following will install a setup as shown in :ref:`architecture`. First you
+need to get a clone. The clone is only needed for the installation procedure
+and some maintenance tasks (alternatively you can create your own fork).
-Install dependencies in a virtualenv:
+.. code:: bash
-.. code:: sh
+ $ cd ~/Downloads
+ $ git clone https://github.com/asciimoo/searx searx
+ $ cd searx
- cd /usr/local/searx
- sudo -H -u searx -i
+**Install** :ref:`searx service <searx.sh>`
-.. code:: sh
+This installs searx as described in :ref:`installation basic`.
- (searx)$ virtualenv searx-ve
- (searx)$ . ./searx-ve/bin/activate
- (searx)$ ./manage.sh update_packages
+.. code:: bash
-Configuration
-==============
+ $ sudo -H ./utils/searx.sh install all
-.. code:: sh
+**Install** :ref:`filtron reverse proxy <filtron.sh>`
- sed -i -e "s/ultrasecretkey/`openssl rand -hex 16`/g" searx/settings.yml
+.. code:: bash
-Edit searx/settings.yml if necessary.
+ $ sudo -H ./utils/filtron.sh install all
-Check
-=====
+**Install** :ref:`result proxy <morty.sh>`
-Start searx:
+.. code:: bash
-.. code:: sh
+ $ sudo -H ./utils/morty.sh install all
- python searx/webapp.py
+If all services are running fine, you can add it to your HTTP server:
-Go to http://localhost:8888
+- :ref:`installation apache`
+- :ref:`installation nginx`
-If everything works fine, disable the debug option in settings.yml:
-
-.. code:: sh
-
- sed -i -e "s/debug : True/debug : False/g" searx/settings.yml
-
-At this point searx is not demonized ; uwsgi allows this.
-
-You can exit the virtualenv and the searx user bash (enter exit command
-twice).
-
-uwsgi
-=====
-
-Install packages:
-
-.. code:: sh
-
- sudo -H apt-get install \
- uwsgi uwsgi-plugin-python
-
-Create the configuration file ``/etc/uwsgi/apps-available/searx.ini`` with this
-content:
-
-.. code:: ini
-
- [uwsgi]
- # Who will run the code
- uid = searx
- gid = searx
-
- # disable logging for privacy
- disable-logging = true
-
- # Number of workers (usually CPU count)
- workers = 4
-
- # The right granted on the created socket
- chmod-socket = 666
-
- # Plugin to use and interpretor config
- single-interpreter = true
- master = true
- plugin = python
- lazy-apps = true
- enable-threads = true
-
- # Module to import
- module = searx.webapp
-
- # Support running the module from a webserver subdirectory.
- route-run = fixpathinfo:
-
- # Virtualenv and python path
- virtualenv = /usr/local/searx/searx-ve/
- pythonpath = /usr/local/searx/
- chdir = /usr/local/searx/searx/
-
-Activate the uwsgi application and restart:
-
-.. code:: sh
-
- cd /etc/uwsgi/apps-enabled
- ln -s ../apps-available/searx.ini
- /etc/init.d/uwsgi restart
-
-Web server
-==========
-
-with nginx
-----------
-
-If nginx is not installed (uwsgi will not work with the package
-nginx-light):
-
-.. code:: sh
-
- sudo -H apt-get install nginx
-
-Hosted at /
-~~~~~~~~~~~
-
-Create the configuration file ``/etc/nginx/sites-available/searx`` with this
-content:
-
-.. code:: nginx
-
- server {
- listen 80;
- server_name searx.example.com;
- root /usr/local/searx/searx;
-
- location /static {
- }
-
- location / {
- include uwsgi_params;
- uwsgi_pass unix:/run/uwsgi/app/searx/socket;
- }
- }
-
-Create a symlink to sites-enabled:
-
-.. code:: sh
-
- sudo -H ln -s /etc/nginx/sites-available/searx /etc/nginx/sites-enabled/searx
-
-Restart service:
-
-.. code:: sh
-
- sudo -H service nginx restart
- sudo -H service uwsgi restart
-
-from subdirectory URL (/searx)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Add this configuration in the server config file
-``/etc/nginx/sites-enabled/default``:
-
-.. code:: nginx
-
- location /searx/static {
- alias /usr/local/searx/searx/static;
- }
-
- location /searx {
- uwsgi_param SCRIPT_NAME /searx;
- include uwsgi_params;
- uwsgi_pass unix:/run/uwsgi/app/searx/socket;
- }
-
-
-**OR** using reverse proxy (Please, note that reverse proxy advised to be used
-in case of single-user or low-traffic instances.)
-
-.. code:: nginx
-
- location /searx/static {
- alias /usr/local/searx/searx/static;
- }
-
- location /searx {
- proxy_pass http://127.0.0.1:8888;
- proxy_set_header Host $host;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Scheme $scheme;
- proxy_set_header X-Script-Name /searx;
- proxy_buffering off;
- }
-
-
-Enable ``base_url`` in ``searx/settings.yml``
-
-.. code:: yaml
-
- base_url : http://your.domain.tld/searx/
-
-Restart service:
-
-.. code:: sh
-
- sudo -H service nginx restart
- sudo -H service uwsgi restart
-
-disable logs
-^^^^^^^^^^^^
-
-for better privacy you can disable nginx logs about searx.
-
-how to proceed: below ``uwsgi_pass`` in ``/etc/nginx/sites-available/default``
-add:
-
-.. code:: nginx
-
- access_log /dev/null;
- error_log /dev/null;
-
-Restart service:
-
-.. code:: sh
-
- sudo -H service nginx restart
-
-with apache
------------
-
-Add wsgi mod:
-
-.. code:: sh
-
- sudo -H apt-get install libapache2-mod-uwsgi
- sudo -H a2enmod uwsgi
-
-Add this configuration in the file ``/etc/apache2/apache2.conf``:
-
-.. code:: apache
-
- <Location />
- Options FollowSymLinks Indexes
- SetHandler uwsgi-handler
- uWSGISocket /run/uwsgi/app/searx/socket
- </Location>
-
-Note that if your instance of searx is not at the root, you should change
-``<Location />`` by the location of your instance, like ``<Location /searx>``.
-
-Restart Apache:
-
-.. code:: sh
-
- sudo -H /etc/init.d/apache2 restart
-
-disable logs
-~~~~~~~~~~~~
-
-For better privacy you can disable Apache logs.
-
-.. warning::
-
- You can only disable logs for the whole (virtual) server not for a specific
- path.
-
-Go back to ``/etc/apache2/apache2.conf`` and above ``<Location />`` add:
-
-.. code:: apache
-
- CustomLog /dev/null combined
-
-Restart Apache:
-
-.. code:: sh
-
- sudo -H /etc/init.d/apache2 restart
-
-How to update
-=============
-
-.. code:: sh
-
- cd /usr/local/searx
- sudo -H -u searx -i
-
-.. code:: sh
-
- (searx)$ . ./searx-ve/bin/activate
- (searx)$ git stash
- (searx)$ git pull origin master
- (searx)$ git stash apply
- (searx)$ ./manage.sh update_packages
-
-.. code:: sh
-
- sudo -H service uwsgi restart
-
-Docker
-======
-
-Make sure you have installed Docker. For instance, you can deploy searx like this:
-
-.. code:: sh
-
- docker pull wonderfall/searx
- docker run -d --name searx -p $PORT:8888 wonderfall/searx
-
-Go to ``http://localhost:$PORT``.
-
-See https://hub.docker.com/r/wonderfall/searx/ for more informations. It's also
-possible to build searx from the embedded Dockerfile.
-
-.. code:: sh
-
- git clone https://github.com/asciimoo/searx.git
- cd searx
- docker build -t whatever/searx .
-
-References
-==========
-
-* https://about.okhin.fr/posts/Searx/ with some additions
-
-* How to: `Setup searx in a couple of hours with a free SSL certificate
- <https://www.reddit.com/r/privacytoolsIO/comments/366kvn/how_to_setup_your_own_privacy_respecting_search/>`__
diff --git a/docs/admin/morty.rst b/docs/admin/morty.rst
index 7d7b34492..2858fde3c 100644
--- a/docs/admin/morty.rst
+++ b/docs/admin/morty.rst
@@ -1,7 +1,14 @@
+
+.. _searx morty:
+
=========================
How to setup result proxy
=========================
+.. sidebar:: further reading
+
+ - :ref:`morty.sh`
+
.. _morty: https://github.com/asciimoo/morty
.. _morty's README: https://github.com/asciimoo/morty
diff --git a/docs/admin/settings.rst b/docs/admin/settings.rst
index 2bfbae35c..8b1cb8dca 100644
--- a/docs/admin/settings.rst
+++ b/docs/admin/settings.rst
@@ -4,11 +4,17 @@
``settings.yml``
================
+This page describe the options possibilities of the :origin:`searx/settings.yml`
+file.
+
.. sidebar:: Further reading ..
- :ref:`search API`
-This page describe the options possibilities of the settings.yml file.
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
.. _settings global:
diff --git a/docs/admin/update-searx.rst b/docs/admin/update-searx.rst
new file mode 100644
index 000000000..c74c9568a
--- /dev/null
+++ b/docs/admin/update-searx.rst
@@ -0,0 +1,23 @@
+.. _update searx:
+
+=============
+How to update
+=============
+
+.. code:: sh
+
+ sudo -H -u searx -i
+ (searx)$ git stash
+ (searx)$ git pull origin master
+ (searx)$ git stash apply
+ (searx)$ ./manage.sh update_packages
+
+Restart uwsgi:
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code:: sh
+
+ sudo -H systemctl restart uwsgi
diff --git a/docs/blog/index.rst b/docs/blog/index.rst
index 94847ad62..5a8135fdc 100644
--- a/docs/blog/index.rst
+++ b/docs/blog/index.rst
@@ -3,7 +3,8 @@ Blog
====
.. toctree::
- :maxdepth: 1
+ :maxdepth: 2
+ :caption: Contents
python3
admin
diff --git a/docs/blog/private-engines.rst b/docs/blog/private-engines.rst
index c26b3ed1c..796f0fc69 100644
--- a/docs/blog/private-engines.rst
+++ b/docs/blog/private-engines.rst
@@ -13,7 +13,7 @@ Private engines
To solve this issue private engines were introduced in :pull:`1823`.
A new option was added to engines named `tokens`. It expects a list
of strings. If the user making a request presents one of the tokens
-of an engine, he/she is able to access information about the engine
+of an engine, they can access information about the engine
and make search requests.
Example configuration to restrict access to the Arch Linux Wiki engine:
diff --git a/docs/build-templates/filtron.rst b/docs/build-templates/filtron.rst
new file mode 100644
index 000000000..83a451820
--- /dev/null
+++ b/docs/build-templates/filtron.rst
@@ -0,0 +1,52 @@
+.. START create user
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ $ sudo -H useradd --shell /bin/bash --system \\
+ --home-dir "$SERVICE_HOME" \\
+ --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+
+ $ sudo -H mkdir "$SERVICE_HOME"
+ $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+
+.. END create user
+
+.. START install go
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: bash
+
+ $ cat > "$GO_ENV" <<EOF
+ export GOPATH=${SERVICE_HOME}/go-apps
+ export PATH=\$PATH:${SERVICE_HOME}/local/go/bin:\$GOPATH/bin
+ EOF
+ $ sudo -i -u "${SERVICE_USER}"
+ (${SERVICE_USER}) $ echo 'source $GO_ENV' >> ~/.profile
+ (${SERVICE_USER}) $ mkdir ${SERVICE_HOME}/local
+ (${SERVICE_USER}) $ wget --progress=bar -O "${GO_TAR}" \\
+ "${GO_PKG_URL}"
+ (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local/go -xzf "${GO_TAR}"
+ (${SERVICE_USER}) $ which go
+ ${SERVICE_HOME}/local/go/bin/go
+
+.. END install go
+
+.. START install filtron
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: bash
+
+ $ sudo -i -u "${SERVICE_USER}"
+ (${SERVICE_USER}) $ go get -v -u github.com/asciimoo/filtron
+
+.. END install filtron
diff --git a/docs/build-templates/morty.rst b/docs/build-templates/morty.rst
new file mode 100644
index 000000000..4a5d1f27f
--- /dev/null
+++ b/docs/build-templates/morty.rst
@@ -0,0 +1,52 @@
+.. START create user
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ $ sudo -H useradd --shell /bin/bash --system \\
+ --home-dir "$SERVICE_HOME" \\
+ --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+
+ $ sudo -H mkdir "$SERVICE_HOME"
+ $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+
+.. END create user
+
+.. START install go
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: bash
+
+ $ cat > "$GO_ENV" <<EOF
+ export GOPATH=${SERVICE_HOME}/go-apps
+ export PATH=\$PATH:${SERVICE_HOME}/local/go/bin:\$GOPATH/bin
+ EOF
+ $ sudo -i -u "${SERVICE_USER}"
+ (${SERVICE_USER}) $ echo 'source $GO_ENV' >> ~/.profile
+ (${SERVICE_USER}) $ mkdir ${SERVICE_HOME}/local
+ (${SERVICE_USER}) $ wget --progress=bar -O "${GO_TAR}" \\
+ "${GO_PKG_URL}"
+ (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local/go -xzf "${GO_TAR}"
+ (${SERVICE_USER}) $ which go
+ ${SERVICE_HOME}/local/go/bin/go
+
+.. END install go
+
+.. START install morty
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: bash
+
+ $ sudo -i -u "${SERVICE_USER}"
+ (${SERVICE_USER}) $ go get -v -u github.com/asciimoo/morty
+
+.. END install morty
diff --git a/docs/build-templates/searx.rst b/docs/build-templates/searx.rst
new file mode 100644
index 000000000..5cb70e95f
--- /dev/null
+++ b/docs/build-templates/searx.rst
@@ -0,0 +1,192 @@
+.. template evaluated by: ./utils/searx.sh docs
+.. hint: all dollar-names are variables, dollar sign itself is quoted by: \\$
+
+.. START distro-packages
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code-block:: sh
+
+ $ sudo -H apt-get install -y \\
+${debian}
+
+ .. group-tab:: Arch Linux
+
+ .. code-block:: sh
+
+ $ sudo -H pacman -S --noconfirm \\
+${arch}
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code-block:: sh
+
+ $ sudo -H dnf install -y \\
+${fedora}
+
+.. END distro-packages
+
+.. START build-packages
+
+.. tabs::
+
+ .. group-tab:: Ubuntu / debian
+
+ .. code-block:: sh
+
+ $ sudo -H apt-get install -y \\
+${debian_build}
+
+ .. group-tab:: Arch Linux
+
+ .. code-block:: sh
+
+ $ sudo -H pacman -S --noconfirm \\
+${arch_build}
+
+ .. group-tab:: Fedora / RHEL
+
+ .. code-block:: sh
+
+ $ sudo -H dnf install -y \\
+${fedora_build}
+
+.. END build-packages
+
+.. START create user
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ $ sudo -H useradd --shell /bin/bash --system \\
+ --home-dir "$SERVICE_HOME" \\
+ --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+
+ $ sudo -H mkdir "$SERVICE_HOME"
+ $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+
+.. END create user
+
+.. START clone searx
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ $ sudo -H -u ${SERVICE_USER} -i
+ (${SERVICE_USER})$ git clone "https://github.com/asciimoo/searx.git" "$SEARX_SRC"
+
+.. END clone searx
+
+.. START create virtualenv
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ (${SERVICE_USER})$ python3 -m venv "${SEARX_PYENV}"
+ (${SERVICE_USER})$ echo ". ${SEARX_PYENV}/bin/activate" >> "$SERVICE_HOME/.profile"
+
+.. END create virtualenv
+
+.. START manage.sh update_packages
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ $ sudo -H -u ${SERVICE_USER} -i
+
+ (${SERVICE_USER})$ command -v python && python --version
+ $SEARX_PYENV/bin/python
+ Python 3.8.1
+
+ # update pip's boilerplate ..
+ pip install -U pip
+ pip install -U setuptools
+ pip install -U wheel
+
+ # jump to searx's working tree and install searx into virtualenv
+ (${SERVICE_USER})$ cd "$SEARX_SRC"
+ (${SERVICE_USER})$ pip install -e .
+
+
+.. END manage.sh update_packages
+
+.. START searx config
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ $ sudo -H cp "$SEARX_SRC/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+ $ sudo -H sed -i -e "s/ultrasecretkey/\\$(openssl rand -hex 16)/g" "$SEARX_SETTINGS_PATH"
+ $ sudo -H sed -i -e "s/{instance_name}/searx@\\$(uname -n)/g" "$SEARX_SETTINGS_PATH"
+
+.. END searx config
+
+.. START check searx installation
+
+.. tabs::
+
+ .. group-tab:: bash
+
+ .. code-block:: sh
+
+ # enable debug ..
+ $ sudo -H sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
+
+ # start webapp
+ $ sudo -H -u ${SERVICE_USER} -i
+ (${SERVICE_USER})$ cd ${SEARX_SRC}
+ (${SERVICE_USER})$ export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
+ (${SERVICE_USER})$ python searx/webapp.py
+
+ # disable debug
+ $ sudo -H sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
+
+Open WEB browser and visit http://$SEARX_INTERNAL_URL . If you are inside a
+container or in a script, test with curl:
+
+.. tabs::
+
+ .. group-tab:: WEB browser
+
+ .. code-block:: sh
+
+ $ xgd-open http://$SEARX_INTERNAL_URL
+
+ .. group-tab:: curl
+
+ .. code-block:: none
+
+ $ curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
+
+ * Trying 127.0.0.1:8888...
+ * TCP_NODELAY set
+ * Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
+ > HEAD / HTTP/1.1
+ > Host: 127.0.0.1:8888
+ > User-Agent: curl/7.68.0
+ > Accept: */*
+ >
+ * Mark bundle as not supporting multiuse
+ * HTTP 1.0, assume close after body
+ < HTTP/1.0 200 OK
+ HTTP/1.0 200 OK
+ ...
+
+.. END check searx installation
diff --git a/docs/conf.py b/docs/conf.py
index 03e977aba..1f79e4c14 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
import sys, os
+from sphinx_build_tools import load_sphinx_config
from searx.version import VERSION_STRING
from pallets_sphinx_themes import ProjectLink
from searx.brand import GIT_URL
+GIT_BRANCH = os.environ.get("GIT_BRANCH", "master")
from searx.brand import SEARX_URL
from searx.brand import DOCS_URL
@@ -22,6 +24,8 @@ master_doc = "index"
source_suffix = '.rst'
numfig = True
+exclude_patterns = ['build-templates/*.rst']
+
from searx import webapp
jinja_contexts = {
'webapp': dict(**webapp.__dict__)
@@ -35,7 +39,7 @@ extlinks['wiki'] = ('https://github.com/asciimoo/searx/wiki/%s', ' ')
extlinks['pull'] = ('https://github.com/asciimoo/searx/pull/%s', 'PR ')
# links to custom brand
-extlinks['origin'] = (GIT_URL + '/blob/master/%s', 'git://')
+extlinks['origin'] = (GIT_URL + '/blob/' + GIT_BRANCH + '/%s', 'git://')
extlinks['patch'] = (GIT_URL + '/commit/%s', '#')
extlinks['search'] = (SEARX_URL + '/%s', '#')
extlinks['docs'] = (DOCS_URL + '/%s', 'docs: ')
@@ -61,6 +65,8 @@ extensions = [
"pallets_sphinx_themes",
"sphinx_issues", # https://github.com/sloria/sphinx-issues/blob/master/README.rst
"sphinxcontrib.jinja", # https://github.com/tardyp/sphinx-jinja
+ "sphinxcontrib.programoutput", # https://github.com/NextThought/sphinxcontrib-programoutput
+ 'linuxdoc.kernel_include', # Implementation of the 'kernel-include' reST-directive.
'linuxdoc.rstFlatTable', # Implementation of the 'flat-table' reST-directive.
'linuxdoc.kfigure', # Sphinx extension which implements scalable image handling.
"sphinx_tabs.tabs", # https://github.com/djungelorm/sphinx-tabs
@@ -112,3 +118,9 @@ html_show_sourcelink = False
latex_documents = [
(master_doc, "searx-{}.tex".format(VERSION_STRING), html_title, author, "manual")
]
+
+# ------------------------------------------------------------------------------
+# Since loadConfig overwrites settings from the global namespace, it has to be
+# the last statement in the conf.py file
+# ------------------------------------------------------------------------------
+load_sphinx_config(globals())
diff --git a/docs/dev/contribution_guide.rst b/docs/dev/contribution_guide.rst
index 459dfb448..f9e9569f9 100644
--- a/docs/dev/contribution_guide.rst
+++ b/docs/dev/contribution_guide.rst
@@ -4,6 +4,11 @@
How to contribute
=================
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: entry
+
Prime directives: Privacy, Hackability
======================================
diff --git a/docs/dev/index.rst b/docs/dev/index.rst
index cb913a82b..ba0a25a9c 100644
--- a/docs/dev/index.rst
+++ b/docs/dev/index.rst
@@ -3,7 +3,8 @@ Developer documentation
=======================
.. toctree::
- :maxdepth: 1
+ :maxdepth: 2
+ :caption: Contents
quickstart
contribution_guide
diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst
index f5957001c..62cd0a984 100644
--- a/docs/dev/makefile.rst
+++ b/docs/dev/makefile.rst
@@ -11,23 +11,17 @@ Makefile Targets
Before looking deeper at the targets, first read about :ref:`makefile setup`
and :ref:`make pyenv`.
+ To install system requirements follow :ref:`buildhosts`.
+
With the aim to simplify development cycles, started with :pull:`1756` a
``Makefile`` based boilerplate was added. If you are not familiar with
Makefiles, we recommend to read gnu-make_ introduction.
The usage is simple, just type ``make {target-name}`` to *build* a target.
-Calling the ``help`` target gives a first overview::
-
- $ make help
- test - run developer tests
- docs - build documentation
- docs-live - autobuild HTML documentation while editing
- run - run developer instance
- install - developer install (./local)
- uninstall - uninstall (./local)
- gh-pages - build docs & deploy on gh-pages branch
- clean - drop builds and environments
- ...
+Calling the ``help`` target gives a first overview (``make help``):
+
+.. program-output:: bash -c "cd ..; make --no-print-directory help"
+
.. contents:: Contents
:depth: 2
@@ -37,27 +31,33 @@ Calling the ``help`` target gives a first overview::
.. _makefile setup:
-Setup
-=====
+Makefile setup
+==============
.. _git stash: https://git-scm.com/docs/git-stash
-The main setup is done in the :origin:`Makefile`::
-
- export GIT_URL=https://github.com/asciimoo/searx
- export SEARX_URL=https://searx.me
- export DOCS_URL=https://asciimoo.github.io/searx
-
.. sidebar:: fork & upstream
Commit changes in your (local) branch, fork or whatever, but do not push them
upstream / `git stash`_ is your friend.
-:GIT_URL: Changes this, to point to your searx fork.
+The main setup is done in the :origin:`Makefile`.
+
+.. literalinclude:: ../../Makefile
+ :start-after: START Makefile setup
+ :end-before: END Makefile setup
+
+:GIT_URL: Changes this, to point to your searx fork.
+:GIT_BRANCH: Changes this, to point to your searx branch.
+:SEARX_URL: Changes this, to point to your searx instance.
+:DOCS_URL: If you host your own (*brand*) documentation, change this URL.
-:SEARX_URL: Changes this, to point to your searx instance.
+If you change any of this build environment variables, you have to run ``make
+buildenv``::
-:DOCS_URL: If you host your own (branded) documentation, change this URL.
+ $ make buildenv
+ build searx/brand.py
+ build utils/brand.env
.. _make pyenv:
@@ -170,7 +170,7 @@ e.g.:
.. code:: sh
- $ make test.pep8 test.unit
+ $ make test.pep8 test.unit test.sh
. ./local/py3/bin/activate; ./manage.sh pep8_check
[!] Running pep8 check
. ./local/py3/bin/activate; ./manage.sh unit_tests
diff --git a/docs/dev/quickstart.rst b/docs/dev/quickstart.rst
index a786eda63..a4fc897c4 100644
--- a/docs/dev/quickstart.rst
+++ b/docs/dev/quickstart.rst
@@ -27,7 +27,7 @@ searx-ve virtualenv and install the required packages using ``manage.sh``.
cd ~/myprojects
git clone https://github.com/asciimoo/searx.git
cd searx
- virtualenv searx-ve
+ python3 -m venv searx-ve
. ./searx-ve/bin/activate
./manage.sh update_dev_packages
diff --git a/docs/dev/reST.rst b/docs/dev/reST.rst
index 4835cbbcf..906a0e9af 100644
--- a/docs/dev/reST.rst
+++ b/docs/dev/reST.rst
@@ -325,8 +325,9 @@ Literal blocks
The simplest form of :duref:`literal-blocks` is a indented block introduced by
two colons (``::``). For highlighting use :dudir:`highlight` or :ref:`reST
-code` directive. To include literals from external files use directive
-:dudir:`literalinclude`.
+code` directive. To include literals from external files use
+:rst:dir:`literalinclude` or :ref:`kernel-include <kernel-include-directive>`
+directive (latter one expands environment variables in the path name).
.. _reST literal:
@@ -1312,9 +1313,8 @@ others are basic-tabs_ and code-tabs_. Below a *group-tab* example from
.. literalinclude:: ../admin/buildhosts.rst
:language: reST
- :start-after: .. _system requirements:
- :end-before: .. _system requirements END:
-
+ :start-after: .. SNIP sh lint requirements
+ :end-before: .. SNAP sh lint requirements
.. _math:
diff --git a/docs/dev/search_api.rst b/docs/dev/search_api.rst
index 922548ffb..960d2fb37 100644
--- a/docs/dev/search_api.rst
+++ b/docs/dev/search_api.rst
@@ -81,7 +81,7 @@ Parameters
Theme of instance.
Please note, available themes depend on an instance. It is possible that an
- instance administrator deleted, created or renamed themes on his/her instance.
+ instance administrator deleted, created or renamed themes on their instance.
See the available options in the preferences page of the instance.
``oscar-style`` : default ``logicodev``
@@ -91,7 +91,7 @@ Parameters
``oscar``.
Please note, available styles depend on an instance. It is possible that an
- instance administrator deleted, created or renamed styles on his/her
+ instance administrator deleted, created or renamed styles on their
instance. See the available options in the preferences page of the instance.
``enabled_plugins`` : optional
diff --git a/docs/index.rst b/docs/index.rst
index b62f4dc5d..9e590867c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -2,7 +2,14 @@
Welcome to searx
================
-Search without being tracked.
+ *Search without being tracked.*
+
+Searx is a free internet metasearch engine which aggregates results from more
+than 70 search services. Users are neither tracked nor profiled. Additionally,
+searx can be used over Tor for online anonymity.
+
+Get started with searx by using one of the Searx-instances_. If you don't trust
+anyone, you can set up your own, see :ref:`installation`.
.. sidebar:: Features
@@ -16,19 +23,14 @@ Search without being tracked.
- Hosted by organizations, such as *La Quadrature du Net*, which promote
digital rights
-Searx is a free internet metasearch engine which aggregates results from more
-than 70 search services. Users are neither tracked nor profiled. Additionally,
-searx can be used over Tor for online anonymity.
-
-Get started with searx by using one of the Searx-instances_. If you don't trust
-anyone, you can set up your own, see :ref:`installation`.
-
.. toctree::
:maxdepth: 2
+ :caption: Contents
user/index
admin/index
dev/index
+ utils/index
blog/index
.. _Searx-instances: https://searx.space
diff --git a/docs/user/conf.py b/docs/user/conf.py
new file mode 100644
index 000000000..53ade4b63
--- /dev/null
+++ b/docs/user/conf.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8; mode: python -*-
+"""Configuration for the Searx user handbook
+"""
+project = 'Searx User-HB'
+version = release = VERSION_STRING
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index' # startdocname
+ , 'searx-user-hb.tex' # targetname
+ , '' # take title from .rst
+ , author # author
+ , 'howto' # documentclass
+ , False # toctree_only
+ ),
+]
+
diff --git a/docs/user/index.rst b/docs/user/index.rst
index b13aca216..96d11bf8a 100644
--- a/docs/user/index.rst
+++ b/docs/user/index.rst
@@ -3,7 +3,8 @@ User documentation
==================
.. toctree::
- :maxdepth: 1
+ :maxdepth: 2
+ :caption: Contents
search_syntax
own-instance
diff --git a/docs/user/own-instance.rst b/docs/user/own-instance.rst
index a2f736562..af415b61d 100644
--- a/docs/user/own-instance.rst
+++ b/docs/user/own-instance.rst
@@ -2,8 +2,10 @@
Why use a private instance?
===========================
-"Is it worth to run my own instance?" is a common question among searx users.
-Before answering this question, see what options a searx user has.
+ *"Is it worth to run my own instance?"*
+
+\.\. is a common question among searx users. Before answering this question,
+see what options a searx user has.
Public instances are open to everyone who has access to its URL. Usually, these
are operated by unknown parties (from the users' point of view). Private
@@ -42,9 +44,9 @@ hidden from visited result pages.
What are the consequences of using public instances?
----------------------------------------------------
-If someone uses a public instance, he/she has to trust the administrator of that
+If someone uses a public instance, they have to trust the administrator of that
instance. This means that the user of the public instance does not know whether
-his/her requests are logged, aggregated and sent or sold to a third party.
+their requests are logged, aggregated and sent or sold to a third party.
Also, public instances without proper protection are more vulnerable to abusing
the search service, In this case the external service in exchange returns
diff --git a/docs/user/public_instances.rst b/docs/user/public_instances.rst
deleted file mode 100644
index f58ab7a02..000000000
--- a/docs/user/public_instances.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-:orphan:
-
-This page page has been moved to `searx.space <https://searx.space/>`__
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
new file mode 100644
index 000000000..86e3fa88a
--- /dev/null
+++ b/docs/utils/filtron.sh.rst
@@ -0,0 +1,80 @@
+
+.. _filtron.sh:
+
+====================
+``utils/filtron.sh``
+====================
+
+.. sidebar:: further reading
+
+ - :ref:`searx filtron`
+ - :ref:`architecture`
+ - :ref:`installation` (:ref:`nginx <installation nginx>` & :ref:`apache
+ <installation apache>`)
+
+.. _Go: https://golang.org/
+.. _filtron: https://github.com/asciimoo/filtron
+.. _filtron README: https://github.com/asciimoo/filtron/blob/master/README.md
+
+To simplify installation and maintenance of a filtron instance you can use the
+script :origin:`utils/filtron.sh`. In most cases you will install filtron_
+simply by running the command:
+
+.. code:: bash
+
+ sudo -H ./utils/filtron.sh install all
+
+The script adds a ``${SERVICE_USER}`` (default:``filtron``) and installs filtron_
+into this user account:
+
+#. Create a separated user account (``filtron``).
+#. Download and install Go_ binary in user's $HOME (``~filtron``).
+#. Install filtron with the package management from Go_ (``go get -v -u
+ github.com/asciimoo/filtron``)
+#. Setup a proper rule configuration :origin:`[ref]
+ <utils/templates/etc/filtron/rules.json>` (``/etc/filtron/rules.json``).
+#. Setup a systemd service unit :origin:`[ref]
+ <utils/templates/lib/systemd/system/filtron.service>`
+ (``/lib/systemd/system/filtron.service``).
+
+
+Create user
+===========
+
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
+ :start-after: START create user
+ :end-before: END create user
+
+
+Install go
+==========
+
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
+ :start-after: START install go
+ :end-before: END install go
+
+
+Install filtron
+===============
+
+Install :origin:`rules.json <utils/templates/etc/filtron/rules.json>` at
+``/etc/filtron/rules.json`` (see :ref:`Sample configuration of filtron`) and
+install filtron software and systemd unit:
+
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
+ :start-after: START install filtron
+ :end-before: END install filtron
+
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
+ :start-after: START install systemd unit
+ :end-before: END install systemd unit
+
+.. _filtron.sh overview:
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory
+(:ref:`toolboxing common`):
+
+.. program-output:: ../utils/filtron.sh --help
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
new file mode 100644
index 000000000..13914af28
--- /dev/null
+++ b/docs/utils/index.rst
@@ -0,0 +1,53 @@
+.. _searx_utils:
+.. _toolboxing:
+
+=======================
+Tooling box ``utils/*``
+=======================
+
+In the folder :origin:`utils/` we maintain some tools useful for admins and
+developers.
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents
+
+ searx.sh
+ filtron.sh
+ morty.sh
+ lxc.sh
+
+.. _toolboxing common:
+
+Common commands & environment
+=============================
+
+Scripts to maintain services often dispose of common commands and environments.
+
+``shell`` : command
+ Opens a shell from the service user ``${SERVICE_USSR}``, very helpful for
+ troubleshooting.
+
+``inspect service`` : command
+ Shows status and log of the service, most often you have a option to enable
+ more verbose debug logs. Very helpful for debugging, but be careful not to
+ enable debugging in a production environment!
+
+``FORCE_TIMEOUT`` : environment
+ Sets timeout for interactive prompts. If you want to run a script in batch
+ job, with defaults choices, set ``FORCE_TIMEOUT=0``. By example; to install a
+ reverse proxy for filtron on all containers of the :ref:`searx suite
+ <lxc-searx.env>` use ::
+
+ sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/filtron.sh apache install
+
+.. _toolboxing setup:
+
+Tooling box setup
+=================
+
+The main setup is done in the :origin:`.config.sh` (read also :ref:`makefile
+setup`).
+
+.. literalinclude:: ../../.config.sh
+ :language: bash
diff --git a/docs/utils/lxc.sh.rst b/docs/utils/lxc.sh.rst
new file mode 100644
index 000000000..ae1412870
--- /dev/null
+++ b/docs/utils/lxc.sh.rst
@@ -0,0 +1,148 @@
+
+.. _snap: https://snapcraft.io
+.. _snapcraft LXD: https://snapcraft.io/lxd
+.. _LXC/LXD Image Server: https://uk.images.linuxcontainers.org/
+.. _LXC: https://linuxcontainers.org/lxc/introduction/
+.. _LXD: https://linuxcontainers.org/lxd/introduction/
+.. _`LXD@github`: https://github.com/lxc/lxd
+
+.. _archlinux: https://www.archlinux.org/
+
+.. _lxc.sh:
+
+================
+``utils/lxc.sh``
+================
+
+.. sidebar:: further reading
+
+ - snap_, `snapcraft LXD`_
+ - LXC_, LXD_
+ - `LXC/LXD Image Server`_
+ - `LXD@github`_
+
+With the use of *Linux Containers* (LXC_) we can scale our tasks over a stack of
+containers, what we call the: *lxc suite*. The *searx suite*
+(:origin:`lxc-searx.env <utils/lxc-searx.env>`) is loaded by default, every time
+you start the ``lxc.sh`` script (*you do not need to care about*).
+
+Before you can start with containers, you need to install and initiate LXD_
+once::
+
+ $ snap install lxd
+ $ lxd init --auto
+
+To make use of the containers from the *searx suite*, you have to build the
+:ref:`LXC suite containers <lxc.sh help>` initial. But be warned, **this might
+take some time**::
+
+ $ sudo -H ./utils/lxc.sh build
+
+A cup of coffee later, your LXC suite is build up and you can run whatever task
+you want / in a selected or even in all :ref:`LXC suite containers <lxc.sh
+help>`. If you do not want to build all containers, **you can build just
+one**::
+
+ $ sudo -H ./utils/lxc.sh build searx-ubu1804
+
+*Good to know ...*
+
+Eeach container shares the root folder of the repository and the
+command ``utils/lxc.sh cmd`` **handles relative path names transparent**,
+compare output of::
+
+ $ sudo -H ./utils/lxc.sh cmd -- ls -la Makefile
+ ...
+
+In the containers, you can run what ever you want, e.g. to start a bash use::
+
+ $ sudo -H ./utils/lxc.sh cmd searx-ubu1804 bash
+ INFO: [searx-ubu1804] bash
+ root@searx-ubu1804:/share/searx#
+
+If there comes the time you want to **get rid off all** the containers and
+**clean up local images** just type::
+
+ $ sudo -H ./utils/lxc.sh remove
+ $ sudo -H ./utils/lxc.sh remove images
+
+
+Install suite
+=============
+
+To install the complete :ref:`searx suite (includes searx, morty & filtron)
+<lxc-searx.env>` into all LXC_ use::
+
+ $ sudo -H ./utils/lxc.sh install suite
+
+The command above installs a searx suite (see :ref:`installation scripts`). To
+get the IP (URL) of the filtron service in the containers use ``show suite``
+command. To test instances from containers just open the URLs in your
+WEB-Browser::
+
+ $ sudo ./utils/lxc.sh show suite | grep filtron
+ [searx-ubu1604] INFO: (eth0) filtron: http://n.n.n.246:4004/ http://n.n.n.246/searx
+ [searx-ubu1804] INFO: (eth0) filtron: http://n.n.n.147:4004/ http://n.n.n.147/searx
+ [searx-ubu1910] INFO: (eth0) filtron: http://n.n.n.140:4004/ http://n.n.n.140/searx
+ [searx-ubu2004] INFO: (eth0) filtron: http://n.n.n.18:4004/ http://n.n.n.18/searx
+ [searx-fedora31] INFO: (eth0) filtron: http://n.n.n.46:4004/ http://n.n.n.46/searx
+ [searx-archlinux] INFO: (eth0) filtron: http://n.n.n.32:4004/ http://n.n.n.32/searx
+
+To :ref:`install a nginx <installation nginx>` reverse proxy for filtron and
+morty use (or alternatively use :ref:`apache <installation apache>`)::
+
+ sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/filtron.sh nginx install
+ sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/morty.sh nginx install
+
+
+Running commands
+================
+
+**Inside containers, you can use make or run scripts** from the
+:ref:`toolboxing`. By example: to setup a :ref:`buildhosts` and run the
+Makefile target ``test`` in the archlinux_ container::
+
+ sudo -H ./utils/lxc.sh cmd searx-archlinux ./utils/searx.sh install buildhost
+ sudo -H ./utils/lxc.sh cmd searx-archlinux make test
+
+
+Setup searx buildhost
+=====================
+
+You can **install the searx buildhost environment** into one or all containers.
+The installation procedure to set up a :ref:`build host<buildhosts>` takes its
+time. Installation in all containers will take more time (time for another cup
+of coffee).::
+
+ sudo -H ./utils/lxc.sh cmd -- ./utils/searx.sh install buildhost
+
+To build (live) documentation inside a archlinux_ container::
+
+ sudo -H ./utils/lxc.sh cmd searx-archlinux make docs-clean docs-live
+ ...
+ [I 200331 15:00:42 server:296] Serving on http://0.0.0.0:8080
+
+To get IP of the container and the port number *live docs* is listening::
+
+ $ sudo ./utils/lxc.sh show suite | grep docs-live
+ ...
+ [searx-archlinux] INFO: (eth0) docs-live: http://n.n.n.12:8080/
+
+
+.. _lxc.sh help:
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory:
+
+.. program-output:: ../utils/lxc.sh --help
+
+
+.. _lxc-searx.env:
+
+searx suite
+===========
+
+.. literalinclude:: ../../utils/lxc-searx.env
+ :language: bash
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
new file mode 100644
index 000000000..3bdf9bdd3
--- /dev/null
+++ b/docs/utils/morty.sh.rst
@@ -0,0 +1,80 @@
+
+.. _morty: https://github.com/asciimoo/morty
+.. _morty's README: https://github.com/asciimoo/morty
+.. _Go: https://golang.org/
+
+.. _morty.sh:
+
+==================
+``utils/morty.sh``
+==================
+
+.. sidebar:: further reading
+
+ - :ref:`architecture`
+ - :ref:`installation` (:ref:`nginx <installation nginx>` & :ref:`apache
+ <installation apache>`)
+ - :ref:`searx morty`
+
+To simplify installation and maintenance of a morty_ instance you can use the
+script :origin:`utils/morty.sh`. In most cases you will install morty_ simply by
+running the command:
+
+.. code:: bash
+
+ sudo -H ./utils/morty.sh install all
+
+The script adds a ``${SERVICE_USER}`` (default:``morty``) and installs morty_
+into this user account:
+
+#. Create a separated user account (``morty``).
+#. Download and install Go_ binary in user's $HOME (``~morty``).
+#. Install morty_ with the package management from Go_ (``go get -v -u
+ github.com/asciimoo/morty``)
+#. Setup a systemd service unit :origin:`[ref]
+ <utils/templates/lib/systemd/system/morty.service>`
+ (``/lib/systemd/system/morty.service``).
+
+.. hint::
+
+ To add morty to your searx instance read chapter :ref:`searx morty`.
+
+Create user
+===========
+
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
+ :start-after: START create user
+ :end-before: END create user
+
+
+Install go
+==========
+
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
+ :start-after: START install go
+ :end-before: END install go
+
+
+Install morty
+=============
+
+Install morty software and systemd unit:
+
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
+ :start-after: START install morty
+ :end-before: END install morty
+
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
+ :start-after: START install systemd unit
+ :end-before: END install systemd unit
+
+.. _morty.sh overview:
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory
+(:ref:`toolboxing common`):
+
+.. program-output:: ../utils/morty.sh --help
+
diff --git a/docs/utils/searx.sh.rst b/docs/utils/searx.sh.rst
new file mode 100644
index 000000000..dd4442f94
--- /dev/null
+++ b/docs/utils/searx.sh.rst
@@ -0,0 +1,39 @@
+
+.. _searx.sh:
+
+==================
+``utils/searx.sh``
+==================
+
+.. sidebar:: further reading
+
+ - :ref:`architecture`
+ - :ref:`installation`
+ - :ref:`installation nginx`
+ - :ref:`installation apache`
+
+To simplify installation and maintenance of a searx instance you can use the
+script :origin:`utils/searx.sh`.
+
+Install
+=======
+
+In most cases you will install searx simply by running the command:
+
+.. code:: bash
+
+ sudo -H ./utils/searx.sh install all
+
+The script adds a ``${SERVICE_USER}`` (default:``searx``) and installs searx
+into this user account. The installation is described in chapter
+:ref:`installation basic`.
+
+.. _intranet reverse proxy:
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory
+(:ref:`toolboxing common`):
+
+.. program-output:: ../utils/searx.sh --help
diff --git a/requirements-dev.txt b/requirements-dev.txt
index eed7ec3d2..5a1ca1599 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -15,3 +15,5 @@ selenium==3.141.0
linuxdoc @ git+http://github.com/return42/linuxdoc.git
sphinx-jinja
sphinx-tabs
+sphinxcontrib-programoutput
+twine
diff --git a/searx/brand.py b/searx/brand.py
index d563447f6..91d2ab321 100644
--- a/searx/brand.py
+++ b/searx/brand.py
@@ -1,4 +1,5 @@
GIT_URL = 'https://github.com/asciimoo/searx'
+GIT_BRANCH = 'master'
ISSUE_URL = 'https://github.com/asciimoo/searx/issues'
SEARX_URL = 'https://searx.me'
DOCS_URL = 'https://asciimoo.github.io/searx'
diff --git a/searx/engines/flickr_noapi.py b/searx/engines/flickr_noapi.py
index c8ee34f7a..1cbb3e0a9 100644
--- a/searx/engines/flickr_noapi.py
+++ b/searx/engines/flickr_noapi.py
@@ -117,14 +117,10 @@ def response(resp):
'img_format': img_format,
'template': 'images.html'
}
- try:
- result['author'] = author
- result['title'] = title
- result['content'] = content
- except:
- result['author'] = ''
- result['title'] = ''
- result['content'] = ''
+ result['author'] = author.encode('utf-8', 'ignore').decode('utf-8')
+ result['source'] = source.encode('utf-8', 'ignore').decode('utf-8')
+ result['title'] = title.encode('utf-8', 'ignore').decode('utf-8')
+ result['content'] = content.encode('utf-8', 'ignore').decode('utf-8')
results.append(result)
return results
diff --git a/searx/engines/yahoo.py b/searx/engines/yahoo.py
index 36c1a11f8..a6b4aeb9f 100644
--- a/searx/engines/yahoo.py
+++ b/searx/engines/yahoo.py
@@ -33,7 +33,7 @@ supported_languages_url = 'https://search.yahoo.com/web/advanced'
results_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' Sr ')]"
url_xpath = './/h3/a/@href'
title_xpath = './/h3/a'
-content_xpath = './/div[@class="compText aAbs"]'
+content_xpath = './/div[contains(@class, "compText")]'
suggestion_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' AlsoTry ')]//a"
time_range_dict = {'day': ['1d', 'd'],
diff --git a/searx/static/plugins/js/search_on_category_select.js b/searx/static/plugins/js/search_on_category_select.js
index 1c42d9e9e..d590ed127 100644
--- a/searx/static/plugins/js/search_on_category_select.js
+++ b/searx/static/plugins/js/search_on_category_select.js
@@ -6,19 +6,37 @@ $(document).ready(function() {
});
$(document.getElementById($(this).attr("for"))).prop('checked', true);
if($('#q').val()) {
+ if (getHttpRequest() == "GET") {
+ $('#search_form').attr('action', $('#search_form').serialize());
+ }
$('#search_form').submit();
}
return false;
});
$('#time-range').change(function(e) {
if($('#q').val()) {
+ if (getHttpRequest() == "GET") {
+ $('#search_form').attr('action', $('#search_form').serialize());
+ }
$('#search_form').submit();
}
});
$('#language').change(function(e) {
if($('#q').val()) {
+ if (getHttpRequest() == "GET") {
+ $('#search_form').attr('action', $('#search_form').serialize());
+ }
$('#search_form').submit();
}
});
}
});
+
+function getHttpRequest() {
+ httpRequest = "POST";
+ urlParams = new URLSearchParams(window.location.search);
+ if (urlParams.has('method')) {
+ httpRequest = urlParams.get('method');
+ }
+ return httpRequest;
+}
diff --git a/searx/static/themes/oscar/css/logicodev-dark.css b/searx/static/themes/oscar/css/logicodev-dark.css
index b2cf38ba5..cf32e76b9 100644
--- a/searx/static/themes/oscar/css/logicodev-dark.css
+++ b/searx/static/themes/oscar/css/logicodev-dark.css
@@ -70,7 +70,21 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
-ms-user-select: none;
}
.onoffswitch-checkbox {
- display: none;
+ opacity: 0;
+ position: absolute;
+}
+.onoffswitch-checkbox:before {
+ content: "";
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ margin-right: 10px;
+ position: absolute;
+ left: 0;
+ bottom: 1px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border-radius: 0px;
}
.onoffswitch-label {
display: block;
@@ -104,7 +118,7 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
top: 0;
bottom: 0;
right: 0px;
- border: 2px solid #FFFFFF !important;
+ border: 2px solid #FFFFFF;
border-radius: 50px !important;
transition: all 0.3s ease-in 0s;
}
@@ -115,6 +129,9 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
right: 71px;
background-color: #A1A1A1;
}
+.onoffswitch-checkbox:focus + .onoffswitch-label .onoffswitch-switch {
+ border: 3px solid #444444;
+}
.result_header {
margin-top: 0px;
margin-bottom: 2px;
@@ -377,6 +394,17 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
.search-margin {
margin-bottom: 0.6em;
}
+.visually-hidden {
+ position: absolute !important;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ clip: rect(1px 1px 1px 1px);
+ /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ white-space: nowrap;
+ /* added line */
+}
#advanced-search-container {
display: none;
text-align: left;
@@ -407,8 +435,8 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
font-weight: bold;
border-bottom: #01d7d4 5px solid;
}
-#check-advanced {
- display: none;
+#check-advanced:focus + label {
+ text-decoration: underline;
}
#check-advanced:checked ~ #advanced-search-container {
display: block;
diff --git a/searx/static/themes/oscar/css/logicodev-dark.min.css b/searx/static/themes/oscar/css/logicodev-dark.min.css
index 603aedfa9..b1eeba8a2 100644
--- a/searx/static/themes/oscar/css/logicodev-dark.min.css
+++ b/searx/static/themes/oscar/css/logicodev-dark.min.css
@@ -1 +1 @@
-*{border-radius:0!important}html{position:relative;min-height:100%;color:#29314d}body{font-family:Roboto,Helvetica,Arial,sans-serif;margin-bottom:80px;background-color:#fff}body a{color:#08c}.footer{position:absolute;bottom:0;width:100%;height:60px;text-align:center;color:#999}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.onoff-checkbox{width:15%}.onoffswitch{position:relative;width:110px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch-checkbox{display:none}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:2px solid #FFF!important;border-radius:50px!important}.onoffswitch-inner{display:block;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:30px;padding:0;line-height:40px;font-size:20px;box-sizing:border-box;content:"";background-color:#EEE}.onoffswitch-switch{display:block;width:37px;background-color:#01d7d4;position:absolute;top:0;bottom:0;right:0;border:2px solid #FFF!important;border-radius:50px!important;transition:all .3s ease-in 0s}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-right:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:71px;background-color:#A1A1A1}.result_header{margin-top:0;margin-bottom:2px;font-size:16px}.result_header .favicon{margin-bottom:-3px}.result_header a{color:#29314d;text-decoration:none}.result_header a:hover{color:#08c}.result_header a:visited{color:#684898}.result_header a .highlight{background-color:#f6f9fa}.result-content,.result-format,.result-source{margin-top:2px;margin-bottom:0;word-wrap:break-word;color:#666;font-size:13px}.result-content .highlight,.result-format .highlight,.result-source .highlight{font-weight:700}.result-source{font-size:10px;float:left}.result-format{font-size:10px;float:right}.external-link{color:#069025;font-size:12px;margin-bottom:15px}.external-link a{margin-right:3px}.result-default,.result-code,.result-torrent,.result-videos,.result-map{clear:both;padding:.5em 4px}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#f6f9fa}.result-images{float:left!important;width:24%;margin:.5%}.result-images a{display:block;width:100%;background-size:cover}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-videos hr{margin:5px 0 15px 0}.result-videos .collapse{width:100%}.result-videos .in{margin-bottom:8px}.result-torrent{clear:both}.result-torrent b{margin-right:5px;margin-left:5px}.result-torrent .seeders{color:#2ecc71}.result-torrent .leechers{color:#f35e77}.result-map{clear:both}.result-code{clear:both}.result-code .code-fork,.result-code .code-fork a{color:#666}.suggestion_item{margin:2px 5px;max-width:100%}.suggestion_item .btn{max-width:100%;white-space:normal;word-wrap:break-word;text-align:left}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:60px}.label-default{color:#a4a4a4;background:0 0}.result .text-muted small{word-wrap:break-word}.modal-wrapper{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-wrapper{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0 none;position:relative}@media screen and (max-width:75em){.img-thumbnail{object-fit:cover}}.infobox .panel-heading{background-color:#f6f9fa}.infobox .panel-heading .panel-title{font-weight:700}.infobox p{font-family:"DejaVu Serif",Georgia,Cambria,"Times New Roman",Times,serif!important;font-style:italic}.infobox .btn{background-color:#2ecc71;border:none}.infobox .btn a{color:#fff;margin:5px}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories,#categories{text-transform:capitalize;margin-bottom:.5rem;display:flex;flex-wrap:wrap;flex-flow:row wrap;align-content:stretch}.search_categories label,#categories label,.search_categories .input-group-addon,#categories .input-group-addon{flex-grow:1;flex-basis:auto;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-top:.4rem;text-align:center;min-width:50px}.search_categories label:last-child,#categories label:last-child,.search_categories .input-group-addon:last-child,#categories .input-group-addon:last-child{border-right:#ddd 1px solid}.search_categories input[type=checkbox]:checked+label,#categories input[type=checkbox]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#main-logo{margin-top:10vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}#q{box-shadow:none;border-right:none;border-color:#a4a4a4}#search_form .input-group-btn .btn{border-color:#a4a4a4}#search_form .input-group-btn .btn:hover{background-color:#2ecc71;color:#fff}.custom-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;color:#666;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGnsAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW86/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0wNy0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC) 96% no-repeat}.search-margin{margin-bottom:.6em}#advanced-search-container{display:none;text-align:left;margin-bottom:1rem;clear:both}#advanced-search-container label,#advanced-search-container .input-group-addon{font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-right:.7rem;padding-left:.7rem}#advanced-search-container label:last-child,#advanced-search-container .input-group-addon:last-child{border-right:#ddd 1px solid}#advanced-search-container input[type=radio]{display:none}#advanced-search-container input[type=radio]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#check-advanced{display:none}#check-advanced:checked~#advanced-search-container{display:block}.advanced{padding:0;margin-top:.3rem;text-align:right}.advanced label,.advanced select{cursor:pointer}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}pre,code{font-family:'Ubuntu Mono','Courier New','Lucida Console',monospace!important}.lineno{margin-right:5px}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#556366;font-style:italic}.highlight .err{border:1px solid #ffa92f}.highlight .k{color:#BE74D5;font-weight:700}.highlight .o{color:#d19a66}.highlight .cm{color:#556366;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#556366;font-style:italic}.highlight .cs{color:#556366;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:#BE74D5;font-weight:700}.highlight .kd{color:#BE74D5;font-weight:700}.highlight .kn{color:#BE74D5;font-weight:700}.highlight .kp{color:#be74d5}.highlight .kr{color:#BE74D5;font-weight:700}.highlight .kt{color:#d46c72}.highlight .m{color:#d19a66}.highlight .s{color:#86c372}.highlight .na{color:#7d9029}.highlight .nb{color:#be74d5}.highlight .nc{color:#61AFEF;font-weight:700}.highlight .no{color:#d19a66}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#61afef}.highlight .nl{color:#a0a000}.highlight .nn{color:#61AFEF;font-weight:700}.highlight .nt{color:#BE74D5;font-weight:700}.highlight .nv{color:#dfc06f}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#d7dae0}.highlight .mf{color:#d19a66}.highlight .mh{color:#d19a66}.highlight .mi{color:#d19a66}.highlight .mo{color:#d19a66}.highlight .sb{color:#86c372}.highlight .sc{color:#86c372}.highlight .sd{color:#86C372;font-style:italic}.highlight .s2{color:#86c372}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#86c372}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:#be74d5}.highlight .sr{color:#b68}.highlight .s1{color:#86c372}.highlight .ss{color:#dfc06f}.highlight .bp{color:#be74d5}.highlight .vc{color:#dfc06f}.highlight .vg{color:#dfc06f}.highlight .vi{color:#dfc06f}.highlight .il{color:#d19a66}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;color:#556366}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}.highlight pre{background-color:#282C34;color:#D7DAE0;border:none;margin-bottom:25px;font-size:15px;padding:20px 10px}.highlight{font-weight:700}.table>tbody>tr>td,.table>tbody>tr>th{vertical-align:middle!important}body{background:#1d1f21 none!important;color:#D5D8D7!important}a{color:#41a2ce!important;text-decoration:none!important}a:hover{color:#5F89AC!important}input,button,textarea,select{border:1px solid #282a2e!important;background-color:#444!important;color:#BBB!important}input:focus,button:focus,textarea:focus,select:focus{border:1px solid #C5C8C6!important;box-shadow:initial!important}div#advanced-search-container div#categories label{background:0 0;border:1px solid #282a2e}ul.nav li a{border:0!important;border-bottom:1px solid #4d3f43!important}#categories *,.modal-wrapper *{background:#1d1f21 none!important;color:#D5D8D7!important}#categories *{border:1px solid #3d3f43!important}#categories :checked+label{border-bottom:4px solid #3d9f94!important}.result-content,.result-source,.result-format{color:#B5B8B7!important}.external-link{color:#35B887!important}.table-striped tr td,.table-striped tr th{border-color:#4d3f43!important}.highlight{background:#333!important}.navbar{background:#1d1f21 none;border:none}.navbar .active,.menu{background:none!important}.label-default{background:0 0;color:#BBB}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus,.nav-tabs.nav-justified>.active>a{background-color:#282a2e!important}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#222426}.btn{color:#BBB;background-color:#444;border:1px solid #282a2e}.btn:hover{color:#444!important;background-color:#BBB!important}.btn-primary.active{color:#C5C8C6;background-color:#5F89AC;border-color:#5F89AC}.panel{border:1px solid #111;background:0 0}.panel-heading{color:#C5C8C6!important;background:#282a2e!important;border-bottom:none}.panel-body{color:#C5C8C6!important;background:#1d1f21!important;border-color:#111!important}p.btn.btn-default{background:0 0}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th,.table-striped>thead>tr:nth-child(odd)>th{background:#2d2f32 none!important;color:#D5D8D7!important}.label-success{background:#1d6f42 none!important}.label-danger{background:#ad1f12 none!important}.searx-navbar{background:#333334;height:2.3rem;font-size:1.3rem;line-height:1.3rem;padding:.5rem;font-weight:700;margin-bottom:.8rem}.searx-navbar a,.searx-navbar a:hover{margin-right:2rem;color:#fff;text-decoration:none}.searx-navbar .instance a{color:#01d7d4;margin-left:2rem}#main-logo{margin-top:20vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}.onoffswitch-inner:before,.onoffswitch-inner:after{background:#1d1f21 none!important}.onoffswitch-switch,.onoffswitch-label{border:2px solid #3d3f43!important}.nav>li>a:hover,.nav>li>a:focus{background-color:#3d3f43!important}.img-thumbnail,.thumbnail{padding:0;line-height:1.42857143;background:0 0;border:none}.modal-content{background:#1d1f21 none!important}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background:rgba(240,0,0,.56)!important;color:#C5C8C6!important}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background:rgba(237,59,59,.61)!important;color:#C5C8C6!important}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background:#66696e!important}.btn-success{color:#C5C8C6;background:#449d44}.btn-danger{color:#C5C8C6;background:#d9534f}.well{background:#444;border-color:#282a2e}.highlight{background-color:transparent!important} \ No newline at end of file
+*{border-radius:0!important}html{position:relative;min-height:100%;color:#29314d}body{font-family:Roboto,Helvetica,Arial,sans-serif;margin-bottom:80px;background-color:#fff}body a{color:#08c}.footer{position:absolute;bottom:0;width:100%;height:60px;text-align:center;color:#999}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.onoff-checkbox{width:15%}.onoffswitch{position:relative;width:110px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch-checkbox{opacity:0;position:absolute}.onoffswitch-checkbox:before{content:"";display:inline-block;width:16px;height:16px;margin-right:10px;position:absolute;left:0;bottom:1px;background-color:#fff;border:1px solid #ccc;border-radius:0}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:2px solid #FFF!important;border-radius:50px!important}.onoffswitch-inner{display:block;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:30px;padding:0;line-height:40px;font-size:20px;box-sizing:border-box;content:"";background-color:#EEE}.onoffswitch-switch{display:block;width:37px;background-color:#01d7d4;position:absolute;top:0;bottom:0;right:0;border:2px solid #FFF;border-radius:50px!important;transition:all .3s ease-in 0s}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-right:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:71px;background-color:#A1A1A1}.onoffswitch-checkbox:focus+.onoffswitch-label .onoffswitch-switch{border:3px solid #444}.result_header{margin-top:0;margin-bottom:2px;font-size:16px}.result_header .favicon{margin-bottom:-3px}.result_header a{color:#29314d;text-decoration:none}.result_header a:hover{color:#08c}.result_header a:visited{color:#684898}.result_header a .highlight{background-color:#f6f9fa}.result-content,.result-format,.result-source{margin-top:2px;margin-bottom:0;word-wrap:break-word;color:#666;font-size:13px}.result-content .highlight,.result-format .highlight,.result-source .highlight{font-weight:700}.result-source{font-size:10px;float:left}.result-format{font-size:10px;float:right}.external-link{color:#069025;font-size:12px;margin-bottom:15px}.external-link a{margin-right:3px}.result-default,.result-code,.result-torrent,.result-videos,.result-map{clear:both;padding:.5em 4px}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#f6f9fa}.result-images{float:left!important;width:24%;margin:.5%}.result-images a{display:block;width:100%;background-size:cover}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-videos hr{margin:5px 0 15px 0}.result-videos .collapse{width:100%}.result-videos .in{margin-bottom:8px}.result-torrent{clear:both}.result-torrent b{margin-right:5px;margin-left:5px}.result-torrent .seeders{color:#2ecc71}.result-torrent .leechers{color:#f35e77}.result-map{clear:both}.result-code{clear:both}.result-code .code-fork,.result-code .code-fork a{color:#666}.suggestion_item{margin:2px 5px;max-width:100%}.suggestion_item .btn{max-width:100%;white-space:normal;word-wrap:break-word;text-align:left}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:60px}.label-default{color:#a4a4a4;background:0 0}.result .text-muted small{word-wrap:break-word}.modal-wrapper{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-wrapper{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0 none;position:relative}@media screen and (max-width:75em){.img-thumbnail{object-fit:cover}}.infobox .panel-heading{background-color:#f6f9fa}.infobox .panel-heading .panel-title{font-weight:700}.infobox p{font-family:"DejaVu Serif",Georgia,Cambria,"Times New Roman",Times,serif!important;font-style:italic}.infobox .btn{background-color:#2ecc71;border:none}.infobox .btn a{color:#fff;margin:5px}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories,#categories{text-transform:capitalize;margin-bottom:.5rem;display:flex;flex-wrap:wrap;flex-flow:row wrap;align-content:stretch}.search_categories label,#categories label,.search_categories .input-group-addon,#categories .input-group-addon{flex-grow:1;flex-basis:auto;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-top:.4rem;text-align:center;min-width:50px}.search_categories label:last-child,#categories label:last-child,.search_categories .input-group-addon:last-child,#categories .input-group-addon:last-child{border-right:#ddd 1px solid}.search_categories input[type=checkbox]:checked+label,#categories input[type=checkbox]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#main-logo{margin-top:10vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}#q{box-shadow:none;border-right:none;border-color:#a4a4a4}#search_form .input-group-btn .btn{border-color:#a4a4a4}#search_form .input-group-btn .btn:hover{background-color:#2ecc71;color:#fff}.custom-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;color:#666;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGnsAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW86/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0wNy0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC) 96% no-repeat}.search-margin{margin-bottom:.6em}.visually-hidden{position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);white-space:nowrap}#advanced-search-container{display:none;text-align:left;margin-bottom:1rem;clear:both}#advanced-search-container label,#advanced-search-container .input-group-addon{font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-right:.7rem;padding-left:.7rem}#advanced-search-container label:last-child,#advanced-search-container .input-group-addon:last-child{border-right:#ddd 1px solid}#advanced-search-container input[type=radio]{display:none}#advanced-search-container input[type=radio]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#check-advanced:focus+label{text-decoration:underline}#check-advanced:checked~#advanced-search-container{display:block}.advanced{padding:0;margin-top:.3rem;text-align:right}.advanced label,.advanced select{cursor:pointer}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}pre,code{font-family:'Ubuntu Mono','Courier New','Lucida Console',monospace!important}.lineno{margin-right:5px}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#556366;font-style:italic}.highlight .err{border:1px solid #ffa92f}.highlight .k{color:#BE74D5;font-weight:700}.highlight .o{color:#d19a66}.highlight .cm{color:#556366;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#556366;font-style:italic}.highlight .cs{color:#556366;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:#BE74D5;font-weight:700}.highlight .kd{color:#BE74D5;font-weight:700}.highlight .kn{color:#BE74D5;font-weight:700}.highlight .kp{color:#be74d5}.highlight .kr{color:#BE74D5;font-weight:700}.highlight .kt{color:#d46c72}.highlight .m{color:#d19a66}.highlight .s{color:#86c372}.highlight .na{color:#7d9029}.highlight .nb{color:#be74d5}.highlight .nc{color:#61AFEF;font-weight:700}.highlight .no{color:#d19a66}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#61afef}.highlight .nl{color:#a0a000}.highlight .nn{color:#61AFEF;font-weight:700}.highlight .nt{color:#BE74D5;font-weight:700}.highlight .nv{color:#dfc06f}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#d7dae0}.highlight .mf{color:#d19a66}.highlight .mh{color:#d19a66}.highlight .mi{color:#d19a66}.highlight .mo{color:#d19a66}.highlight .sb{color:#86c372}.highlight .sc{color:#86c372}.highlight .sd{color:#86C372;font-style:italic}.highlight .s2{color:#86c372}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#86c372}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:#be74d5}.highlight .sr{color:#b68}.highlight .s1{color:#86c372}.highlight .ss{color:#dfc06f}.highlight .bp{color:#be74d5}.highlight .vc{color:#dfc06f}.highlight .vg{color:#dfc06f}.highlight .vi{color:#dfc06f}.highlight .il{color:#d19a66}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;color:#556366}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}.highlight pre{background-color:#282C34;color:#D7DAE0;border:none;margin-bottom:25px;font-size:15px;padding:20px 10px}.highlight{font-weight:700}.table>tbody>tr>td,.table>tbody>tr>th{vertical-align:middle!important}body{background:#1d1f21 none!important;color:#D5D8D7!important}a{color:#41a2ce!important;text-decoration:none!important}a:hover{color:#5F89AC!important}input,button,textarea,select{border:1px solid #282a2e!important;background-color:#444!important;color:#BBB!important}input:focus,button:focus,textarea:focus,select:focus{border:1px solid #C5C8C6!important;box-shadow:initial!important}div#advanced-search-container div#categories label{background:0 0;border:1px solid #282a2e}ul.nav li a{border:0!important;border-bottom:1px solid #4d3f43!important}#categories *,.modal-wrapper *{background:#1d1f21 none!important;color:#D5D8D7!important}#categories *{border:1px solid #3d3f43!important}#categories :checked+label{border-bottom:4px solid #3d9f94!important}.result-content,.result-source,.result-format{color:#B5B8B7!important}.external-link{color:#35B887!important}.table-striped tr td,.table-striped tr th{border-color:#4d3f43!important}.highlight{background:#333!important}.navbar{background:#1d1f21 none;border:none}.navbar .active,.menu{background:none!important}.label-default{background:0 0;color:#BBB}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus,.nav-tabs.nav-justified>.active>a{background-color:#282a2e!important}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#222426}.btn{color:#BBB;background-color:#444;border:1px solid #282a2e}.btn:hover{color:#444!important;background-color:#BBB!important}.btn-primary.active{color:#C5C8C6;background-color:#5F89AC;border-color:#5F89AC}.panel{border:1px solid #111;background:0 0}.panel-heading{color:#C5C8C6!important;background:#282a2e!important;border-bottom:none}.panel-body{color:#C5C8C6!important;background:#1d1f21!important;border-color:#111!important}p.btn.btn-default{background:0 0}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th,.table-striped>thead>tr:nth-child(odd)>th{background:#2d2f32 none!important;color:#D5D8D7!important}.label-success{background:#1d6f42 none!important}.label-danger{background:#ad1f12 none!important}.searx-navbar{background:#333334;height:2.3rem;font-size:1.3rem;line-height:1.3rem;padding:.5rem;font-weight:700;margin-bottom:.8rem}.searx-navbar a,.searx-navbar a:hover{margin-right:2rem;color:#fff;text-decoration:none}.searx-navbar .instance a{color:#01d7d4;margin-left:2rem}#main-logo{margin-top:20vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}.onoffswitch-inner:before,.onoffswitch-inner:after{background:#1d1f21 none!important}.onoffswitch-switch,.onoffswitch-label{border:2px solid #3d3f43!important}.nav>li>a:hover,.nav>li>a:focus{background-color:#3d3f43!important}.img-thumbnail,.thumbnail{padding:0;line-height:1.42857143;background:0 0;border:none}.modal-content{background:#1d1f21 none!important}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background:rgba(240,0,0,.56)!important;color:#C5C8C6!important}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background:rgba(237,59,59,.61)!important;color:#C5C8C6!important}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background:#66696e!important}.btn-success{color:#C5C8C6;background:#449d44}.btn-danger{color:#C5C8C6;background:#d9534f}.well{background:#444;border-color:#282a2e}.highlight{background-color:transparent!important} \ No newline at end of file
diff --git a/searx/static/themes/oscar/css/logicodev.css b/searx/static/themes/oscar/css/logicodev.css
index d5a2aa6a9..76af58357 100644
--- a/searx/static/themes/oscar/css/logicodev.css
+++ b/searx/static/themes/oscar/css/logicodev.css
@@ -43,7 +43,21 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
-ms-user-select: none;
}
.onoffswitch-checkbox {
- display: none;
+ opacity: 0;
+ position: absolute;
+}
+.onoffswitch-checkbox:before {
+ content: "";
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ margin-right: 10px;
+ position: absolute;
+ left: 0;
+ bottom: 1px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border-radius: 0px;
}
.onoffswitch-label {
display: block;
@@ -77,7 +91,7 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
top: 0;
bottom: 0;
right: 0px;
- border: 2px solid #FFFFFF !important;
+ border: 2px solid #FFFFFF;
border-radius: 50px !important;
transition: all 0.3s ease-in 0s;
}
@@ -88,6 +102,9 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
right: 71px;
background-color: #A1A1A1;
}
+.onoffswitch-checkbox:focus + .onoffswitch-label .onoffswitch-switch {
+ border: 3px solid #444444;
+}
.result_header {
margin-top: 0px;
margin-bottom: 2px;
@@ -350,6 +367,17 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
.search-margin {
margin-bottom: 0.6em;
}
+.visually-hidden {
+ position: absolute !important;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ clip: rect(1px 1px 1px 1px);
+ /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ white-space: nowrap;
+ /* added line */
+}
#advanced-search-container {
display: none;
text-align: left;
@@ -380,8 +408,8 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
font-weight: bold;
border-bottom: #01d7d4 5px solid;
}
-#check-advanced {
- display: none;
+#check-advanced:focus + label {
+ text-decoration: underline;
}
#check-advanced:checked ~ #advanced-search-container {
display: block;
diff --git a/searx/static/themes/oscar/css/logicodev.min.css b/searx/static/themes/oscar/css/logicodev.min.css
index 7f221d5ec..88b9dae54 100644
--- a/searx/static/themes/oscar/css/logicodev.min.css
+++ b/searx/static/themes/oscar/css/logicodev.min.css
@@ -1 +1 @@
-.searx-navbar{background:#29314d;height:2.3rem;font-size:1.3rem;line-height:1.3rem;padding:.5rem;font-weight:700;margin-bottom:.8rem}.searx-navbar a,.searx-navbar a:hover{margin-right:2rem;color:#fff;text-decoration:none}.searx-navbar .instance a{color:#01d7d4;margin-left:2rem}#main-logo{margin-top:20vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}*{border-radius:0!important}html{position:relative;min-height:100%;color:#29314d}body{font-family:Roboto,Helvetica,Arial,sans-serif;margin-bottom:80px;background-color:#fff}body a{color:#08c}.footer{position:absolute;bottom:0;width:100%;height:60px;text-align:center;color:#999}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.onoff-checkbox{width:15%}.onoffswitch{position:relative;width:110px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch-checkbox{display:none}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:2px solid #FFF!important;border-radius:50px!important}.onoffswitch-inner{display:block;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:30px;padding:0;line-height:40px;font-size:20px;box-sizing:border-box;content:"";background-color:#EEE}.onoffswitch-switch{display:block;width:37px;background-color:#01d7d4;position:absolute;top:0;bottom:0;right:0;border:2px solid #FFF!important;border-radius:50px!important;transition:all .3s ease-in 0s}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-right:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:71px;background-color:#A1A1A1}.result_header{margin-top:0;margin-bottom:2px;font-size:16px}.result_header .favicon{margin-bottom:-3px}.result_header a{color:#29314d;text-decoration:none}.result_header a:hover{color:#08c}.result_header a:visited{color:#684898}.result_header a .highlight{background-color:#f6f9fa}.result-content,.result-format,.result-source{margin-top:2px;margin-bottom:0;word-wrap:break-word;color:#666;font-size:13px}.result-content .highlight,.result-format .highlight,.result-source .highlight{font-weight:700}.result-source{font-size:10px;float:left}.result-format{font-size:10px;float:right}.external-link{color:#069025;font-size:12px;margin-bottom:15px}.external-link a{margin-right:3px}.result-default,.result-code,.result-torrent,.result-videos,.result-map{clear:both;padding:.5em 4px}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#f6f9fa}.result-images{float:left!important;width:24%;margin:.5%}.result-images a{display:block;width:100%;background-size:cover}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-videos hr{margin:5px 0 15px 0}.result-videos .collapse{width:100%}.result-videos .in{margin-bottom:8px}.result-torrent{clear:both}.result-torrent b{margin-right:5px;margin-left:5px}.result-torrent .seeders{color:#2ecc71}.result-torrent .leechers{color:#f35e77}.result-map{clear:both}.result-code{clear:both}.result-code .code-fork,.result-code .code-fork a{color:#666}.suggestion_item{margin:2px 5px;max-width:100%}.suggestion_item .btn{max-width:100%;white-space:normal;word-wrap:break-word;text-align:left}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:60px}.label-default{color:#a4a4a4;background:0 0}.result .text-muted small{word-wrap:break-word}.modal-wrapper{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-wrapper{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0 none;position:relative}@media screen and (max-width:75em){.img-thumbnail{object-fit:cover}}.infobox .panel-heading{background-color:#f6f9fa}.infobox .panel-heading .panel-title{font-weight:700}.infobox p{font-family:"DejaVu Serif",Georgia,Cambria,"Times New Roman",Times,serif!important;font-style:italic}.infobox .btn{background-color:#2ecc71;border:none}.infobox .btn a{color:#fff;margin:5px}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories,#categories{text-transform:capitalize;margin-bottom:.5rem;display:flex;flex-wrap:wrap;flex-flow:row wrap;align-content:stretch}.search_categories label,#categories label,.search_categories .input-group-addon,#categories .input-group-addon{flex-grow:1;flex-basis:auto;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-top:.4rem;text-align:center;min-width:50px}.search_categories label:last-child,#categories label:last-child,.search_categories .input-group-addon:last-child,#categories .input-group-addon:last-child{border-right:#ddd 1px solid}.search_categories input[type=checkbox]:checked+label,#categories input[type=checkbox]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#main-logo{margin-top:10vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}#q{box-shadow:none;border-right:none;border-color:#a4a4a4}#search_form .input-group-btn .btn{border-color:#a4a4a4}#search_form .input-group-btn .btn:hover{background-color:#2ecc71;color:#fff}.custom-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;color:#666;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGnsAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW86/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0wNy0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC) 96% no-repeat}.search-margin{margin-bottom:.6em}#advanced-search-container{display:none;text-align:left;margin-bottom:1rem;clear:both}#advanced-search-container label,#advanced-search-container .input-group-addon{font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-right:.7rem;padding-left:.7rem}#advanced-search-container label:last-child,#advanced-search-container .input-group-addon:last-child{border-right:#ddd 1px solid}#advanced-search-container input[type=radio]{display:none}#advanced-search-container input[type=radio]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#check-advanced{display:none}#check-advanced:checked~#advanced-search-container{display:block}.advanced{padding:0;margin-top:.3rem;text-align:right}.advanced label,.advanced select{cursor:pointer}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}pre,code{font-family:'Ubuntu Mono','Courier New','Lucida Console',monospace!important}.lineno{margin-right:5px}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#556366;font-style:italic}.highlight .err{border:1px solid #ffa92f}.highlight .k{color:#BE74D5;font-weight:700}.highlight .o{color:#d19a66}.highlight .cm{color:#556366;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#556366;font-style:italic}.highlight .cs{color:#556366;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:#BE74D5;font-weight:700}.highlight .kd{color:#BE74D5;font-weight:700}.highlight .kn{color:#BE74D5;font-weight:700}.highlight .kp{color:#be74d5}.highlight .kr{color:#BE74D5;font-weight:700}.highlight .kt{color:#d46c72}.highlight .m{color:#d19a66}.highlight .s{color:#86c372}.highlight .na{color:#7d9029}.highlight .nb{color:#be74d5}.highlight .nc{color:#61AFEF;font-weight:700}.highlight .no{color:#d19a66}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#61afef}.highlight .nl{color:#a0a000}.highlight .nn{color:#61AFEF;font-weight:700}.highlight .nt{color:#BE74D5;font-weight:700}.highlight .nv{color:#dfc06f}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#d7dae0}.highlight .mf{color:#d19a66}.highlight .mh{color:#d19a66}.highlight .mi{color:#d19a66}.highlight .mo{color:#d19a66}.highlight .sb{color:#86c372}.highlight .sc{color:#86c372}.highlight .sd{color:#86C372;font-style:italic}.highlight .s2{color:#86c372}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#86c372}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:#be74d5}.highlight .sr{color:#b68}.highlight .s1{color:#86c372}.highlight .ss{color:#dfc06f}.highlight .bp{color:#be74d5}.highlight .vc{color:#dfc06f}.highlight .vg{color:#dfc06f}.highlight .vi{color:#dfc06f}.highlight .il{color:#d19a66}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;color:#556366}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}.highlight pre{background-color:#282C34;color:#D7DAE0;border:none;margin-bottom:25px;font-size:15px;padding:20px 10px}.highlight{font-weight:700}.table>tbody>tr>td,.table>tbody>tr>th{vertical-align:middle!important} \ No newline at end of file
+.searx-navbar{background:#29314d;height:2.3rem;font-size:1.3rem;line-height:1.3rem;padding:.5rem;font-weight:700;margin-bottom:.8rem}.searx-navbar a,.searx-navbar a:hover{margin-right:2rem;color:#fff;text-decoration:none}.searx-navbar .instance a{color:#01d7d4;margin-left:2rem}#main-logo{margin-top:20vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}*{border-radius:0!important}html{position:relative;min-height:100%;color:#29314d}body{font-family:Roboto,Helvetica,Arial,sans-serif;margin-bottom:80px;background-color:#fff}body a{color:#08c}.footer{position:absolute;bottom:0;width:100%;height:60px;text-align:center;color:#999}input[type=checkbox]:checked+.label_hide_if_checked,input[type=checkbox]:checked+.label_hide_if_not_checked+.label_hide_if_checked{display:none}input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbox]:not(:checked)+.label_hide_if_checked+.label_hide_if_not_checked{display:none}.onoff-checkbox{width:15%}.onoffswitch{position:relative;width:110px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch-checkbox{opacity:0;position:absolute}.onoffswitch-checkbox:before{content:"";display:inline-block;width:16px;height:16px;margin-right:10px;position:absolute;left:0;bottom:1px;background-color:#fff;border:1px solid #ccc;border-radius:0}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:2px solid #FFF!important;border-radius:50px!important}.onoffswitch-inner{display:block;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:30px;padding:0;line-height:40px;font-size:20px;box-sizing:border-box;content:"";background-color:#EEE}.onoffswitch-switch{display:block;width:37px;background-color:#01d7d4;position:absolute;top:0;bottom:0;right:0;border:2px solid #FFF;border-radius:50px!important;transition:all .3s ease-in 0s}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-right:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:71px;background-color:#A1A1A1}.onoffswitch-checkbox:focus+.onoffswitch-label .onoffswitch-switch{border:3px solid #444}.result_header{margin-top:0;margin-bottom:2px;font-size:16px}.result_header .favicon{margin-bottom:-3px}.result_header a{color:#29314d;text-decoration:none}.result_header a:hover{color:#08c}.result_header a:visited{color:#684898}.result_header a .highlight{background-color:#f6f9fa}.result-content,.result-format,.result-source{margin-top:2px;margin-bottom:0;word-wrap:break-word;color:#666;font-size:13px}.result-content .highlight,.result-format .highlight,.result-source .highlight{font-weight:700}.result-source{font-size:10px;float:left}.result-format{font-size:10px;float:right}.external-link{color:#069025;font-size:12px;margin-bottom:15px}.external-link a{margin-right:3px}.result-default,.result-code,.result-torrent,.result-videos,.result-map{clear:both;padding:.5em 4px}.result-default:hover,.result-code:hover,.result-torrent:hover,.result-videos:hover,.result-map:hover{background-color:#f6f9fa}.result-images{float:left!important;width:24%;margin:.5%}.result-images a{display:block;width:100%;background-size:cover}.img-thumbnail{margin:5px;max-height:128px;min-height:128px}.result-videos{clear:both}.result-videos hr{margin:5px 0 15px 0}.result-videos .collapse{width:100%}.result-videos .in{margin-bottom:8px}.result-torrent{clear:both}.result-torrent b{margin-right:5px;margin-left:5px}.result-torrent .seeders{color:#2ecc71}.result-torrent .leechers{color:#f35e77}.result-map{clear:both}.result-code{clear:both}.result-code .code-fork,.result-code .code-fork a{color:#666}.suggestion_item{margin:2px 5px;max-width:100%}.suggestion_item .btn{max-width:100%;white-space:normal;word-wrap:break-word;text-align:left}.result_download{margin-right:5px}#pagination{margin-top:30px;padding-bottom:60px}.label-default{color:#a4a4a4;background:0 0}.result .text-muted small{word-wrap:break-word}.modal-wrapper{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-wrapper{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0 none;position:relative}@media screen and (max-width:75em){.img-thumbnail{object-fit:cover}}.infobox .panel-heading{background-color:#f6f9fa}.infobox .panel-heading .panel-title{font-weight:700}.infobox p{font-family:"DejaVu Serif",Georgia,Cambria,"Times New Roman",Times,serif!important;font-style:italic}.infobox .btn{background-color:#2ecc71;border:none}.infobox .btn a{color:#fff;margin:5px}.infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed}.infobox .infobox_part:last-child{margin-bottom:0}.search_categories,#categories{text-transform:capitalize;margin-bottom:.5rem;display:flex;flex-wrap:wrap;flex-flow:row wrap;align-content:stretch}.search_categories label,#categories label,.search_categories .input-group-addon,#categories .input-group-addon{flex-grow:1;flex-basis:auto;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-top:.4rem;text-align:center;min-width:50px}.search_categories label:last-child,#categories label:last-child,.search_categories .input-group-addon:last-child,#categories .input-group-addon:last-child{border-right:#ddd 1px solid}.search_categories input[type=checkbox]:checked+label,#categories input[type=checkbox]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#main-logo{margin-top:10vh;margin-bottom:25px}#main-logo>img{max-width:350px;width:80%}#q{box-shadow:none;border-right:none;border-color:#a4a4a4}#search_form .input-group-btn .btn{border-color:#a4a4a4}#search_form .input-group-btn .btn:hover{background-color:#2ecc71;color:#fff}.custom-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;color:#666;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGnsAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW86/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0wNy0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC) 96% no-repeat}.search-margin{margin-bottom:.6em}.visually-hidden{position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);white-space:nowrap}#advanced-search-container{display:none;text-align:left;margin-bottom:1rem;clear:both}#advanced-search-container label,#advanced-search-container .input-group-addon{font-size:1.2rem;font-weight:400;background-color:#fff;border:#ddd 1px solid;border-right:none;color:#666;padding-bottom:.4rem;padding-right:.7rem;padding-left:.7rem}#advanced-search-container label:last-child,#advanced-search-container .input-group-addon:last-child{border-right:#ddd 1px solid}#advanced-search-container input[type=radio]{display:none}#advanced-search-container input[type=radio]:checked+label{color:#29314d;font-weight:700;border-bottom:#01d7d4 5px solid}#check-advanced:focus+label{text-decoration:underline}#check-advanced:checked~#advanced-search-container{display:block}.advanced{padding:0;margin-top:.3rem;text-align:right}.advanced label,.advanced select{cursor:pointer}.cursor-text{cursor:text!important}.cursor-pointer{cursor:pointer!important}pre,code{font-family:'Ubuntu Mono','Courier New','Lucida Console',monospace!important}.lineno{margin-right:5px}.highlight .hll{background-color:#ffc}.highlight{background:#f8f8f8}.highlight .c{color:#556366;font-style:italic}.highlight .err{border:1px solid #ffa92f}.highlight .k{color:#BE74D5;font-weight:700}.highlight .o{color:#d19a66}.highlight .cm{color:#556366;font-style:italic}.highlight .cp{color:#bc7a00}.highlight .c1{color:#556366;font-style:italic}.highlight .cs{color:#556366;font-style:italic}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:red}.highlight .gh{color:navy;font-weight:700}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:navy;font-weight:700}.highlight .gs{font-weight:700}.highlight .gu{color:purple;font-weight:700}.highlight .gt{color:#04d}.highlight .kc{color:#BE74D5;font-weight:700}.highlight .kd{color:#BE74D5;font-weight:700}.highlight .kn{color:#BE74D5;font-weight:700}.highlight .kp{color:#be74d5}.highlight .kr{color:#BE74D5;font-weight:700}.highlight .kt{color:#d46c72}.highlight .m{color:#d19a66}.highlight .s{color:#86c372}.highlight .na{color:#7d9029}.highlight .nb{color:#be74d5}.highlight .nc{color:#61AFEF;font-weight:700}.highlight .no{color:#d19a66}.highlight .nd{color:#a2f}.highlight .ni{color:#999;font-weight:700}.highlight .ne{color:#D2413A;font-weight:700}.highlight .nf{color:#61afef}.highlight .nl{color:#a0a000}.highlight .nn{color:#61AFEF;font-weight:700}.highlight .nt{color:#BE74D5;font-weight:700}.highlight .nv{color:#dfc06f}.highlight .ow{color:#A2F;font-weight:700}.highlight .w{color:#d7dae0}.highlight .mf{color:#d19a66}.highlight .mh{color:#d19a66}.highlight .mi{color:#d19a66}.highlight .mo{color:#d19a66}.highlight .sb{color:#86c372}.highlight .sc{color:#86c372}.highlight .sd{color:#86C372;font-style:italic}.highlight .s2{color:#86c372}.highlight .se{color:#B62;font-weight:700}.highlight .sh{color:#86c372}.highlight .si{color:#B68;font-weight:700}.highlight .sx{color:#be74d5}.highlight .sr{color:#b68}.highlight .s1{color:#86c372}.highlight .ss{color:#dfc06f}.highlight .bp{color:#be74d5}.highlight .vc{color:#dfc06f}.highlight .vg{color:#dfc06f}.highlight .vi{color:#dfc06f}.highlight .il{color:#d19a66}.highlight .lineno{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;color:#556366}.highlight .lineno::selection{background:0 0}.highlight .lineno::-moz-selection{background:0 0}.highlight pre{background-color:#282C34;color:#D7DAE0;border:none;margin-bottom:25px;font-size:15px;padding:20px 10px}.highlight{font-weight:700}.table>tbody>tr>td,.table>tbody>tr>th{vertical-align:middle!important} \ No newline at end of file
diff --git a/searx/static/themes/oscar/js/searx.js b/searx/static/themes/oscar/js/searx.js
index c93f597fe..2cd9ca26a 100644
--- a/searx/static/themes/oscar/js/searx.js
+++ b/searx/static/themes/oscar/js/searx.js
@@ -291,7 +291,7 @@ $(document).ready(function(){
}
})
.fail(function() {
- $(result_table_loadicon).html($(result_table_loadicon).html() + "<p class=\"text-muted\">could not load data!</p>");
+ $(result_table_loadicon).html($(result_table_loadicon).html() + "<p class=\"text-muted\">"+could_not_load+"</p>");
});
}
}
diff --git a/searx/static/themes/oscar/js/searx.min.js b/searx/static/themes/oscar/js/searx.min.js
index 08aba2b4b..52a58800b 100644
--- a/searx/static/themes/oscar/js/searx.min.js
+++ b/searx/static/themes/oscar/js/searx.min.js
@@ -1,2 +1,2 @@
-/*! oscar/searx.min.js | 03-06-2020 | */
-requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),window.searx=function(a){"use strict";var b=a.currentScript||function(){var b=a.getElementsByTagName("script");return b[b.length-1]}();return{autocompleter:"true"===b.getAttribute("data-autocompleter"),method:b.getAttribute("data-method")}}(document),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"./autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){var a="";searx.autocompleter&&($("#q").on("keydown",function(b){13==b.which&&(a=$("#q").val())}),$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()}),$("#q").bind("typeahead:selected",function(b,c){a&&$("#q").val(a),$("#search_form").submit()}))}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&($(this).hasClass("collapsed")?new_html=$(this).html().replace(a,b):new_html=$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&($(this).hasClass("btn-default")?new_html=$(this).html().replace(b,c):new_html=$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".media-loader").click(function(){var a=$(this).data("target"),b=$(a+" > iframe"),c=b.attr("src");void 0!==c&&!1!==c||b.attr("src",b.data("src"))}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||-1==i.indexOf(d)){switch(c+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(-1!=b.tags[d].indexOf(":")){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">could not load data!</p>')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(a){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";var h=L.map(b),i="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",j='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',k=new L.TileLayer(i,{minZoom:1,maxZoom:19,attribution:j}),l="https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",m='Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';new L.TileLayer(l,{minZoom:1,maxZoom:19,attribution:m});map_bounds?setTimeout(function(){h.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?h.setView(new L.LatLng(d,c),e):h.setView(new L.LatLng(d,c),8)),h.addLayer(k);var n={"OSM Mapnik":k};L.control.layers(n).addTo(h),g&&L.geoJson(g).addTo(h)}),$(this).off(a)})}),$(document).ready(function(){$("#allow-all-engines").click(function(){$(".onoffswitch-checkbox").each(function(){this.checked=!1})}),$("#disable-all-engines").click(function(){$(".onoffswitch-checkbox").each(function(){this.checked=!0})})}); \ No newline at end of file
+/*! oscar/searx.min.js | 15-06-2020 | */
+requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),window.searx=function(a){"use strict";var b=a.currentScript||function(){var b=a.getElementsByTagName("script");return b[b.length-1]}();return{autocompleter:"true"===b.getAttribute("data-autocompleter"),method:b.getAttribute("data-method")}}(document),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"./autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){var a="";searx.autocompleter&&($("#q").on("keydown",function(b){13==b.which&&(a=$("#q").val())}),$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()}),$("#q").bind("typeahead:selected",function(b,c){a&&$("#q").val(a),$("#search_form").submit()}))}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&($(this).hasClass("collapsed")?new_html=$(this).html().replace(a,b):new_html=$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&($(this).hasClass("btn-default")?new_html=$(this).html().replace(b,c):new_html=$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".media-loader").click(function(){var a=$(this).data("target"),b=$(a+" > iframe"),c=b.attr("src");void 0!==c&&!1!==c||b.attr("src",b.data("src"))}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||-1==i.indexOf(d)){switch(c+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(-1!=b.tags[d].indexOf(":")){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">'+could_not_load+"</p>")})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(a){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";var h=L.map(b),i="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",j='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',k=new L.TileLayer(i,{minZoom:1,maxZoom:19,attribution:j}),l="https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",m='Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';new L.TileLayer(l,{minZoom:1,maxZoom:19,attribution:m});map_bounds?setTimeout(function(){h.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?h.setView(new L.LatLng(d,c),e):h.setView(new L.LatLng(d,c),8)),h.addLayer(k);var n={"OSM Mapnik":k};L.control.layers(n).addTo(h),g&&L.geoJson(g).addTo(h)}),$(this).off(a)})}),$(document).ready(function(){$("#allow-all-engines").click(function(){$(".onoffswitch-checkbox").each(function(){this.checked=!1})}),$("#disable-all-engines").click(function(){$(".onoffswitch-checkbox").each(function(){this.checked=!0})})}); \ No newline at end of file
diff --git a/searx/static/themes/oscar/js/searx_src/leaflet_map.js b/searx/static/themes/oscar/js/searx_src/leaflet_map.js
index 3c8c616b1..06931b474 100644
--- a/searx/static/themes/oscar/js/searx_src/leaflet_map.js
+++ b/searx/static/themes/oscar/js/searx_src/leaflet_map.js
@@ -90,7 +90,7 @@ $(document).ready(function(){
}
})
.fail(function() {
- $(result_table_loadicon).html($(result_table_loadicon).html() + "<p class=\"text-muted\">could not load data!</p>");
+ $(result_table_loadicon).html($(result_table_loadicon).html() + "<p class=\"text-muted\">"+could_not_load+"</p>");
});
}
}
diff --git a/searx/static/themes/oscar/less/logicodev/advanced.less b/searx/static/themes/oscar/less/logicodev/advanced.less
index 4c3827b30..b8e10de4d 100644
--- a/searx/static/themes/oscar/less/logicodev/advanced.less
+++ b/searx/static/themes/oscar/less/logicodev/advanced.less
@@ -31,8 +31,8 @@
}
}
-#check-advanced {
- display: none;
+#check-advanced:focus + label {
+ text-decoration: underline;
}
#check-advanced:checked ~ #advanced-search-container {
diff --git a/searx/static/themes/oscar/less/logicodev/onoff.less b/searx/static/themes/oscar/less/logicodev/onoff.less
index f47189216..fbd2983eb 100644
--- a/searx/static/themes/oscar/less/logicodev/onoff.less
+++ b/searx/static/themes/oscar/less/logicodev/onoff.less
@@ -9,7 +9,21 @@
-ms-user-select: none;
}
.onoffswitch-checkbox {
- display: none;
+ opacity: 0;
+ position: absolute;
+}
+.onoffswitch-checkbox:before {
+ content: "";
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ margin-right: 10px;
+ position: absolute;
+ left: 0;
+ bottom: 1px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border-radius: 0px;
}
.onoffswitch-label {
display: block;
@@ -44,7 +58,7 @@
top: 0;
bottom: 0;
right: 0px;
- border: 2px solid #FFFFFF !important;
+ border: 2px solid #FFFFFF;
border-radius: 50px !important;
transition: all 0.3s ease-in 0s;
}
@@ -55,3 +69,6 @@
right: 71px;
background-color: #A1A1A1;
}
+.onoffswitch-checkbox:focus + .onoffswitch-label .onoffswitch-switch {
+ border: 3px solid #444444;
+}
diff --git a/searx/static/themes/oscar/less/logicodev/search.less b/searx/static/themes/oscar/less/logicodev/search.less
index d65e30563..ff94bfefb 100644
--- a/searx/static/themes/oscar/less/logicodev/search.less
+++ b/searx/static/themes/oscar/less/logicodev/search.less
@@ -77,4 +77,14 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
.search-margin {
margin-bottom: 0.6em;
-} \ No newline at end of file
+}
+
+.visually-hidden {
+ position: absolute !important;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ white-space: nowrap; /* added line */
+}
diff --git a/searx/static/themes/simple/js/searx.head.min.js b/searx/static/themes/simple/js/searx.head.min.js
index 00c711c79..8cd0df3d6 100644
--- a/searx/static/themes/simple/js/searx.head.min.js
+++ b/searx/static/themes/simple/js/searx.head.min.js
@@ -1,4 +1,4 @@
-/*! simple/searx.min.js | 06-08-2019 | https://github.com/asciimoo/searx */
+/*! simple/searx.min.js | 15-06-2020 | */
(function(t,e){"use strict";var a=e.currentScript||function(){var t=e.getElementsByTagName("script");return t[t.length-1]}();t.searx={touch:"ontouchstart"in t||t.DocumentTouch&&document instanceof DocumentTouch||false,method:a.getAttribute("data-method"),autocompleter:a.getAttribute("data-autocompleter")==="true",search_on_category_select:a.getAttribute("data-search-on-category-select")==="true",infinite_scroll:a.getAttribute("data-infinite-scroll")==="true",static_path:a.getAttribute("data-static-path"),no_item_found:a.getAttribute("data-no-item-found")};e.getElementsByTagName("html")[0].className=t.searx.touch?"js touch":"js"})(window,document);
//# sourceMappingURL=searx.head.min.js.map \ No newline at end of file
diff --git a/searx/static/themes/simple/js/searx.js b/searx/static/themes/simple/js/searx.js
index e191f2487..3e9ad3e7f 100644
--- a/searx/static/themes/simple/js/searx.js
+++ b/searx/static/themes/simple/js/searx.js
@@ -1314,7 +1314,7 @@ module.exports = AutoComplete;
})
.catch(function() {
result_table_loadicon.classList.remove('invisible');
- result_table_loadicon.innerHTML = "could not load data!";
+ result_table_loadicon.innerHTML = could_not_load;
});
}
}
diff --git a/searx/static/themes/simple/js/searx.min.js b/searx/static/themes/simple/js/searx.min.js
index 42e8a845c..264a7fb42 100644
--- a/searx/static/themes/simple/js/searx.min.js
+++ b/searx/static/themes/simple/js/searx.min.js
@@ -1,4 +1,4 @@
-/*! simple/searx.min.js | 06-08-2019 | https://github.com/asciimoo/searx */
+/*! simple/searx.min.js | 15-06-2020 | */
window.searx=function(t,a){"use strict";if(t.Element){(function(e){e.matches=e.matches||e.matchesSelector||e.webkitMatchesSelector||e.msMatchesSelector||function(e){var t=this,n=(t.parentNode||t.document).querySelectorAll(e),i=-1;while(n[++i]&&n[i]!=t);return!!n[i]}})(Element.prototype)}function o(e,t,n){try{e.call(t,n)}catch(e){console.log(e)}}var s=window.searx||{};s.on=function(i,e,r,t){t=t||false;if(typeof i!=="string"){i.addEventListener(e,r,t)}else{a.addEventListener(e,function(e){var t=e.target||e.srcElement,n=false;while(t&&t.matches&&t!==a&&!(n=t.matches(i)))t=t.parentElement;if(n)o(r,t,e)},t)}};s.ready=function(e){if(document.readyState!="loading"){e.call(t)}else{t.addEventListener("DOMContentLoaded",e.bind(t))}};s.http=function(e,t,n){var i=new XMLHttpRequest,r=function(){},a=function(){},o={then:function(e){r=e;return o},catch:function(e){a=e;return o}};try{i.open(e,t,true);i.onload=function(){if(i.status==200){r(i.response,i.responseType)}else{a(Error(i.statusText))}};i.onerror=function(){a(Error("Network Error"))};i.onabort=function(){a(Error("Transaction is aborted"))};i.send()}catch(e){a(e)}return o};s.loadStyle=function(e){var t=s.static_path+e,n="style_"+e.replace(".","_"),i=a.getElementById(n);if(i===null){i=a.createElement("link");i.setAttribute("id",n);i.setAttribute("rel","stylesheet");i.setAttribute("type","text/css");i.setAttribute("href",t);a.body.appendChild(i)}};s.loadScript=function(e,t){var n=s.static_path+e,i="script_"+e.replace(".","_"),r=a.getElementById(i);if(r===null){r=a.createElement("script");r.setAttribute("id",i);r.setAttribute("src",n);r.onload=t;r.onerror=function(){r.setAttribute("error","1")};a.body.appendChild(r)}else if(!r.hasAttribute("error")){try{t.apply(r,[])}catch(e){console.log(e)}}else{console.log("callback not executed : script '"+n+"' not loaded.")}};s.insertBefore=function(e,t){element.parentNode.insertBefore(e,t)};s.insertAfter=function(e,t){t.parentNode.insertBefore(e,t.nextSibling)};s.on(".close","click",function(e){var t=e.target||e.srcElement;this.parentNode.classList.add("invisible")});return s}(window,document);(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.AutoComplete=e()}})(function(){var e,t,n;return function a(o,s,l){function u(n,e){if(!s[n]){if(!o[n]){var t=typeof require=="function"&&require;if(!e&&t)return t(n,!0);if(c)return c(n,!0);var i=new Error("Cannot find module '"+n+"'");throw i.code="MODULE_NOT_FOUND",i}var r=s[n]={exports:{}};o[n][0].call(r.exports,function(e){var t=o[n][1][e];return u(t?t:e)},r,r.exports,a,o,s,l)}return s[n].exports}var c=typeof require=="function"&&require;for(var e=0;e<l.length;e++)u(l[e]);return u}({1:[function(e,t,n){
/*
@@ -21,5 +21,5 @@ window.searx=function(t,a){"use strict";if(t.Element){(function(e){e.matches=e.m
* @license Free to use under the MIT License.
*
*/
-(function(u,c){"use strict";function e(e,t,n,i){this.container_selector=e;this.results_selector=t;this.img_selector=n;this.margin=10;this.maxHeight=i;this._alignAllDone=true}e.prototype._getHeigth=function(e,t){var n=0,i;t-=e.length*this.margin;for(var r=0;r<e.length;r++){i=e[r];if(i.naturalWidth>0&&i.naturalHeight>0){n+=i.naturalWidth/i.naturalHeight}else{n+=1}}return t/n};e.prototype._setSize=function(e,t){var n,i,r=e.length;for(var a=0;a<r;a++){n=e[a];if(n.naturalWidth>0&&n.naturalHeight>0){i=t*n.naturalWidth/n.naturalHeight}else{i=t}n.style.width=i+"px";n.style.height=t+"px";n.style.marginLeft="3px";n.style.marginTop="3px";n.style.marginRight=this.margin-7+"px";n.style.marginBottom=this.margin-7+"px"}};e.prototype._alignImgs=function(e){var t,n,i=c.querySelector(this.container_selector).clientWidth;e:while(e.length>0){for(var r=1;r<=e.length;r++){t=e.slice(0,r);n=this._getHeigth(t,i);if(n<this.maxHeight){this._setSize(t,n);e=e.slice(r);continue e}}this._setSize(t,Math.min(this.maxHeight,n));break}};e.prototype.align=function(e){var t=c.querySelectorAll(this.results_selector),n=t.length,i=null,r=null,a=[];for(var o=0;o<n;o++){r=t[o];if(r.previousElementSibling!==i&&a.length>0){this._alignImgs(a);a=[]}a.push(r.querySelector(this.img_selector));i=r}if(a.length>0){this._alignImgs(a)}};e.prototype.watch=function(){var e,t,n,i,r=this,a=c.querySelectorAll(this.results_selector),o=a.length;function s(e){r.align()}function l(e){if(r._alignAllDone){r._alignAllDone=false;setTimeout(function(){r.align();r._alignAllDone=true},100)}}u.addEventListener("resize",l);u.addEventListener("pageshow",s);for(e=0;e<o;e++){t=a[e].querySelector(this.img_selector);if(typeof t!=="undefined"){t.addEventListener("load",l);t.addEventListener("error",l)}}};u.searx.ImageLayout=e})(window,document);searx.ready(function(){searx.on(".result","click",function(){n(this)(true)});searx.on(".result a","focus",function(e){var t=e.target;while(t!==undefined){if(t.classList.contains("result")){if(t.getAttribute("data-vim-selected")===null){n(t)(true)}break}t=t.parentNode}},true);var d={27:{key:"Escape",fun:t,des:"remove focus from the focused input",cat:"Control"},73:{key:"i",fun:o,des:"focus on the search input",cat:"Control"},66:{key:"b",fun:r(-window.innerHeight),des:"scroll one page up",cat:"Navigation"},70:{key:"f",fun:r(window.innerHeight),des:"scroll one page down",cat:"Navigation"},85:{key:"u",fun:r(-window.innerHeight/2),des:"scroll half a page up",cat:"Navigation"},68:{key:"d",fun:r(window.innerHeight/2),des:"scroll half a page down",cat:"Navigation"},71:{key:"g",fun:a(-document.body.scrollHeight,"top"),des:"scroll to the top of the page",cat:"Navigation"},86:{key:"v",fun:a(document.body.scrollHeight,"bottom"),des:"scroll to the bottom of the page",cat:"Navigation"},75:{key:"k",fun:n("up"),des:"select previous search result",cat:"Results"},74:{key:"j",fun:n("down"),des:"select next search result",cat:"Results"},80:{key:"p",fun:i(0),des:"go to previous page",cat:"Results"},78:{key:"n",fun:i(1),des:"go to next page",cat:"Results"},79:{key:"o",fun:s(false),des:"open search result",cat:"Results"},84:{key:"t",fun:s(true),des:"open the result in a new tab",cat:"Results"},82:{key:"r",fun:e,des:"reload page from the server",cat:"Control"},72:{key:"h",fun:u,des:"toggle help window",cat:"Other"}};searx.on(document,"keydown",function(e){if(d.hasOwnProperty(e.keyCode)&&!e.ctrlKey&&!e.altKey&&!e.shiftKey&&!e.metaKey){var t=e.target.tagName.toLowerCase();if(e.keyCode===27){if(t==="input"||t==="select"||t==="textarea"){d[e.keyCode].fun()}}else{if(e.target===document.body||t==="a"||t==="button"){e.preventDefault();d[e.keyCode].fun()}}}});function n(d){return function(e){var t=document.querySelector(".result[data-vim-selected]"),n=d;if(t===null){t=document.querySelector(".result");if(t===null){return}if(d==="down"||d==="up"){n=t}}var i,r=document.querySelectorAll(".result");if(typeof n!=="string"){i=n}else{switch(n){case"visible":var a=document.documentElement.scrollTop||document.body.scrollTop;var o=a+document.documentElement.clientHeight;for(var s=0;s<r.length;s++){i=r[s];var l=i.offsetTop;var u=l+i.clientHeight;if(u<=o&&l>a){break}}break;case"down":i=t.nextElementSibling;if(i===null){i=r[0]}break;case"up":i=t.previousElementSibling;if(i===null){i=r[r.length-1]}break;case"bottom":i=r[r.length-1];break;case"top":default:i=r[0]}}if(i){t.removeAttribute("data-vim-selected");i.setAttribute("data-vim-selected","true");var c=i.querySelector("h3 a")||i.querySelector("a");if(c!==null){c.focus()}if(!e){f()}}}}function e(){document.location.reload(true)}function t(){if(document.activeElement){document.activeElement.blur()}}function i(t){return function(){var e=$('div#pagination button[type="submit"]');if(e.length!==2){console.log("page navigation with this theme is not supported");return}if(t>=0&&t<e.length){e[t].click()}else{console.log("pageButtonClick(): invalid argument")}}}function f(){var e=document.querySelector(".result[data-vim-selected]");if(e===null){return}var t=document.documentElement.scrollTop||document.body.scrollTop,n=document.documentElement.clientHeight,i=e.offsetTop,r=i+e.clientHeight,a=120;if(e.previousElementSibling===null&&r<n){window.scroll(window.scrollX,0);return}if(t>i-a){window.scroll(window.scrollX,i-a)}else{var o=t+n;if(o<r+a){window.scroll(window.scrollX,r-n+a)}}}function r(e){return function(){window.scrollBy(0,e);n("visible")()}}function a(e,t){return function(){window.scrollTo(0,e);n(t)()}}function o(){window.scrollTo(0,0);document.querySelector("#q").focus()}function s(n){return function(){var e=document.querySelector(".result[data-vim-selected] h3 a");if(e!==null){var t=e.getAttribute("href");if(n){window.open(t)}else{window.location.href=t}}}}function l(e){var n={};for(var t in d){var i=d[t];n[i.cat]=n[i.cat]||[];n[i.cat].push(i)}var r=Object.keys(n).sort(function(e,t){return n[t].length-n[e].length});if(r.length===0){return}var a='<a href="#" class="close" aria-label="close" title="close">×</a>';a+="<h3>How to navigate searx with Vim-like hotkeys</h3>";a+="<table>";for(var o=0;o<r.length;o++){var s=n[r[o]];var l=o===r.length-1;var u=o%2===0;if(u){a+="<tr>"}a+="<td>";a+="<h4>"+s[0].cat+"</h4>";a+='<ul class="list-unstyled">';for(var c in s){a+="<li><kbd>"+s[c].key+"</kbd> "+s[c].des+"</li>"}a+="</ul>";a+="</td>";if(!u||l){a+="</tr>"}}a+="</table>";e.innerHTML=a}function u(){var e=document.querySelector("#vim-hotkeys-help");console.log(e);if(e===undefined||e===null){e=document.createElement("div");e.id="vim-hotkeys-help";e.className="dialog-modal";e.style="width: 40%";l(e);var t=document.getElementsByTagName("body")[0];t.appendChild(e)}else{e.classList.toggle("invisible");return}}});(function(e,c,v){"use strict";v.ready(function(){v.on(".searx_overpass_request","click",function(e){this.classList.remove("searx_overpass_request");var t="https://overpass-api.de/api/interpreter?data=";var n=t+"[out:json][timeout:25];(";var i=");out meta;";var r=this.dataset.osmId;var a=this.dataset.osmType;var o=c.querySelector("#"+this.dataset.resultTable);var s=c.querySelector("#"+this.dataset.resultTableLoadicon);var l=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(r&&a&&o){var u=null;switch(a){case"node":u=n+"node("+r+");"+i;break;case"way":u=n+"way("+r+");"+i;break;case"relation":u=n+"relation("+r+");"+i;break;default:break}if(u){v.http("GET",u).then(function(e,t){e=JSON.parse(e);if(e&&e.elements&&e.elements[0]){var n=e.elements[0];var i="";for(var r in n.tags){if(n.tags.name===null||l.indexOf(r)==-1){i+="<tr><td>"+r+"</td><td>";switch(r){case"phone":case"fax":i+='<a href="tel:'+n.tags[r].replace(/ /g,"")+'">'+n.tags[r]+"</a>";break;case"email":i+='<a href="mailto:'+n.tags[r]+'">'+n.tags[r]+"</a>";break;case"website":case"url":i+='<a href="'+n.tags[r]+'">'+n.tags[r]+"</a>";break;case"wikidata":i+='<a href="https://www.wikidata.org/wiki/'+n.tags[r]+'">'+n.tags[r]+"</a>";break;case"wikipedia":if(n.tags[r].indexOf(":")!=-1){i+='<a href="https://'+n.tags[r].substring(0,n.tags[r].indexOf(":"))+".wikipedia.org/wiki/"+n.tags[r].substring(n.tags[r].indexOf(":")+1)+'">'+n.tags[r]+"</a>";break}default:i+=n.tags[r];break}i+="</td></tr>"}}s.parentNode.removeChild(s);o.classList.remove("invisible");o.querySelector("tbody").innerHTML=i}}).catch(function(){s.classList.remove("invisible");s.innerHTML="could not load data!"})}}e.preventDefault()});v.on(".searx_init_map","click",function(e){this.classList.remove("searx_init_map");var d=this.dataset.leafletTarget;var f=parseFloat(this.dataset.mapLon);var p=parseFloat(this.dataset.mapLat);var h=parseFloat(this.dataset.mapZoom);var m=JSON.parse(this.dataset.mapBoundingbox);var g=JSON.parse(this.dataset.mapGeojson);v.loadStyle("leaflet/leaflet.css");v.loadScript("leaflet/leaflet.js",function(){var e=null;if(m){var t=L.latLng(m[0],m[2]);var n=L.latLng(m[1],m[3]);e=L.latLngBounds(t,n)}var i=L.map(d);var r="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";var a='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';var o=new L.TileLayer(r,{minZoom:1,maxZoom:19,attribution:a});var s="https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png";var l='Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';var u=new L.TileLayer(s,{minZoom:1,maxZoom:19,attribution:l});if(e){setTimeout(function(){i.fitBounds(e,{maxZoom:17})},0)}else if(f&&p){if(h){i.setView(new L.latLng(p,f),h)}else{i.setView(new L.latLng(p,f),8)}}i.addLayer(o);var c={"OSM Mapnik":o};L.control.layers(c).addTo(i);if(g){L.geoJson(g).addTo(i)}});e.preventDefault()})})})(window,document,window.searx);(function(e,o,t){"use strict";t.ready(function(){t.image_thumbnail_layout=new t.ImageLayout("#urls","#urls .result-images","img.image_thumbnail",200);t.image_thumbnail_layout.watch();t.on(".btn-collapse","click",function(e){var t=this.getAttribute("data-btn-text-collapsed");var n=this.getAttribute("data-btn-text-not-collapsed");var i=this.getAttribute("data-target");var r=o.querySelector(i);var a=this.innerHTML;if(this.classList.contains("collapsed")){a=a.replace(t,n)}else{a=a.replace(n,t)}this.innerHTML=a;this.classList.toggle("collapsed");r.classList.toggle("invisible")});t.on(".media-loader","click",function(e){var t=this.getAttribute("data-target");var n=o.querySelector(t+" > iframe");var i=n.getAttribute("src");if(i===null||i===undefined||i===false){n.setAttribute("src",n.getAttribute("data-src"))}});e.addEventListener("scroll",function(){var e=o.getElementById("backToTop"),t=document.documentElement.scrollTop||document.body.scrollTop;if(e!==null){if(t>=200){e.style.opacity=1}else{e.style.opacity=0}}})})})(window,document,window.searx);(function(t,i,n){"use strict";var r=true,a="q",o;function s(e){if(e.setSelectionRange){var t=e.value.length;e.setSelectionRange(t,t)}}function l(){if(o.value.length>0){var e=document.getElementById("search");setTimeout(e.submit.bind(e),0)}}function u(e){var t=document.getElementById("clear_search");var n=function(){if(e.value.length===0){t.classList.add("empty")}else{t.classList.remove("empty")}};n();t.addEventListener("click",function(){e.value="";e.focus();n()});e.addEventListener("keyup",n,false)}n.ready(function(){o=i.getElementById(a);function e(e){if(r){s(o);r=false}else{}}if(o!==null){u(o);if(n.autocompleter){n.autocomplete=AutoComplete.call(t,{Url:"./autocompleter",EmptyMessage:n.no_item_found,HttpMethod:n.method,MinChars:4,Delay:300},"#"+a);t.addEventListener("resize",function(){var e=new CustomEvent("position");o.dispatchEvent(e)})}o.addEventListener("focus",e,false);o.focus()}if(o!==null&&n.search_on_category_select){i.querySelector(".help").className="invisible";n.on("#categories input","change",function(e){var t,n=i.querySelectorAll('#categories input[type="checkbox"]');for(t=0;t<n.length;t++){if(n[t]!==this&&n[t].checked){n[t].click()}}if(!this.checked){this.click()}l();return false});n.on(i.getElementById("time_range"),"change",l);n.on(i.getElementById("language"),"change",l)}})})(window,document,window.searx);
+(function(u,c){"use strict";function e(e,t,n,i){this.container_selector=e;this.results_selector=t;this.img_selector=n;this.margin=10;this.maxHeight=i;this._alignAllDone=true}e.prototype._getHeigth=function(e,t){var n=0,i;t-=e.length*this.margin;for(var r=0;r<e.length;r++){i=e[r];if(i.naturalWidth>0&&i.naturalHeight>0){n+=i.naturalWidth/i.naturalHeight}else{n+=1}}return t/n};e.prototype._setSize=function(e,t){var n,i,r=e.length;for(var a=0;a<r;a++){n=e[a];if(n.naturalWidth>0&&n.naturalHeight>0){i=t*n.naturalWidth/n.naturalHeight}else{i=t}n.style.width=i+"px";n.style.height=t+"px";n.style.marginLeft="3px";n.style.marginTop="3px";n.style.marginRight=this.margin-7+"px";n.style.marginBottom=this.margin-7+"px"}};e.prototype._alignImgs=function(e){var t,n,i=c.querySelector(this.container_selector).clientWidth;e:while(e.length>0){for(var r=1;r<=e.length;r++){t=e.slice(0,r);n=this._getHeigth(t,i);if(n<this.maxHeight){this._setSize(t,n);e=e.slice(r);continue e}}this._setSize(t,Math.min(this.maxHeight,n));break}};e.prototype.align=function(e){var t=c.querySelectorAll(this.results_selector),n=t.length,i=null,r=null,a=[];for(var o=0;o<n;o++){r=t[o];if(r.previousElementSibling!==i&&a.length>0){this._alignImgs(a);a=[]}a.push(r.querySelector(this.img_selector));i=r}if(a.length>0){this._alignImgs(a)}};e.prototype.watch=function(){var e,t,n,i,r=this,a=c.querySelectorAll(this.results_selector),o=a.length;function s(e){r.align()}function l(e){if(r._alignAllDone){r._alignAllDone=false;setTimeout(function(){r.align();r._alignAllDone=true},100)}}u.addEventListener("resize",l);u.addEventListener("pageshow",s);for(e=0;e<o;e++){t=a[e].querySelector(this.img_selector);if(typeof t!=="undefined"){t.addEventListener("load",l);t.addEventListener("error",l)}}};u.searx.ImageLayout=e})(window,document);searx.ready(function(){searx.on(".result","click",function(){n(this)(true)});searx.on(".result a","focus",function(e){var t=e.target;while(t!==undefined){if(t.classList.contains("result")){if(t.getAttribute("data-vim-selected")===null){n(t)(true)}break}t=t.parentNode}},true);var d={27:{key:"Escape",fun:t,des:"remove focus from the focused input",cat:"Control"},73:{key:"i",fun:o,des:"focus on the search input",cat:"Control"},66:{key:"b",fun:r(-window.innerHeight),des:"scroll one page up",cat:"Navigation"},70:{key:"f",fun:r(window.innerHeight),des:"scroll one page down",cat:"Navigation"},85:{key:"u",fun:r(-window.innerHeight/2),des:"scroll half a page up",cat:"Navigation"},68:{key:"d",fun:r(window.innerHeight/2),des:"scroll half a page down",cat:"Navigation"},71:{key:"g",fun:a(-document.body.scrollHeight,"top"),des:"scroll to the top of the page",cat:"Navigation"},86:{key:"v",fun:a(document.body.scrollHeight,"bottom"),des:"scroll to the bottom of the page",cat:"Navigation"},75:{key:"k",fun:n("up"),des:"select previous search result",cat:"Results"},74:{key:"j",fun:n("down"),des:"select next search result",cat:"Results"},80:{key:"p",fun:i(0),des:"go to previous page",cat:"Results"},78:{key:"n",fun:i(1),des:"go to next page",cat:"Results"},79:{key:"o",fun:s(false),des:"open search result",cat:"Results"},84:{key:"t",fun:s(true),des:"open the result in a new tab",cat:"Results"},82:{key:"r",fun:e,des:"reload page from the server",cat:"Control"},72:{key:"h",fun:u,des:"toggle help window",cat:"Other"}};searx.on(document,"keydown",function(e){if(d.hasOwnProperty(e.keyCode)&&!e.ctrlKey&&!e.altKey&&!e.shiftKey&&!e.metaKey){var t=e.target.tagName.toLowerCase();if(e.keyCode===27){if(t==="input"||t==="select"||t==="textarea"){d[e.keyCode].fun()}}else{if(e.target===document.body||t==="a"||t==="button"){e.preventDefault();d[e.keyCode].fun()}}}});function n(d){return function(e){var t=document.querySelector(".result[data-vim-selected]"),n=d;if(t===null){t=document.querySelector(".result");if(t===null){return}if(d==="down"||d==="up"){n=t}}var i,r=document.querySelectorAll(".result");if(typeof n!=="string"){i=n}else{switch(n){case"visible":var a=document.documentElement.scrollTop||document.body.scrollTop;var o=a+document.documentElement.clientHeight;for(var s=0;s<r.length;s++){i=r[s];var l=i.offsetTop;var u=l+i.clientHeight;if(u<=o&&l>a){break}}break;case"down":i=t.nextElementSibling;if(i===null){i=r[0]}break;case"up":i=t.previousElementSibling;if(i===null){i=r[r.length-1]}break;case"bottom":i=r[r.length-1];break;case"top":default:i=r[0]}}if(i){t.removeAttribute("data-vim-selected");i.setAttribute("data-vim-selected","true");var c=i.querySelector("h3 a")||i.querySelector("a");if(c!==null){c.focus()}if(!e){f()}}}}function e(){document.location.reload(true)}function t(){if(document.activeElement){document.activeElement.blur()}}function i(t){return function(){var e=$('div#pagination button[type="submit"]');if(e.length!==2){console.log("page navigation with this theme is not supported");return}if(t>=0&&t<e.length){e[t].click()}else{console.log("pageButtonClick(): invalid argument")}}}function f(){var e=document.querySelector(".result[data-vim-selected]");if(e===null){return}var t=document.documentElement.scrollTop||document.body.scrollTop,n=document.documentElement.clientHeight,i=e.offsetTop,r=i+e.clientHeight,a=120;if(e.previousElementSibling===null&&r<n){window.scroll(window.scrollX,0);return}if(t>i-a){window.scroll(window.scrollX,i-a)}else{var o=t+n;if(o<r+a){window.scroll(window.scrollX,r-n+a)}}}function r(e){return function(){window.scrollBy(0,e);n("visible")()}}function a(e,t){return function(){window.scrollTo(0,e);n(t)()}}function o(){window.scrollTo(0,0);document.querySelector("#q").focus()}function s(n){return function(){var e=document.querySelector(".result[data-vim-selected] h3 a");if(e!==null){var t=e.getAttribute("href");if(n){window.open(t)}else{window.location.href=t}}}}function l(e){var n={};for(var t in d){var i=d[t];n[i.cat]=n[i.cat]||[];n[i.cat].push(i)}var r=Object.keys(n).sort(function(e,t){return n[t].length-n[e].length});if(r.length===0){return}var a='<a href="#" class="close" aria-label="close" title="close">×</a>';a+="<h3>How to navigate searx with Vim-like hotkeys</h3>";a+="<table>";for(var o=0;o<r.length;o++){var s=n[r[o]];var l=o===r.length-1;var u=o%2===0;if(u){a+="<tr>"}a+="<td>";a+="<h4>"+s[0].cat+"</h4>";a+='<ul class="list-unstyled">';for(var c in s){a+="<li><kbd>"+s[c].key+"</kbd> "+s[c].des+"</li>"}a+="</ul>";a+="</td>";if(!u||l){a+="</tr>"}}a+="</table>";e.innerHTML=a}function u(){var e=document.querySelector("#vim-hotkeys-help");console.log(e);if(e===undefined||e===null){e=document.createElement("div");e.id="vim-hotkeys-help";e.className="dialog-modal";e.style="width: 40%";l(e);var t=document.getElementsByTagName("body")[0];t.appendChild(e)}else{e.classList.toggle("invisible");return}}});(function(e,c,v){"use strict";v.ready(function(){v.on(".searx_overpass_request","click",function(e){this.classList.remove("searx_overpass_request");var t="https://overpass-api.de/api/interpreter?data=";var n=t+"[out:json][timeout:25];(";var i=");out meta;";var r=this.dataset.osmId;var a=this.dataset.osmType;var o=c.querySelector("#"+this.dataset.resultTable);var s=c.querySelector("#"+this.dataset.resultTableLoadicon);var l=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(r&&a&&o){var u=null;switch(a){case"node":u=n+"node("+r+");"+i;break;case"way":u=n+"way("+r+");"+i;break;case"relation":u=n+"relation("+r+");"+i;break;default:break}if(u){v.http("GET",u).then(function(e,t){e=JSON.parse(e);if(e&&e.elements&&e.elements[0]){var n=e.elements[0];var i="";for(var r in n.tags){if(n.tags.name===null||l.indexOf(r)==-1){i+="<tr><td>"+r+"</td><td>";switch(r){case"phone":case"fax":i+='<a href="tel:'+n.tags[r].replace(/ /g,"")+'">'+n.tags[r]+"</a>";break;case"email":i+='<a href="mailto:'+n.tags[r]+'">'+n.tags[r]+"</a>";break;case"website":case"url":i+='<a href="'+n.tags[r]+'">'+n.tags[r]+"</a>";break;case"wikidata":i+='<a href="https://www.wikidata.org/wiki/'+n.tags[r]+'">'+n.tags[r]+"</a>";break;case"wikipedia":if(n.tags[r].indexOf(":")!=-1){i+='<a href="https://'+n.tags[r].substring(0,n.tags[r].indexOf(":"))+".wikipedia.org/wiki/"+n.tags[r].substring(n.tags[r].indexOf(":")+1)+'">'+n.tags[r]+"</a>";break}default:i+=n.tags[r];break}i+="</td></tr>"}}s.parentNode.removeChild(s);o.classList.remove("invisible");o.querySelector("tbody").innerHTML=i}}).catch(function(){s.classList.remove("invisible");s.innerHTML=could_not_load})}}e.preventDefault()});v.on(".searx_init_map","click",function(e){this.classList.remove("searx_init_map");var d=this.dataset.leafletTarget;var f=parseFloat(this.dataset.mapLon);var p=parseFloat(this.dataset.mapLat);var h=parseFloat(this.dataset.mapZoom);var m=JSON.parse(this.dataset.mapBoundingbox);var g=JSON.parse(this.dataset.mapGeojson);v.loadStyle("leaflet/leaflet.css");v.loadScript("leaflet/leaflet.js",function(){var e=null;if(m){var t=L.latLng(m[0],m[2]);var n=L.latLng(m[1],m[3]);e=L.latLngBounds(t,n)}var i=L.map(d);var r="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";var a='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';var o=new L.TileLayer(r,{minZoom:1,maxZoom:19,attribution:a});var s="https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png";var l='Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';var u=new L.TileLayer(s,{minZoom:1,maxZoom:19,attribution:l});if(e){setTimeout(function(){i.fitBounds(e,{maxZoom:17})},0)}else if(f&&p){if(h){i.setView(new L.latLng(p,f),h)}else{i.setView(new L.latLng(p,f),8)}}i.addLayer(o);var c={"OSM Mapnik":o};L.control.layers(c).addTo(i);if(g){L.geoJson(g).addTo(i)}});e.preventDefault()})})})(window,document,window.searx);(function(e,o,t){"use strict";t.ready(function(){t.image_thumbnail_layout=new t.ImageLayout("#urls","#urls .result-images","img.image_thumbnail",200);t.image_thumbnail_layout.watch();t.on(".btn-collapse","click",function(e){var t=this.getAttribute("data-btn-text-collapsed");var n=this.getAttribute("data-btn-text-not-collapsed");var i=this.getAttribute("data-target");var r=o.querySelector(i);var a=this.innerHTML;if(this.classList.contains("collapsed")){a=a.replace(t,n)}else{a=a.replace(n,t)}this.innerHTML=a;this.classList.toggle("collapsed");r.classList.toggle("invisible")});t.on(".media-loader","click",function(e){var t=this.getAttribute("data-target");var n=o.querySelector(t+" > iframe");var i=n.getAttribute("src");if(i===null||i===undefined||i===false){n.setAttribute("src",n.getAttribute("data-src"))}});e.addEventListener("scroll",function(){var e=o.getElementById("backToTop"),t=document.documentElement.scrollTop||document.body.scrollTop;if(e!==null){if(t>=200){e.style.opacity=1}else{e.style.opacity=0}}})})})(window,document,window.searx);(function(t,i,n){"use strict";var r=true,a="q",o;function s(e){if(e.setSelectionRange){var t=e.value.length;e.setSelectionRange(t,t)}}function l(){if(o.value.length>0){var e=document.getElementById("search");setTimeout(e.submit.bind(e),0)}}function u(e){var t=document.getElementById("clear_search");var n=function(){if(e.value.length===0){t.classList.add("empty")}else{t.classList.remove("empty")}};n();t.addEventListener("click",function(){e.value="";e.focus();n()});e.addEventListener("keyup",n,false)}n.ready(function(){o=i.getElementById(a);function e(e){if(r){s(o);r=false}else{}}if(o!==null){u(o);if(n.autocompleter){n.autocomplete=AutoComplete.call(t,{Url:"./autocompleter",EmptyMessage:n.no_item_found,HttpMethod:n.method,MinChars:4,Delay:300},"#"+a);t.addEventListener("resize",function(){var e=new CustomEvent("position");o.dispatchEvent(e)})}o.addEventListener("focus",e,false);o.focus()}if(o!==null&&n.search_on_category_select){i.querySelector(".help").className="invisible";n.on("#categories input","change",function(e){var t,n=i.querySelectorAll('#categories input[type="checkbox"]');for(t=0;t<n.length;t++){if(n[t]!==this&&n[t].checked){n[t].click()}}if(!this.checked){this.click()}l();return false});n.on(i.getElementById("time_range"),"change",l);n.on(i.getElementById("language"),"change",l)}})})(window,document,window.searx);
//# sourceMappingURL=searx.min.js.map \ No newline at end of file
diff --git a/searx/static/themes/simple/js/searx.min.js.map b/searx/static/themes/simple/js/searx.min.js.map
index d7a1eeac8..81e7ddffa 100644
--- a/searx/static/themes/simple/js/searx.min.js.map
+++ b/searx/static/themes/simple/js/searx.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["searx.js"],"names":["window","searx","w","d","Element","ElementPrototype","matches","matchesSelector","webkitMatchesSelector","msMatchesSelector","selector","node","this","nodes","parentNode","document","querySelectorAll","i","prototype","callbackSafe","callback","el","e","call","exception","console","log","on","obj","eventType","useCapture","addEventListener","target","srcElement","found","parentElement","ready","readyState","bind","http","method","url","req","XMLHttpRequest","resolve","reject","promise","then","catch","open","onload","status","response","responseType","Error","statusText","onerror","onabort","send","ex","loadStyle","src","path","static_path","id","replace","s","getElementById","createElement","setAttribute","body","appendChild","loadScript","hasAttribute","apply","insertBefore","newNode","referenceNode","element","insertAfter","nextSibling","classList","add","f","exports","module","define","amd","g","global","self","AutoComplete","t","n","r","o","u","a","require","code","l","length","1","ConditionOperator","EventType","params","Array","isArray","forEach","elements","input","specificParams","merge","defaults","DOMResults","create","Input","nodeName","match","getAttribute","_Position","$Listeners","blur","_Blur","destroy","focus","_Focus","keyup","event","KEYUP","keydown","KEYDOWN","position","getEventsByType","type","mappings","key","KeyboardMappings","Event","undefined","eventIdentifier","condition","mapping","Operator","AND","OR","Not","hasOwnProperty","Is","keyCode","From","To","name","Conditions","Callback","makeRequest","propertyHttpHeaders","Object","getOwnPropertyNames","HttpHeaders","request","_HttpMethod","_Url","queryParams","_Pre","queryParamsStringify","encodeURIComponent","_QueryArg","indexOf","setRequestHeader","onreadystatechange","$Cache","ajax","timeout","$AjaxTimer","clearTimeout","setTimeout","Delay","Request","abort","cache","_Cache","removeEventListener","removeChild","tmp","arguments","EmptyMessage","Highlight","getRegex","value","RegExp","transform","Content-type","Limit","MinChars","HttpMethod","QueryArg","Url","Enter","liActive","querySelector","preventDefault","_Select","KeyUpAndDown_down","KeyUpAndDown_up","first","last","active","currentIndex","children","lisCount","getElementsByTagName","remove","item","AlphaNum","oldValue","currentValue","_MinChars","_Render","_Post","_Open","_EmptyMessage","emptyMessage","_Limit","limit","isNaN","parseInt","minchars","_Highlight","label","now","li","onclick","onmouseenter","offsetTop","offsetHeight","offsetLeft","clientWidth","ul","_RenderRaw","_RenderResponseItems","hasChildNodes","childNodes","reverse","Math","min","abs","innerHTML","Label","Value","returnResponse","json","JSON","parse","keys","push","ImageLayout","container_selector","results_selector","img_selector","maxHeight","margin","_alignAllDone","_getHeigth","images","width","img","naturalWidth","naturalHeight","_setSize","height","imgWidth","imagesLength","style","marginLeft","marginTop","marginRight","marginBottom","_alignImgs","imgGroup","slice","h","containerWidth","align","results_selectorNode","results_length","previous","current","previousElementSibling","watch","imgNodeLength","results_nodes","throttleAlign","highlightResult","contains","vimKeys","27","fun","removeFocus","des","cat","73","searchInputFocus","66","scrollPage","innerHeight","70","85","68","71","scrollPageTo","scrollHeight","86","75","74","80","pageButtonClick","78","79","openResult","84","82","reloadPage","72","toggleHelp","ctrlKey","altKey","shiftKey","metaKey","tagName","toLowerCase","which","noScroll","effectiveWhich","next","results","top","documentElement","scrollTop","bot","clientHeight","etop","ebot","nextElementSibling","removeAttribute","link","scrollPageToSelected","location","reload","activeElement","num","buttons","$","click","sel","wtop","wheight","offset","scroll","scrollX","wbot","amount","scrollBy","nav","scrollTo","newTab","href","initHelpContent","divElement","categories","k","sorted","sort","b","html","lastCategory","cj","helpPanel","className","toggle","overpass_url","query_start","query_end","osm_id","dataset","osmId","osm_type","osmType","result_table","resultTable","result_table_loadicon","resultTableLoadicon","osm_ignore_tags","query","contentType","newHtml","row","tags","substring","leaflet_target","leafletTarget","map_lon","parseFloat","mapLon","map_lat","mapLat","map_zoom","mapZoom","map_boundingbox","mapBoundingbox","map_geojson","mapGeojson","map_bounds","southWest","L","latLng","northEast","latLngBounds","map","osmMapnikUrl","osmMapnikAttrib","osmMapnik","TileLayer","minZoom","maxZoom","attribution","osmWikimediaUrl","osmWikimediaAttrib","osmWikimedia","fitBounds","setView","addLayer","baseLayers","OSM Mapnik","control","layers","addTo","geoJson","image_thumbnail_layout","btnLabelCollapsed","btnLabelNotCollapsed","targetElement","iframe_load","srctest","opacity","firstFocus","qinput_id","qinput","placeCursorAtEnd","setSelectionRange","len","submitIfQuery","search","submit","createClearButton","cs","updateClearButton","placeCursorAtEndOnce","autocompleter","autocomplete","no_item_found","CustomEvent","dispatchEvent","search_on_category_select","checked"],"mappings":"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","file":"searx.min.js"} \ No newline at end of file
+{"version":3,"sources":["searx.js"],"names":["window","searx","w","d","Element","ElementPrototype","matches","matchesSelector","webkitMatchesSelector","msMatchesSelector","selector","node","this","nodes","parentNode","document","querySelectorAll","i","prototype","callbackSafe","callback","el","e","call","exception","console","log","on","obj","eventType","useCapture","addEventListener","target","srcElement","found","parentElement","ready","readyState","bind","http","method","url","req","XMLHttpRequest","resolve","reject","promise","then","catch","open","onload","status","response","responseType","Error","statusText","onerror","onabort","send","ex","loadStyle","src","path","static_path","id","replace","s","getElementById","createElement","setAttribute","body","appendChild","loadScript","hasAttribute","apply","insertBefore","newNode","referenceNode","element","insertAfter","nextSibling","classList","add","f","exports","module","define","amd","g","global","self","AutoComplete","t","n","r","o","u","a","require","code","l","length","1","ConditionOperator","EventType","params","Array","isArray","forEach","elements","input","specificParams","merge","defaults","DOMResults","create","Input","nodeName","match","getAttribute","_Position","$Listeners","blur","_Blur","destroy","focus","_Focus","keyup","event","KEYUP","keydown","KEYDOWN","position","getEventsByType","type","mappings","key","KeyboardMappings","Event","undefined","eventIdentifier","condition","mapping","Operator","AND","OR","Not","hasOwnProperty","Is","keyCode","From","To","name","Conditions","Callback","makeRequest","propertyHttpHeaders","Object","getOwnPropertyNames","HttpHeaders","request","_HttpMethod","_Url","queryParams","_Pre","queryParamsStringify","encodeURIComponent","_QueryArg","indexOf","setRequestHeader","onreadystatechange","$Cache","ajax","timeout","$AjaxTimer","clearTimeout","setTimeout","Delay","Request","abort","cache","_Cache","removeEventListener","removeChild","tmp","arguments","EmptyMessage","Highlight","getRegex","value","RegExp","transform","Content-type","Limit","MinChars","HttpMethod","QueryArg","Url","Enter","liActive","querySelector","preventDefault","_Select","KeyUpAndDown_down","KeyUpAndDown_up","first","last","active","currentIndex","children","lisCount","getElementsByTagName","remove","item","AlphaNum","oldValue","currentValue","_MinChars","_Render","_Post","_Open","_EmptyMessage","emptyMessage","_Limit","limit","isNaN","parseInt","minchars","_Highlight","label","now","li","onclick","onmouseenter","offsetTop","offsetHeight","offsetLeft","clientWidth","ul","_RenderRaw","_RenderResponseItems","hasChildNodes","childNodes","reverse","Math","min","abs","innerHTML","Label","Value","returnResponse","json","JSON","parse","keys","push","ImageLayout","container_selector","results_selector","img_selector","maxHeight","margin","_alignAllDone","_getHeigth","images","width","img","naturalWidth","naturalHeight","_setSize","height","imgWidth","imagesLength","style","marginLeft","marginTop","marginRight","marginBottom","_alignImgs","imgGroup","slice","h","containerWidth","align","results_selectorNode","results_length","previous","current","previousElementSibling","watch","imgNodeLength","results_nodes","throttleAlign","highlightResult","contains","vimKeys","27","fun","removeFocus","des","cat","73","searchInputFocus","66","scrollPage","innerHeight","70","85","68","71","scrollPageTo","scrollHeight","86","75","74","80","pageButtonClick","78","79","openResult","84","82","reloadPage","72","toggleHelp","ctrlKey","altKey","shiftKey","metaKey","tagName","toLowerCase","which","noScroll","effectiveWhich","next","results","top","documentElement","scrollTop","bot","clientHeight","etop","ebot","nextElementSibling","removeAttribute","link","scrollPageToSelected","location","reload","activeElement","num","buttons","$","click","sel","wtop","wheight","offset","scroll","scrollX","wbot","amount","scrollBy","nav","scrollTo","newTab","href","initHelpContent","divElement","categories","k","sorted","sort","b","html","lastCategory","cj","helpPanel","className","toggle","overpass_url","query_start","query_end","osm_id","dataset","osmId","osm_type","osmType","result_table","resultTable","result_table_loadicon","resultTableLoadicon","osm_ignore_tags","query","contentType","newHtml","row","tags","substring","could_not_load","leaflet_target","leafletTarget","map_lon","parseFloat","mapLon","map_lat","mapLat","map_zoom","mapZoom","map_boundingbox","mapBoundingbox","map_geojson","mapGeojson","map_bounds","southWest","L","latLng","northEast","latLngBounds","map","osmMapnikUrl","osmMapnikAttrib","osmMapnik","TileLayer","minZoom","maxZoom","attribution","osmWikimediaUrl","osmWikimediaAttrib","osmWikimedia","fitBounds","setView","addLayer","baseLayers","OSM Mapnik","control","layers","addTo","geoJson","image_thumbnail_layout","btnLabelCollapsed","btnLabelNotCollapsed","targetElement","iframe_load","srctest","opacity","firstFocus","qinput_id","qinput","placeCursorAtEnd","setSelectionRange","len","submitIfQuery","search","submit","createClearButton","cs","updateClearButton","placeCursorAtEndOnce","autocompleter","autocomplete","no_item_found","CustomEvent","dispatchEvent","search_on_category_select","checked"],"mappings":"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","file":"searx.min.js"} \ No newline at end of file
diff --git a/searx/static/themes/simple/js/searx_src/searx_mapresult.js b/searx/static/themes/simple/js/searx_src/searx_mapresult.js
index 869d07da0..267ac94cb 100644
--- a/searx/static/themes/simple/js/searx_src/searx_mapresult.js
+++ b/searx/static/themes/simple/js/searx_src/searx_mapresult.js
@@ -97,7 +97,7 @@
})
.catch(function() {
result_table_loadicon.classList.remove('invisible');
- result_table_loadicon.innerHTML = "could not load data!";
+ result_table_loadicon.innerHTML = could_not_load;
});
}
}
diff --git a/searx/templates/__common__/about.html b/searx/templates/__common__/about.html
index d3e8d06a6..9741b5162 100644
--- a/searx/templates/__common__/about.html
+++ b/searx/templates/__common__/about.html
@@ -59,12 +59,16 @@
<h2 id='add to browser'>How to set as the default search engine?</h2>
- <dt>Firefox</dt>
+ <p>
+ Searx supports <a href="https://github.com/dewitt/opensearch/blob/master/opensearch-1-1-draft-6.md">OpenSearch</a>.
+ For more information on changing your default search engine, see your browser's documentation:
+ </p>
- <dd>
- <a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a>
- searx as a search engine on any version of Firefox! (javascript required)
- </dd>
+ <ul>
+ <li><a href="https://support.mozilla.org/en-US/kb/add-or-remove-search-engine-firefox">Firefox</a></li>
+ <li><a href="https://support.microsoft.com/en-us/help/4028574/microsoft-edge-change-the-default-search-engine" >Microsoft Egde</a></li>
+ <li>Chrome based browsers <a href="https://www.chromium.org/tab-to-search">only add websites that the user navigates to without a path.</a>
+ </ul>
<h2>Where to find anonymous usage statistics of this instance ?</h2>
diff --git a/searx/templates/__common__/opensearch.xml b/searx/templates/__common__/opensearch.xml
index 15d3eb792..27634245f 100644
--- a/searx/templates/__common__/opensearch.xml
+++ b/searx/templates/__common__/opensearch.xml
@@ -6,23 +6,13 @@
<Image>{{ urljoin(host, url_for('static', filename='img/favicon.png')) }}</Image>
<LongName>searx metasearch</LongName>
{% if opensearch_method == 'get' %}
- <Url type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
- {% if autocomplete %}
- <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
- <Param name="format" value="x-suggestions" />
- <Param name="q" value="{searchTerms}" />
- </Url>
- {% endif %}
+ <Url rel="results" type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
{% else %}
- <Url type="text/html" method="post" template="{{ host }}">
+ <Url rel="results" type="text/html" method="post" template="{{ host }}">
<Param name="q" value="{searchTerms}" />
</Url>
- {% if autocomplete %}
- <!-- TODO, POST REQUEST doesn't work -->
- <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
- <Param name="format" value="x-suggestions" />
- <Param name="q" value="{searchTerms}" />
- </Url>
- {% endif %}
+ {% endif %}
+ {% if autocomplete %}
+ <Url rel="suggestions" type="application/json" template="{{ host }}autocompleter"/>
{% endif %}
</OpenSearchDescription>
diff --git a/searx/templates/__common__/translations.js.tpl b/searx/templates/__common__/translations.js.tpl
new file mode 100644
index 000000000..8453aba69
--- /dev/null
+++ b/searx/templates/__common__/translations.js.tpl
@@ -0,0 +1 @@
+var could_not_load = '{{ _('could not load data') }}!';
diff --git a/searx/templates/oscar/advanced.html b/searx/templates/oscar/advanced.html
index bf5f86324..0b13d50d6 100644
--- a/searx/templates/oscar/advanced.html
+++ b/searx/templates/oscar/advanced.html
@@ -1,4 +1,4 @@
-<input type="checkbox" name="advanced_search" id="check-advanced" {% if advanced_search %} checked="checked"{% endif %}>
+<input type="checkbox" name="advanced_search" class="visually-hidden" id="check-advanced" {% if advanced_search %} checked="checked"{% endif %}>
<label for="check-advanced">{{- "" -}}
<span class="glyphicon glyphicon-cog"></span>
{{- _('Advanced settings') -}}
diff --git a/searx/templates/oscar/base.html b/searx/templates/oscar/base.html
index cd3d23df5..5bdfc5190 100644
--- a/searx/templates/oscar/base.html
+++ b/searx/templates/oscar/base.html
@@ -1,6 +1,6 @@
{% from 'oscar/macros.html' import icon %}
<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ preferences.get_value('locale') }}" xml:lang="{{ preferences.get_value('locale') }}"{% if rtl %} dir="rtl"{% endif %}>
<head>
<meta charset="UTF-8" />
<meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
@@ -8,9 +8,9 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="searx/{{ searx_version }}">
<meta name="referrer" content="no-referrer">
- <meta name="viewport" content="width=device-width, initial-scale=1 , maximum-scale=1.0, user-scalable=1" />
+ <meta name="viewport" content="width=device-width, initial-scale=1 , maximum-scale=2.0, user-scalable=1" />
{% block meta %}{% endblock %}
-
+ <script src="{{ url_for('js_translations') }}"></script>
<title>{% block title %}{% endblock %}{{ instance_name }}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}" type="text/css" />
{% if preferences.get_value('oscar-style') -%}
diff --git a/searx/templates/oscar/index.html b/searx/templates/oscar/index.html
index b941f5f20..48c49d64a 100644
--- a/searx/templates/oscar/index.html
+++ b/searx/templates/oscar/index.html
@@ -6,10 +6,10 @@
{% if cookies['oscar-style'] == 'pointhi' %}
<h1 class="text-hide center-block"><img class="center-block img-responsive" src="{{ url_for('static', filename='img/searx_logo.png') }}" alt="searx logo"/>searx</h1>
{% else %}
- <h1 class="text-hide center-block" id="main-logo">
+ <div class="text-hide center-block" id="main-logo">
<img class="center-block img-responsive" src="{{ url_for('static', filename='img/logo_searx_a.png') }}" alt="searx logo" />
searx
- </h1>
+ </div>
{% endif %}
</div>
</div>
diff --git a/searx/templates/oscar/infobox.html b/searx/templates/oscar/infobox.html
index 04f2d5f22..6ae7965b6 100644
--- a/searx/templates/oscar/infobox.html
+++ b/searx/templates/oscar/infobox.html
@@ -5,7 +5,7 @@
{% for u in infobox.urls %}{% if u.official %} <a href="{{ u.url }}">{{ u.domain }}</a>{% endif %}{% endfor %}
</div>
<div class="panel-body">
- {% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" alt="{{ infobox.infobox }}" />{% endif %}
+ {% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" />{% endif %}
{% if infobox.content %}<bdi><p class="infobox_part">{{ infobox.content | safe }}</p></bdi>{% endif %}
diff --git a/searx/templates/oscar/languages.html b/searx/templates/oscar/languages.html
index 5aff9f918..9c00c9cea 100644
--- a/searx/templates/oscar/languages.html
+++ b/searx/templates/oscar/languages.html
@@ -1,3 +1,4 @@
+<label class="visually-hidden" for="language">{{ _('Language') }}</label>
<select class="language custom-select form-control" id="language" name="language" accesskey="l">
<option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
{%- for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) -%}
diff --git a/searx/templates/oscar/macros.html b/searx/templates/oscar/macros.html
index d2d1dc643..809c9f595 100644
--- a/searx/templates/oscar/macros.html
+++ b/searx/templates/oscar/macros.html
@@ -60,15 +60,15 @@
{%- endif %}
{%- endmacro %}
-{% macro preferences_item_header(info, label, rtl) -%}
+{% macro preferences_item_header(info, label, rtl, id) -%}
{% if rtl %}
<div class="row form-group">
- <label class="col-sm-3 col-md-2 pull-right">{{ label }}</label>
+ <label class="col-sm-3 col-md-2 pull-right"{% if id %} for="{{id}}"{% endif %}>{{ label }}</label>
<span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
<div class="col-sm-4 col-md-4">
{% else %}
<div class="row form-group">
- <label class="col-sm-3 col-md-2">{{ label }}</label>
+ <label class="col-sm-3 col-md-2"{% if id %} for="{{id}}"{% endif %}>{{ label }}</label>
<div class="col-sm-4 col-md-4">
{% endif %}
{%- endmacro %}
@@ -91,6 +91,7 @@
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
+ <label class="visually-hidden" for="{{ id }}">{{ _('Allow') }}</label>
</div>
{%- endmacro %}
diff --git a/searx/templates/oscar/preferences.html b/searx/templates/oscar/preferences.html
index 0d96ff5f2..8d37b8be2 100644
--- a/searx/templates/oscar/preferences.html
+++ b/searx/templates/oscar/preferences.html
@@ -30,9 +30,9 @@
<div class="col-sm-11 col-md-10">
{% include 'oscar/categories.html' %}
</div>
- <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label>
+ <label class="col-sm-3 col-md-2" for="categories">{{ _('Default categories') }}</label>
{% else %}
- <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label>
+ <label class="col-sm-3 col-md-2" for="categories">{{ _('Default categories') }}</label>
<div class="col-sm-11 col-md-10 search-categories">
{% include 'oscar/categories.html' %}
</div>
@@ -40,14 +40,14 @@
</div>
{% set language_label = _('Search language') %}
{% set language_info = _('What language do you prefer for search?') %}
- {{ preferences_item_header(language_info, language_label, rtl) }}
+ {{ preferences_item_header(language_info, language_label, rtl, 'language') }}
{% include 'oscar/languages.html' %}
{{ preferences_item_footer(language_info, language_label, rtl) }}
{% set locale_label = _('Interface language') %}
{% set locale_info = _('Change the language of the layout') %}
- {{ preferences_item_header(locale_info, locale_label, rtl) }}
- <select class="form-control" name='locale'>
+ {{ preferences_item_header(locale_info, locale_label, rtl, 'locale') }}
+ <select class="form-control" name="locale" id="locale">
{% for locale_id,locale_name in locales.items() | sort %}
<option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
{% endfor %}
@@ -56,8 +56,8 @@
{% set autocomplete_label = _('Autocomplete') %}
{% set autocomplete_info = _('Find stuff as you type') %}
- {{ preferences_item_header(autocomplete_info, autocomplete_label, rtl) }}
- <select class="form-control" name="autocomplete">
+ {{ preferences_item_header(autocomplete_info, autocomplete_label, rtl, 'autocomplete') }}
+ <select class="form-control" name="autocomplete" id="autocomplete">
<option value=""> - </option>
{% for backend in autocomplete_backends %}
<option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
@@ -67,8 +67,8 @@
{% set image_proxy_label = _('Image proxy') %}
{% set image_proxy_info = _('Proxying image results through searx') %}
- {{ preferences_item_header(image_proxy_info, image_proxy_label, rtl) }}
- <select class="form-control" name='image_proxy'>
+ {{ preferences_item_header(image_proxy_info, image_proxy_label, rtl, 'image_proxy') }}
+ <select class="form-control" name="image_proxy" id="image_proxy">
<option value="1" {% if image_proxy %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
<option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled')}}</option>
</select>
@@ -76,8 +76,8 @@
{% set method_label = _('Method') %}
{% set method_info = _('Change how forms are submited, <a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods" rel="external">learn more about request methods</a>') %}
- {{ preferences_item_header(method_info, method_label, rtl) }}
- <select class="form-control" name='method'>
+ {{ preferences_item_header(method_info, method_label, rtl, 'method') }}
+ <select class="form-control" name="method" id="method">
<option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
<option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
</select>
@@ -85,8 +85,8 @@
{% set safesearch_label = _('SafeSearch') %}
{% set safesearch_info = _('Filter content') %}
- {{ preferences_item_header(safesearch_info, safesearch_label, rtl) }}
- <select class="form-control" name='safesearch'>
+ {{ preferences_item_header(safesearch_info, safesearch_label, rtl, 'safesearch') }}
+ <select class="form-control" name="safesearch" id="safesearch">
<option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
<option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
<option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
@@ -95,16 +95,16 @@
{% set theme_label = _('Themes') %}
{% set theme_info = _('Change searx layout') %}
- {{ preferences_item_header(theme_info, theme_label, rtl) }}
- <select class="form-control" name="theme">
+ {{ preferences_item_header(theme_info, theme_label, rtl, 'theme') }}
+ <select class="form-control" name="theme" id="theme">
{% for name in themes %}
<option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
{% endfor %}
</select>
{{ preferences_item_footer(theme_info, theme_label, rtl) }}
- {{ preferences_item_header(_('Choose style for this theme'), _('Style'), rtl) }}
- <select class="form-control" name='oscar-style'>
+ {{ preferences_item_header(_('Choose style for this theme'), _('Style'), rtl, 'oscar_style') }}
+ <select class="form-control" name="oscar-style" id="oscar_style">
<option value="logicodev" >Logicodev</option>
<option value="pointhi" {% if preferences.get_value('oscar-style') == 'pointhi' %}selected="selected"{% endif %}>Pointhi</option>
<option value="logicodev-dark" {% if preferences.get_value('oscar-style') == 'logicodev-dark' %}selected="selected"{% endif %}>Logicodev dark</option>
@@ -113,8 +113,8 @@
{% set label = _('Results on new tabs') %}
{% set info = _('Open result links on new browser tabs') %}
- {{ preferences_item_header(info, label, rtl) }}
- <select class="form-control" name='results_on_new_tab'>
+ {{ preferences_item_header(info, label, rtl, 'results_on_new_tab') }}
+ <select class="form-control" name="results_on_new_tab" id="results_on_new_tab">
<option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
<option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
</select>
@@ -122,8 +122,8 @@
{% set label = _('Open Access DOI resolver') %}
{% set info = _('Redirect to open-access versions of publications when available (plugin required)') %}
- {{ preferences_item_header(info, label, rtl) }}
- <select class="form-control" id='doi_resolver' name='doi_resolver'>
+ {{ preferences_item_header(info, label, rtl, 'doi_resolver') }}
+ <select class="form-control" name="doi_resolver" id="doi_resolver">
{% for doi_resolver_name,doi_resolver_url in doi_resolvers.items() %}
<option value="{{ doi_resolver_name }}" {% if doi_resolver_name == current_doi_resolver %}selected="selected"{% endif %}>
{{ doi_resolver_name }} - {{ doi_resolver_url }}
@@ -134,8 +134,8 @@
{% set label = _('Engine tokens') %}
{% set info = _('Access tokens for private engines') %}
- {{ preferences_item_header(info, label, rtl) }}
- <input class="form-control" id='tokens' name='tokens' value='{{ preferences.tokens.get_value() }}'/>
+ {{ preferences_item_header(info, label, rtl, 'tokens') }}
+ <input class="form-control" id="tokens" name="tokens" value='{{ preferences.tokens.get_value() }}'/>
{{ preferences_item_footer(info, label, rtl) }}
</div>
</fieldset>
@@ -173,23 +173,23 @@
<table class="table table-hover table-condensed table-striped">
<tr>
{% if not rtl %}
- <th>{{ _("Allow") }}</th>
- <th>{{ _("Engine name") }}</th>
- <th>{{ _("Shortcut") }}</th>
- <th>{{ _("Selected language") }}</th>
- <th>{{ _("SafeSearch") }}</th>
- <th>{{ _("Time range") }}</th>
- <th>{{ _("Avg. time") }}</th>
- <th>{{ _("Max time") }}</th>
+ <th scope="col">{{ _("Allow") }}</th>
+ <th scope="col">{{ _("Engine name") }}</th>
+ <th scope="col">{{ _("Shortcut") }}</th>
+ <th scope="col">{{ _("Selected language") }}</th>
+ <th scope="col">{{ _("SafeSearch") }}</th>
+ <th scope="col">{{ _("Time range") }}</th>
+ <th scope="col">{{ _("Avg. time") }}</th>
+ <th scope="col">{{ _("Max time") }}</th>
{% else %}
- <th class="text-right">{{ _("Max time") }}</th>
- <th class="text-right">{{ _("Avg. time") }}</th>
- <th class="text-right">{{ _("Time range") }}</th>
- <th class="text-right">{{ _("SafeSearch") }}</th>
- <th class="text-right">{{ _("Selected language") }}</th>
- <th class="text-right">{{ _("Shortcut") }}</th>
- <th class="text-right">{{ _("Engine name") }}</th>
- <th class="text-right">{{ _("Allow") }}</th>
+ <th scope="col" class="text-right">{{ _("Max time") }}</th>
+ <th scope="col" class="text-right">{{ _("Avg. time") }}</th>
+ <th scope="col" class="text-right">{{ _("Time range") }}</th>
+ <th scope="col" class="text-right">{{ _("SafeSearch") }}</th>
+ <th scope="col" class="text-right">{{ _("Selected language") }}</th>
+ <th scope="col" class="text-right">{{ _("Shortcut") }}</th>
+ <th scope="col" class="text-right">{{ _("Engine name") }}</th>
+ <th scope="col" class="text-right">{{ _("Allow") }}</th>
{% endif %}
</tr>
{% for search_engine in engines_by_category[categ] %}
@@ -199,21 +199,21 @@
<td class="onoff-checkbox">
{{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
</td>
- <th>{{ search_engine.name }}</th>
+ <th scope="row">{{ search_engine.name }}</th>
<td class="name">{{ shortcuts[search_engine.name] }}</td>
<td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td>
<td>{{ support_toggle(search_engine.safesearch==True) }}</td>
<td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
- <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
- <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
- {% else %}
- <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
- <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{% if stats[search_engine.name]['warn_time'] %}{{ icon('exclamation-sign')}} {% endif %}{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{% if stats[search_engine.name]['warn_timeout'] %}{{ icon('exclamation-sign') }} {% endif %}{{ search_engine.timeout }}</td>
+ {% else %}
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}{% if stats[search_engine.name]['warn_time'] %} {{ icon('exclamation-sign')}}{% endif %}</td>
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}{% if stats[search_engine.name]['warn_time'] %} {{ icon('exclamation-sign')}}{% endif %}</td>
<td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
<td>{{ support_toggle(search_engine.safesearch==True) }}</td>
<td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td>
<td>{{ shortcuts[search_engine.name] }}</td>
- <th>{{ search_engine.name }}</th>
+ <th scope="row">{{ search_engine.name }}</th>
<td class="onoff-checkbox">
{{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
</td>
@@ -241,7 +241,7 @@
<h3 class="panel-title">{{ _(plugin.name) }}</h3>
</div>
<div class="panel-body">
- <div class="col-xs-6 col-sm-4 col-md-6">{{ _(plugin.description) }}</div>
+ <div class="col-xs-6 col-sm-4 col-md-6"><label for="{{'plugin_' + plugin.id}}">{{ _(plugin.description) }}</label></div>
<div class="col-xs-6 col-sm-4 col-md-6">
<div class="onoff-checkbox">
{{ checkbox_toggle('plugin_' + plugin.id, plugin.id not in allowed_plugins) }}
diff --git a/searx/templates/oscar/result_templates/default.html b/searx/templates/oscar/result_templates/default.html
index 885cbbfa8..57365f34e 100644
--- a/searx/templates/oscar/result_templates/default.html
+++ b/searx/templates/oscar/result_templates/default.html
@@ -16,7 +16,7 @@
{%- if result.img_src -%}
<div class="container-fluid">
<div class="row">
-<img src="{{ image_proxify(result.img_src) }}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" style="width: auto; max-height: 60px; min-height: 60px;" class="col-xs-2 col-sm-4 col-md-4 result-content">
+<img src="{{ image_proxify(result.img_src) }}" title="{{ result.title|striptags }}" style="width: auto; max-height: 60px; min-height: 60px;" class="col-xs-2 col-sm-4 col-md-4 result-content">
{% if result.content %}<p class="result-content col-xs-8 col-sm-8 col-md-8">{{ result.content|safe }}</p>{% endif -%}
</div>
</div>
diff --git a/searx/templates/oscar/result_templates/videos.html b/searx/templates/oscar/result_templates/videos.html
index 51ed5a025..6d9101d2e 100644
--- a/searx/templates/oscar/result_templates/videos.html
+++ b/searx/templates/oscar/result_templates/videos.html
@@ -15,7 +15,7 @@
<div class="container-fluid">
<div class="row">
- <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img class="thumbnail col-xs-6 col-sm-4 col-md-4 result-content" src="{{ image_proxify(result.thumbnail) }}" alt="{{ result.title|striptags }} {{ result.engine }}" /></a>
+ <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img class="thumbnail col-xs-6 col-sm-4 col-md-4 result-content" src="{{ image_proxify(result.thumbnail) }}" /></a>
{% if result.author %}<p class="col-xs-12 col-sm-8 col-md-8 result-content"><b>{{ _('Author') }}</b>: {{ result.author }}</p>{% endif %}
{% if result.length %}<p class="col-xs-12 col-sm-8 col-md-8 result-content"><b>{{ _('Length') }}</b>: {{ result.length }}</p>{% endif %}
{% if result.content %}<p class="col-xs-12 col-sm-8 col-md-8 result-content">{{ result.content|safe }}</p>{% endif %}
diff --git a/searx/templates/oscar/time-range.html b/searx/templates/oscar/time-range.html
index fb1c0754b..181b57681 100644
--- a/searx/templates/oscar/time-range.html
+++ b/searx/templates/oscar/time-range.html
@@ -1,3 +1,4 @@
+<label class="visually-hidden" for="time-range">{{ _('Time range') }}</label>
<select name="time_range" id="time-range" class="custom-select form-control" accesskey="t">{{- "" -}}
<option id="time-range-anytime" value="" {{ "selected" if time_range=="" or not time_range else ""}}>
{{- _('Anytime') -}}
diff --git a/searx/templates/simple/base.html b/searx/templates/simple/base.html
index 650ef771c..5cb1e1757 100644
--- a/searx/templates/simple/base.html
+++ b/searx/templates/simple/base.html
@@ -12,6 +12,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<title>{% block title %}{% endblock %}{{ instance_name }}</title>
{% block meta %}{% endblock %}
+ <script src="{{ url_for('js_translations') }}"></script>
{% if rtl %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/searx-rtl.min.css') }}" type="text/css" media="screen" />
{% else %}
diff --git a/searx/webapp.py b/searx/webapp.py
index 2ba8ccfb8..e1b6bea1c 100755
--- a/searx/webapp.py
+++ b/searx/webapp.py
@@ -335,8 +335,15 @@ def image_proxify(url):
if not request.preferences.get_value('image_proxy'):
return url
- if url.startswith('data:image/jpeg;base64,'):
- return url
+ if url.startswith('data:image/'):
+ # 50 is an arbitrary number to get only the beginning of the image.
+ partial_base64 = url[len('data:image/'):50].split(';')
+ if len(partial_base64) == 2 \
+ and partial_base64[0] in ['gif', 'png', 'jpeg', 'pjpeg', 'webp', 'tiff', 'bmp']\
+ and partial_base64[1].startswith('base64,'):
+ return url
+ else:
+ return None
if settings.get('result_proxy'):
return proxify(url)
@@ -949,7 +956,7 @@ def opensearch():
resp = Response(response=ret,
status=200,
- mimetype="text/xml")
+ mimetype="application/opensearchdescription+xml")
return resp
@@ -1021,6 +1028,14 @@ def config():
})
+@app.route('/translations.js')
+def js_translations():
+ return render(
+ 'translations.js.tpl',
+ override_theme='__common__',
+ ), {'Content-Type': 'text/javascript; charset=UTF-8'}
+
+
@app.errorhandler(404)
def page_not_found(e):
return render('404.html'), 404
diff --git a/utils/brand.env b/utils/brand.env
index 7fe1a3911..5d5b39513 100644
--- a/utils/brand.env
+++ b/utils/brand.env
@@ -1,4 +1,5 @@
export GIT_URL='https://github.com/asciimoo/searx'
+export GIT_BRANCH='master'
export ISSUE_URL='https://github.com/asciimoo/searx/issues'
export SEARX_URL='https://searx.me'
export DOCS_URL='https://asciimoo.github.io/searx'
diff --git a/utils/filtron.sh b/utils/filtron.sh
new file mode 100755
index 000000000..8986fb0ef
--- /dev/null
+++ b/utils/filtron.sh
@@ -0,0 +1,561 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck disable=SC2119,SC2001
+
+# shellcheck source=utils/lib.sh
+source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+# shellcheck source=utils/brand.env
+source "${REPO_ROOT}/utils/brand.env"
+source_dot_config
+source "${REPO_ROOT}/utils/lxc-searx.env"
+in_container && lxc_set_suite_env
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
+
+FILTRON_URL_PATH="${FILTRON_URL_PATH:-$(echo "${PUBLIC_URL}" \
+| sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
+[[ "${FILTRON_URL_PATH}" == "${PUBLIC_URL}" ]] && FILTRON_URL_PATH=/
+
+FILTRON_ETC="/etc/filtron"
+FILTRON_RULES="$FILTRON_ETC/rules.json"
+
+FILTRON_API="${FILTRON_API:-127.0.0.1:4005}"
+FILTRON_LISTEN="${FILTRON_LISTEN:-127.0.0.1:4004}"
+FILTRON_TARGET="${FILTRON_TARGET:-127.0.0.1:8888}"
+
+SERVICE_NAME="filtron"
+SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
+SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
+SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
+SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
+
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
+
+GO_ENV="${SERVICE_HOME}/.go_env"
+GO_PKG_URL="https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
+GO_TAR=$(basename "$GO_PKG_URL")
+
+APACHE_FILTRON_SITE="searx.conf"
+NGINX_FILTRON_SITE="searx.conf"
+
+# shellcheck disable=SC2034
+CONFIG_FILES=(
+ "${FILTRON_RULES}"
+ "${SERVICE_SYSTEMD_UNIT}"
+)
+
+# ----------------------------------------------------------------------------
+usage() {
+# ----------------------------------------------------------------------------
+
+ # shellcheck disable=SC1117
+ cat <<EOF
+usage::
+ $(basename "$0") shell
+ $(basename "$0") install [all|user|rules]
+ $(basename "$0") update [filtron]
+ $(basename "$0") remove [all]
+ $(basename "$0") activate [service]
+ $(basename "$0") deactivate [service]
+ $(basename "$0") inspect [service]
+ $(basename "$0") option [debug-on|debug-off]
+ $(basename "$0") apache [install|remove]
+ $(basename "$0") nginx [install|remove]
+
+shell
+ start interactive shell from user ${SERVICE_USER}
+install / remove
+ :all: complete setup of filtron service
+ :user: add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
+ :rules: reinstall filtron rules $FILTRON_RULES
+update filtron
+ Update filtron installation ($SERVICE_HOME)
+activate service
+ activate and start service daemon (systemd unit)
+deactivate service
+ stop and deactivate service daemon (systemd unit)
+inspect service
+ show service status and log
+option
+ set one of the available options
+apache (${PUBLIC_URL})
+ :install: apache site with a reverse proxy (ProxyPass)
+ :remove: apache site ${APACHE_FILTRON_SITE}
+nginx (${PUBLIC_URL})
+ :install: nginx site with a reverse proxy (ProxyPass)
+ :remove: nginx site ${NGINX_FILTRON_SITE}
+
+filtron rules: ${FILTRON_RULES}
+
+If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
+ PUBLIC_URL : ${PUBLIC_URL}
+ PUBLIC_HOST : ${PUBLIC_HOST}
+ SERVICE_USER : ${SERVICE_USER}
+ FILTRON_TARGET : ${FILTRON_TARGET}
+ FILTRON_API : ${FILTRON_API}
+ FILTRON_LISTEN : ${FILTRON_LISTEN}
+EOF
+ if in_container; then
+ # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
+ for ip in $(global_IPs) ; do
+ if [[ $ip =~ .*:.* ]]; then
+ echo " container URL (IPv6): http://[${ip#*|}]:4005/"
+ else
+ # IPv4:
+ echo " container URL (IPv4): http://${ip#*|}:4005/"
+ fi
+ done
+ fi
+ [[ -n ${1} ]] && err_msg "$1"
+}
+
+main() {
+ required_commands \
+ sudo install git wget curl \
+ || exit
+
+ local _usage="unknown or missing $1 command $2"
+
+ case $1 in
+ --getenv) var="$2"; echo "${!var}"; exit 0;;
+ -h|--help) usage; exit 0;;
+
+ shell)
+ sudo_or_exit
+ interactive_shell "${SERVICE_USER}"
+ ;;
+ inspect)
+ case $2 in
+ service)
+ sudo_or_exit
+ inspect_service
+ ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ install)
+ rst_title "$SERVICE_NAME" part
+ sudo_or_exit
+ case $2 in
+ all) install_all ;;
+ user) assert_user ;;
+ rules)
+ rst_title "Re-Install filtron rules"
+ echo
+ install_template --no-eval "$FILTRON_RULES" root root 644
+ systemd_restart_service "${SERVICE_NAME}"
+ ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ update)
+ sudo_or_exit
+ case $2 in
+ filtron) update_filtron ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ remove)
+ sudo_or_exit
+ case $2 in
+ all) remove_all;;
+ user) drop_service_account "${SERVICE_USER}" ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ activate)
+ sudo_or_exit
+ case $2 in
+ service) systemd_activate_service "${SERVICE_NAME}" ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ deactivate)
+ sudo_or_exit
+ case $2 in
+ service) systemd_deactivate_service "${SERVICE_NAME}" ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ apache)
+ sudo_or_exit
+ case $2 in
+ install) install_apache_site ;;
+ remove) remove_apache_site ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ nginx)
+ sudo_or_exit
+ case $2 in
+ install) install_nginx_site ;;
+ remove) remove_nginx_site ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ option)
+ sudo_or_exit
+ case $2 in
+ debug-on) echo; enable_debug ;;
+ debug-off) echo; disable_debug ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ doc) rst-doc ;;
+ *) usage "unknown or missing command $1"; exit 42;;
+ esac
+}
+
+install_all() {
+ rst_title "Install $SERVICE_NAME (service)"
+ assert_user
+ wait_key
+ install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
+ wait_key
+ install_filtron
+ wait_key
+ systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+ wait_key
+ echo
+ if ! service_is_available "http://${FILTRON_LISTEN}" ; then
+ err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
+ fi
+ if apache_is_installed; then
+ info_msg "Apache is installed on this host."
+ if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+ install_apache_site
+ fi
+ elif nginx_is_installed; then
+ info_msg "nginx is installed on this host."
+ if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+ install_nginx_site
+ fi
+ fi
+ if ask_yn "Do you want to inspect the installation?" Ny; then
+ inspect_service
+ fi
+
+}
+
+remove_all() {
+ rst_title "De-Install $SERVICE_NAME (service)"
+
+ rst_para "\
+It goes without saying that this script can only be used to remove
+installations that were installed with this script."
+
+ if ! systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
+ return 42
+ fi
+ drop_service_account "${SERVICE_USER}"
+ rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
+ if service_is_available "${PUBLIC_URL}"; then
+ MSG="** Don't forget to remove your public site! (${PUBLIC_URL}) **" wait_key 10
+ fi
+}
+
+assert_user() {
+ rst_title "user $SERVICE_USER" section
+ echo
+ tee_stderr 1 <<EOF | bash | prefix_stdout
+useradd --shell /bin/bash --system \
+ --home-dir "$SERVICE_HOME" \
+ --comment 'Reverse HTTP proxy to filter requests' $SERVICE_USER
+mkdir "$SERVICE_HOME"
+chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+groups $SERVICE_USER
+EOF
+ SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
+ export SERVICE_HOME
+ echo "export SERVICE_HOME=$SERVICE_HOME"
+
+ cat > "$GO_ENV" <<EOF
+export GOPATH=\$HOME/go-apps
+export PATH=\$PATH:\$HOME/local/go/bin:\$GOPATH/bin
+EOF
+ echo "Environment $GO_ENV has been setup."
+
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER"
+grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
+EOF
+}
+
+filtron_is_installed() {
+ [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
+}
+
+_svcpr=" ${_Yellow}|${SERVICE_USER}|${_creset} "
+
+install_filtron() {
+ rst_title "Install filtron in user's ~/go-apps" section
+ echo
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+go get -v -u github.com/asciimoo/filtron
+EOF
+ install_template --no-eval "$FILTRON_RULES" root root 644
+}
+
+update_filtron() {
+ rst_title "Update filtron" section
+ echo
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+go get -v -u github.com/asciimoo/filtron
+EOF
+}
+
+inspect_service() {
+
+ rst_title "service status & log"
+
+ cat <<EOF
+
+sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
+
+ PUBLIC_URL : ${PUBLIC_URL}
+ PUBLIC_HOST : ${PUBLIC_HOST}
+ FILTRON_URL_PATH : ${FILTRON_URL_PATH}
+ FILTRON_API : ${FILTRON_API}
+ FILTRON_LISTEN : ${FILTRON_LISTEN}
+ FILTRON_TARGET : ${FILTRON_TARGET}
+
+EOF
+
+ if service_account_is_available "$SERVICE_USER"; then
+ info_msg "service account $SERVICE_USER available."
+ else
+ err_msg "service account $SERVICE_USER not available!"
+ fi
+ if go_is_available "$SERVICE_USER"; then
+ info_msg "~$SERVICE_USER: go is installed"
+ else
+ err_msg "~$SERVICE_USER: go is not installed"
+ fi
+ if filtron_is_installed; then
+ info_msg "~$SERVICE_USER: filtron app is installed"
+ else
+ err_msg "~$SERVICE_USER: filtron app is not installed!"
+ fi
+
+ if ! service_is_available "http://${FILTRON_API}"; then
+ err_msg "API not available at: http://${FILTRON_API}"
+ fi
+
+ if ! service_is_available "http://${FILTRON_LISTEN}" ; then
+ err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
+ fi
+
+ if service_is_available "http://${FILTRON_TARGET}" ; then
+ info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
+ fi
+
+ if ! service_is_available "${PUBLIC_URL}"; then
+ warn_msg "Public service at ${PUBLIC_URL} is not available!"
+ if ! in_container; then
+ warn_msg "Check if public name is correct and routed or use the public IP from above."
+ fi
+ fi
+
+ if in_container; then
+ lxc_suite_info
+ else
+ info_msg "public URL --> ${PUBLIC_URL}"
+ info_msg "internal URL --> http://${FILTRON_LISTEN}"
+ fi
+
+
+ local _debug_on
+ if ask_yn "Enable filtron debug mode?"; then
+ enable_debug
+ _debug_on=1
+ fi
+ echo
+ systemctl --no-pager -l status "${SERVICE_NAME}"
+ echo
+
+ info_msg "public URL --> ${PUBLIC_URL}"
+ # shellcheck disable=SC2059
+ printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
+ read -r -s -n1 -t 5
+ echo
+ while true; do
+ trap break 2
+ journalctl -f -u "${SERVICE_NAME}"
+ done
+
+ if [[ $_debug_on == 1 ]]; then
+ disable_debug
+ fi
+ return 0
+}
+
+
+enable_debug() {
+ info_msg "try to enable debug mode ..."
+ python <<EOF
+import sys, json
+
+debug = {
+ u'name': u'debug request'
+ , u'filters': []
+ , u'interval': 0
+ , u'limit': 0
+ , u'actions': [{u'name': u'log'}]
+}
+
+with open('$FILTRON_RULES') as rules:
+ j = json.load(rules)
+
+pos = None
+for i in range(len(j)):
+ if j[i].get('name') == 'debug request':
+ pos = i
+ break
+if pos is not None:
+ j[pos] = debug
+else:
+ j.append(debug)
+with open('$FILTRON_RULES', 'w') as rules:
+ json.dump(j, rules, indent=2, sort_keys=True)
+
+EOF
+ systemctl restart "${SERVICE_NAME}.service"
+}
+
+disable_debug() {
+ info_msg "try to disable debug mode ..."
+ python <<EOF
+import sys, json
+with open('$FILTRON_RULES') as rules:
+ j = json.load(rules)
+
+pos = None
+for i in range(len(j)):
+ if j[i].get('name') == 'debug request':
+ pos = i
+ break
+if pos is not None:
+ del j[pos]
+ with open('$FILTRON_RULES', 'w') as rules:
+ json.dump(j, rules, indent=2, sort_keys=True)
+EOF
+ systemctl restart "${SERVICE_NAME}.service"
+}
+
+install_apache_site() {
+
+ rst_title "Install Apache site $APACHE_FILTRON_SITE"
+
+ rst_para "\
+This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SITE})"
+
+ ! apache_is_installed && info_msg "Apache is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ else
+ install_apache
+ fi
+
+ "${REPO_ROOT}/utils/searx.sh" install uwsgi
+
+ apache_install_site --variant=filtron "${APACHE_FILTRON_SITE}"
+
+ info_msg "testing public url .."
+ if ! service_is_available "${PUBLIC_URL}"; then
+ err_msg "Public service at ${PUBLIC_URL} is not available!"
+ fi
+}
+
+remove_apache_site() {
+
+ rst_title "Remove Apache site $APACHE_FILTRON_SITE"
+
+ rst_para "\
+This removes apache site ${APACHE_FILTRON_SITE}."
+
+ ! apache_is_installed && err_msg "Apache is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ fi
+
+ apache_remove_site "$APACHE_FILTRON_SITE"
+
+}
+
+install_nginx_site() {
+
+ rst_title "Install nginx site $NGINX_FILTRON_SITE"
+
+ rst_para "\
+This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_FILTRON_SITE})"
+
+ ! nginx_is_installed && info_msg "nginx is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ else
+ install_nginx
+ fi
+
+ "${REPO_ROOT}/utils/searx.sh" install uwsgi
+
+ # shellcheck disable=SC2034
+ SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
+ # shellcheck disable=SC2034
+ SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
+ nginx_install_app --variant=filtron "${NGINX_FILTRON_SITE}"
+
+ info_msg "testing public url .."
+ if ! service_is_available "${PUBLIC_URL}"; then
+ err_msg "Public service at ${PUBLIC_URL} is not available!"
+ fi
+}
+
+remove_nginx_site() {
+
+ rst_title "Remove nginx site $NGINX_FILTRON_SITE"
+
+ rst_para "\
+This removes nginx site ${NGINX_FILTRON_SITE}."
+
+ ! nginx_is_installed && err_msg "nginx is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ fi
+
+ nginx_remove_site "$FILTRON_FILTRON_SITE"
+
+}
+
+
+rst-doc() {
+
+ eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/filtron.rst")\""
+
+ echo -e "\n.. START install systemd unit"
+ cat <<EOF
+.. tabs::
+
+ .. group-tab:: systemd
+
+ .. code:: bash
+
+EOF
+ eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
+ echo -e "\n.. END install systemd unit"
+
+ # for DIST_NAME in ubuntu-20.04 arch fedora; do
+ # (
+ # DIST_ID=${DIST_NAME%-*}
+ # DIST_VERS=${DIST_NAME#*-}
+ # [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
+ # # ...
+ # )
+ # done
+}
+
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------
diff --git a/utils/lib.sh b/utils/lib.sh
new file mode 100755
index 000000000..922227a89
--- /dev/null
+++ b/utils/lib.sh
@@ -0,0 +1,1519 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck disable=SC2059,SC1117
+
+# ubuntu, debian, arch, fedora ...
+DIST_ID=$(source /etc/os-release; echo "$ID");
+# shellcheck disable=SC2034
+DIST_VERS=$(source /etc/os-release; echo "$VERSION_ID");
+
+ADMIN_NAME="${ADMIN_NAME:-$(git config user.name)}"
+ADMIN_NAME="${ADMIN_NAME:-$USER}"
+
+ADMIN_EMAIL="${ADMIN_EMAIL:-$(git config user.email)}"
+ADMIN_EMAIL="${ADMIN_EMAIL:-$USER@$(hostname)}"
+
+if [[ -z "${REPO_ROOT}" ]]; then
+ REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")
+ while [ -h "${REPO_ROOT}" ] ; do
+ REPO_ROOT=$(readlink "${REPO_ROOT}")
+ done
+ REPO_ROOT=$(cd "${REPO_ROOT}/.." && pwd -P )
+fi
+
+if [[ -z ${TEMPLATES} ]]; then
+ TEMPLATES="${REPO_ROOT}/utils/templates"
+fi
+
+if [[ -z "$CACHE" ]]; then
+ CACHE="${REPO_ROOT}/cache"
+fi
+
+if [[ -z ${DIFF_CMD} ]]; then
+ DIFF_CMD="diff -u"
+ if command -v colordiff >/dev/null; then
+ DIFF_CMD="colordiff -u"
+ fi
+fi
+
+DOT_CONFIG="${DOT_CONFIG:-${REPO_ROOT}/.config.sh}"
+
+source_dot_config() {
+ if [[ ! -e "${DOT_CONFIG}" ]]; then
+ err_msg "configuration does not extsts at: ${DOT_CONFIG}"
+ return 42
+ fi
+ # shellcheck disable=SC1090
+ source "${DOT_CONFIG}"
+}
+
+sudo_or_exit() {
+ # usage: sudo_or_exit
+
+ if [ ! "$(id -u)" -eq 0 ]; then
+ err_msg "this command requires root (sudo) privilege!" >&2
+ exit 42
+ fi
+}
+
+required_commands() {
+
+ # usage: required_commands [cmd1 ...]
+
+ local exit_val=0
+ while [ -n "$1" ]; do
+
+ if ! command -v "$1" &>/dev/null; then
+ err_msg "missing command $1"
+ exit_val=42
+ fi
+ shift
+ done
+ return $exit_val
+}
+
+# colors
+# ------
+
+# shellcheck disable=SC2034
+set_terminal_colors() {
+ _colors=8
+ _creset='\e[0m' # reset all attributes
+
+ _Black='\e[0;30m'
+ _White='\e[1;37m'
+ _Red='\e[0;31m'
+ _Green='\e[0;32m'
+ _Yellow='\e[0;33m'
+ _Blue='\e[0;34m'
+ _Violet='\e[0;35m'
+ _Cyan='\e[0;36m'
+
+ _BBlack='\e[1;30m'
+ _BWhite='\e[1;37m'
+ _BRed='\e[1;31m'
+ _BGreen='\e[1;32m'
+ _BYellow='\e[1;33m'
+ _BBlue='\e[1;34m'
+ _BPurple='\e[1;35m'
+ _BCyan='\e[1;36m'
+}
+
+if [ ! -p /dev/stdout ]; then
+ set_terminal_colors
+fi
+
+# reST
+# ----
+
+if command -v fmt >/dev/null; then
+ export FMT="fmt -u"
+else
+ export FMT="cat"
+fi
+
+rst_title() {
+ # usage: rst_title <header-text> [part|chapter|section]
+
+ case ${2-chapter} in
+ part) printf "\n${_BGreen}${1//?/=}${_creset}\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/=}${_creset}\n";;
+ chapter) printf "\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/=}${_creset}\n";;
+ section) printf "\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/-}${_creset}\n";;
+ *)
+ err_msg "invalid argument '${2}' in line $(caller)"
+ return 42
+ ;;
+ esac
+}
+
+rst_para() {
+ # usage: RST_INDENT=1 rst_para "lorem ipsum ..."
+ local prefix=''
+ if [[ -n $RST_INDENT ]] && [[ $RST_INDENT -gt 0 ]]; then
+ prefix="$(for i in $(seq 1 "$RST_INDENT"); do printf " "; done)"
+ echo -en "\n$*\n" | $FMT | prefix_stdout "$prefix"
+ else
+ echo -en "\n$*\n" | $FMT
+ fi
+}
+
+die() {
+ echo -e "${_BRed}ERROR:${_creset} ${BASH_SOURCE[1]}: line ${BASH_LINENO[0]}: ${2-died ${1-1}}" >&2;
+ exit "${1-1}"
+}
+
+die_caller() {
+ echo -e "${_BRed}ERROR:${_creset} ${BASH_SOURCE[2]}: line ${BASH_LINENO[1]}: ${FUNCNAME[1]}(): ${2-died ${1-1}}" >&2;
+ exit "${1-1}"
+}
+
+err_msg() { echo -e "${_BRed}ERROR:${_creset} $*" >&2; }
+warn_msg() { echo -e "${_BBlue}WARN:${_creset} $*" >&2; }
+info_msg() { echo -e "${_BYellow}INFO:${_creset} $*" >&2; }
+
+clean_stdin() {
+ if [[ $(uname -s) != 'Darwin' ]]; then
+ while read -r -n1 -t 0.1; do : ; done
+ fi
+}
+
+wait_key(){
+ # usage: waitKEY [<timeout in sec>]
+
+ clean_stdin
+ local _t=$1
+ local msg="${MSG}"
+ [[ -z "$msg" ]] && msg="${_Green}** press any [${_BCyan}KEY${_Green}] to continue **${_creset}"
+
+ [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+ [[ -n $_t ]] && _t="-t $_t"
+ printf "$msg"
+ # shellcheck disable=SC2086
+ read -r -s -n1 $_t
+ echo
+ clean_stdin
+}
+
+ask_yn() {
+ # usage: ask_yn <prompt-text> [Ny|Yn] [<timeout in sec>]
+
+ local EXIT_YES=0 # exit status 0 --> successful
+ local EXIT_NO=1 # exit status 1 --> error code
+
+ local _t=$3
+ [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+ [[ -n $_t ]] && _t="-t $_t"
+ case "${FORCE_SELECTION:-${2}}" in
+ Y) return ${EXIT_YES} ;;
+ N) return ${EXIT_NO} ;;
+ Yn)
+ local exit_val=${EXIT_YES}
+ local choice="[${_BGreen}YES${_creset}/no]"
+ local default="Yes"
+ ;;
+ *)
+ local exit_val=${EXIT_NO}
+ local choice="[${_BGreen}NO${_creset}/yes]"
+ local default="No"
+ ;;
+ esac
+ echo
+ while true; do
+ clean_stdin
+ printf "$1 ${choice} "
+ # shellcheck disable=SC2086
+ read -r -n1 $_t
+ if [[ -z $REPLY ]]; then
+ printf "$default\n"; break
+ elif [[ $REPLY =~ ^[Yy]$ ]]; then
+ exit_val=${EXIT_YES}
+ printf "\n"
+ break
+ elif [[ $REPLY =~ ^[Nn]$ ]]; then
+ exit_val=${EXIT_NO}
+ printf "\n"
+ break
+ fi
+ _t=""
+ err_msg "invalid choice"
+ done
+ clean_stdin
+ return $exit_val
+}
+
+tee_stderr () {
+
+ # usage::
+ # tee_stderr 1 <<EOF | python -i
+ # print("hello")
+ # EOF
+ # ...
+ # >>> print("hello")
+ # hello
+
+ local _t="0";
+ if [[ -n $1 ]] ; then _t="$1"; fi
+
+ (while read -r line; do
+ # shellcheck disable=SC2086
+ sleep $_t
+ echo -e "$line" >&2
+ echo "$line"
+ done)
+}
+
+prefix_stdout () {
+ # usage: <cmd> | prefix_stdout [prefix]
+
+ local prefix="${_BYellow}-->|${_creset}"
+
+ if [[ -n $1 ]] ; then prefix="$1"; fi
+
+ # shellcheck disable=SC2162
+ (while IFS= read line; do
+ echo -e "${prefix}$line"
+ done)
+}
+
+append_line() {
+
+ # usage: append_line <line> <file>
+ #
+ # Append line if not exists, create file if not exists. E.g::
+ #
+ # append_line 'source ~/.foo' ~/bashrc
+
+ local LINE=$1
+ local FILE=$2
+ grep -qFs -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
+}
+
+cache_download() {
+
+ # usage: cache_download <url> <local-filename>
+
+ local exit_value=0
+
+ if [[ -n ${SUDO_USER} ]]; then
+ sudo -u "${SUDO_USER}" mkdir -p "${CACHE}"
+ else
+ mkdir -p "${CACHE}"
+ fi
+
+ if [[ -f "${CACHE}/$2" ]] ; then
+ info_msg "already cached: $1"
+ info_msg " --> ${CACHE}/$2"
+ fi
+
+ if [[ ! -f "${CACHE}/$2" ]]; then
+ info_msg "caching: $1"
+ info_msg " --> ${CACHE}/$2"
+ if [[ -n ${SUDO_USER} ]]; then
+ sudo -u "${SUDO_USER}" wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
+ else
+ wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
+ fi
+ if [[ ! $exit_value = 0 ]]; then
+ err_msg "failed to download: $1"
+ fi
+ fi
+}
+
+backup_file() {
+
+ # usage: backup_file /path/to/file.foo
+
+ local stamp
+ stamp=$(date +"_%Y%m%d_%H%M%S")
+ info_msg "create backup: ${1}${stamp}"
+ cp -a "${1}" "${1}${stamp}"
+}
+
+choose_one() {
+
+ # usage:
+ #
+ # DEFAULT_SELECT= 2 \
+ # choose_one <name> "your selection?" "Coffee" "Coffee with milk"
+
+ local default=${DEFAULT_SELECT-1}
+ local REPLY
+ local env_name=$1 && shift
+ local choice=$1;
+ local max="${#@}"
+ local _t
+ [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+ [[ -n $_t ]] && _t="-t $_t"
+
+ list=("$@")
+ echo -e "${_BGreen}Menu::${_creset}"
+ for ((i=1; i<= $((max -1)); i++)); do
+ if [[ "$i" == "$default" ]]; then
+ echo -e " ${_BGreen}$i.${_creset}) ${list[$i]} [default]"
+ else
+ echo -e " $i.) ${list[$i]}"
+ fi
+ done
+ while true; do
+ clean_stdin
+ printf "$1 [${_BGreen}$default${_creset}] "
+
+ if (( 10 > max )); then
+ # shellcheck disable=SC2086
+ read -r -n1 $_t
+ else
+ # shellcheck disable=SC2086,SC2229
+ read -r $_t
+ fi
+ # selection fits
+ [[ $REPLY =~ ^-?[0-9]+$ ]] && (( REPLY > 0 )) && (( REPLY < max )) && break
+
+ # take default
+ [[ -z $REPLY ]] && REPLY=$default && break
+
+ _t=""
+ err_msg "invalid choice"
+ done
+ eval "$env_name"='${list[${REPLY}]}'
+ echo
+ clean_stdin
+}
+
+install_template() {
+
+ # usage:
+ #
+ # install_template [--no-eval] [--variant=<name>] \
+ # {file} [{owner} [{group} [{chmod}]]]
+ #
+ # E.g. the origin of variant 'raw' of /etc/updatedb.conf is::
+ #
+ # ${TEMPLATES}/etc/updatedb.conf:raw
+ #
+ # To install variant 'raw' of /etc/updatedb.conf without evaluated
+ # replacements you can use::
+ #
+ # install_template --variant=raw --no-eval \
+ # /etc/updatedb.conf root root 644
+
+ local _reply=""
+ local do_eval=1
+ local variant=""
+ local pos_args=("$0")
+
+ for i in "$@"; do
+ case $i in
+ --no-eval) do_eval=0; shift ;;
+ --variant=*) variant=":${i#*=}"; shift ;;
+ *) pos_args+=("$i") ;;
+ esac
+ done
+
+ local dst="${pos_args[1]}"
+ local template_origin="${TEMPLATES}${dst}${variant}"
+ local template_file="${TEMPLATES}${dst}"
+
+ local owner="${pos_args[2]-$(id -un)}"
+ local group="${pos_args[3]-$(id -gn)}"
+ local chmod="${pos_args[4]-644}"
+
+ info_msg "install (eval=$do_eval): ${dst}"
+ [[ -n $variant ]] && info_msg "variant --> ${variant}"
+
+ if [[ ! -f "${template_origin}" ]] ; then
+ err_msg "${template_origin} does not exists"
+ err_msg "... can't install $dst"
+ wait_key 30
+ return 42
+ fi
+
+ if [[ "$do_eval" == "1" ]]; then
+ template_file="${CACHE}${dst}${variant}"
+ info_msg "BUILD template ${template_file}"
+ if [[ -n ${SUDO_USER} ]]; then
+ sudo -u "${SUDO_USER}" mkdir -p "$(dirname "${template_file}")"
+ else
+ mkdir -p "$(dirname "${template_file}")"
+ fi
+ # shellcheck disable=SC2086
+ eval "echo \"$(cat ${template_origin})\"" > "${template_file}"
+ if [[ -n ${SUDO_USER} ]]; then
+ chown "${SUDO_USER}:${SUDO_USER}" "${template_file}"
+ fi
+ else
+ template_file=$template_origin
+ fi
+
+ mkdir -p "$(dirname "${dst}")"
+
+ if [[ ! -f "${dst}" ]]; then
+ info_msg "install: ${template_file}"
+ sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
+ "${template_file}" "${dst}" | prefix_stdout
+ return $?
+ fi
+
+ if [[ -f "${dst}" ]] && cmp --silent "${template_file}" "${dst}" ; then
+ info_msg "file ${dst} allready installed"
+ return 0
+ fi
+
+ info_msg "diffrent file ${dst} allready exists on this host"
+
+ while true; do
+ choose_one _reply "choose next step with file $dst" \
+ "replace file" \
+ "leave file unchanged" \
+ "interactiv shell" \
+ "diff files"
+
+ case $_reply in
+ "replace file")
+ info_msg "install: ${template_file}"
+ sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
+ "${template_file}" "${dst}" | prefix_stdout
+ break
+ ;;
+ "leave file unchanged")
+ break
+ ;;
+ "interactiv shell")
+ echo -e "// edit ${_Red}${dst}${_creset} to your needs"
+ echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
+ sudo -H -u "${owner}" -i
+ $DIFF_CMD "${dst}" "${template_file}"
+ echo
+ echo -e "// ${_BBlack}did you edit file ...${_creset}"
+ echo -en "// ${_Red}${dst}${_creset}"
+ if ask_yn "//${_BBlack}... to your needs?${_creset}"; then
+ break
+ fi
+ ;;
+ "diff files")
+ $DIFF_CMD "${dst}" "${template_file}" | prefix_stdout
+ esac
+ done
+}
+
+
+service_is_available() {
+
+ # usage: service_is_available <URL>
+
+ [[ -z $1 ]] && die_caller 42 "missing argument <URL>"
+ local URL="$1"
+ http_code=$(curl -H 'Cache-Control: no-cache' \
+ --silent -o /dev/null --head --write-out '%{http_code}' --insecure \
+ "${URL}")
+ exit_val=$?
+ if [[ $exit_val = 0 ]]; then
+ info_msg "got $http_code from ${URL}"
+ fi
+ case "$http_code" in
+ 404|410|423) exit_val=$http_code;;
+ esac
+ return "$exit_val"
+}
+
+# golang
+# ------
+
+go_is_available() {
+
+ # usage: go_is_available $SERVICE_USER && echo "go is installed!"
+
+ sudo -i -u "${1}" which go &>/dev/null
+}
+
+install_go() {
+
+ # usage: install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
+
+ local _svcpr=" ${_Yellow}|${3}|${_creset} "
+
+ rst_title "Install Go in user's HOME" section
+
+ rst_para "download and install go binary .."
+ cache_download "${1}" "${2}"
+
+ tee_stderr 0.1 <<EOF | sudo -i -u "${3}" | prefix_stdout "$_svcpr"
+echo \$PATH
+echo \$GOPATH
+mkdir -p \$HOME/local
+rm -rf \$HOME/local/go
+tar -C \$HOME/local -xzf ${CACHE}/${2}
+EOF
+ sudo -i -u "${3}" <<EOF | prefix_stdout
+! which go >/dev/null && echo "ERROR - Go Installation not found in PATH!?!"
+which go >/dev/null && go version && echo "congratulations -- Go installation OK :)"
+EOF
+}
+
+# system accounts
+# ---------------
+
+service_account_is_available() {
+
+ # usage: service_account_is_available "$SERVICE_USER" && echo "OK"
+
+ sudo -i -u "$1" echo \$HOME &>/dev/null
+}
+
+drop_service_account() {
+
+ # usage: drop_service_account "${SERVICE_USER}"
+
+ rst_title "Drop ${1} HOME" section
+ if ask_yn "Do you really want to drop ${1} home folder?"; then
+ userdel -r -f "${1}" 2>&1 | prefix_stdout
+ else
+ rst_para "Leave HOME folder $(du -sh "${1}") unchanged."
+ fi
+}
+
+interactive_shell(){
+
+ # usage: interactive_shell "${SERVICE_USER}"
+
+ echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
+ sudo -H -u "${1}" -i
+}
+
+
+# systemd
+# -------
+
+SYSTEMD_UNITS="${SYSTEMD_UNITS:-/lib/systemd/system}"
+
+systemd_install_service() {
+
+ # usage: systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+
+ rst_title "Install System-D Unit ${1}" section
+ echo
+ install_template "${2}" root root 644
+ wait_key
+ systemd_activate_service "${1}"
+}
+
+systemd_remove_service() {
+
+ # usage: systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+
+ if ! ask_yn "Do you really want to deinstall systemd unit ${1}?"; then
+ return 42
+ fi
+ systemd_deactivate_service "${1}"
+ rm "${2}" 2>&1 | prefix_stdout
+}
+
+systemd_activate_service() {
+
+ # usage: systemd_activate_service "${SERVICE_NAME}"
+
+ rst_title "Activate ${1} (service)" section
+ echo
+ tee_stderr <<EOF | bash 2>&1
+systemctl enable ${1}.service
+systemctl restart ${1}.service
+EOF
+ tee_stderr <<EOF | bash 2>&1
+systemctl status --no-pager ${1}.service
+EOF
+}
+
+systemd_deactivate_service() {
+
+ # usage: systemd_deactivate_service "${SERVICE_NAME}"
+
+ rst_title "De-Activate ${1} (service)" section
+ echo
+ tee_stderr <<EOF | bash 2>&1 | prefix_stdout
+systemctl stop ${1}.service
+systemctl disable ${1}.service
+EOF
+}
+
+systemd_restart_service() {
+
+ # usage: systemd_restart_service "${SERVICE_NAME}"
+
+ rst_title "Restart ${1} (service)" section
+ echo
+ tee_stderr <<EOF | bash 2>&1
+systemctl restart ${1}.service
+EOF
+ tee_stderr <<EOF | bash 2>&1
+systemctl status --no-pager ${1}.service
+EOF
+}
+
+
+# nginx
+# -----
+
+nginx_distro_setup() {
+ # shellcheck disable=SC2034
+
+ NGINX_DEFAULT_SERVER=/etc/nginx/nginx.conf
+
+ # Including *location* directives from a dedicated config-folder into the
+ # server directive is, what what fedora (already) does.
+ NGINX_APPS_ENABLED="/etc/nginx/default.d"
+
+ # We add a apps-available folder and linking configurations into the
+ # NGINX_APPS_ENABLED folder. See also nginx_include_apps_enabled().
+ NGINX_APPS_AVAILABLE="/etc/nginx/default.apps-available"
+
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ NGINX_PACKAGES="nginx"
+ NGINX_DEFAULT_SERVER=/etc/nginx/sites-available/default
+ ;;
+ arch-*)
+ NGINX_PACKAGES="nginx-mainline"
+ ;;
+ fedora-*)
+ NGINX_PACKAGES="nginx"
+ ;;
+ *)
+ err_msg "$DIST_ID-$DIST_VERS: nginx not yet implemented"
+ ;;
+ esac
+}
+nginx_distro_setup
+
+install_nginx(){
+ info_msg "installing nginx ..."
+ pkg_install "${NGINX_PACKAGES}"
+ case $DIST_ID-$DIST_VERS in
+ arch-*|fedora-*)
+ systemctl enable nginx
+ systemctl start nginx
+ ;;
+ esac
+}
+
+nginx_is_installed() {
+ command -v nginx &>/dev/null
+}
+
+nginx_reload() {
+
+ info_msg "reload nginx .."
+ echo
+ if ! nginx -t; then
+ err_msg "testing nginx configuration failed"
+ return 42
+ fi
+ systemctl restart nginx
+}
+
+nginx_install_app() {
+
+ # usage: nginx_install_app [<template option> ...] <myapp>
+ #
+ # <template option>: see install_template
+
+ local template_opts=()
+ local pos_args=("$0")
+
+ for i in "$@"; do
+ case $i in
+ -*) template_opts+=("$i");;
+ *) pos_args+=("$i");;
+ esac
+ done
+
+ nginx_include_apps_enabled "${NGINX_DEFAULT_SERVER}"
+
+ install_template "${template_opts[@]}" \
+ "${NGINX_APPS_AVAILABLE}/${pos_args[1]}" \
+ root root 644
+ nginx_enable_app "${pos_args[1]}"
+ info_msg "installed nginx app: ${pos_args[1]}"
+}
+
+nginx_include_apps_enabled() {
+
+ # Add the *NGINX_APPS_ENABLED* infrastruture to a nginx server block. Such
+ # infrastruture is already known from fedora, including apps (location
+ # directives) from the /etc/nginx/default.d folder into the *default* nginx
+ # server.
+
+ # usage: nginx_include_apps_enabled <config-file>
+ #
+ # config-file: Config file with server directive in.
+
+ [[ -z $1 ]] && die_caller 42 "missing argument <config-file>"
+ local server_conf="$1"
+
+ # include /etc/nginx/default.d/*.conf;
+ local include_directive="include ${NGINX_APPS_ENABLED}/*.conf;"
+ local include_directive_re="^\s*include ${NGINX_APPS_ENABLED}/\*\.conf;"
+
+ info_msg "checking existence: '${include_directive}' in file ${server_conf}"
+ if grep "${include_directive_re}" "${server_conf}"; then
+ info_msg "OK, already exists."
+ return
+ fi
+
+ info_msg "add missing directive: '${include_directive}'"
+ cp "${server_conf}" "${server_conf}.bak"
+
+ (
+ local line
+ local stage=0
+ while IFS= read -r line
+ do
+ echo "$line"
+ if [[ $stage = 0 ]]; then
+ if [[ $line =~ ^[[:space:]]*server*[[:space:]]*\{ ]]; then
+ stage=1
+ fi
+ fi
+
+ if [[ $stage = 1 ]]; then
+ echo " # Load configuration files for the default server block."
+ echo " $include_directive"
+ echo ""
+ stage=2
+ fi
+ done < "${server_conf}.bak"
+ ) > "${server_conf}"
+
+}
+
+nginx_remove_app() {
+
+ # usage: nginx_remove_app <myapp.conf>
+
+ info_msg "remove nginx app: $1"
+ nginx_dissable_app "$1"
+ rm -f "${NGINX_APPS_AVAILABLE}/$1"
+}
+
+nginx_enable_app() {
+
+ # usage: nginx_enable_app <myapp.conf>
+
+ local CONF="$1"
+
+ info_msg "enable nginx app: ${CONF}"
+ mkdir -p "${NGINX_APPS_ENABLED}"
+ rm -f "${NGINX_APPS_ENABLED}/${CONF}"
+ ln -s "${NGINX_APPS_AVAILABLE}/${CONF}" "${NGINX_APPS_ENABLED}/${CONF}"
+ nginx_reload
+}
+
+nginx_dissable_app() {
+
+ # usage: nginx_disable_app <myapp.conf>
+
+ local CONF="$1"
+
+ info_msg "disable nginx app: ${CONF}"
+ rm -f "${NGINX_APPS_ENABLED}/${CONF}"
+ nginx_reload
+}
+
+
+# Apache
+# ------
+
+apache_distro_setup() {
+ # shellcheck disable=SC2034
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ # debian uses the /etc/apache2 path, while other distros use
+ # the apache default at /etc/httpd
+ APACHE_SITES_AVAILABLE="/etc/apache2/sites-available"
+ APACHE_SITES_ENABLED="/etc/apache2/sites-enabled"
+ APACHE_MODULES="/usr/lib/apache2/modules"
+ APACHE_PACKAGES="apache2"
+ ;;
+ arch-*)
+ APACHE_SITES_AVAILABLE="/etc/httpd/sites-available"
+ APACHE_SITES_ENABLED="/etc/httpd/sites-enabled"
+ APACHE_MODULES="modules"
+ APACHE_PACKAGES="apache"
+ ;;
+ fedora-*)
+ APACHE_SITES_AVAILABLE="/etc/httpd/sites-available"
+ APACHE_SITES_ENABLED="/etc/httpd/sites-enabled"
+ APACHE_MODULES="modules"
+ APACHE_PACKAGES="httpd"
+ ;;
+ *)
+ err_msg "$DIST_ID-$DIST_VERS: apache not yet implemented"
+ ;;
+ esac
+}
+
+apache_distro_setup
+
+install_apache(){
+ info_msg "installing apache ..."
+ pkg_install "$APACHE_PACKAGES"
+ case $DIST_ID-$DIST_VERS in
+ arch-*|fedora-*)
+ if ! grep "IncludeOptional sites-enabled" "/etc/httpd/conf/httpd.conf"; then
+ echo "IncludeOptional sites-enabled/*.conf" >> "/etc/httpd/conf/httpd.conf"
+ fi
+ systemctl enable httpd
+ systemctl start httpd
+ ;;
+ esac
+}
+
+apache_is_installed() {
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*) (command -v apachectl) &>/dev/null;;
+ arch-*) (command -v httpd) &>/dev/null;;
+ fedora-*) (command -v httpd) &>/dev/null;;
+ esac
+}
+
+apache_reload() {
+
+ info_msg "reload apache .."
+ echo
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ sudo -H apachectl configtest
+ sudo -H systemctl force-reload apache2
+ ;;
+ arch-*| fedora-*)
+ sudo -H httpd -t
+ sudo -H systemctl force-reload httpd
+ ;;
+ esac
+}
+
+apache_install_site() {
+
+ # usage: apache_install_site [<template option> ...] <mysite.conf>
+ #
+ # <template option>: see install_template
+
+ local template_opts=()
+ local pos_args=("$0")
+
+ for i in "$@"; do
+ case $i in
+ -*) template_opts+=("$i");;
+ *) pos_args+=("$i");;
+ esac
+ done
+
+ install_template "${template_opts[@]}" \
+ "${APACHE_SITES_AVAILABLE}/${pos_args[1]}" \
+ root root 644
+ apache_enable_site "${pos_args[1]}"
+ info_msg "installed apache site: ${pos_args[1]}"
+}
+
+apache_remove_site() {
+
+ # usage: apache_remove_site <mysite.conf>
+
+ info_msg "remove apache site: $1"
+ apache_dissable_site "$1"
+ rm -f "${APACHE_SITES_AVAILABLE}/$1"
+}
+
+apache_enable_site() {
+
+ # usage: apache_enable_site <mysite.conf>
+
+ local CONF="$1"
+
+ info_msg "enable apache site: ${CONF}"
+
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ sudo -H a2ensite -q "${CONF}"
+ ;;
+ arch-*)
+ mkdir -p "${APACHE_SITES_ENABLED}"
+ rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+ ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
+ ;;
+ fedora-*)
+ mkdir -p "${APACHE_SITES_ENABLED}"
+ rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+ ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
+ ;;
+ esac
+ apache_reload
+}
+
+apache_dissable_site() {
+
+ # usage: apache_disable_site <mysite.conf>
+
+ local CONF="$1"
+
+ info_msg "disable apache site: ${CONF}"
+
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ sudo -H a2dissite -q "${CONF}"
+ ;;
+ arch-*)
+ rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+ ;;
+ fedora-*)
+ rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+ ;;
+ esac
+ apache_reload
+}
+
+# uWSGI
+# -----
+
+uWSGI_SETUP="${uWSGI_SETUP:=/etc/uwsgi}"
+uWSGI_USER=
+uWSGI_GROUP=
+
+# How distros manage uWSGI apps is very different. From uWSGI POV read:
+# - https://uwsgi-docs.readthedocs.io/en/latest/Management.html
+
+uWSGI_distro_setup() {
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
+ # For uWSGI debian uses the LSB init process, this might be changed
+ # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
+ uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
+ uWSGI_APPS_ENABLED="${uWSGI_SETUP}/apps-enabled"
+ uWSGI_PACKAGES="uwsgi"
+ ;;
+ arch-*)
+ # systemd --> /usr/lib/systemd/system/uwsgi@.service
+ # For uWSGI archlinux uses systemd template units, see
+ # - http://0pointer.de/blog/projects/instances.html
+ # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+ uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-archlinux"
+ uWSGI_APPS_ENABLED="${uWSGI_SETUP}"
+ uWSGI_PACKAGES="uwsgi"
+ ;;
+ fedora-*)
+ # systemd --> /usr/lib/systemd/system/uwsgi.service
+ # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
+ # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+ uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
+ uWSGI_APPS_ENABLED="${uWSGI_SETUP}.d"
+ uWSGI_PACKAGES="uwsgi"
+ uWSGI_USER="uwsgi"
+ uWSGI_GROUP="uwsgi"
+ ;;
+ *)
+ err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+ ;;
+esac
+}
+
+uWSGI_distro_setup
+
+install_uwsgi(){
+ info_msg "installing uwsgi ..."
+ pkg_install "$uWSGI_PACKAGES"
+ case $DIST_ID-$DIST_VERS in
+ fedora-*)
+ # enable & start should be called once at uWSGI installation time
+ systemctl enable uwsgi
+ systemctl restart uwsgi
+ ;;
+ esac
+}
+
+uWSGI_restart() {
+
+ # usage: uWSGI_restart() <myapp.ini>
+
+ local CONF="$1"
+
+ [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
+ info_msg "restart uWSGI service"
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ # the 'service' method seems broken in that way, that it (re-)starts
+ # the whole uwsgi process.
+ service uwsgi restart "${CONF%.*}"
+ ;;
+ arch-*)
+ # restart systemd template instance
+ if uWSGI_app_available "${CONF}"; then
+ systemctl restart "uwsgi@${CONF%.*}"
+ else
+ info_msg "[uWSGI:systemd-template] ${CONF} not installed (no need to restart)"
+ fi
+ ;;
+ fedora-*)
+ # in emperor mode, just touch the file to restart
+ if uWSGI_app_enabled "${CONF}"; then
+ touch "${uWSGI_APPS_ENABLED}/${CONF}"
+ # it seems, there is a polling time in between touch and restart
+ # of the service.
+ sleep 3
+ else
+ info_msg "[uWSGI:emperor] ${CONF} not installed (no need to restart)"
+ fi
+ ;;
+ *)
+ err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+ return 42
+ ;;
+ esac
+}
+
+uWSGI_prepare_app() {
+
+ # usage: uWSGI_prepare_app <myapp.ini>
+
+ [[ -z $1 ]] && die_caller 42 "missing argument <myapp.ini>"
+
+ local APP="${1%.*}"
+
+ case $DIST_ID-$DIST_VERS in
+ fedora-*)
+ # in emperor mode, the uwsgi user is the owner of the sockets
+ info_msg "prepare (uwsgi:uwsgi) /run/uwsgi/app/${APP}"
+ mkdir -p "/run/uwsgi/app/${APP}"
+ chown -R "uwsgi:uwsgi" "/run/uwsgi/app/${APP}"
+ ;;
+ *)
+ info_msg "prepare (${SERVICE_USER}:${SERVICE_GROUP}) /run/uwsgi/app/${APP}"
+ mkdir -p "/run/uwsgi/app/${APP}"
+ chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "/run/uwsgi/app/${APP}"
+ ;;
+ esac
+}
+
+
+uWSGI_app_available() {
+ # usage: uWSGI_app_available <myapp.ini>
+ local CONF="$1"
+
+ [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
+ [[ -f "${uWSGI_APPS_AVAILABLE}/${CONF}" ]]
+}
+
+uWSGI_install_app() {
+
+ # usage: uWSGI_install_app [<template option> ...] <myapp.ini>
+ #
+ # <template option>: see install_template
+
+ local pos_args=("$0")
+
+ for i in "$@"; do
+ case $i in
+ -*) template_opts+=("$i");;
+ *) pos_args+=("$i");;
+ esac
+ done
+ uWSGI_prepare_app "${pos_args[1]}"
+ mkdir -p "${uWSGI_APPS_AVAILABLE}"
+ install_template "${template_opts[@]}" \
+ "${uWSGI_APPS_AVAILABLE}/${pos_args[1]}" \
+ root root 644
+ uWSGI_enable_app "${pos_args[1]}"
+ uWSGI_restart "${pos_args[1]}"
+ info_msg "uWSGI app: ${pos_args[1]} is installed"
+}
+
+uWSGI_remove_app() {
+
+ # usage: uWSGI_remove_app <myapp.ini>
+
+ local CONF="$1"
+
+ [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
+ info_msg "remove uWSGI app: ${CONF}"
+ uWSGI_disable_app "${CONF}"
+ uWSGI_restart "${CONF}"
+ rm -f "${uWSGI_APPS_AVAILABLE}/${CONF}"
+}
+
+uWSGI_app_enabled() {
+ # usage: uWSGI_app_enabled <myapp.ini>
+
+ local exit_val=0
+ local CONF="$1"
+
+ [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ [[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]]
+ exit_val=$?
+ ;;
+ arch-*)
+ systemctl -q is-enabled "uwsgi@${CONF%.*}"
+ exit_val=$?
+ ;;
+ fedora-*)
+ [[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]]
+ exit_val=$?
+ ;;
+ *)
+ # FIXME
+ err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+ exit_val=1
+ ;;
+ esac
+ return $exit_val
+}
+
+# shellcheck disable=SC2164
+uWSGI_enable_app() {
+
+ # usage: uWSGI_enable_app <myapp.ini>
+
+ local CONF="$1"
+
+ [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ mkdir -p "${uWSGI_APPS_ENABLED}"
+ rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+ ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
+ info_msg "enabled uWSGI app: ${CONF} (restart required)"
+ ;;
+ arch-*)
+ mkdir -p "${uWSGI_APPS_ENABLED}"
+ rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+ ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
+ systemctl enable "uwsgi@${CONF%.*}"
+ info_msg "enabled uWSGI app: ${CONF} (restart required)"
+ ;;
+ fedora-*)
+ mkdir -p "${uWSGI_APPS_ENABLED}"
+ rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+ ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
+ chown "${uWSGI_USER}:${uWSGI_GROUP}" "${uWSGI_APPS_ENABLED}/${CONF}"
+ info_msg "enabled uWSGI app: ${CONF}"
+ ;;
+ *)
+ # FIXME
+ err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+ ;;
+ esac
+}
+
+uWSGI_disable_app() {
+
+ # usage: uWSGI_disable_app <myapp.ini>
+
+ local CONF="$1"
+
+ [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ service uwsgi stop "${CONF%.*}"
+ rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+ info_msg "disabled uWSGI app: ${CONF} (restart uWSGI required)"
+ ;;
+ arch-*)
+ systemctl stop "uwsgi@${CONF%.*}"
+ systemctl disable "uwsgi@${CONF%.*}"
+ rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+ ;;
+ fedora-*)
+ # in emperor mode, just remove the app.ini file
+ rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+ ;;
+ *)
+ # FIXME
+ err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+ ;;
+ esac
+}
+
+# distro's package manager
+# ------------------------
+
+_apt_pkg_info_is_updated=0
+
+pkg_install() {
+
+ # usage: TITEL='install foobar' pkg_install foopkg barpkg
+
+ rst_title "${TITLE:-installation of packages}" section
+ echo -e "\npackage(s)::\n"
+ # shellcheck disable=SC2068
+ echo " " $@ | $FMT
+
+ if ! ask_yn "Should packages be installed?" Yn 30; then
+ return 42
+ fi
+ case $DIST_ID in
+ ubuntu|debian)
+ if [[ $_apt_pkg_info_is_updated == 0 ]]; then
+ export _apt_pkg_info_is_updated=1
+ apt update
+ fi
+ # shellcheck disable=SC2068
+ apt-get install -m -y $@
+ ;;
+ arch)
+ # shellcheck disable=SC2068
+ pacman -Sy --noconfirm $@
+ ;;
+ fedora)
+ # shellcheck disable=SC2068
+ dnf install -y $@
+ ;;
+ esac
+}
+
+pkg_remove() {
+
+ # usage: TITEL='remove foobar' pkg_remove foopkg barpkg
+
+ rst_title "${TITLE:-remove packages}" section
+ echo -e "\npackage(s)::\n"
+ # shellcheck disable=SC2068
+ echo " " $@ | $FMT
+
+ if ! ask_yn "Should packages be removed (purge)?" Yn 30; then
+ return 42
+ fi
+ case $DIST_ID in
+ ubuntu|debian)
+ # shellcheck disable=SC2068
+ apt-get purge --autoremove --ignore-missing -y $@
+ ;;
+ arch)
+ # shellcheck disable=SC2068
+ pacman -R --noconfirm $@
+ ;;
+ fedora)
+ # shellcheck disable=SC2068
+ dnf remove -y $@
+ ;;
+ esac
+}
+
+pkg_is_installed() {
+
+ # usage: pkg_is_install foopkg || pkg_install foopkg
+
+ case $DIST_ID in
+ ubuntu|debian)
+ dpkg -l "$1" &> /dev/null
+ return $?
+ ;;
+ arch)
+ pacman -Qsq "$1" &> /dev/null
+ return $?
+ ;;
+ fedora)
+ dnf list -q --installed "$1" &> /dev/null
+ return $?
+ ;;
+ esac
+}
+
+# git tooling
+# -----------
+
+# shellcheck disable=SC2164
+git_clone() {
+
+ # usage:
+ #
+ # git_clone <url> <name> [<branch> [<user>]]
+ # git_clone <url> <path> [<branch> [<user>]]
+ #
+ # First form uses $CACHE/<name> as destination folder, second form clones
+ # into <path>. If repository is allready cloned, pull from <branch> and
+ # update working tree (if needed, the caller has to stash local changes).
+ #
+ # git clone https://github.com/asciimoo/searx searx-src origin/master searxlogin
+ #
+
+ local url="$1"
+ local dest="$2"
+ local branch="$3"
+ local user="$4"
+ local bash_cmd="bash"
+ local remote="origin"
+
+ if [[ ! "${dest:0:1}" = "/" ]]; then
+ dest="$CACHE/$dest"
+ fi
+
+ [[ -z $branch ]] && branch=master
+ [[ -z $user ]] && [[ -n "${SUDO_USER}" ]] && user="${SUDO_USER}"
+ [[ -n $user ]] && bash_cmd="sudo -H -u $user -i"
+
+ if [[ -d "${dest}" ]] ; then
+ info_msg "already cloned: $dest"
+ tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 | prefix_stdout " ${_Yellow}|$user|${_creset} "
+cd "${dest}"
+git checkout -m -B "$branch" --track "$remote/$branch"
+git pull --all
+EOF
+ else
+ info_msg "clone into: $dest"
+ tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 | prefix_stdout " ${_Yellow}|$user|${_creset} "
+mkdir -p "$(dirname "$dest")"
+cd "$(dirname "$dest")"
+git clone --branch "$branch" --origin "$remote" "$url" "$(basename "$dest")"
+EOF
+ fi
+}
+
+# containers
+# ----------
+
+in_container() {
+ # Test if shell runs in a container.
+ #
+ # usage: in_container && echo "process running inside a LXC container"
+ # in_container || echo "process is not running inside a LXC container"
+ #
+ # sudo_or_exit
+ # hint: Reads init process environment, therefore root access is required!
+ # to be safe, take a look at the environment of process 1 (/sbin/init)
+ # grep -qa 'container=lxc' /proc/1/environ
+
+ # see lxc_init_container_env
+ [[ -f /.lxcenv ]]
+}
+
+LXC_ENV_FOLDER=
+if in_container; then
+ # shellcheck disable=SC2034
+ LXC_ENV_FOLDER="lxc/$(hostname)/"
+fi
+
+lxc_init_container_env() {
+
+ # usage: lxc_init_container_env <name>
+
+ # Create a /.lxcenv file in the root folder. Call this once after the
+ # container is inital started and before installing any boilerplate stuff.
+
+ info_msg "create /.lxcenv in container $1"
+ cat <<EOF | lxc exec "${1}" -- bash | prefix_stdout "[${_BBlue}${1}${_creset}] "
+touch "/.lxcenv"
+ls -l "/.lxcenv"
+EOF
+}
+
+# apt packages
+LXC_BASE_PACKAGES_debian="bash git build-essential python3 virtualenv"
+
+# pacman packages
+LXC_BASE_PACKAGES_arch="bash git base-devel python python-virtualenv"
+
+# dnf packages
+LXC_BASE_PACKAGES_fedora="bash git @development-tools python virtualenv"
+
+case $DIST_ID in
+ ubuntu|debian) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_debian}" ;;
+ arch) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_arch}" ;;
+ fedora) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_fedora}" ;;
+ *) err_msg "$DIST_ID-$DIST_VERS: pkg_install LXC_BASE_PACKAGES not yet implemented" ;;
+esac
+
+lxc_install_base_packages() {
+ info_msg "install LXC_BASE_PACKAGES in container $1"
+ pkg_install "${LXC_BASE_PACKAGES}"
+}
+
+
+lxc_image_copy() {
+
+ # usage: lxc_copy_image <remote image> <local image>
+ #
+ # lxc_copy_image "images:ubuntu/19.10" "ubu1910"
+
+ if lxc_image_exists "local:${LXC_SUITE[i+1]}"; then
+ info_msg "image ${LXC_SUITE[i]} already copied --> ${LXC_SUITE[i+1]}"
+ else
+ info_msg "copy image locally ${LXC_SUITE[i]} --> ${LXC_SUITE[i+1]}"
+ lxc image copy "${LXC_SUITE[i]}" local: \
+ --alias "${LXC_SUITE[i+1]}" | prefix_stdout
+ fi
+}
+
+lxc_init_container() {
+
+ # usage: lxc_init_container <image name> <container name>
+
+ local image_name="$1"
+ local container_name="$2"
+
+ if lxc info "${container_name}" &>/dev/null; then
+ info_msg "container '${container_name}' already exists"
+ else
+ info_msg "create container instance: ${container_name}"
+ lxc init "local:${image_name}" "${container_name}"
+ fi
+}
+
+lxc_exists(){
+
+ # usage: lxc_exists <name> || echo "container <name> does not exists"
+
+ lxc info "$1" &>/dev/null
+}
+
+lxc_image_exists(){
+ # usage: lxc_image_exists <alias> || echo "image <alias> does locally not exists"
+
+ lxc image info "local:$1" &>/dev/null
+
+}
+
+lxc_delete_container() {
+
+ # usage: lxc_delete_container <container-name>
+
+ if lxc info "$1" &>/dev/null; then
+ info_msg "stop & delete instance ${_BBlue}${1}${_creset}"
+ lxc stop "$1" &>/dev/null
+ lxc delete "$1" | prefix_stdout
+ else
+ warn_msg "instance '$1' does not exist / can't delete :o"
+ fi
+}
+
+lxc_delete_local_image() {
+
+ # usage: lxc_delete_local_image <container-name>
+
+ info_msg "delete image 'local:$i'"
+ lxc image delete "local:$i"
+}
+
+
+# IP
+# --
+
+global_IPs(){
+ # usage: global_IPS
+ #
+ # print list of host's SCOPE global addresses and adapters e.g::
+ #
+ # $ global_IPs
+ # enp4s0|192.168.1.127
+ # lxdbr0|10.246.86.1
+ # lxdbr0|fd42:8c58:2cd:b73f::1
+
+ ip -o addr show | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\1|\2/p'
+}
+
+primary_ip() {
+
+ case $DIST_ID in
+ arch)
+ ip -o addr show \
+ | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\2/p' \
+ | head -n 1
+ ;;
+ *) hostname -I | cut -d' ' -f1 ;;
+ esac
+}
+
+# URL
+# ---
+
+url_replace_hostname(){
+
+ # usage: url_replace_hostname <url> <new hostname>
+
+ # to replace hostname by primary IP::
+ #
+ # url_replace_hostname http://searx-ubu1604/morty $(primary_ip)
+ # http://10.246.86.250/morty
+
+ # shellcheck disable=SC2001
+ echo "$1" | sed "s|\(http[s]*://\)[^/]*\(.*\)|\1$2\2|"
+}
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
new file mode 100644
index 000000000..a51312fb8
--- /dev/null
+++ b/utils/lxc-searx.env
@@ -0,0 +1,95 @@
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck shell=bash
+
+# This file is a setup of a LXC suite. It is sourced from different context, do
+# not manipulate the environment directly, implement functions and manipulate
+# environment only is subshells!
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+# shellcheck disable=SC2034
+LXC_SUITE_NAME="searx"
+lxc_set_suite_env() {
+ # name of https://images.linuxcontainers.org
+ export LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
+ export LXC_HOST_PREFIX="${LXC_SUITE_NAME:-searx}"
+ export LXC_SUITE=(
+
+ # to disable containers, comment out lines ..
+
+ # end of standard support see https://wiki.ubuntu.com/Releases
+ "$LINUXCONTAINERS_ORG_NAME:ubuntu/16.04" "ubu1604" # April 2021
+ "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04" "ubu1804" # April 2023
+ "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.10" "ubu1910" # July 2020
+ "$LINUXCONTAINERS_ORG_NAME:ubuntu/20.04" "ubu2004" # future (EOL 2030)
+
+ # EOL see https://fedoraproject.org/wiki/Releases
+ "$LINUXCONTAINERS_ORG_NAME:fedora/31" "fedora31"
+
+ # rolling releases see https://www.archlinux.org/releng/releases/
+ "$LINUXCONTAINERS_ORG_NAME:archlinux" "archlinux"
+ )
+
+ PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+ if in_container; then
+ # container hostnames do not have a DNS entry: use primary IP!
+ PUBLIC_URL="http://$(primary_ip)/searx"
+
+ # make GUEST's services public to the HOST
+ FILTRON_API="0.0.0.0:4005"
+ FILTRON_LISTEN="0.0.0.0:4004"
+ MORTY_LISTEN="0.0.0.0:3000"
+
+ # export LXC specific environment
+ export PUBLIC_URL FILTRON_API FILTRON_LISTEN MORTY_LISTEN
+ fi
+}
+
+lxc_suite_install_info() {
+ (
+ lxc_set_suite_env
+ cat <<EOF
+LXC suite: ${LXC_SUITE_NAME} --> ${PUBLIC_URL}
+ suite includes searx, morty & filtron
+suite images:
+$(echo " ${LOCAL_IMAGES[*]}" | $FMT)
+suite containers:
+$(echo " ${CONTAINERS[*]}" | $FMT)
+EOF
+ )
+ }
+
+lxc_suite_install() {
+ (
+ lxc_set_suite_env
+ FORCE_TIMEOUT=0
+ export FORCE_TIMEOUT
+ "${LXC_REPO_ROOT}/utils/searx.sh" install all
+ "${LXC_REPO_ROOT}/utils/morty.sh" install all
+ "${LXC_REPO_ROOT}/utils/filtron.sh" install all
+
+ rst_title "suite installation finished ($(hostname))" part
+ lxc_suite_info
+ echo
+ )
+}
+
+lxc_suite_info() {
+ (
+ lxc_set_suite_env
+ for ip in $(global_IPs) ; do
+ if [[ $ip =~ .*:.* ]]; then
+ info_msg "(${ip%|*}) IPv6: http://[${ip#*|}]"
+ else
+ # IPv4:
+ # shellcheck disable=SC2034,SC2031
+ info_msg "(${ip%|*}) filtron: http://${ip#*|}:4004/ $PUBLIC_URL"
+ info_msg "(${ip%|*}) morty: http://${ip#*|}:3000/ $PUBLIC_URL_MORTY"
+ info_msg "(${ip%|*}) docs-live: http://${ip#*|}:8080/"
+ fi
+ done
+ )
+}
diff --git a/utils/lxc.sh b/utils/lxc.sh
new file mode 100755
index 000000000..a0688bc07
--- /dev/null
+++ b/utils/lxc.sh
@@ -0,0 +1,552 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# shellcheck source=utils/lib.sh
+source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+source_dot_config
+
+# load environment of the LXC suite
+LXC_ENV="${LXC_ENV:-${REPO_ROOT}/utils/lxc-searx.env}"
+source "$LXC_ENV"
+lxc_set_suite_env
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+#
+# read also:
+# - https://lxd.readthedocs.io/en/latest/
+
+LXC_HOST_PREFIX="${LXC_HOST_PREFIX:-test}"
+
+# where all folders from HOST are mounted
+LXC_SHARE_FOLDER="/share"
+LXC_REPO_ROOT="${LXC_SHARE_FOLDER}/$(basename "${REPO_ROOT}")"
+
+ubu1604_boilerplate="
+export DEBIAN_FRONTEND=noninteractive
+apt-get update -y
+apt-get upgrade -y
+apt-get install -y git curl wget
+"
+ubu1804_boilerplate="$ubu1604_boilerplate"
+ubu1904_boilerplate="$ubu1804_boilerplate"
+ubu1910_boilerplate="$ubu1904_boilerplate"
+
+# shellcheck disable=SC2034
+ubu2004_boilerplate="
+$ubu1910_boilerplate
+echo 'Set disable_coredump false' >> /etc/sudo.conf
+"
+
+# shellcheck disable=SC2034
+archlinux_boilerplate="
+pacman -Syu --noconfirm
+pacman -S --noconfirm inetutils git curl wget sudo
+echo 'Set disable_coredump false' >> /etc/sudo.conf
+"
+
+# shellcheck disable=SC2034
+fedora31_boilerplate="
+dnf update -y
+dnf install -y git curl wget hostname
+echo 'Set disable_coredump false' >> /etc/sudo.conf
+"
+
+REMOTE_IMAGES=()
+CONTAINERS=()
+LOCAL_IMAGES=()
+
+for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+ REMOTE_IMAGES=("${REMOTE_IMAGES[@]}" "${LXC_SUITE[i]}")
+ CONTAINERS=("${CONTAINERS[@]}" "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}")
+ LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${LXC_SUITE[i+1]}")
+done
+
+HOST_USER="${SUDO_USER:-$USER}"
+HOST_USER_ID=$(id -u "${HOST_USER}")
+HOST_GROUP_ID=$(id -g "${HOST_USER}")
+
+# ----------------------------------------------------------------------------
+usage() {
+# ----------------------------------------------------------------------------
+ _cmd="$(basename "$0")"
+ cat <<EOF
+usage::
+ $_cmd build [containers|<name>]
+ $_cmd copy [images]
+ $_cmd remove [containers|<name>|images]
+ $_cmd [start|stop] [containers|<name>]
+ $_cmd show [images|suite|info|config [<name>]]
+ $_cmd cmd [--|<name>] '...'
+ $_cmd install [suite|base [<name>]]
+
+build
+ :containers: build, launch all containers and 'install base' packages
+ :<name>: build, launch container <name> and 'install base' packages
+copy:
+ :images: copy remote images of the suite into local storage
+remove
+ :containers: delete all 'containers' or only <container-name>
+ :images: delete local images of the suite
+start/stop
+ :containers: start/stop all 'containers' from the suite
+ :<name>: start/stop container <name> from suite
+show
+ :info: show info of all (or <name>) containers from LXC suite
+ :config: show config of all (or <name>) containers from the LXC suite
+ :suite: show services of all (or <name>) containers from the LXC suite
+ :images: show information of local images
+cmd
+ use single qoutes to evaluate in container's bash, e.g.: 'echo \$(hostname)'
+ -- run command '...' in all containers of the LXC suite
+ :<name>: run command '...' in container <name>
+install
+ :base: prepare LXC; install basic packages
+ :suite: install LXC ${LXC_SUITE_NAME} suite into all (or <name>) containers
+
+EOF
+ usage_containers
+ [ -n "${1+x}" ] && err_msg "$1"
+}
+
+usage_containers() {
+ lxc_suite_install_info
+ [ -n "${1+x}" ] && err_msg "$1"
+}
+
+lxd_info() {
+
+ cat <<EOF
+
+LXD is needed, to install run::
+
+ snap install lxd
+ lxd init --auto
+
+EOF
+}
+
+main() {
+
+ local exit_val
+ local _usage="unknown or missing $1 command $2"
+
+ # don't check prerequisite when in recursion
+ if [[ ! $1 == __* ]]; then
+ if ! in_container; then
+ ! required_commands lxc && lxd_info && exit 42
+ fi
+ [[ -z $LXC_SUITE ]] && err_msg "missing LXC_SUITE" && exit 42
+ fi
+
+ case $1 in
+ --getenv) var="$2"; echo "${!var}"; exit 0;;
+ -h|--help) usage; exit 0;;
+
+ build)
+ sudo_or_exit
+ case $2 in
+ ${LXC_HOST_PREFIX}-*) build_container "$2" ;;
+ ''|--|containers) build_all_containers ;;
+ *) usage "$_usage"; exit 42;;
+ esac
+ ;;
+ copy)
+ case $2 in
+ ''|images) lxc_copy_images_localy;;
+ *) usage "$_usage"; exit 42;;
+ esac
+ ;;
+ remove)
+ sudo_or_exit
+ case $2 in
+ ''|--|containers) remove_containers ;;
+ images) lxc_delete_images_localy ;;
+ ${LXC_HOST_PREFIX}-*)
+ ! lxc_exists "$2" && warn_msg "container not yet exists: $2" && exit 0
+ if ask_yn "Do you really want to delete container $2"; then
+ lxc_delete_container "$2"
+ fi
+ ;;
+ *) usage "uknown or missing container <name> $2"; exit 42;;
+ esac
+ ;;
+ start|stop)
+ sudo_or_exit
+ case $2 in
+ ''|--|containers) lxc_cmd "$1" ;;
+ ${LXC_HOST_PREFIX}-*)
+ ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
+ info_msg "lxc $1 $2"
+ lxc "$1" "$2" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+ ;;
+ *) usage "uknown or missing container <name> $2"; exit 42;;
+ esac
+ ;;
+ show)
+ sudo_or_exit
+ case $2 in
+ suite)
+ case $3 in
+ ${LXC_HOST_PREFIX}-*)
+ lxc exec -t "$3" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \
+ | prefix_stdout "[${_BBlue}$3${_creset}] "
+ ;;
+ *) show_suite;;
+ esac
+ ;;
+ images) show_images ;;
+ config)
+ case $3 in
+ ${LXC_HOST_PREFIX}-*)
+ ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
+ lxc config show "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
+ ;;
+ *)
+ rst_title "container configurations"
+ echo
+ lxc list "$LXC_HOST_PREFIX-"
+ echo
+ lxc_cmd config show
+ ;;
+ esac
+ ;;
+ info)
+ case $3 in
+ ${LXC_HOST_PREFIX}-*)
+ ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
+ lxc info "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
+ ;;
+ *)
+ rst_title "container info"
+ echo
+ lxc_cmd info
+ ;;
+ esac
+ ;;
+ *) usage "$_usage"; exit 42;;
+ esac
+ ;;
+ __show)
+ # wrapped show commands, called once in each container
+ case $2 in
+ suite) lxc_suite_info ;;
+ esac
+ ;;
+ cmd)
+ sudo_or_exit
+ shift
+ case $1 in
+ --) shift; lxc_exec "$@" ;;
+ ${LXC_HOST_PREFIX}-*)
+ ! lxc_exists "$1" && usage_containers "unknown container: $1" && exit 42
+ local name=$1
+ shift
+ lxc_exec_cmd "${name}" "$@"
+ ;;
+ *) usage_containers "unknown container: $1" && exit 42
+ esac
+ ;;
+ install)
+ sudo_or_exit
+ case $2 in
+ suite|base)
+ case $3 in
+ ${LXC_HOST_PREFIX}-*)
+ ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
+ lxc_exec_cmd "$3" "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
+ ;;
+ ''|--) lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2" ;;
+ *) usage_containers "unknown container: $3" && exit 42
+ esac
+ ;;
+ *) usage "$_usage"; exit 42 ;;
+ esac
+ ;;
+ __install)
+ # wrapped install commands, called once in each container
+ # shellcheck disable=SC2119
+ case $2 in
+ suite) lxc_suite_install ;;
+ base) FORCE_TIMEOUT=0 lxc_install_base_packages ;;
+ esac
+ ;;
+ doc)
+ echo
+ echo ".. generic utils/lxc.sh documentation"
+ ;;
+ -*) usage "unknown option $1"; exit 42;;
+ *) usage "unknown or missing command $1"; exit 42;;
+ esac
+}
+
+
+build_all_containers() {
+ rst_title "Build all LXC containers of suite"
+ echo
+ usage_containers
+ lxc_copy_images_localy
+ lxc_init_all_containers
+ lxc_config_all_containers
+ lxc_boilerplate_all_containers
+ rst_title "install LXC base packages" section
+ echo
+ lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install base
+ echo
+ lxc list "$LXC_HOST_PREFIX"
+}
+
+build_container() {
+ rst_title "Build container $1"
+
+ local remote_image
+ local container
+ local image
+ local boilerplate_script
+
+ for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+ if [ "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}" = "$1" ]; then
+ remote_image="${LXC_SUITE[i]}"
+ container="${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}"
+ image="${LXC_SUITE[i+1]}"
+ boilerplate_script="${image}_boilerplate"
+ boilerplate_script="${!boilerplate_script}"
+ break
+ fi
+ done
+ echo
+ if [ -z "$container" ]; then
+ err_msg "container $1 unknown"
+ usage_containers
+ return 42
+ fi
+ lxc_image_copy "${remote_image}" "${image}"
+ rst_title "init container" section
+ lxc_init_container "${image}" "${container}"
+ rst_title "configure container" section
+ lxc_config_container "${container}"
+ rst_title "run LXC boilerplate scripts" section
+ lxc_install_boilerplate "${container}" "$boilerplate_script"
+ echo
+ rst_title "install LXC base packages" section
+ lxc_exec_cmd "${container}" "${LXC_REPO_ROOT}/utils/lxc.sh" __install base \
+ | prefix_stdout "[${_BBlue}${container}${_creset}] "
+ echo
+ lxc list "$container"
+}
+
+remove_containers() {
+ rst_title "Remove all LXC containers of suite"
+ rst_para "existing containers matching ${_BGreen}$LXC_HOST_PREFIX-*${_creset}"
+ echo
+ lxc list "$LXC_HOST_PREFIX-"
+ echo -en "\\n${_BRed}LXC containers to delete::${_creset}\\n\\n ${CONTAINERS[*]}\\n" | $FMT
+ local default=Ny
+ [[ $FORCE_TIMEOUT = 0 ]] && default=Yn
+ if ask_yn "Do you really want to delete these containers" $default; then
+ for i in "${CONTAINERS[@]}"; do
+ lxc_delete_container "$i"
+ done
+ fi
+ echo
+ lxc list "$LXC_HOST_PREFIX-"
+}
+
+# images
+# ------
+
+lxc_copy_images_localy() {
+ rst_title "copy images" section
+ for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+ lxc_image_copy "${LXC_SUITE[i]}" "${LXC_SUITE[i+1]}"
+ done
+ # lxc image list local: && wait_key
+}
+
+lxc_delete_images_localy() {
+ rst_title "Delete LXC images"
+ rst_para "local existing images"
+ echo
+ lxc image list local:
+ echo -en "\\n${_BRed}LXC images to delete::${_creset}\\n\\n ${LOCAL_IMAGES[*]}\\n"
+ if ask_yn "Do you really want to delete these images"; then
+ for i in "${LOCAL_IMAGES[@]}"; do
+ lxc_delete_local_image "$i"
+ done
+ fi
+
+ for i in $(lxc image list --format csv | grep '^,' | sed 's/,\([^,]*\).*$/\1/'); do
+ if ask_yn "Image $i has no alias, do you want to delete the image?" Yn; then
+ lxc_delete_local_image "$i"
+ fi
+ done
+
+ echo
+ lxc image list local:
+}
+
+show_images(){
+ rst_title "local images"
+ echo
+ lxc image list local:
+ echo -en "\\n${_Green}LXC suite images::${_creset}\\n\\n ${LOCAL_IMAGES[*]}\\n"
+ wait_key
+ for i in "${LOCAL_IMAGES[@]}"; do
+ if lxc_image_exists "$i"; then
+ info_msg "lxc image info ${_BBlue}${i}${_creset}"
+ lxc image info "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+ else
+ warn_msg "image ${_BBlue}$i${_creset} does not yet exists"
+ fi
+ done
+
+}
+
+
+# container
+# ---------
+
+show_suite(){
+ rst_title "LXC suite ($LXC_HOST_PREFIX-*)"
+ echo
+ lxc list "$LXC_HOST_PREFIX-"
+ echo
+ for i in "${CONTAINERS[@]}"; do
+ if ! lxc_exists "$i"; then
+ warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
+ else
+ lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \
+ | prefix_stdout "[${_BBlue}${i}${_creset}] "
+ echo
+ fi
+ done
+}
+
+lxc_cmd() {
+ for i in "${CONTAINERS[@]}"; do
+ if ! lxc_exists "$i"; then
+ warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
+ else
+ info_msg "lxc $* $i"
+ lxc "$@" "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+ fi
+ done
+}
+
+lxc_exec_cmd() {
+ local name="$1"
+ shift
+ exit_val=
+ info_msg "[${_BBlue}${name}${_creset}] ${_BGreen}${*}${_creset}"
+ lxc exec -t --cwd "${LXC_REPO_ROOT}" "${name}" -- bash -c "$*"
+ exit_val=$?
+ if [[ $exit_val -ne 0 ]]; then
+ warn_msg "[${_BBlue}${name}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
+ else
+ info_msg "[${_BBlue}${name}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"
+ fi
+}
+
+lxc_exec() {
+ for i in "${CONTAINERS[@]}"; do
+ if ! lxc_exists "$i"; then
+ warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
+ else
+ lxc_exec_cmd "${i}" "$@" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+ fi
+ done
+}
+
+lxc_init_all_containers() {
+ rst_title "init all containers" section
+
+ local image_name
+ local container_name
+
+ for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+ lxc_init_container "${LXC_SUITE[i+1]}" "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}"
+ done
+}
+
+lxc_config_all_containers() {
+ rst_title "configure all containers" section
+
+ for i in "${CONTAINERS[@]}"; do
+ lxc_config_container "${i}"
+ done
+}
+
+lxc_config_container() {
+ info_msg "[${_BBlue}$1${_creset}] configure container ..."
+
+ info_msg "[${_BBlue}$1${_creset}] map uid/gid from host to container"
+ # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps
+ echo -e -n "uid $HOST_USER_ID 0\\ngid $HOST_GROUP_ID 0"\
+ | lxc config set "$1" raw.idmap -
+
+ info_msg "[${_BBlue}$1${_creset}] share ${REPO_ROOT} (repo_share) from HOST into container"
+ # https://lxd.readthedocs.io/en/latest/instances/#type-disk
+ lxc config device add "$1" repo_share disk \
+ source="${REPO_ROOT}" \
+ path="${LXC_REPO_ROOT}" &>/dev/null
+ # lxc config show "$1" && wait_key
+}
+
+lxc_boilerplate_all_containers() {
+ rst_title "run LXC boilerplate scripts" section
+
+ local boilerplate_script
+ local image_name
+
+ for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+
+ image_name="${LXC_SUITE[i+1]}"
+ boilerplate_script="${image_name}_boilerplate"
+ boilerplate_script="${!boilerplate_script}"
+
+ lxc_install_boilerplate "${LXC_HOST_PREFIX}-${image_name}" "$boilerplate_script"
+
+ if [[ -z "${boilerplate_script}" ]]; then
+ err_msg "[${_BBlue}${container_name}${_creset}] no boilerplate for image '${image_name}'"
+ fi
+ done
+}
+
+lxc_install_boilerplate() {
+
+ # usage: lxc_install_boilerplate <container-name> <string: shell commands ..>
+ #
+ # usage: lxc_install_boilerplate searx-archlinux "${archlinux_boilerplate}"
+
+ local container_name="$1"
+ local boilerplate_script="$2"
+
+ info_msg "[${_BBlue}${container_name}${_creset}] init .."
+ if lxc start -q "${container_name}" &>/dev/null; then
+ sleep 5 # guest needs some time to come up and get an IP
+ fi
+ lxc_init_container_env "${container_name}"
+ info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."
+ cat <<EOF | lxc exec "${container_name}" -- bash | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
+rm -f "/.lxcenv.mk"
+ln -s "${LXC_REPO_ROOT}/utils/makefile.lxc" "/.lxcenv.mk"
+ls -l "/.lxcenv.mk"
+EOF
+
+ info_msg "[${_BBlue}${container_name}${_creset}] run LXC boilerplate scripts .."
+ if lxc start -q "${container_name}" &>/dev/null; then
+ sleep 5 # guest needs some time to come up and get an IP
+ fi
+ if [[ -n "${boilerplate_script}" ]]; then
+ echo "${boilerplate_script}" \
+ | lxc exec "${container_name}" -- bash \
+ | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
+ fi
+}
+
+
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------
diff --git a/utils/makefile.include b/utils/makefile.include
index 716889c02..65aca70f0 100644
--- a/utils/makefile.include
+++ b/utils/makefile.include
@@ -1,12 +1,25 @@
# -*- coding: utf-8; mode: makefile-gmake -*-
+ifeq (,$(wildcard /.lxcenv.mk))
+PHONY += lxc-activate lxc-purge
+lxc-activate:
+ @$(MAKE) -s -f /share/searx/utils/makefile.lxc lxc-activate
+lxc-purge:
+ $(Q)rm -rf ./lxc
+else
+ include /.lxcenv.mk
+endif
+
+ifeq (,$(wildcard /.lxcenv.mk))
make-help:
+else
+make-help: lxc-help
+endif
@echo ' make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
@echo ' make V=2 [targets] 2 => give reason for rebuild of target'
quiet_cmd_common_clean = CLEAN $@
cmd_common_clean = \
- rm -rf tests/build ;\
find . -name '*.orig' -exec rm -f {} + ;\
find . -name '*.rej' -exec rm -f {} + ;\
find . -name '*~' -exec rm -f {} + ;\
@@ -126,3 +139,4 @@ echo-cmd = $(if $($(quiet)cmd_$(1)),echo '$(call escsq,$($(quiet)cmd_$(1)))$(ech
# printing commands
cmd = @$(echo-cmd) $(cmd_$(1))
+.PHONY: $(PHONY)
diff --git a/utils/makefile.lxc b/utils/makefile.lxc
new file mode 100644
index 000000000..fd999e40d
--- /dev/null
+++ b/utils/makefile.lxc
@@ -0,0 +1,29 @@
+# -*- coding: utf-8; mode: makefile-gmake -*-
+#
+# LXC environment
+# ===============
+#
+# To activate/deactivate LXC makefile environment in a container, set/unset link
+# from root '/.lxcenv.mk' to *this* file::
+#
+# sudo make ./utils/makefile.lxc lxc-activate
+# sudo make ./utils/makefile.lxc lxc-deactivate
+
+LXC_ENV_FOLDER=lxc/$(shell hostname)/
+
+lxc-help::
+ @echo 'LXC: running in container LXC_ENV_FOLDER=$(LXC_ENV_FOLDER)'
+
+# If not activated, serve target 'lxc-activate' ..
+ifeq (,$(wildcard /.lxcenv.mk))
+PHONY += lxc-activate
+lxc-activate:
+ ln -s "$(abspath $(lastword $(MAKEFILE_LIST)))" "/.lxcenv.mk"
+else
+# .. and if activated, serve target 'lxc-deactivate'.
+PHONY += lxc-deactivate
+lxc-deactivate:
+ rm /.lxcenv.mk
+endif
+
+.PHONY: $(PHONY)
diff --git a/utils/makefile.python b/utils/makefile.python
index 590bbdb46..df16acbbf 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -8,9 +8,9 @@ export PYTHONPATH := $(SITE_PYTHON):$$PYTHONPATH
export PY_ENV PYDIST PYBUILD
# folder where the python distribution takes place
-PYDIST ?= ./py_dist
+PYDIST = ./$(LXC_ENV_FOLDER)dist
# folder where the python intermediate build files take place
-PYBUILD ?= ./py_build
+PYBUILD = ./$(LXC_ENV_FOLDER)build
# python version to use
PY ?=3
# $(PYTHON) points to the python interpreter from the OS! The python from the
@@ -30,8 +30,7 @@ PYLINT_RC ?= .pylintrc
TEST_FOLDER ?= ./tests
TEST ?= .
-VTENV_OPTS = "--no-site-packages"
-PY_ENV = ./local/py$(PY)
+PY_ENV = ./$(LXC_ENV_FOLDER)local/py$(PY)
PY_ENV_BIN = $(PY_ENV)/bin
PY_ENV_ACT = . $(PY_ENV_BIN)/activate
@@ -41,6 +40,7 @@ ifeq ($(OS),Windows_NT)
PY_ENV_ACT = $(PY_ENV_BIN)/activate
endif
+VTENV_OPTS ?=
ifeq ($(PYTHON),python)
VIRTUALENV = virtualenv
else
diff --git a/utils/makefile.sphinx b/utils/makefile.sphinx
index 2c1922fc9..1b0f42ccb 100644
--- a/utils/makefile.sphinx
+++ b/utils/makefile.sphinx
@@ -1,17 +1,19 @@
# -*- coding: utf-8; mode: makefile-gmake -*-
+export DOCS_FOLDER DOCS_BUILD DOCS_DIST BOOKS_FOLDER BOOKS_DIST
+
# You can set these variables from the command line.
SPHINXOPTS ?=
SPHINXBUILD ?= $(PY_ENV_BIN)/sphinx-build
SPHINX_CONF ?= conf.py
-DOCS_FOLDER ?= docs
-DOCS_BUILD ?= build/docs
-DOCS_DIST ?= dist/docs
+DOCS_FOLDER = ./docs
+DOCS_BUILD = ./$(LXC_ENV_FOLDER)build/docs
+DOCS_DIST = ./$(LXC_ENV_FOLDER)dist/docs
GH_PAGES ?= gh-pages
-BOOKS_FOLDER ?= docs
-BOOKS_DIST ?= dist/books
+BOOKS_FOLDER = ./docs
+BOOKS_DIST = ./$(LXC_ENV_FOLDER)dist/books
ifeq ($(KBUILD_VERBOSE),1)
SPHINX_VERBOSE = "-v"
@@ -54,11 +56,13 @@ docs-help:
# requirements
# ------------------------------------------------------------------------------
-sphinx-doc: $(PY_ENV)
+sphinx-doc-prebuilds:: $(PY_ENV)
+
+sphinx-doc: sphinx-doc-prebuilds
@echo "PYENV installing Sphinx$(SPHINXVERS)"
$(Q)$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) 'Sphinx$(SPHINXVERS)'
-sphinx-live: $(PY_ENV)
+sphinx-live: sphinx-doc-prebuilds
@echo "PYENV installing Sphinx$(SPHINXVERS)"
$(Q)$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) 'Sphinx$(SPHINXVERS)' sphinx-autobuild
@@ -113,7 +117,7 @@ quiet_cmd_sphinx_clean = CLEAN $@
# targets
# ------------------------------------------------------------------------------
-# build PDF of whole documentation in: $(DOCS_DIST)/pdf
+# build PDF of whole documentation in: $(DOCS_DIST)/pdf
PHONY += sphinx-pdf
sphinx-pdf: sphinx-latex
@@ -154,7 +158,7 @@ $(BOOKS_HTML): sphinx-doc | $(BOOKS_DIST)
-b html \
-c $(DOCS_FOLDER) \
-d $(DOCS_BUILD)/books/$(patsubst books/%.html,%,$@)/.doctrees \
- $(patsubst books/%.html,%,$@) \
+ $(BOOKS_FOLDER)/$(patsubst books/%.html,%,$@) \
$(BOOKS_DIST)/$(patsubst books/%.html,%,$@)
@echo "SPHINX $@ --> file://$(abspath $(BOOKS_DIST)/$(patsubst books/%.html,%,$@))"
@@ -166,7 +170,7 @@ $(BOOKS_LIVE): sphinx-live | $(BOOKS_DIST)
-b html \
-c $(DOCS_FOLDER) \
-d $(DOCS_BUILD)/books/$(patsubst books/%.live,%,$@)/.doctrees \
- $(patsubst books/%.live,%,$@) \
+ $(BOOKS_FOLDER)/$(patsubst books/%.live,%,$@) \
$(BOOKS_DIST)/$(patsubst books/%.live,%,$@)
$(BOOKS_PDF): %.pdf : %.latex
@@ -182,7 +186,7 @@ $(BOOKS_LATEX): sphinx-doc | $(BOOKS_DIST)
-b latex \
-c $(DOCS_FOLDER) \
-d $(DOCS_BUILD)/books/$(patsubst books/%.latex,%,$@)/.doctrees \
- $(patsubst books/%.latex,%,$@) \
+ $(BOOKS_FOLDER)/$(patsubst books/%.latex,%,$@) \
$(DOCS_BUILD)/latex/$(patsubst books/%.latex,%,$@)
@echo "SPHINX $@ --> file://$(abspath $(DOCS_BUILD)/latex/$(patsubst books/%.latex,%,$@))"
diff --git a/utils/morty.sh b/utils/morty.sh
new file mode 100755
index 000000000..75bfeeedf
--- /dev/null
+++ b/utils/morty.sh
@@ -0,0 +1,546 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# shellcheck source=utils/lib.sh
+source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+# shellcheck source=utils/brand.env
+source "${REPO_ROOT}/utils/brand.env"
+source_dot_config
+SEARX_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+source "${REPO_ROOT}/utils/lxc-searx.env"
+in_container && lxc_set_suite_env
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
+PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty/}"
+
+PUBLIC_URL_MORTY="${PUBLIC_URL_MORTY:-$(echo "$SEARX_URL" | sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}}"
+
+# shellcheck disable=SC2034
+MORTY_TIMEOUT=5
+
+SERVICE_NAME="morty"
+SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
+SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
+SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
+SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
+# shellcheck disable=SC2034
+SERVICE_ENV_DEBUG=false
+
+GO_ENV="${SERVICE_HOME}/.go_env"
+GO_PKG_URL="https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
+GO_TAR=$(basename "$GO_PKG_URL")
+
+# shellcheck disable=SC2034
+CONFIG_FILES=()
+
+# Apache Settings
+
+APACHE_MORTY_SITE="morty.conf"
+NGINX_MORTY_SITE="morty.conf"
+
+# ----------------------------------------------------------------------------
+usage() {
+# ----------------------------------------------------------------------------
+
+ # shellcheck disable=SC1117
+ cat <<EOF
+usage::
+ $(basename "$0") shell
+ $(basename "$0") install [all|user]
+ $(basename "$0") update [morty]
+ $(basename "$0") remove [all]
+ $(basename "$0") activate [service]
+ $(basename "$0") deactivate [service]
+ $(basename "$0") inspect [service]
+ $(basename "$0") option [debug-on|debug-off|new-key]
+ $(basename "$0") apache [install|remove]
+ $(basename "$0") nginx [install|remove]
+ $(basename "$0") info [searx]
+
+shell
+ start interactive shell from user ${SERVICE_USER}
+install / remove
+ all: complete setup of morty service
+ user: add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
+update morty
+ Update morty installation ($SERVICE_HOME)
+activate service
+ activate and start service daemon (systemd unit)
+deactivate service
+ stop and deactivate service daemon (systemd unit)
+inspect service
+ show service status and log
+option
+ set one of the available options
+ :new-key: set new morty key
+apache : ${PUBLIC_URL_MORTY}
+ :install: apache site with a reverse proxy (ProxyPass)
+ :remove: apache site ${APACHE_MORTY_SITE}
+nginx (${PUBLIC_URL_MORTY})
+ :install: nginx site with a reverse proxy (ProxyPass)
+ :remove: nginx site ${NGINX_MORTY_SITE}
+
+If needed, set the environment variables in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
+ PUBLIC_URL_MORTY: ${PUBLIC_URL_MORTY}
+ MORTY_LISTEN: ${MORTY_LISTEN}
+ SERVICE_USER: ${SERVICE_USER}
+EOF
+ if in_container; then
+ # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
+ for ip in $(global_IPs) ; do
+ if [[ $ip =~ .*:.* ]]; then
+ echo " container URL (IPv6): http://[${ip#*|}]:3000/"
+ else
+ # IPv4:
+ echo " container URL (IPv4): http://${ip#*|}:3000/"
+ fi
+ done
+ fi
+ echo
+ info_searx
+
+ [[ -n ${1} ]] && err_msg "$1"
+}
+
+info_searx() {
+ # shellcheck disable=SC1117
+ cat <<EOF
+To activate result and image proxy in searx, edit settings.yml (read:
+${DOCS_URL}/admin/morty.html)::
+ result_proxy:
+ url : ${PUBLIC_URL_MORTY}
+ server:
+ image_proxy : True
+EOF
+}
+
+main() {
+ required_commands \
+ sudo install git wget curl \
+ || exit
+
+ local _usage="ERROR: unknown or missing $1 command $2"
+
+ case $1 in
+ --getenv) var="$2"; echo "${!var}"; exit 0;;
+ -h|--help) usage; exit 0;;
+
+ shell)
+ sudo_or_exit
+ interactive_shell "${SERVICE_USER}"
+ ;;
+ inspect)
+ case $2 in
+ service)
+ sudo_or_exit
+ inspect_service
+ ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ install)
+ rst_title "$SERVICE_NAME" part
+ sudo_or_exit
+ case $2 in
+ all) install_all ;;
+ user) assert_user ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ update)
+ sudo_or_exit
+ case $2 in
+ morty) update_morty ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ remove)
+ sudo_or_exit
+ case $2 in
+ all) remove_all;;
+ user) drop_service_account "${SERVICE_USER}" ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ activate)
+ sudo_or_exit
+ case $2 in
+ service) systemd_activate_service "${SERVICE_NAME}" ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ deactivate)
+ sudo_or_exit
+ case $2 in
+ service) systemd_deactivate_service "${SERVICE_NAME}" ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ apache)
+ sudo_or_exit
+ case $2 in
+ install) install_apache_site ;;
+ remove) remove_apache_site ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ nginx)
+ sudo_or_exit
+ case $2 in
+ install) install_nginx_site ;;
+ remove) remove_nginx_site ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ info)
+ case $2 in
+ searx) info_searx ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ option)
+ sudo_or_exit
+ case $2 in
+ new-key) set_new_key ;;
+ debug-on) enable_debug ;;
+ debug-off) disable_debug ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ doc) rst-doc ;;
+ *) usage "ERROR: unknown or missing command $1"; exit 42;;
+ esac
+}
+
+install_all() {
+
+ MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
+
+ rst_title "Install $SERVICE_NAME (service)"
+ assert_user
+ wait_key
+ install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
+ wait_key
+ install_morty
+ wait_key
+ systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+ wait_key
+ if ! service_is_available "http://${MORTY_LISTEN}" ; then
+ err_msg "Morty does not listening on: http://${MORTY_LISTEN}"
+ fi
+ if apache_is_installed; then
+ info_msg "Apache is installed on this host."
+ if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+ install_apache_site
+ fi
+ elif nginx_is_installed; then
+ info_msg "nginx is installed on this host."
+ if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+ install_nginx_site
+ fi
+ fi
+ info_searx
+ if ask_yn "Add image and result proxy to searx settings.yml?" Yn; then
+ "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
+ "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
+ fi
+
+ if ask_yn "Do you want to inspect the installation?" Ny; then
+ inspect_service
+ fi
+
+}
+
+remove_all() {
+ rst_title "De-Install $SERVICE_NAME (service)"
+
+ rst_para "\
+It goes without saying that this script can only be used to remove
+installations that were installed with this script."
+
+ if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
+ drop_service_account "${SERVICE_USER}"
+ fi
+}
+
+assert_user() {
+ rst_title "user $SERVICE_USER" section
+ echo
+ tee_stderr 1 <<EOF | bash | prefix_stdout
+useradd --shell /bin/bash --system \
+ --home-dir "$SERVICE_HOME" \
+ --comment 'Web content sanitizer proxy' $SERVICE_USER
+mkdir "$SERVICE_HOME"
+chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+groups $SERVICE_USER
+EOF
+ SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
+ export SERVICE_HOME
+ echo "export SERVICE_HOME=$SERVICE_HOME"
+
+ cat > "$GO_ENV" <<EOF
+export GOPATH=\$HOME/go-apps
+export PATH=\$PATH:\$HOME/local/go/bin:\$GOPATH/bin
+EOF
+ echo "Environment $GO_ENV has been setup."
+
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER"
+grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
+EOF
+}
+
+morty_is_installed() {
+ [[ -f $SERVICE_HOME/go-apps/bin/morty ]]
+}
+
+_svcpr=" ${_Yellow}|${SERVICE_USER}|${_creset} "
+
+install_morty() {
+ rst_title "Install morty in user's ~/go-apps" section
+ echo
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+go get -v -u github.com/asciimoo/morty
+EOF
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+cd \$GOPATH/src/github.com/asciimoo/morty
+go test
+go test -benchmem -bench .
+EOF
+}
+
+update_morty() {
+ rst_title "Update morty" section
+ echo
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+go get -v -u github.com/asciimoo/morty
+EOF
+ tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+cd \$GOPATH/src/github.com/asciimoo/morty
+go test
+go test -benchmem -bench .
+EOF
+}
+
+set_service_env_debug() {
+
+ # usage: set_service_env_debug [false|true]
+
+ # shellcheck disable=SC2034
+ local SERVICE_ENV_DEBUG="${1:-false}"
+ if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
+ systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+ fi
+}
+
+inspect_service() {
+
+ rst_title "service status & log"
+
+ cat <<EOF
+
+sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
+
+ MORTY_LISTEN : ${MORTY_LISTEN}
+
+EOF
+
+ if service_account_is_available "$SERVICE_USER"; then
+ info_msg "service account $SERVICE_USER available."
+ else
+ err_msg "service account $SERVICE_USER not available!"
+ fi
+ if go_is_available "$SERVICE_USER"; then
+ info_msg "~$SERVICE_USER: go is installed"
+ else
+ err_msg "~$SERVICE_USER: go is not installed"
+ fi
+ if morty_is_installed; then
+ info_msg "~$SERVICE_USER: morty app is installed"
+ else
+ err_msg "~$SERVICE_USER: morty app is not installed!"
+ fi
+
+ if ! service_is_available "http://${MORTY_LISTEN}" ; then
+ err_msg "Morty does not listening on: http://${MORTY_LISTEN}"
+ echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
+ wait_key
+ fi
+
+ if ! service_is_available "${PUBLIC_URL_MORTY}"; then
+ warn_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
+ if ! in_container; then
+ warn_msg "Check if public name is correct and routed or use the public IP from above."
+ fi
+ fi
+
+ if in_container; then
+ lxc_suite_info
+ else
+ info_msg "public URL --> ${PUBLIC_URL_MORTY}"
+ info_msg "morty URL --> http://${MORTY_LISTEN}"
+ fi
+
+ local _debug_on
+ if ask_yn "Enable morty debug mode (needs reinstall of systemd service)?"; then
+ enable_debug
+ _debug_on=1
+ else
+ systemctl --no-pager -l status "${SERVICE_NAME}"
+ fi
+ echo
+
+ # shellcheck disable=SC2059
+ printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
+ read -r -s -n1 -t 5
+ echo
+ while true; do
+ trap break 2
+ journalctl -f -u "${SERVICE_NAME}"
+ done
+
+ if [[ $_debug_on == 1 ]]; then
+ FORCE_SELECTION=Y disable_debug
+ fi
+ return 0
+}
+
+enable_debug() {
+ warn_msg "Do not enable debug in production enviroments!!"
+ info_msg "Enabling debug option needs to reinstall systemd service!"
+ set_service_env_debug true
+}
+
+disable_debug() {
+ info_msg "Disabling debug option needs to reinstall systemd service!"
+ set_service_env_debug false
+}
+
+
+set_new_key() {
+ rst_title "Set morty key"
+ echo
+
+ MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
+ info_msg "morty key: '${MORTY_KEY}'"
+
+ warn_msg "this will need to reinstall services .."
+ MSG="${_Green}press any [${_BCyan}KEY${_Green}] to continue // stop with [${_BCyan}CTRL-C${_creset}]" wait_key
+
+ systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+ "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
+ "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
+}
+
+
+install_apache_site() {
+
+ rst_title "Install Apache site $APACHE_MORTY_SITE"
+
+ rst_para "\
+This installs a reverse proxy (ProxyPass) into apache site (${APACHE_MORTY_SITE})"
+
+ ! apache_is_installed && err_msg "Apache is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ else
+ install_apache
+ fi
+
+ apache_install_site "${APACHE_MORTY_SITE}"
+
+ info_msg "testing public url .."
+ if ! service_is_available "${PUBLIC_URL_MORTY}"; then
+ err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
+ fi
+}
+
+remove_apache_site() {
+
+ rst_title "Remove Apache site $APACHE_MORTY_SITE"
+
+ rst_para "\
+This removes apache site ${APACHE_MORTY_SITE}."
+
+ ! apache_is_installed && err_msg "Apache is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ fi
+
+ apache_remove_site "$APACHE_MORTY_SITE"
+}
+
+install_nginx_site() {
+
+ rst_title "Install nginx site $NGINX_MORTY_SITE"
+
+ rst_para "\
+This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_MORTY_SITE})"
+
+ ! nginx_is_installed && err_msg "nginx is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ else
+ install_nginx
+ fi
+
+ "${REPO_ROOT}/utils/searx.sh" install uwsgi
+
+ # shellcheck disable=SC2034
+ SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
+ # shellcheck disable=SC2034
+ SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
+ nginx_install_app "${NGINX_MORTY_SITE}"
+
+ info_msg "testing public url .."
+ if ! service_is_available "${PUBLIC_URL_MORTY}"; then
+ err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
+ fi
+}
+
+remove_nginx_site() {
+
+ rst_title "Remove nginx site $NGINX_MORTY_SITE"
+
+ rst_para "\
+This removes nginx site ${NGINX_MORTY_SITE}."
+
+ ! nginx_is_installed && err_msg "nginx is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ fi
+
+ nginx_remove_site "$NGINX_MORTY_SITE"
+
+}
+
+rst-doc() {
+
+ eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/morty.rst")\""
+
+ echo -e "\n.. START install systemd unit"
+ cat <<EOF
+.. tabs::
+
+ .. group-tab:: systemd
+
+ .. code:: bash
+
+EOF
+ eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
+ echo -e "\n.. END install systemd unit"
+
+ # for DIST_NAME in ubuntu-20.04 arch fedora; do
+ # (
+ # DIST_ID=${DIST_NAME%-*}
+ # DIST_VERS=${DIST_NAME#*-}
+ # [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
+ # # ...
+ # )
+ # done
+}
+
+
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------
diff --git a/utils/searx.sh b/utils/searx.sh
new file mode 100755
index 000000000..4009fddb0
--- /dev/null
+++ b/utils/searx.sh
@@ -0,0 +1,869 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck disable=SC2001
+
+# shellcheck source=utils/lib.sh
+source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+# shellcheck source=utils/brand.env
+source "${REPO_ROOT}/utils/brand.env"
+source_dot_config
+source "${REPO_ROOT}/utils/lxc-searx.env"
+in_container && lxc_set_suite_env
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+
+SEARX_INTERNAL_HTTP="${SEARX_INTERNAL_HTTP:-127.0.0.1:8888}"
+
+SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "${PUBLIC_URL}" \
+| sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
+[[ "${SEARX_URL_PATH}" == "${PUBLIC_URL}" ]] && SEARX_URL_PATH=/
+SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(echo "$PUBLIC_URL" \
+| sed -e 's,^.*://\([^\:/]*\).*,\1,g') }"
+
+SERVICE_NAME="searx"
+SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
+SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
+SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
+
+GIT_BRANCH="${GIT_BRANCH:-master}"
+SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
+SEARX_SRC="${SERVICE_HOME}/searx-src"
+SEARX_SETTINGS_PATH="/etc/searx/settings.yml"
+SEARX_UWSGI_APP="searx.ini"
+# shellcheck disable=SC2034
+SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
+
+# apt packages
+SEARX_PACKAGES_debian="\
+virtualenv python3-dev python3-babel python3-venv
+uwsgi uwsgi-plugin-python3
+git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev
+shellcheck"
+
+BUILD_PACKAGES_debian="\
+firefox graphviz imagemagick texlive-xetex librsvg2-bin
+texlive-latex-recommended texlive-extra-utils ttf-dejavu
+latexmk"
+
+# pacman packages
+SEARX_PACKAGES_arch="\
+python-virtualenv python python-pip python-lxml python-babel
+uwsgi uwsgi-plugin-python
+git base-devel libxml2
+shellcheck"
+
+BUILD_PACKAGES_arch="\
+firefox graphviz imagemagick texlive-bin extra/librsvg
+texlive-core texlive-latexextra ttf-dejavu"
+
+# dnf packages
+SEARX_PACKAGES_fedora="\
+virtualenv python python-pip python-lxml python-babel
+uwsgi uwsgi-plugin-python3
+git @development-tools libxml2
+ShellCheck"
+
+BUILD_PACKAGES_fedora="\
+firefox graphviz graphviz-gd ImageMagick librsvg2-tools
+texlive-xetex-bin texlive-collection-fontsrecommended
+texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
+dejavu-sans-mono-fonts"
+
+case $DIST_ID-$DIST_VERS in
+ ubuntu-16.04|ubuntu-18.04)
+ SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
+ BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
+ APACHE_PACKAGES="$APACHE_PACKAGES libapache2-mod-proxy-uwsgi"
+ ;;
+ ubuntu-20.04)
+ # https://askubuntu.com/a/1224710
+ SEARX_PACKAGES="${SEARX_PACKAGES_debian} python-is-python3"
+ BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
+ ;;
+ ubuntu-*|debian-*)
+ SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
+ BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
+ ;;
+ arch-*)
+ SEARX_PACKAGES="${SEARX_PACKAGES_arch}"
+ BUILD_PACKAGES="${BUILD_PACKAGES_arch}"
+ ;;
+ fedora-*)
+ SEARX_PACKAGES="${SEARX_PACKAGES_fedora}"
+ BUILD_PACKAGES="${BUILD_PACKAGES_fedora}"
+ ;;
+esac
+
+# Apache Settings
+APACHE_SEARX_SITE="searx.conf"
+
+# shellcheck disable=SC2034
+CONFIG_FILES=(
+ "${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}"
+)
+
+# shellcheck disable=SC2034
+CONFIG_BACKUP_ENCRYPTED=(
+ "${SEARX_SETTINGS_PATH}"
+)
+
+# ----------------------------------------------------------------------------
+usage() {
+# ----------------------------------------------------------------------------
+
+ # shellcheck disable=SC1117
+ cat <<EOF
+usage::
+ $(basename "$0") shell
+ $(basename "$0") install [all|user|searx-src|pyenv|uwsgi|packages|buildhost]
+ $(basename "$0") update [searx]
+ $(basename "$0") remove [all|user|pyenv|searx-src]
+ $(basename "$0") activate [service]
+ $(basename "$0") deactivate [service]
+ $(basename "$0") inspect [service]
+ $(basename "$0") option [debug-[on|off]|image-proxy-[on|off]|result-proxy <url> <key>]
+ $(basename "$0") apache [install|remove]
+
+shell
+ start interactive shell from user ${SERVICE_USER}
+install / remove
+ :all: complete (de-) installation of searx service
+ :user: add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
+ :searx-src: clone $GIT_URL
+ :pyenv: create/remove virtualenv (python) in $SEARX_PYENV
+ :uwsgi: install searx uWSGI application
+ :settings: reinstall settings from ${REPO_ROOT}/searx/settings.yml
+ :packages: install needed packages from OS package manager
+ :buildhost: install packages from OS package manager needed by buildhosts
+update searx
+ Update searx installation ($SERVICE_HOME)
+activate service
+ activate and start service daemon (systemd unit)
+deactivate service
+ stop and deactivate service daemon (systemd unit)
+inspect service
+ run some small tests and inspect service's status and log
+option
+ set one of the available options
+apache
+ :install: apache site with the searx uwsgi app
+ :remove: apache site ${APACHE_FILTRON_SITE}
+
+searx settings: ${SEARX_SETTINGS_PATH}
+
+If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
+ PUBLIC_URL : ${PUBLIC_URL}
+ SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
+ SERVICE_USER : ${SERVICE_USER}
+ SEARX_INTERNAL_HTTP : http://${SEARX_INTERNAL_HTTP}
+EOF
+ if in_container; then
+ # searx is listening on 127.0.0.1 and not available from outside container
+ # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
+ echo -e "${_BBlack}HINT:${_creset} searx only listen on loopback device" \
+ "${_BBlack}inside${_creset} the container."
+ for ip in $(global_IPs) ; do
+ if [[ $ip =~ .*:.* ]]; then
+ echo " container (IPv6): [${ip#*|}]"
+ else
+ # IPv4:
+ echo " container (IPv4): ${ip#*|}"
+ fi
+ done
+ fi
+ [[ -n ${1} ]] && err_msg "$1"
+}
+
+main() {
+ required_commands \
+ sudo systemctl install git wget curl \
+ || exit
+
+ local _usage="unknown or missing $1 command $2"
+
+ case $1 in
+ --getenv) var="$2"; echo "${!var}"; exit 0;;
+ -h|--help) usage; exit 0;;
+ shell)
+ sudo_or_exit
+ interactive_shell "${SERVICE_USER}"
+ ;;
+ inspect)
+ case $2 in
+ service)
+ sudo_or_exit
+ inspect_service
+ ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ install)
+ rst_title "$SEARX_INSTANCE_NAME" part
+ sudo_or_exit
+ case $2 in
+ all) install_all ;;
+ user) assert_user ;;
+ pyenv) create_pyenv ;;
+ searx-src) clone_searx ;;
+ settings) install_settings ;;
+ uwsgi)
+ install_searx_uwsgi
+ if ! service_is_available "http://${SEARX_INTERNAL_HTTP}"; then
+ err_msg "URL http://${SEARX_INTERNAL_HTTP} not available, check searx & uwsgi setup!"
+ fi
+ ;;
+ packages)
+ pkg_install "$SEARX_PACKAGES"
+ ;;
+ buildhost)
+ pkg_install "$SEARX_PACKAGES"
+ pkg_install "$BUILD_PACKAGES"
+ ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ update)
+ sudo_or_exit
+ case $2 in
+ searx) update_searx;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ remove)
+ sudo_or_exit
+ case $2 in
+ all) remove_all;;
+ user) drop_service_account "${SERVICE_USER}";;
+ pyenv) remove_pyenv ;;
+ searx-src) remove_searx ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ activate)
+ sudo_or_exit
+ case $2 in
+ service)
+ activate_service ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ deactivate)
+ sudo_or_exit
+ case $2 in
+ service) deactivate_service ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ option)
+ sudo_or_exit
+ case $2 in
+ debug-on) echo; enable_debug ;;
+ debug-off) echo; disable_debug ;;
+ result-proxy) set_result_proxy "$3" "$4" ;;
+ image-proxy-on) enable_image_proxy ;;
+ image-proxy-off) disable_image_proxy ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ apache)
+ sudo_or_exit
+ case $2 in
+ install) install_apache_site ;;
+ remove) remove_apache_site ;;
+ *) usage "$_usage"; exit 42;;
+ esac ;;
+ doc) rst-doc;;
+ *) usage "unknown or missing command $1"; exit 42;;
+ esac
+}
+
+_service_prefix=" ${_Yellow}|$SERVICE_USER|${_creset} "
+
+install_all() {
+ rst_title "Install $SEARX_INSTANCE_NAME (service)"
+ pkg_install "$SEARX_PACKAGES"
+ wait_key
+ assert_user
+ wait_key
+ clone_searx
+ wait_key
+ create_pyenv
+ wait_key
+ install_settings
+ wait_key
+ test_local_searx
+ wait_key
+ install_searx_uwsgi
+ if ! service_is_available "http://${SEARX_INTERNAL_HTTP}"; then
+ err_msg "URL http://${SEARX_INTERNAL_HTTP} not available, check searx & uwsgi setup!"
+ fi
+ if ask_yn "Do you want to inspect the installation?" Ny; then
+ inspect_service
+ fi
+}
+
+update_searx() {
+ rst_title "Update searx instance"
+
+ echo
+ tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+git checkout -B "$GIT_BRANCH"
+git pull
+pip install -U pip
+pip install -U setuptools
+pip install -U wheel
+pip install -U -e .
+EOF
+ install_settings
+ uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+remove_all() {
+ rst_title "De-Install $SEARX_INSTANCE_NAME (service)"
+
+ rst_para "\
+It goes without saying that this script can only be used to remove
+installations that were installed with this script."
+
+ if ! ask_yn "Do you really want to deinstall $SEARX_INSTANCE_NAME?"; then
+ return
+ fi
+ remove_searx_uwsgi
+ drop_service_account "${SERVICE_USER}"
+ remove_settings
+ wait_key
+ if service_is_available "${PUBLIC_URL}"; then
+ MSG="** Don't forgett to remove your public site! (${PUBLIC_URL}) **" wait_key 10
+ fi
+}
+
+assert_user() {
+ rst_title "user $SERVICE_USER" section
+ echo
+ tee_stderr 1 <<EOF | bash | prefix_stdout
+useradd --shell /bin/bash --system \
+ --home-dir "$SERVICE_HOME" \
+ --comment 'Privacy-respecting metasearch engine' $SERVICE_USER
+mkdir "$SERVICE_HOME"
+chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+groups $SERVICE_USER
+EOF
+ #SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
+ #export SERVICE_HOME
+ #echo "export SERVICE_HOME=$SERVICE_HOME"
+}
+
+clone_is_available() {
+ [[ -f "$SEARX_SRC/.git/config" ]]
+}
+
+# shellcheck disable=SC2164
+clone_searx() {
+ rst_title "Clone searx sources" section
+ echo
+ if ! sudo -i -u "$SERVICE_USER" ls -d "$REPO_ROOT" > /dev/null; then
+ die 42 "user '$SERVICE_USER' missed read permission: $REPO_ROOT"
+ fi
+ SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME 2>/dev/null)"
+ if [[ ! "${SERVICE_HOME}" ]]; then
+ err_msg "to clone searx sources, user $SERVICE_USER hast to be created first"
+ return 42
+ fi
+ export SERVICE_HOME
+ git_clone "$REPO_ROOT" "$SEARX_SRC" \
+ "$GIT_BRANCH" "$SERVICE_USER"
+
+ pushd "${SEARX_SRC}" > /dev/null
+ tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+cd "${SEARX_SRC}"
+git remote set-url origin ${GIT_URL}
+git config user.email "$ADMIN_EMAIL"
+git config user.name "$ADMIN_NAME"
+git config --list
+EOF
+ popd > /dev/null
+}
+
+install_settings() {
+ rst_title "${SEARX_SETTINGS_PATH}" section
+ if ! clone_is_available; then
+ err_msg "you have to install searx first"
+ exit 42
+ fi
+ mkdir -p "$(dirname ${SEARX_SETTINGS_PATH})"
+
+ if [[ ! -f ${SEARX_SETTINGS_PATH} ]]; then
+ info_msg "install settings ${REPO_ROOT}/searx/settings.yml"
+ info_msg " --> ${SEARX_SETTINGS_PATH}"
+ cp "${REPO_ROOT}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+ configure_searx
+ return
+ fi
+
+ rst_para "Diff between origin's setting file (+) and current (-):"
+ echo
+ $DIFF_CMD "${SEARX_SETTINGS_PATH}" "${SEARX_SRC}/searx/settings.yml"
+
+ local action
+ choose_one action "What should happen to the settings file? " \
+ "keep configuration unchanged" \
+ "use origin settings" \
+ "start interactiv shell"
+ case $action in
+ "keep configuration unchanged")
+ info_msg "leave settings file unchanged"
+ ;;
+ "use origin settings")
+ backup_file "${SEARX_SETTINGS_PATH}"
+ info_msg "install origin settings"
+ cp "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+ ;;
+ "start interactiv shell")
+ backup_file "${SEARX_SETTINGS_PATH}"
+ echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
+ sudo -H -i
+ rst_para 'Diff between new setting file (-) and current (+):'
+ echo
+ $DIFF_CMD "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+ wait_key
+ ;;
+ esac
+}
+
+remove_settings() {
+ rst_title "remove searx settings" section
+ echo
+ info_msg "delete ${SEARX_SETTINGS_PATH}"
+ rm -f "${SEARX_SETTINGS_PATH}"
+}
+
+remove_searx() {
+ rst_title "Drop searx sources" section
+ if ask_yn "Do you really want to drop searx sources ($SEARX_SRC)?"; then
+ rm -rf "$SEARX_SRC"
+ else
+ rst_para "Leave searx sources unchanged."
+ fi
+}
+
+pyenv_is_available() {
+ [[ -f "${SEARX_PYENV}/bin/activate" ]]
+}
+
+create_pyenv() {
+ rst_title "Create virtualenv (python)" section
+ echo
+ if [[ ! -f "${SEARX_SRC}/manage.sh" ]]; then
+ err_msg "to create pyenv for searx, searx has to be cloned first"
+ return 42
+ fi
+ info_msg "create pyenv in ${SEARX_PYENV}"
+ tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+rm -rf "${SEARX_PYENV}"
+python3 -m venv "${SEARX_PYENV}"
+grep -qFs -- 'source ${SEARX_PYENV}/bin/activate' ~/.profile \
+ || echo 'source ${SEARX_PYENV}/bin/activate' >> ~/.profile
+EOF
+ info_msg "inspect python's virtual environment"
+ tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+command -v python && python --version
+EOF
+ wait_key
+ info_msg "install needed python packages"
+ tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+pip install -U pip
+pip install -U setuptools
+pip install -U wheel
+pip install -U -e .
+cd ${SEARX_SRC}
+pip install -e .
+EOF
+}
+
+remove_pyenv() {
+ rst_title "Remove virtualenv (python)" section
+ if ! ask_yn "Do you really want to drop ${SEARX_PYENV} ?"; then
+ return
+ fi
+ info_msg "remove pyenv activation from ~/.profile"
+ tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+grep -v 'source ${SEARX_PYENV}/bin/activate' ~/.profile > ~/.profile.##
+mv ~/.profile.## ~/.profile
+EOF
+ rm -rf "${SEARX_PYENV}"
+}
+
+configure_searx() {
+ rst_title "Configure searx" section
+ rst_para "Setup searx config located at $SEARX_SETTINGS_PATH"
+ echo
+ tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "$SEARX_SETTINGS_PATH"
+sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS_PATH"
+EOF
+}
+
+test_local_searx() {
+ rst_title "Testing searx instance localy" section
+ echo
+
+ if service_is_available "http://${SEARX_INTERNAL_HTTP}" &>/dev/null; then
+ err_msg "URL/port http://${SEARX_INTERNAL_HTTP} is already in use, you"
+ err_msg "should stop that service before starting local tests!"
+ if ! ask_yn "Continue with local tests?"; then
+ return
+ fi
+ fi
+ sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
+ tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
+cd ${SEARX_SRC}
+timeout 10 python searx/webapp.py &
+sleep 3
+curl --location --verbose --head --insecure $SEARX_INTERNAL_HTTP
+EOF
+ sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
+}
+
+install_searx_uwsgi() {
+ rst_title "Install searx's uWSGI app (searx.ini)" section
+ echo
+ install_uwsgi
+ uWSGI_install_app "$SEARX_UWSGI_APP"
+}
+
+remove_searx_uwsgi() {
+ rst_title "Remove searx's uWSGI app (searx.ini)" section
+ echo
+ uWSGI_remove_app "$SEARX_UWSGI_APP"
+}
+
+activate_service() {
+ rst_title "Activate $SEARX_INSTANCE_NAME (service)" section
+ echo
+ uWSGI_enable_app "$SEARX_UWSGI_APP"
+ uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+deactivate_service() {
+ rst_title "De-Activate $SEARX_INSTANCE_NAME (service)" section
+ echo
+ uWSGI_disable_app "$SEARX_UWSGI_APP"
+ uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+enable_image_proxy() {
+ info_msg "try to enable image_proxy ..."
+ tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/image_proxy : False/image_proxy : True/g" "$SEARX_SETTINGS_PATH"
+EOF
+ uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+disable_image_proxy() {
+ info_msg "try to enable image_proxy ..."
+ tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/image_proxy : True/image_proxy : False/g" "$SEARX_SETTINGS_PATH"
+EOF
+ uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+enable_debug() {
+ warn_msg "Do not enable debug in production enviroments!!"
+ info_msg "try to enable debug mode ..."
+ tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
+EOF
+ uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+disable_debug() {
+ info_msg "try to disable debug mode ..."
+ tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
+EOF
+ uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+set_result_proxy() {
+
+ # usage: set_result_proxy <URL> [<key>]
+
+ info_msg "try to set result proxy: '$1' ($2)"
+ cp "${SEARX_SETTINGS_PATH}" "${SEARX_SETTINGS_PATH}.bak"
+ _set_result_proxy "$1" "$2" > "${SEARX_SETTINGS_PATH}"
+}
+
+_set_result_proxy() {
+ local line
+ local stage=0
+ local url=" url: $1"
+ local key=" key: !!binary \"$2\""
+ if [[ -z $2 ]]; then
+ key=
+ fi
+
+ while IFS= read -r line
+ do
+ if [[ $stage = 0 ]] || [[ $stage = 2 ]] ; then
+ if [[ $line =~ ^[[:space:]]*#*[[:space:]]*result_proxy[[:space:]]*:[[:space:]]*$ ]]; then
+ if [[ $stage = 0 ]]; then
+ stage=1
+ echo "result_proxy:"
+ continue
+ elif [[ $stage = 2 ]]; then
+ continue
+ fi
+ fi
+ fi
+ if [[ $stage = 1 ]] || [[ $stage = 2 ]] ; then
+ if [[ $line =~ ^[[:space:]]*#*[[:space:]]*url[[:space:]]*:[[:space:]] ]]; then
+ [[ $stage = 1 ]] && echo "$url"
+ continue
+ elif [[ $line =~ ^[[:space:]]*#*[[:space:]]*key[[:space:]]*:[[:space:]] ]]; then
+ [[ $stage = 1 ]] && [[ -n $key ]] && echo "$key"
+ continue
+ elif [[ $line =~ ^[[:space:]]*$ ]]; then
+ stage=2
+ fi
+ fi
+ echo "$line"
+ done < "${SEARX_SETTINGS_PATH}.bak"
+}
+
+function has_substring() {
+ [[ "$1" != "${2/$1/}" ]]
+}
+inspect_service() {
+ rst_title "service status & log"
+ cat <<EOF
+
+sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
+
+ PUBLIC_URL : ${PUBLIC_URL}
+ SEARX_URL_PATH : ${SEARX_URL_PATH}
+ SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
+ SEARX_INTERNAL_HTTP : ${SEARX_INTERNAL_HTTP}
+
+EOF
+
+ if service_account_is_available "$SERVICE_USER"; then
+ info_msg "Service account $SERVICE_USER exists."
+ else
+ err_msg "Service account $SERVICE_USER does not exists!"
+ fi
+
+ if pyenv_is_available; then
+ info_msg "~$SERVICE_USER: python environment is available."
+ else
+ err_msg "~$SERVICE_USER: python environment is not available!"
+ fi
+
+ if clone_is_available; then
+ info_msg "~$SERVICE_USER: Searx software is installed."
+ else
+ err_msg "~$SERVICE_USER: Missing searx software!"
+ fi
+
+ if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
+ info_msg "uWSGI app $SEARX_UWSGI_APP is enabled."
+ else
+ err_msg "uWSGI app $SEARX_UWSGI_APP not enabled!"
+ fi
+
+ uWSGI_app_available "$SEARX_UWSGI_APP" \
+ || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
+
+ if in_container; then
+ lxc_suite_info
+ else
+ info_msg "public URL --> ${PUBLIC_URL}"
+ info_msg "internal URL --> http://${SEARX_INTERNAL_HTTP}"
+ fi
+
+ if ! service_is_available "http://${SEARX_INTERNAL_HTTP}"; then
+ err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_HTTP} is not available!"
+ MSG="${_Green}[${_BCyan}CTRL-C${_Green}] to stop or [${_BCyan}KEY${_Green}] to continue"\
+ wait_key
+ fi
+
+ if ! service_is_available "${PUBLIC_URL}"; then
+ warn_msg "Public service at ${PUBLIC_URL} is not available!"
+ if ! in_container; then
+ warn_msg "Check if public name is correct and routed or use the public IP from above."
+ fi
+ fi
+
+ local _debug_on
+ if ask_yn "Enable searx debug mode?"; then
+ enable_debug
+ _debug_on=1
+ fi
+ echo
+
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*)
+ systemctl --no-pager -l status "${SERVICE_NAME}"
+ ;;
+ arch-*)
+ systemctl --no-pager -l status "uwsgi@${SERVICE_NAME%.*}"
+ ;;
+ fedora-*)
+ systemctl --no-pager -l status uwsgi
+ ;;
+ esac
+
+ # shellcheck disable=SC2059
+ printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
+ read -r -s -n1 -t 5
+ echo
+
+ while true; do
+ trap break 2
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*) tail -f /var/log/uwsgi/app/searx.log ;;
+ arch-*) journalctl -f -u "uwsgi@${SERVICE_NAME%.*}" ;;
+ fedora-*) journalctl -f -u uwsgi ;;
+ esac
+ done
+
+ if [[ $_debug_on == 1 ]]; then
+ disable_debug
+ fi
+ return 0
+}
+
+install_apache_site() {
+ rst_title "Install Apache site $APACHE_SEARX_SITE"
+
+ rst_para "\
+This installs the searx uwsgi app as apache site. If your server is public to
+the internet, you should instead use a reverse proxy (filtron) to block
+excessively bot queries."
+
+ ! apache_is_installed && err_msg "Apache is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ else
+ install_apache
+ fi
+
+ apache_install_site --variant=uwsgi "${APACHE_SEARX_SITE}"
+
+ rst_title "Install searx's uWSGI app (searx.ini)" section
+ echo
+ uWSGI_install_app --variant=socket "$SEARX_UWSGI_APP"
+
+ if ! service_is_available "${PUBLIC_URL}"; then
+ err_msg "Public service at ${PUBLIC_URL} is not available!"
+ fi
+}
+
+remove_apache_site() {
+
+ rst_title "Remove Apache site ${APACHE_SEARX_SITE}"
+
+ rst_para "\
+This removes apache site ${APACHE_SEARX_SITE}."
+
+ ! apache_is_installed && err_msg "Apache is not installed."
+
+ if ! ask_yn "Do you really want to continue?" Yn; then
+ return
+ fi
+
+ apache_remove_site "${APACHE_SEARX_SITE}"
+
+ rst_title "Remove searx's uWSGI app (searx.ini)" section
+ echo
+ uWSGI_remove_app "$SEARX_UWSGI_APP"
+}
+
+rst-doc() {
+ local debian="${SEARX_PACKAGES_debian}"
+ local arch="${SEARX_PACKAGES_arch}"
+ local fedora="${SEARX_PACKAGES_fedora}"
+ local debian_build="${BUILD_PACKAGES_debian}"
+ local arch_build="${BUILD_PACKAGES_arch}"
+ local fedora_build="${BUILD_PACKAGES_fedora}"
+ debian="$(echo "${debian}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+ arch="$(echo "${arch}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+ fedora="$(echo "${fedora}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+ debian_build="$(echo "${debian_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+ arch_build="$(echo "${arch_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+ fedora_build="$(echo "${fedora_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+
+ eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searx.rst")\""
+
+ # I use ubuntu-20.04 here to demonstrate that versions are also suported,
+ # normaly debian-* and ubuntu-* are most the same.
+
+ for DIST_NAME in ubuntu-20.04 arch fedora; do
+ (
+ DIST_ID=${DIST_NAME%-*}
+ DIST_VERS=${DIST_NAME#*-}
+ [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
+ uWSGI_distro_setup
+
+ echo -e "\n.. START searx uwsgi-description $DIST_NAME"
+
+ case $DIST_ID-$DIST_VERS in
+ ubuntu-*|debian-*) cat <<EOF
+# init.d --> /usr/share/doc/uwsgi/README.Debian.gz
+# For uWSGI debian uses the LSB init process, this might be changed
+# one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
+
+create ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}
+enable: sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP} ${uWSGI_APPS_ENABLED}/
+start: sudo -H service uwsgi start ${SEARX_UWSGI_APP%.*}
+restart: sudo -H service uwsgi restart ${SEARX_UWSGI_APP%.*}
+stop: sudo -H service uwsgi stop ${SEARX_UWSGI_APP%.*}
+disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+EOF
+ ;;
+ arch-*) cat <<EOF
+# systemd --> /usr/lib/systemd/system/uwsgi@.service
+# For uWSGI archlinux uses systemd template units, see
+# - http://0pointer.de/blog/projects/instances.html
+# - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+
+create: ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+enable: sudo -H systemctl enable uwsgi@${SEARX_UWSGI_APP%.*}
+start: sudo -H systemctl start uwsgi@${SEARX_UWSGI_APP%.*}
+restart: sudo -H systemctl restart uwsgi@${SEARX_UWSGI_APP%.*}
+stop: sudo -H systemctl stop uwsgi@${SEARX_UWSGI_APP%.*}
+disable: sudo -H systemctl disable uwsgi@${SEARX_UWSGI_APP%.*}
+EOF
+ ;;
+ fedora-*) cat <<EOF
+# systemd --> /usr/lib/systemd/system/uwsgi.service
+# The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
+# - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+
+create: ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+restart: sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+EOF
+ ;;
+ esac
+ echo -e ".. END searx uwsgi-description $DIST_NAME"
+
+ echo -e "\n.. START searx uwsgi-appini $DIST_NAME"
+ eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}")\""
+ echo -e "\n.. END searx uwsgi-appini $DIST_NAME"
+
+ )
+ done
+
+}
+
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------
diff --git a/utils/site-python/sphinx_build_tools.py b/utils/site-python/sphinx_build_tools.py
new file mode 100644
index 000000000..b9ebdeacc
--- /dev/null
+++ b/utils/site-python/sphinx_build_tools.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8; mode: python -*-
+"""Implement some sphinx-build tools.
+
+"""
+
+import os
+import sys
+from sphinx.util.pycompat import execfile_
+
+# ------------------------------------------------------------------------------
+def load_sphinx_config(namespace):
+# ------------------------------------------------------------------------------
+
+ u"""Load an additional configuration file into *namespace*.
+
+ The name of the configuration file is taken from the environment
+ ``SPHINX_CONF``. The external configuration file extends (or overwrites) the
+ configuration values from the origin ``conf.py``. With this you are able to
+ maintain *build themes*. To your docs/conf.py add::
+
+ from sphinx_build_tools import load_sphinx_config
+ ...
+
+ # Since loadConfig overwrites settings from the global namespace, it has to be
+ # the last statement in the conf.py file
+
+ load_sphinx_config(globals())
+
+ """
+
+ config_file = os.environ.get("SPHINX_CONF", None)
+ if (config_file is not None
+ and os.path.normpath(namespace["__file__"]) != os.path.normpath(config_file) ):
+ config_file = os.path.abspath(config_file)
+
+ if os.path.isfile(config_file):
+ sys.stdout.write(
+ "load additional sphinx-config: %s\n"
+ % config_file)
+ config = namespace.copy()
+ config['__file__'] = config_file
+ execfile_(config_file, config)
+ del config['__file__']
+ namespace.update(config)
+ else:
+ sys.stderr.write(
+ "WARNING: additional sphinx-config not found: %s\n"
+ % config_file)
diff --git a/utils/templates/etc/apache2 b/utils/templates/etc/apache2
new file mode 120000
index 000000000..558a90717
--- /dev/null
+++ b/utils/templates/etc/apache2
@@ -0,0 +1 @@
+httpd \ No newline at end of file
diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
new file mode 100644
index 000000000..fff70fa8f
--- /dev/null
+++ b/utils/templates/etc/filtron/rules.json
@@ -0,0 +1,129 @@
+[
+ {
+ "name": "roboagent limit",
+ "filters": [
+ "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby|UniversalFeedParser)"
+ ],
+ "limit": 0,
+ "stop": true,
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ },
+ {
+ "name": "botlimit",
+ "filters": [
+ "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
+ ],
+ "limit": 0,
+ "stop": true,
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ },
+ {
+ "name": "suspiciously frequent IP",
+ "filters": [],
+ "interval": 600,
+ "limit": 30,
+ "aggregations": [
+ "Header:X-Forwarded-For"
+ ],
+ "actions":[
+ {"name":"log"}
+ ]
+ },
+ {
+ "name": "search request",
+ "filters": [
+ "Param:q",
+ "Path=^(/|/search)$"
+ ],
+ "interval": 61,
+ "limit": 999,
+ "subrules": [
+ {
+ "name": "missing Accept-Language",
+ "filters": ["!Header:Accept-Language"],
+ "limit": 0,
+ "stop": true,
+ "actions": [
+ {"name":"log"},
+ {"name": "block",
+ "params": {"message": "Rate limit exceeded"}}
+ ]
+ },
+ {
+ "name": "suspiciously Connection=close header",
+ "filters": ["Header:Connection=close"],
+ "limit": 0,
+ "stop": true,
+ "actions": [
+ {"name":"log"},
+ {"name": "block",
+ "params": {"message": "Rate limit exceeded"}}
+ ]
+ },
+ {
+ "name": "IP limit",
+ "interval": 61,
+ "limit": 9,
+ "stop": true,
+ "aggregations": [
+ "Header:X-Forwarded-For"
+ ],
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ },
+ {
+ "name": "rss/json limit",
+ "filters": [
+ "Param:format=(csv|json|rss)"
+ ],
+ "interval": 121,
+ "limit": 2,
+ "stop": true,
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ },
+ {
+ "name": "useragent limit",
+ "interval": 61,
+ "limit": 199,
+ "aggregations": [
+ "Header:User-Agent"
+ ],
+ "actions": [
+ { "name": "log"},
+ { "name": "block",
+ "params": {
+ "message": "Rate limit exceeded"
+ }
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/utils/templates/etc/httpd/sites-available/morty.conf b/utils/templates/etc/httpd/sites-available/morty.conf
new file mode 100644
index 000000000..daeb3635a
--- /dev/null
+++ b/utils/templates/etc/httpd/sites-available/morty.conf
@@ -0,0 +1,28 @@
+# -*- coding: utf-8; mode: apache -*-
+
+LoadModule headers_module ${APACHE_MODULES}/mod_headers.so
+LoadModule proxy_module ${APACHE_MODULES}/mod_proxy.so
+LoadModule proxy_http_module ${APACHE_MODULES}/mod_proxy_http.so
+#LoadModule setenvif_module ${APACHE_MODULES}/mod_setenvif.so
+
+# SetEnvIf Request_URI "${PUBLIC_URL_PATH_MORTY}" dontlog
+# CustomLog /dev/null combined env=dontlog
+
+<Location ${PUBLIC_URL_PATH_MORTY} >
+
+ <IfModule mod_security2.c>
+ SecRuleEngine Off
+ </IfModule>
+
+ Require all granted
+
+ Order deny,allow
+ Deny from all
+ #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPreserveHost On
+ ProxyPass http://${MORTY_LISTEN}
+ RequestHeader set X-Script-Name ${PUBLIC_URL_PATH_MORTY}
+
+</Location>
diff --git a/utils/templates/etc/httpd/sites-available/searx.conf:filtron b/utils/templates/etc/httpd/sites-available/searx.conf:filtron
new file mode 100644
index 000000000..379d47e24
--- /dev/null
+++ b/utils/templates/etc/httpd/sites-available/searx.conf:filtron
@@ -0,0 +1,33 @@
+# -*- coding: utf-8; mode: apache -*-
+
+LoadModule headers_module ${APACHE_MODULES}/mod_headers.so
+LoadModule proxy_module ${APACHE_MODULES}/mod_proxy.so
+LoadModule proxy_http_module ${APACHE_MODULES}/mod_proxy_http.so
+#LoadModule setenvif_module ${APACHE_MODULES}/mod_setenvif.so
+
+# SetEnvIf Request_URI "${FILTRON_URL_PATH}" dontlog
+# CustomLog /dev/null combined env=dontlog
+
+# SecRuleRemoveById 981054
+# SecRuleRemoveById 981059
+# SecRuleRemoveById 981060
+# SecRuleRemoveById 950907
+
+<Location ${FILTRON_URL_PATH} >
+
+ <IfModule mod_security2.c>
+ SecRuleEngine Off
+ </IfModule>
+
+ Require all granted
+
+ Order deny,allow
+ Deny from all
+ #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPreserveHost On
+ ProxyPass http://${FILTRON_LISTEN}
+ RequestHeader set X-Script-Name ${FILTRON_URL_PATH}
+
+</Location>
diff --git a/utils/templates/etc/httpd/sites-available/searx.conf:uwsgi b/utils/templates/etc/httpd/sites-available/searx.conf:uwsgi
new file mode 100644
index 000000000..ef702de3a
--- /dev/null
+++ b/utils/templates/etc/httpd/sites-available/searx.conf:uwsgi
@@ -0,0 +1,27 @@
+# -*- coding: utf-8; mode: apache -*-
+
+LoadModule headers_module ${APACHE_MODULES}/mod_headers.so
+LoadModule proxy_module ${APACHE_MODULES}/mod_proxy.so
+LoadModule proxy_uwsgi_module ${APACHE_MODULES}/mod_proxy_uwsgi.so
+# LoadModule setenvif_module ${APACHE_MODULES}/mod_setenvif.so
+
+# SetEnvIf Request_URI "${SEARX_URL_PATH}" dontlog
+# CustomLog /dev/null combined env=dontlog
+
+<Location ${SEARX_URL_PATH}>
+
+ <IfModule mod_security2.c>
+ SecRuleEngine Off
+ </IfModule>
+
+ Require all granted
+
+ Order deny,allow
+ Deny from all
+ # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+ Allow from all
+
+ ProxyPreserveHost On
+ ProxyPass unix:${SEARX_UWSGI_SOCKET}|uwsgi://uwsgi-uds-searx/
+
+</Location>
diff --git a/utils/templates/etc/nginx/default.apps-available/morty.conf b/utils/templates/etc/nginx/default.apps-available/morty.conf
new file mode 100644
index 000000000..e7ffa27e2
--- /dev/null
+++ b/utils/templates/etc/nginx/default.apps-available/morty.conf
@@ -0,0 +1,11 @@
+# https://example.org/morty
+
+location /morty {
+ proxy_pass http://127.0.0.1:3000/;
+
+ proxy_set_header Host \$http_host;
+ proxy_set_header Connection \$http_connection;
+ proxy_set_header X-Real-IP \$remote_addr;
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme \$scheme;
+}
diff --git a/utils/templates/etc/nginx/default.apps-available/searx.conf:filtron b/utils/templates/etc/nginx/default.apps-available/searx.conf:filtron
new file mode 100644
index 000000000..d3137e42d
--- /dev/null
+++ b/utils/templates/etc/nginx/default.apps-available/searx.conf:filtron
@@ -0,0 +1,16 @@
+# https://example.org/searx
+
+location ${SEARX_URL_PATH} {
+ proxy_pass http://127.0.0.1:4004/;
+
+ proxy_set_header Host \$http_host;
+ proxy_set_header Connection \$http_connection;
+ proxy_set_header X-Real-IP \$remote_addr;
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme \$scheme;
+ proxy_set_header X-Script-Name ${SEARX_URL_PATH};
+}
+
+location ${SEARX_URL_PATH}/static {
+ alias ${SEARX_SRC}/searx/static;
+}
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
new file mode 100644
index 000000000..51f659d0f
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -0,0 +1,80 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}/searx
+
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
+# disable logging for privacy
+logger = systemd
+disable-logging = true
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python
+
+# By default the Python plugin does not initialize the GIL. This means your
+# app-generated threads will not run. If you need threads, remember to enable
+# them with enable-threads. Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SEARX_SRC}
+
+
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
+http = ${SEARX_INTERNAL_HTTP}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+# mkdir -p /run/uwsgi/app/searx
+# chown -R ${SERVICE_USER}:${SERVICE_GROUP} /run/uwsgi/app/searx
+#
+# socket = /run/uwsgi/app/searx/socket \ No newline at end of file
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
new file mode 100644
index 000000000..eeabb3715
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
@@ -0,0 +1,80 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}/searx
+
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
+# disable logging for privacy
+logger = systemd
+disable-logging = true
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python
+
+# By default the Python plugin does not initialize the GIL. This means your
+# app-generated threads will not run. If you need threads, remember to enable
+# them with enable-threads. Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SEARX_SRC}
+
+
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
+# http = ${SEARX_INTERNAL_HTTP}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+# mkdir -p /run/uwsgi/app/searx
+# chown -R ${SERVICE_USER}:${SERVICE_GROUP} /run/uwsgi/app/searx
+#
+socket = /run/uwsgi/app/searx/socket \ No newline at end of file
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
new file mode 100644
index 000000000..9785d7cd1
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -0,0 +1,79 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}/searx
+
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
+# disable logging for privacy
+disable-logging = true
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python3,http
+
+# By default the Python plugin does not initialize the GIL. This means your
+# app-generated threads will not run. If you need threads, remember to enable
+# them with enable-threads. Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SEARX_SRC}
+
+
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
+http = ${SEARX_INTERNAL_HTTP}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+# mkdir -p /run/uwsgi/app/searx
+# chmod -R ${SERVICE_USER}:${SERVICE_GROUP} /run/uwsgi/app/searx
+#
+# socket = /run/uwsgi/app/searx/socket \ No newline at end of file
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini:socket b/utils/templates/etc/uwsgi/apps-available/searx.ini:socket
new file mode 100644
index 000000000..88436e5eb
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini:socket
@@ -0,0 +1,79 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}/searx
+
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
+# disable logging for privacy
+disable-logging = true
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python3,http
+
+# By default the Python plugin does not initialize the GIL. This means your
+# app-generated threads will not run. If you need threads, remember to enable
+# them with enable-threads. Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SEARX_SRC}
+
+
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
+# http = ${SEARX_INTERNAL_HTTP}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+# mkdir -p /run/uwsgi/app/searx
+# chown -R ${SERVICE_USER}:${SERVICE_GROUP} /run/uwsgi/app/searx
+#
+socket = /run/uwsgi/app/searx/socket \ No newline at end of file
diff --git a/utils/templates/lib/systemd/system/filtron.service b/utils/templates/lib/systemd/system/filtron.service
new file mode 100644
index 000000000..3b0c6edcc
--- /dev/null
+++ b/utils/templates/lib/systemd/system/filtron.service
@@ -0,0 +1,29 @@
+[Unit]
+
+Description=${SERVICE_NAME}
+After=syslog.target
+After=network.target
+
+[Service]
+
+Type=simple
+User=${SERVICE_USER}
+Group=${SERVICE_GROUP}
+WorkingDirectory=${SERVICE_HOME}
+ExecStart=${SERVICE_HOME}/go-apps/bin/filtron -api '${FILTRON_API}' -listen '${FILTRON_LISTEN}' -rules '${FILTRON_RULES}' -target '${FILTRON_TARGET}'
+
+Restart=always
+Environment=USER=${SERVICE_USER} HOME=${SERVICE_HOME}
+
+# Some distributions may not support these hardening directives. If you cannot
+# start the service due to an unknown option, comment out the ones not supported
+# by your version of systemd.
+
+ProtectSystem=full
+PrivateDevices=yes
+PrivateTmp=yes
+NoNewPrivileges=true
+
+[Install]
+
+WantedBy=multi-user.target
diff --git a/utils/templates/lib/systemd/system/morty.service b/utils/templates/lib/systemd/system/morty.service
new file mode 100644
index 000000000..25b676b51
--- /dev/null
+++ b/utils/templates/lib/systemd/system/morty.service
@@ -0,0 +1,29 @@
+[Unit]
+
+Description=${SERVICE_NAME}
+After=syslog.target
+After=network.target
+
+[Service]
+
+Type=simple
+User=${SERVICE_USER}
+Group=${SERVICE_GROUP}
+WorkingDirectory=${SERVICE_HOME}
+ExecStart=${SERVICE_HOME}/go-apps/bin/morty -key '${MORTY_KEY}' -listen '${MORTY_LISTEN}' -timeout ${MORTY_TIMEOUT}
+
+Restart=always
+Environment=USER=${SERVICE_USER} HOME=${SERVICE_HOME} DEBUG=${SERVICE_ENV_DEBUG}
+
+# Some distributions may not support these hardening directives. If you cannot
+# start the service due to an unknown option, comment out the ones not supported
+# by your version of systemd.
+
+ProtectSystem=full
+PrivateDevices=yes
+PrivateTmp=yes
+NoNewPrivileges=true
+
+[Install]
+
+WantedBy=multi-user.target