Compare commits

..

2 Commits

Author SHA1 Message Date
Markus Heiser ac631cc1a9
Merge e0a3dee3bf into 75c9de02d1 2023-09-23 12:27:14 +02:00
Markus Heiser e0a3dee3bf [POC] limiter: change PING of link_token mehtod from CSS to <img>
while PR #2357 [1] was being implemented the question came up:

    would be better to change the PING resource from CSS to an image so that
    some terminal based browser may still able to pass the test [1]

This patch implements a POC in where a <img src=token> tag is loaded instaed a
CSS.

To test this patch activate limiter and link_token method [3] and start a
developer instance::

    make run

In your terminal browser open http://127.0.0.1:8888/search?q=foo

If the browser is suitable for the link_token method, it loads the image and the
following messages appear::

    DEBUG   searx.botdetection.limiter    : OK 127.0.0.1/32: /clientft61aak7fzyu6o6v.svg ...
    DEBUG   searx.botdetection.link_token : token is valid --> True
    DEBUG   searx.botdetection.link_token : store ping_key for (client) network 127.0.0.1/32 (IP 127.0.0.1) -> SearXNG_limiter.ping[...]

Browsers that do not load images will be blocked: If you try by example::

    lynx http://127.0.0.1:8888/search?q=foo

you will see a WARNING message like::

    WARNING searx.botdetection.link_token : missing ping (IP: 127.0.0.1/32) / request: SearXNG_limiter.ping[...]

----

[1] 80aaef6c95
[2] https://github.com/searxng/searxng/pull/2357#issuecomment-1574898834
[3] activate limiter and link_token method

```diff
diff --git a/searx/botdetection/limiter.toml b/searx/botdetection/limiter.toml
index 71a231e8f..7e1dba755 100644
--- a/searx/botdetection/limiter.toml
+++ b/searx/botdetection/limiter.toml
@@ -17,6 +17,6 @@ ipv6_prefix = 48
 filter_link_local = false

 # acrivate link_token method in the ip_limit method
-link_token = false
+link_token = true

diff --git a/searx/settings.yml b/searx/settings.yml
index a82a3432d..e7b983afc 100644
--- a/searx/settings.yml
+++ b/searx/settings.yml
@@ -73,7 +73,7 @@ server:
   # public URL of the instance, to ensure correct inbound links. Is overwritten
   # by ${SEARXNG_URL}.
   base_url: false  # "http://example.com/location"
-  limiter: false  # rate limit the number of request on the instance, block some bots
+  limiter: true  # rate limit the number of request on the instance, block some bots

   # If your instance owns a /etc/searxng/settings.yml file, then set the following
   # values there.
```

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2023-06-03 18:49:21 +02:00
596 changed files with 70403 additions and 115565 deletions

View File

@ -16,9 +16,6 @@ max_line_length = 119
[*.html] [*.html]
indent_size = 4 indent_size = 4
[*.js]
indent_size = 2
[*.json] [*.json]
indent_size = 4 indent_size = 4
insert_final_newline = ignore insert_final_newline = ignore

View File

@ -1,4 +1,4 @@
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2 version: 2
updates: updates:
- package-ecosystem: "pip" - package-ecosystem: "pip"
@ -8,9 +8,6 @@ updates:
day: "friday" day: "friday"
open-pull-requests-limit: 5 open-pull-requests-limit: 5
target-branch: "master" target-branch: "master"
commit-message:
prefix: "[upd] pypi:"
- package-ecosystem: "npm" - package-ecosystem: "npm"
directory: "/searx/static/themes/simple" directory: "/searx/static/themes/simple"
schedule: schedule:
@ -18,5 +15,3 @@ updates:
day: "friday" day: "friday"
open-pull-requests-limit: 5 open-pull-requests-limit: 5
target-branch: "master" target-branch: "master"
commit-message:
prefix: "[upd] npm:"

View File

@ -1,31 +0,0 @@
name: "Checker"
on:
schedule:
- cron: "0 4 * * 5"
workflow_dispatch:
jobs:
checker:
name: Checker
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Ubuntu packages
run: |
sudo ./utils/searxng.sh install packages
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
architecture: 'x64'
- name: Install Python dependencies
run: |
make V=1 install
- name: Checker
run: |
make search.checker

View File

@ -7,7 +7,7 @@ on:
jobs: jobs:
updateData: updateData:
name: Update data - ${{ matrix.fetch }} name: Update data - ${{ matrix.fetch }}
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
if: ${{ github.repository_owner == 'searxng'}} if: ${{ github.repository_owner == 'searxng'}}
strategy: strategy:
fail-fast: false fail-fast: false
@ -29,9 +29,9 @@ jobs:
sudo ./utils/searxng.sh install packages sudo ./utils/searxng.sh install packages
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: '3.12' python-version: '3.9'
architecture: 'x64' architecture: 'x64'
- name: Install Python dependencies - name: Install Python dependencies
@ -46,18 +46,18 @@ jobs:
- name: Create Pull Request - name: Create Pull Request
id: cpr id: cpr
uses: peter-evans/create-pull-request@v6 uses: peter-evans/create-pull-request@v3
with: with:
commit-message: '[data] update searx.data - ${{ matrix.fetch }}' commit-message: Update searx.data - ${{ matrix.fetch }}
committer: searxng-bot <noreply@github.com> committer: searxng-bot <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
signoff: false signoff: false
branch: update_data_${{ matrix.fetch }} branch: update_data_${{ matrix.fetch }}
delete-branch: true delete-branch: true
draft: false draft: false
title: '[data] update searx.data - ${{ matrix.fetch }}' title: 'Update searx.data - ${{ matrix.fetch }}'
body: | body: |
update searx.data - ${{ matrix.fetch }} Update searx.data - ${{ matrix.fetch }}
labels: | labels: |
data data

View File

@ -16,7 +16,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-20.04] os: [ubuntu-20.04]
python-version: ["3.9", "3.10", "3.11", "3.12",] python-version: ["3.8", "3.9", "3.10", "3.11"]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -25,7 +25,7 @@ jobs:
sudo ./utils/searxng.sh install packages sudo ./utils/searxng.sh install packages
sudo apt install firefox sudo apt install firefox
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
architecture: 'x64' architecture: 'x64'
@ -45,6 +45,14 @@ jobs:
make V=1 gecko.driver make V=1 gecko.driver
- name: Run tests - name: Run tests
run: make V=1 ci.test run: make V=1 ci.test
- name: Test coverage
run: make V=1 test.coverage
- name: Store coverage result
uses: actions/upload-artifact@v3
with:
name: coverage-${{ matrix.python-version }}
path: coverage/
retention-days: 60
themes: themes:
name: Themes name: Themes
@ -55,9 +63,9 @@ jobs:
- name: Install Ubuntu packages - name: Install Ubuntu packages
run: sudo ./utils/searxng.sh install buildhost run: sudo ./utils/searxng.sh install buildhost
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: '3.12' python-version: '3.9'
architecture: 'x64' architecture: 'x64'
- name: Cache Python dependencies - name: Cache Python dependencies
id: cache-python id: cache-python
@ -67,7 +75,7 @@ jobs:
./local ./local
./.nvm ./.nvm
./node_modules ./node_modules
key: python-ubuntu-20.04-3.12-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }} key: python-ubuntu-20.04-3.9-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }}
- name: Install node dependencies - name: Install node dependencies
run: make V=1 node.env run: make V=1 node.env
- name: Build themes - name: Build themes
@ -87,9 +95,9 @@ jobs:
- name: Install Ubuntu packages - name: Install Ubuntu packages
run: sudo ./utils/searxng.sh install buildhost run: sudo ./utils/searxng.sh install buildhost
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: '3.12' python-version: '3.9'
architecture: 'x64' architecture: 'x64'
- name: Cache Python dependencies - name: Cache Python dependencies
id: cache-python id: cache-python
@ -99,7 +107,7 @@ jobs:
./local ./local
./.nvm ./.nvm
./node_modules ./node_modules
key: python-ubuntu-20.04-3.12-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }} key: python-ubuntu-20.04-3.9-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }}
- name: Build documentation - name: Build documentation
run: | run: |
make V=1 docs.clean docs.html make V=1 docs.clean docs.html
@ -112,7 +120,7 @@ jobs:
FOLDER: dist/docs FOLDER: dist/docs
CLEAN: true # Automatically remove deleted files from the deploy branch CLEAN: true # Automatically remove deleted files from the deploy branch
SINGLE_COMMIT: True SINGLE_COMMIT: True
COMMIT_MESSAGE: '[doc] build from commit ${{ github.sha }}' COMMIT_MESSAGE: build from commit ${{ github.sha }}
babel: babel:
name: Update translations branch name: Update translations branch
@ -131,9 +139,9 @@ jobs:
fetch-depth: '0' fetch-depth: '0'
token: ${{ secrets.WEBLATE_GITHUB_TOKEN }} token: ${{ secrets.WEBLATE_GITHUB_TOKEN }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: '3.12' python-version: '3.9'
architecture: 'x64' architecture: 'x64'
- name: Cache Python dependencies - name: Cache Python dependencies
id: cache-python id: cache-python
@ -143,7 +151,7 @@ jobs:
./local ./local
./.nvm ./.nvm
./node_modules ./node_modules
key: python-ubuntu-20.04-3.12-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }} key: python-ubuntu-20.04-3.9-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }}
- name: weblate & git setup - name: weblate & git setup
env: env:
WEBLATE_CONFIG: ${{ secrets.WEBLATE_CONFIG }} WEBLATE_CONFIG: ${{ secrets.WEBLATE_CONFIG }}
@ -155,6 +163,7 @@ jobs:
- name: Update transations - name: Update transations
id: update id: update
run: | run: |
git restore utils/brand.env
make V=1 weblate.push.translations make V=1 weblate.push.translations
dockers: dockers:
@ -175,9 +184,9 @@ jobs:
# make sure "make docker.push" can get the git history # make sure "make docker.push" can get the git history
fetch-depth: '0' fetch-depth: '0'
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: '3.12' python-version: '3.9'
architecture: 'x64' architecture: 'x64'
- name: Cache Python dependencies - name: Cache Python dependencies
id: cache-python id: cache-python
@ -187,7 +196,7 @@ jobs:
./local ./local
./.nvm ./.nvm
./node_modules ./node_modules
key: python-ubuntu-20.04-3.12-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }} key: python-ubuntu-20.04-3.9-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }}
- name: Set up QEMU - name: Set up QEMU
if: env.DOCKERHUB_USERNAME != null if: env.DOCKERHUB_USERNAME != null
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1

View File

@ -16,9 +16,9 @@ jobs:
fetch-depth: '0' fetch-depth: '0'
token: ${{ secrets.WEBLATE_GITHUB_TOKEN }} token: ${{ secrets.WEBLATE_GITHUB_TOKEN }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: '3.12' python-version: '3.9'
architecture: 'x64' architecture: 'x64'
- name: Cache Python dependencies - name: Cache Python dependencies
id: cache-python id: cache-python
@ -28,7 +28,7 @@ jobs:
./local ./local
./.nvm ./.nvm
./node_modules ./node_modules
key: python-ubuntu-20.04-3.12-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }} key: python-ubuntu-20.04-3.9-${{ hashFiles('requirements*.txt', 'setup.py','.nvmrc', 'package.json') }}
- name: weblate & git setup - name: weblate & git setup
env: env:
WEBLATE_CONFIG: ${{ secrets.WEBLATE_CONFIG }} WEBLATE_CONFIG: ${{ secrets.WEBLATE_CONFIG }}
@ -45,15 +45,15 @@ jobs:
uses: peter-evans/create-pull-request@v3 uses: peter-evans/create-pull-request@v3
with: with:
token: ${{ secrets.WEBLATE_GITHUB_TOKEN }} token: ${{ secrets.WEBLATE_GITHUB_TOKEN }}
commit-message: '[l10n] update translations from Weblate' commit-message: Update translations
committer: searxng-bot <searxng-bot@users.noreply.github.com> committer: searxng-bot <searxng-bot@users.noreply.github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
signoff: false signoff: false
branch: translations_update branch: translations_update
delete-branch: true delete-branch: true
draft: false draft: false
title: '[l10n] update translations from Weblate' title: 'Update translations'
body: | body: |
update translations from Weblate Update translations
labels: | labels: |
translation translation

3
.gitignore vendored
View File

@ -23,6 +23,3 @@ gh-pages/
.idea/ .idea/
searx/version_frozen.py searx/version_frozen.py
.dir-locals.el
.python-version

2
.nvmrc
View File

@ -1 +1 @@
v20.10 v16.15.1

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8; mode: conf-unix -*- # -*- coding: utf-8; mode: conf -*-
# lint Python modules using external checkers. # lint Python modules using external checkers.
# #
# This is the main checker controlling the other ones and the reports # This is the main checker controlling the other ones and the reports
@ -27,7 +27,7 @@ ignore-patterns=
#init-hook= #init-hook=
# Use multiple processes to speed up Pylint. # Use multiple processes to speed up Pylint.
jobs=0 jobs=1
# List of plugins (as comma separated values of python modules names) to load, # List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers. # usually to register additional checkers.
@ -338,7 +338,6 @@ valid-metaclass-classmethod-first-arg=mcs
# Maximum number of arguments for function / method # Maximum number of arguments for function / method
max-args=8 max-args=8
max-positional-arguments=14
# Maximum number of attributes for a class (see R0902). # Maximum number of attributes for a class (see R0902).
max-attributes=20 max-attributes=20

View File

@ -1,2 +0,0 @@
python 3.12.0
shellcheck 0.9.0

View File

@ -170,7 +170,3 @@ features or generally made searx better:
- @blob42 `<https://blob42.xyz>`_ - @blob42 `<https://blob42.xyz>`_
- Paolo Basso `<https://github.com/paolobasso99>` - Paolo Basso `<https://github.com/paolobasso99>`
- Bernie Huang `<https://github.com/BernieHuang2008>` - Bernie Huang `<https://github.com/BernieHuang2008>`
- Austin Olacsi `<https://github.com/Austin-Olacsi>`
- @micsthepick
- Daniel Kukula `<https://github.com/dkuku>`
- Patrick Evans `https://github.com/holysoles`

View File

@ -2,7 +2,7 @@
## Resources in the documentation ## Resources in the documentation
* [Development quickstart](https://docs.searxng.org/dev/quickstart.html) * [Development quickstart](https://docs.searxng.org/dev/contribution_guide.html)
* [Contribution guide](https://docs.searxng.org/dev/contribution_guide.html) * [Contribution guide](https://docs.searxng.org/dev/contribution_guide.html)
## Submitting PRs ## Submitting PRs

View File

@ -1,4 +1,4 @@
FROM alpine:3.20 FROM alpine:3.18
ENTRYPOINT ["/sbin/tini","--","/usr/local/searxng/dockerfiles/docker-entrypoint.sh"] ENTRYPOINT ["/sbin/tini","--","/usr/local/searxng/dockerfiles/docker-entrypoint.sh"]
EXPOSE 8080 EXPOSE 8080
VOLUME /etc/searxng VOLUME /etc/searxng
@ -15,9 +15,7 @@ ENV INSTANCE_NAME=searxng \
MORTY_KEY= \ MORTY_KEY= \
MORTY_URL= \ MORTY_URL= \
SEARXNG_SETTINGS_PATH=/etc/searxng/settings.yml \ SEARXNG_SETTINGS_PATH=/etc/searxng/settings.yml \
UWSGI_SETTINGS_PATH=/etc/searxng/uwsgi.ini \ UWSGI_SETTINGS_PATH=/etc/searxng/uwsgi.ini
UWSGI_WORKERS=%k \
UWSGI_THREADS=4
WORKDIR /usr/local/searxng WORKDIR /usr/local/searxng
@ -35,6 +33,7 @@ RUN apk add --no-cache -t build-dependencies \
git \ git \
&& apk add --no-cache \ && apk add --no-cache \
ca-certificates \ ca-certificates \
su-exec \
python3 \ python3 \
py3-pip \ py3-pip \
libxml2 \ libxml2 \
@ -44,7 +43,7 @@ RUN apk add --no-cache -t build-dependencies \
uwsgi \ uwsgi \
uwsgi-python3 \ uwsgi-python3 \
brotli \ brotli \
&& pip3 install --break-system-packages --no-cache -r requirements.txt \ && pip3 install --no-cache -r requirements.txt \
&& apk del build-dependencies \ && apk del build-dependencies \
&& rm -rf /root/.cache && rm -rf /root/.cache

View File

@ -44,10 +44,10 @@ lxc.clean:
PHONY += search.checker search.checker.% PHONY += search.checker search.checker.%
search.checker: install search.checker: install
$(Q)./manage pyenv.cmd searxng-checker -v $(Q)./manage pyenv.cmd searx-checker -v
search.checker.%: install search.checker.%: install
$(Q)./manage pyenv.cmd searxng-checker -v "$(subst _, ,$(patsubst search.checker.%,%,$@))" $(Q)./manage pyenv.cmd searx-checker -v "$(subst _, ,$(patsubst search.checker.%,%,$@))"
PHONY += test ci.test test.shell PHONY += test ci.test test.shell
ci.test: test.yamllint test.black test.pyright test.pylint test.unit test.robot test.rst test.pybabel ci.test: test.yamllint test.black test.pyright test.pylint test.unit test.robot test.rst test.pybabel
@ -56,7 +56,7 @@ test.shell:
$(Q)shellcheck -x -s dash \ $(Q)shellcheck -x -s dash \
dockerfiles/docker-entrypoint.sh dockerfiles/docker-entrypoint.sh
$(Q)shellcheck -x -s bash \ $(Q)shellcheck -x -s bash \
utils/brand.sh \ utils/brand.env \
$(MTOOLS) \ $(MTOOLS) \
utils/lib.sh \ utils/lib.sh \
utils/lib_sxng*.sh \ utils/lib_sxng*.sh \
@ -74,14 +74,16 @@ test.shell:
# wrap ./manage script # wrap ./manage script
MANAGE += buildenv
MANAGE += weblate.translations.commit weblate.push.translations MANAGE += weblate.translations.commit weblate.push.translations
MANAGE += data.all data.traits data.useragents data.locales MANAGE += data.all data.traits data.useragents
MANAGE += docs.html docs.live docs.gh-pages docs.prebuild docs.clean MANAGE += docs.html docs.live docs.gh-pages docs.prebuild docs.clean
MANAGE += docker.build docker.push docker.buildx MANAGE += docker.build docker.push docker.buildx
MANAGE += gecko.driver MANAGE += gecko.driver
MANAGE += node.env node.env.dev node.clean MANAGE += node.env node.env.dev node.clean
MANAGE += py.build py.clean MANAGE += py.build py.clean
MANAGE += pyenv pyenv.install pyenv.uninstall MANAGE += pyenv pyenv.install pyenv.uninstall
MANAGE += pypi.upload pypi.upload.test
MANAGE += format.python MANAGE += format.python
MANAGE += test.yamllint test.pylint test.pyright test.black test.pybabel test.unit test.coverage test.robot test.rst test.clean MANAGE += test.yamllint test.pylint test.pyright test.black test.pybabel test.unit test.coverage test.robot test.rst test.clean
MANAGE += themes.all themes.simple themes.simple.test pygments.less MANAGE += themes.all themes.simple themes.simple.test pygments.less

View File

@ -66,7 +66,7 @@ A user_, admin_ and developer_ handbook is available on the homepage_.
Contact Contact
======= =======
Ask questions or chat with the SearXNG community (this not a chatbot) on Ask questions or just chat about SearXNG on
IRC IRC
`#searxng on libera.chat <https://web.libera.chat/?channel=#searxng>`_ `#searxng on libera.chat <https://web.libera.chat/?channel=#searxng>`_
@ -121,9 +121,9 @@ You can contribute from your browser using `GitHub Codespaces`_:
- Click on the ``Codespaces`` tab instead of ``Local`` - Click on the ``Codespaces`` tab instead of ``Local``
- Click on ``Create codespace on master`` - Click on ``Create codespace on master``
- VSCode is going to start in the browser - VSCode is going to start in the browser
- Wait for ``git pull && make install`` to appear and then disappear - Wait for ``git pull && make install`` to appears and then to disapear
- You have `120 hours per month`_ (see also your `list of existing Codespaces`_) - You have `120 hours per month`_ (see also your `list of existing Codespaces`_)
- You can start SearXNG using ``make run`` in the terminal or by pressing ``Ctrl+Shift+B`` - You can start SearXNG using ``make run`` in the terminal or by pressing ``Ctrl+Shift+B``.
.. _GitHub Codespaces: https://docs.github.com/en/codespaces/overview .. _GitHub Codespaces: https://docs.github.com/en/codespaces/overview
.. _120 hours per month: https://github.com/settings/billing .. _120 hours per month: https://github.com/settings/billing

View File

@ -175,4 +175,4 @@ unset MORTY_KEY
# Start uwsgi # Start uwsgi
printf 'Listen on %s\n' "${BIND_ADDRESS}" printf 'Listen on %s\n' "${BIND_ADDRESS}"
exec uwsgi --master --uid searxng --gid searxng --http-socket "${BIND_ADDRESS}" "${UWSGI_SETTINGS_PATH}" exec su-exec searxng:searxng uwsgi --master --http-socket "${BIND_ADDRESS}" "${UWSGI_SETTINGS_PATH}"

View File

@ -4,12 +4,8 @@ uid = searxng
gid = searxng gid = searxng
# Number of workers (usually CPU count) # Number of workers (usually CPU count)
# default value: %k (= number of CPU core, see Dockerfile) workers = %k
workers = $(UWSGI_WORKERS) threads = 4
# Number of threads per worker
# default value: 4 (see Dockerfile)
threads = $(UWSGI_THREADS)
# The right granted on the created socket # The right granted on the created socket
chmod-socket = 666 chmod-socket = 666
@ -42,13 +38,9 @@ buffer-size = 8192
# See https://github.com/searx/searx-docker/issues/24 # See https://github.com/searx/searx-docker/issues/24
add-header = Connection: close add-header = Connection: close
# Follow SIGTERM convention
# See https://github.com/searxng/searxng/issues/3427
die-on-term
# uwsgi serves the static files # uwsgi serves the static files
# expires set to one year since there are hashes
static-map = /static=/usr/local/searxng/searx/static static-map = /static=/usr/local/searxng/searx/static
# expires set to one day static-expires = /* 31557600
static-expires = /* 86400
static-gzip-all = True static-gzip-all = True
offload-threads = %k offload-threads = %k

View File

@ -69,6 +69,10 @@ Sample response
{ {
"enabled": true, "enabled": true,
"name": "HTTPS rewrite" "name": "HTTPS rewrite"
},
{
"enabled": false,
"name": "Vim-like hotkeys"
} }
], ],
"safe_search": 0 "safe_search": 0
@ -84,9 +88,9 @@ HTML of the site. URL of the SearXNG instance and values are customizable.
.. code:: html .. code:: html
<form method="post" action="https://example.org/"> <form method="post" action="https://example.org/">
<!-- search --> <input type="text" name="q"> <!-- search --> <input type="text" name="q" />
<!-- categories --> <input type="hidden" name="categories" value="general,social media"> <!-- categories --> <input type="hidden" name="categories" value="general,social media" />
<!-- language --> <input type="hidden" name="lang" value="all"> <!-- language --> <input type="hidden" name="lang" value="all" />
<!-- locale --> <input type="hidden" name="locale" value="en"> <!-- locale --> <input type="hidden" name="locale" value="en" />
<!-- date filter --> <input type="hidden" name="time_range" value="month"> <!-- date filter --> <input type="hidden" name="time_range" value="month" />
</form> </form>

View File

@ -15,8 +15,6 @@ Administrator documentation
installation-apache installation-apache
update-searxng update-searxng
answer-captcha answer-captcha
searx.favicons
searx.limiter
api api
architecture architecture
plugins plugins

View File

@ -92,9 +92,6 @@ instance using `docker run <https://docs.docker.com/engine/reference/run/>`_:
searxng/searxng searxng/searxng
2f998.... # container's ID 2f998.... # container's ID
The environment variables UWSGI_WORKERS and UWSGI_THREADS overwrite the default
number of UWSGI processes and UWSGI threads specified in `/etc/searxng/uwsgi.ini`.
Open your WEB browser and visit the URL: Open your WEB browser and visit the URL:
.. code:: sh .. code:: sh

View File

@ -96,7 +96,7 @@ Modify the ``/etc/searxng/settings.yml`` to your needs:
.. literalinclude:: ../../utils/templates/etc/searxng/settings.yml .. literalinclude:: ../../utils/templates/etc/searxng/settings.yml
:language: yaml :language: yaml
:end-before: # hostnames: :end-before: # hostname_replace:
To see the entire file jump to :origin:`utils/templates/etc/searxng/settings.yml` To see the entire file jump to :origin:`utils/templates/etc/searxng/settings.yml`
@ -104,7 +104,7 @@ Modify the ``/etc/searxng/settings.yml`` to your needs:
.. literalinclude:: ../../searx/settings.yml .. literalinclude:: ../../searx/settings.yml
:language: yaml :language: yaml
:end-before: # hostnames: :end-before: # hostname_replace:
To see the entire file jump to :origin:`searx/settings.yml` To see the entire file jump to :origin:`searx/settings.yml`

View File

@ -1,251 +0,0 @@
.. _favicons:
========
Favicons
========
.. sidebar:: warning
Don't activate the favicons before reading the documentation.
.. contents::
:depth: 2
:local:
:backlinks: entry
Activating the favicons in SearXNG is very easy, but this **generates a
significantly higher load** in the client/server communication and increases
resources needed on the server.
To mitigate these disadvantages, various methods have been implemented,
including a *cache*. The cache must be parameterized according to your own
requirements and maintained regularly.
To activate favicons in SearXNG's result list, set a default
``favicon_resolver`` in the :ref:`search <settings search>` settings:
.. code:: yaml
search:
favicon_resolver: "duckduckgo"
By default and without any extensions, SearXNG serves these resolvers:
- ``duckduckgo``
- ``allesedv``
- ``google``
- ``yandex``
With the above setting favicons are displayed, the user has the option to
deactivate this feature in his settings. If the user is to have the option of
selecting from several *resolvers*, a further setting is required / but this
setting will be discussed :ref:`later <register resolvers>` in this article,
first we have to setup the favicons cache.
Infrastructure
==============
The infrastructure for providing the favicons essentially consists of three
parts:
- :py:obj:`Favicons-Proxy <.favicons.proxy>` (aka *proxy*)
- :py:obj:`Favicons-Resolvers <.favicons.resolvers>` (aka *resolver*)
- :py:obj:`Favicons-Cache <.favicons.cache>` (aka *cache*)
To protect the privacy of users, the favicons are provided via a *proxy*. This
*proxy* is automatically activated with the above activation of a *resolver*.
Additional requests are required to provide the favicons: firstly, the *proxy*
must process the incoming requests and secondly, the *resolver* must make
outgoing requests to obtain the favicons from external sources.
A *cache* has been developed to massively reduce both, incoming and outgoing
requests. This *cache* is also activated automatically with the above
activation of a *resolver*. In its defaults, however, the *cache* is minimal
and not well suitable for a production environment!
.. _favicon cache setup:
Setting up the cache
====================
To parameterize the *cache* and more settings of the favicons infrastructure, a
TOML_ configuration is created in the file ``/etc/searxng/favicons.toml``.
.. code:: toml
[favicons]
cfg_schema = 1 # config's schema version no.
[favicons.cache]
db_url = "/var/cache/searxng/faviconcache.db" # default: "/tmp/faviconcache.db"
LIMIT_TOTAL_BYTES = 2147483648 # 2 GB / default: 50 MB
# HOLD_TIME = 5184000 # 60 days / default: 30 days
# BLOB_MAX_BYTES = 40960 # 40 KB / default 20 KB
# MAINTENANCE_MODE = "off" # default: "auto"
# MAINTENANCE_PERIOD = 600 # 10min / default: 1h
:py:obj:`cfg_schema <.FaviconConfig.cfg_schema>`:
Is required to trigger any processes required for future upgrades / don't
change it.
:py:obj:`cache.db_url <.FaviconCacheConfig.db_url>`:
The path to the (SQLite_) database file. The default path is in the `/tmp`_
folder, which is deleted on every reboot and is therefore unsuitable for a
production environment. The FHS_ provides the folder for the
application cache
The FHS_ provides the folder `/var/cache`_ for the cache of applications, so a
suitable storage location of SearXNG's caches is folder ``/var/cache/searxng``.
In container systems, a volume should be mounted for this folder and in a
standard installation (compare :ref:`create searxng user`), the folder must be
created and the user under which the SearXNG process is running must be given
write permission to this folder.
.. code:: bash
$ sudo mkdir /var/cache/searxng
$ sudo chown root:searxng /var/cache/searxng/
$ sudo chmod g+w /var/cache/searxng/
:py:obj:`cache.LIMIT_TOTAL_BYTES <.FaviconCacheConfig.LIMIT_TOTAL_BYTES>`:
Maximum of bytes stored in the cache of all blobs. The limit is only reached
at each maintenance interval after which the oldest BLOBs are deleted; the
limit is exceeded during the maintenance period.
.. attention::
If the maintenance period is too long or maintenance is switched
off completely, the cache grows uncontrollably.
SearXNG hosters can change other parameters of the cache as required:
- :py:obj:`cache.HOLD_TIME <.FaviconCacheConfig.HOLD_TIME>`
- :py:obj:`cache.BLOB_MAX_BYTES <.FaviconCacheConfig.BLOB_MAX_BYTES>`
Maintenance of the cache
------------------------
Regular maintenance of the cache is required! By default, regular maintenance
is triggered automatically as part of the client requests:
- :py:obj:`cache.MAINTENANCE_MODE <.FaviconCacheConfig.MAINTENANCE_MODE>` (default ``auto``)
- :py:obj:`cache.MAINTENANCE_PERIOD <.FaviconCacheConfig.MAINTENANCE_PERIOD>` (default ``6000`` / 1h)
As an alternative to maintenance as part of the client request process, it is
also possible to carry out maintenance using an external process. For example,
by creating a :man:`crontab` entry for maintenance:
.. code:: bash
$ python -m searx.favicons cache maintenance
The following command can be used to display the state of the cache:
.. code:: bash
$ python -m searx.favicons cache state
.. _favicon proxy setup:
Proxy configuration
===================
Most of the options of the :py:obj:`Favicons-Proxy <.favicons.proxy>` are
already set sensibly with settings from the :ref:`settings.yml <searxng
settings.yml>` and should not normally be adjusted.
.. code:: toml
[favicons.proxy]
max_age = 5184000 # 60 days / default: 7 days (604800 sec)
:py:obj:`max_age <.FaviconProxyConfig.max_age>`:
The `HTTP Cache-Control max-age`_ response directive indicates that the
response remains fresh until N seconds after the response is generated. This
setting therefore determines how long a favicon remains in the client's cache.
As a rule, in the favicons infrastructure of SearXNG's this setting only
affects favicons whose byte size exceeds :ref:`BLOB_MAX_BYTES <favicon cache
setup>` (the other favicons that are already in the cache are embedded as
`data URL`_ in the :py:obj:`generated HTML <.favicons.proxy.favicon_url>`,
which can greatly reduce the number of additional requests).
.. _register resolvers:
Register resolvers
------------------
A :py:obj:`resolver <.favicon.resolvers>` is a function that obtains the favicon
from an external source. The resolver functions available to the user are
registered with their fully qualified name (FQN_) in a ``resolver_map``.
If no ``resolver_map`` is defined in the ``favicon.toml``, the favicon
infrastructure of SearXNG generates this ``resolver_map`` automatically
depending on the ``settings.yml``. SearXNG would automatically generate the
following TOML configuration from the following YAML configuration:
.. code:: yaml
search:
favicon_resolver: "duckduckgo"
.. code:: toml
[favicons.proxy.resolver_map]
"duckduckgo" = "searx.favicons.resolvers.duckduckgo"
If this automatism is not desired, then (and only then) a separate
``resolver_map`` must be created. For example, to give the user two resolvers to
choose from, the following configuration could be used:
.. code:: toml
[favicons.proxy.resolver_map]
"duckduckgo" = "searx.favicons.resolvers.duckduckgo"
"allesedv" = "searx.favicons.resolvers.allesedv"
# "google" = "searx.favicons.resolvers.google"
# "yandex" = "searx.favicons.resolvers.yandex"
.. note::
With each resolver, the resource requirement increases significantly.
The number of resolvers increases:
- the number of incoming/outgoing requests and
- the number of favicons to be stored in the cache.
In the following we list the resolvers available in the core of SearXNG, but via
the FQN_ it is also possible to implement your own resolvers and integrate them
into the *proxy*:
- :py:obj:`searx.favicons.resolvers.duckduckgo`
- :py:obj:`searx.favicons.resolvers.allesedv`
- :py:obj:`searx.favicons.resolvers.google`
- :py:obj:`searx.favicons.resolvers.yandex`
.. _SQLite:
https://www.sqlite.org/
.. _FHS:
https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
.. _`/var/cache`:
https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s05.html
.. _`/tmp`:
https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s18.html
.. _TOML:
https://toml.io/en/
.. _HTTP Cache-Control max-age:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives
.. _data URL:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
.. _FQN: https://en.wikipedia.org/wiki/Fully_qualified_name

View File

@ -1,17 +0,0 @@
.. _limiter:
=======
Limiter
=======
.. sidebar:: info
The limiter requires a :ref:`Redis <settings redis>` database.
.. contents::
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.limiter
:members:

View File

@ -1,5 +1,3 @@
.. _searxng settings.yml:
======== ========
Settings Settings
======== ========

View File

@ -204,7 +204,7 @@ Example configuration to restrict access to the Arch Linux Wiki engine:
tokens: [ 'my-secret-token' ] tokens: [ 'my-secret-token' ]
Unless a user has configured the right token, the engine is going to be hidden Unless a user has configured the right token, the engine is going to be hidden
from them. It is not going to be included in the list of engines on the from him/her. It is not going to be included in the list of engines on the
Preferences page and in the output of `/config` REST API call. Preferences page and in the output of `/config` REST API call.
Tokens can be added to one's configuration on the Preferences page under "Engine Tokens can be added to one's configuration on the Preferences page under "Engine

View File

@ -9,7 +9,6 @@
search: search:
safe_search: 0 safe_search: 0
autocomplete: "" autocomplete: ""
favicon_resolver: ""
default_lang: "" default_lang: ""
ban_time_on_fail: 5 ban_time_on_fail: 5
max_ban_time_on_fail: 120 max_ban_time_on_fail: 120
@ -42,11 +41,6 @@
- ``qwant`` - ``qwant``
- ``wikipedia`` - ``wikipedia``
``favicon_resolver``:
To activate favicons in SearXNG's result list select a default
favicon-resolver, leave blank to turn off the feature. Don't activate the
favicons before reading the :ref:`Favicons documentation <favicons>`.
``default_lang``: ``default_lang``:
Default search language - leave blank to detect from browser information or Default search language - leave blank to detect from browser information or
use codes from :origin:`searx/languages.py`. use codes from :origin:`searx/languages.py`.

View File

@ -12,42 +12,39 @@
bind_address: "127.0.0.1" bind_address: "127.0.0.1"
secret_key: "ultrasecretkey" # change this! secret_key: "ultrasecretkey" # change this!
limiter: false limiter: false
public_instance: false
image_proxy: false image_proxy: false
default_http_headers: default_http_headers:
X-Content-Type-Options : nosniff X-Content-Type-Options : nosniff
X-XSS-Protection : 1; mode=block
X-Download-Options : noopen X-Download-Options : noopen
X-Robots-Tag : noindex, nofollow X-Robots-Tag : noindex, nofollow
Referrer-Policy : no-referrer Referrer-Policy : no-referrer
``base_url`` : ``$SEARXNG_URL``
The base URL where SearXNG is deployed. Used to create correct inbound links.
``port`` & ``bind_address``: ``$SEARXNG_PORT`` & ``$SEARXNG_BIND_ADDRESS`` ``base_url`` : ``$SEARXNG_URL`` :ref:`buildenv <make buildenv>`
The base URL where SearXNG is deployed. Used to create correct inbound links.
If you change the value, don't forget to rebuild instance's environment
(:ref:`utils/brand.env <make buildenv>`)
``port`` & ``bind_address``: ``$SEARXNG_PORT`` & ``$SEARXNG_BIND_ADDRESS`` :ref:`buildenv <make buildenv>`
Port number and *bind address* of the SearXNG web application if you run it Port number and *bind address* of the SearXNG web application if you run it
directly using ``python searx/webapp.py``. Doesn't apply to a SearXNG directly using ``python searx/webapp.py``. Doesn't apply to a SearXNG
services running behind a proxy and using socket communications. services running behind a proxy and using socket communications. If you
change the value, don't forget to rebuild instance's environment
(:ref:`utils/brand.env <make buildenv>`)
``secret_key`` : ``$SEARXNG_SECRET`` ``secret_key`` : ``$SEARXNG_SECRET``
Used for cryptography purpose. Used for cryptography purpose.
``limiter`` : ``$SEARXNG_LIMITER`` .. _limiter:
``limiter`` :
Rate limit the number of request on the instance, block some bots. The Rate limit the number of request on the instance, block some bots. The
:ref:`limiter` requires a :ref:`settings redis` database. :ref:`limiter src` requires a :ref:`settings redis` database.
.. _public_instance:
``public_instance`` : ``$SEARXNG_PUBLIC_INSTANCE``
Setting that allows to enable features specifically for public instances (not
needed for local usage). By set to ``true`` the following features are
activated:
- :py:obj:`searx.botdetection.link_token` in the :ref:`limiter`
.. _image_proxy: .. _image_proxy:
``image_proxy`` : ``$SEARXNG_IMAGE_PROXY`` ``image_proxy`` :
Allow your instance of SearXNG of being able to proxy images. Uses memory space. Allow your instance of SearXNG of being able to proxy images. Uses memory space.
.. _HTTP headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers .. _HTTP headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers

View File

@ -20,11 +20,10 @@
theme_args: theme_args:
simple_style: auto simple_style: auto
search_on_category_select: true search_on_category_select: true
hotkeys: default
.. _static_use_hash: .. _static_use_hash:
``static_use_hash`` : ``$SEARXNG_STATIC_USE_HASH`` ``static_use_hash`` :
Enables `cache busting`_ of static files. Enables `cache busting`_ of static files.
``default_locale`` : ``default_locale`` :
@ -58,13 +57,10 @@
Name of the theme you want to use by default on your SearXNG instance. Name of the theme you want to use by default on your SearXNG instance.
``theme_args.simple_style``: ``theme_args.simple_style``:
Style of simple theme: ``auto``, ``light``, ``dark``, ``black`` Style of simple theme: ``auto``, ``light``, ``dark``
``results_on_new_tab``: ``results_on_new_tab``:
Open result links in a new tab by default. Open result links in a new tab by default.
``search_on_category_select``: ``search_on_category_select``:
Perform search immediately if a category selected. Disable to select multiple categories. Perform search immediately if a category selected. Disable to select multiple categories.
``hotkeys``:
Hotkeys to use in the search interface: ``default``, ``vim`` (Vim-like).

View File

@ -113,7 +113,7 @@ ${fedora_build}
(${SERVICE_USER})$ command -v python && python --version (${SERVICE_USER})$ command -v python && python --version
$SEARXNG_PYENV/bin/python $SEARXNG_PYENV/bin/python
Python 3.11.10 Python 3.8.1
# update pip's boilerplate .. # update pip's boilerplate ..
pip install -U pip pip install -U pip
@ -123,7 +123,7 @@ ${fedora_build}
# jump to SearXNG's working tree and install SearXNG into virtualenv # jump to SearXNG's working tree and install SearXNG into virtualenv
(${SERVICE_USER})$ cd \"$SEARXNG_SRC\" (${SERVICE_USER})$ cd \"$SEARXNG_SRC\"
(${SERVICE_USER})$ pip install --use-pep517 --no-build-isolation -e . (${SERVICE_USER})$ pip install -e .
.. END manage.sh update_packages .. END manage.sh update_packages

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import sys, os import sys, os
from pathlib import Path
from pallets_sphinx_themes import ProjectLink from pallets_sphinx_themes import ProjectLink
from searx import get_setting from searx import get_setting
@ -13,6 +13,7 @@ project = 'SearXNG'
copyright = 'SearXNG team' copyright = 'SearXNG team'
author = 'SearXNG team' author = 'SearXNG team'
release, version = VERSION_STRING, VERSION_STRING release, version = VERSION_STRING, VERSION_STRING
SEARXNG_URL = get_setting('server.base_url') or 'https://example.org/searxng' SEARXNG_URL = get_setting('server.base_url') or 'https://example.org/searxng'
ISSUE_URL = get_setting('brand.issue_url') ISSUE_URL = get_setting('brand.issue_url')
DOCS_URL = get_setting('brand.docs_url') DOCS_URL = get_setting('brand.docs_url')
@ -21,9 +22,6 @@ PRIVACYPOLICY_URL = get_setting('general.privacypolicy_url')
CONTACT_URL = get_setting('general.contact_url') CONTACT_URL = get_setting('general.contact_url')
WIKI_URL = get_setting('brand.wiki_url') WIKI_URL = get_setting('brand.wiki_url')
SOURCEDIR = Path(__file__).parent.parent / "searx"
os.environ['SOURCEDIR'] = str(SOURCEDIR)
# hint: sphinx.ext.viewcode won't highlight when 'highlight_language' [1] is set # hint: sphinx.ext.viewcode won't highlight when 'highlight_language' [1] is set
# to string 'none' [2] # to string 'none' [2]
# #
@ -127,7 +125,6 @@ extensions = [
"sphinx_tabs.tabs", # https://github.com/djungelorm/sphinx-tabs "sphinx_tabs.tabs", # https://github.com/djungelorm/sphinx-tabs
'myst_parser', # https://www.sphinx-doc.org/en/master/usage/markdown.html 'myst_parser', # https://www.sphinx-doc.org/en/master/usage/markdown.html
'notfound.extension', # https://github.com/readthedocs/sphinx-notfound-page 'notfound.extension', # https://github.com/readthedocs/sphinx-notfound-page
'sphinxcontrib.autodoc_pydantic', # https://github.com/mansenfranzen/autodoc_pydantic
] ]
autodoc_default_options = { autodoc_default_options = {

View File

@ -271,54 +271,22 @@ type.
``images`` ``images``
---------- ----------
.. list-table:: Parameter of the **images** media type .. table:: Parameter of the **images** media type:
:header-rows: 2
:width: 100% :width: 100%
* - result-parameter ========================= =====================================================
- Python type result-parameter information
- information ------------------------- -----------------------------------------------------
template is set to ``images.html``
* - template ========================= =====================================================
- :py:class:`str` url string, url to the result site
- is set to ``images.html`` title string, title of the result *(partly implemented)*
content *(partly implemented)*
* - url publishedDate :py:class:`datetime.datetime`,
- :py:class:`str` time of publish *(partly implemented)*
- url to the result site img\_src string, url to the result image
thumbnail\_src string, url to a small-preview image
* - title ========================= =====================================================
- :py:class:`str`
- title of the result
* - content
- :py:class:`str`
- description of the image
* - publishedDate
- :py:class:`datetime <datetime.datetime>`
- time of publish
* - img_src
- :py:class:`str`
- url to the result image
* - thumbnail_src
- :py:class:`str`
- url to a small-preview image
* - resolution
- :py:class:`str`
- the resolution of the image (e.g. ``1920 x 1080`` pixel)
* - img_format
- :py:class:`str`
- the format of the image (e.g. ``png``)
* - filesize
- :py:class:`str`
- size of bytes in :py:obj:`human readable <searx.humanize_bytes>` notation
(e.g. ``MB`` for 1024 \* 1024 Bytes filesize).
.. _template videos: .. _template videos:
@ -339,8 +307,6 @@ type.
content *(not implemented yet)* content *(not implemented yet)*
publishedDate :py:class:`datetime.datetime`, time of publish publishedDate :py:class:`datetime.datetime`, time of publish
thumbnail string, url to a small-preview image thumbnail string, url to a small-preview image
length :py:class:`datetime.timedelta`, duration of result
views string, view count in humanized number format
========================= ===================================================== ========================= =====================================================
@ -500,72 +466,3 @@ type.
- :py:class:`str` - :py:class:`str`
- URL to full article, HTML version - URL to full article, HTML version
.. _template packages:
``packages``
------------
.. list-table:: Parameter of the **packages** media type
:header-rows: 2
:width: 100%
* - result-parameter
- Python type
- information
* - template
- :py:class:`str`
- is set to ``packages.html``
* - title
- :py:class:`str`
- title of the result
* - content
- :py:class:`str`
- abstract
* - package_name
- :py:class:`str`
- the name of the package
* - version
- :py:class:`str`
- the current version of the package
* - maintainer
- :py:class:`str`
- the maintainer or author of the project
* - publishedDate
- :py:class:`datetime <datetime.datetime>`
- date of latest update or release
* - tags
- :py:class:`List <list>`\ [\ :py:class:`str`\ ]
- free tag list
* - popularity
- :py:class:`str`
- the popularity of the package, e.g. rating or download count
* - license_name
- :py:class:`str`
- the name of the license
* - license_url
- :py:class:`str`
- the web location of a license copy
* - homepage
- :py:class:`str`
- the url of the project's homepage
* - source_code_url
- :py:class:`str`
- the location of the project's source code
* - links
- :py:class:`dict`
- additional links in the form of ``{'link_name': 'http://example.com'}``

View File

@ -25,7 +25,7 @@ Relational Database Management System (RDBMS) are supported:
- :ref:`engine sqlite` - :ref:`engine sqlite`
- :ref:`engine postgresql` - :ref:`engine postgresql`
- :ref:`engine mysql_server` & :ref:`engine mariadb_server` - :ref:`engine mysql_server`
All of the engines above are just commented out in the :origin:`settings.yml All of the engines above are just commented out in the :origin:`settings.yml
<searx/settings.yml>`, as you have to set the required attributes for the <searx/settings.yml>`, as you have to set the required attributes for the
@ -119,16 +119,3 @@ MySQL
.. automodule:: searx.engines.mysql_server .. automodule:: searx.engines.mysql_server
:members: :members:
.. _engine mariadb_server:
MariaDB
--------
.. sidebar:: info
- :origin:`mariadb_server.py <searx/engines/mariadb_server.py>`
- ``pip install`` :pypi:`mariadb <mariadb>`
.. automodule:: searx.engines.mariadb_server
:members:

View File

@ -1,13 +0,0 @@
.. _alpinelinux engine:
=====================
Alpine Linux Packages
=====================
.. contents::
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.alpinelinux
:members:

View File

@ -1,13 +0,0 @@
.. _bpb engine:
===
Bpb
===
.. contents:: Contents
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.bpb
:members:

View File

@ -1,8 +0,0 @@
.. _discourse engine:
================
Discourse Forums
================
.. automodule:: searx.engines.discourse
:members:

View File

@ -12,7 +12,7 @@ DuckDuckGo Engines
.. automodule:: searx.engines.duckduckgo .. automodule:: searx.engines.duckduckgo
:members: :members:
.. automodule:: searx.engines.duckduckgo_extra .. automodule:: searx.engines.duckduckgo_images
:members: :members:
.. automodule:: searx.engines.duckduckgo_definitions .. automodule:: searx.engines.duckduckgo_definitions

View File

@ -1,8 +0,0 @@
.. _gitea geizhals:
========
Geizhals
========
.. automodule:: searx.engines.geizhals
:members:

View File

@ -1,8 +0,0 @@
.. _gitea engine:
=====
Gitea
=====
.. automodule:: searx.engines.gitea
:members:

View File

@ -1,8 +0,0 @@
.. _gitlab engine:
======
GitLab
======
.. automodule:: searx.engines.gitlab
:members:

View File

@ -1,13 +0,0 @@
.. _mastodon engine:
========
Mastodon
========
.. contents:: Contents
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.mastodon
:members:

View File

@ -1,13 +0,0 @@
.. _mrs engine:
=========================
Matrix Rooms Search (MRS)
=========================
.. contents:: Contents
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.mrs
:members:

View File

@ -1,13 +0,0 @@
.. _voidlinux mullvad_leta:
============
Mullvad-Leta
============
.. contents:: Contents
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.mullvad_leta
:members:

View File

@ -1,13 +0,0 @@
.. _engine presearch:
================
Presearch Engine
================
.. contents::
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.presearch
:members:

View File

@ -1,13 +0,0 @@
.. _RadioBrowser engine:
============
RadioBrowser
============
.. contents::
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.radio_browser
:members:

View File

@ -1,13 +0,0 @@
.. _voidlinux engine:
==========================
Void Linux binary packages
==========================
.. contents:: Contents
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.voidlinux
:members:

View File

@ -6,7 +6,6 @@ Developer documentation
:maxdepth: 2 :maxdepth: 2
quickstart quickstart
rtm_asdf
contribution_guide contribution_guide
engines/index engines/index
search_api search_api

View File

@ -61,9 +61,13 @@ working tree and release a ``make install`` to get a virtualenv with a
$ make install $ make install
PYENV [virtualenv] installing ./requirements*.txt into local/py3 PYENV [virtualenv] installing ./requirements*.txt into local/py3
... ...
PYENV [install] pip install --use-pep517 --no-build-isolation -e 'searx[test]' PYENV OK
PYENV [install] pip install -e 'searx[test]'
... ...
Successfully installed searxng-2023.7.19+a446dea1b Successfully installed argparse-1.4.0 searx
BUILDENV INFO:searx:load the default settings from ./searx/settings.yml
BUILDENV INFO:searx:Initialisation done
BUILDENV build utils/brand.env
If you release ``make install`` multiple times the installation will only If you release ``make install`` multiple times the installation will only
rebuild if the sha256 sum of the *requirement files* fails. With other words: rebuild if the sha256 sum of the *requirement files* fails. With other words:
@ -78,9 +82,13 @@ the check fails if you edit the requirements listed in
... ...
PYENV [virtualenv] installing ./requirements*.txt into local/py3 PYENV [virtualenv] installing ./requirements*.txt into local/py3
... ...
PYENV [install] pip install --use-pep517 --no-build-isolation -e 'searx[test]' PYENV OK
PYENV [install] pip install -e 'searx[test]'
... ...
Successfully installed searxng-2023.7.19+a446dea1b Successfully installed argparse-1.4.0 searx
BUILDENV INFO:searx:load the default settings from ./searx/settings.yml
BUILDENV INFO:searx:Initialisation done
BUILDENV build utils/brand.env
.. sidebar:: drop environment .. sidebar:: drop environment
@ -90,6 +98,67 @@ the check fails if you edit the requirements listed in
If you think, something goes wrong with your ./local environment or you change If you think, something goes wrong with your ./local environment or you change
the :origin:`setup.py` file, you have to call :ref:`make clean`. the :origin:`setup.py` file, you have to call :ref:`make clean`.
.. _make buildenv:
``make buildenv``
=================
Rebuild instance's environment with the modified settings from the
:ref:`settings brand` and :ref:`settings server` section of your
:ref:`settings.yml <settings location>`.
What is the :origin:`utils/brand.env` needed for and why do you need to rebuild
it if necessary?
Short answer: :ref:`installation and maintenance <searxng maintenance>`
scripts are running outside of instance's runtime environment and need some
values defined in the runtime environment.
All the SearXNG setups are centralized in the :ref:`settings.yml` file. This
setup is available as long we are in a *installed instance*. E.g. the
*installed instance* on the server or the *installed developer instance* at
``./local`` (the later one is created by a :ref:`make install <make install>` or
:ref:`make run <make run>`).
Tasks running outside of an *installed instance*, especially :ref:`installation
and maintenance <searxng maintenance>` tasks running at (pre-) installation time
do not have access to the SearXNG setup (from a *installed instance*). Those
tasks need a *build environment*.
The ``make buildenv`` target will update the *build environment* in:
- :origin:`utils/brand.env`
Tasks running outside of an *installed instance*, need the following settings
from the YAML configuration:
- ``SEARXNG_URL`` from :ref:`server.base_url <settings server>` (aka
``PUBLIC_URL``)
- ``SEARXNG_BIND_ADDRESS`` from :ref:`server.bind_address <settings server>`
- ``SEARXNG_PORT`` from :ref:`server.port <settings server>`
The ``GIT_URL`` and ``GIT_BRANCH`` in the origin:`utils/brand.env` file, are
read from the git VCS and the branch that is checked out when ``make
buildenv`` command runs.
.. _brand:
**I would like to create my own brand, how should I proceed?**
Create a remote branch (``example.org``), checkout the remote branch (on your
local developer desktop) and in the :origin:`searx/settings.yml` file in the
:ref:`settings server` section set ``base_url``. Run ``make buildenv`` and
create a commit for your brand.
On your server you clone the branch (``example.org``) into your HOME folder
``~`` from where you run the :ref:`installation <installation>` and
:ref:`maintenance <searxng maintenance>` task.
To upgrade you brand, rebase on SearXNG's master branch (on your local
developer desktop), force push it to your remote branch. Go to your server, do
a force pull and run :ref:`sudo -H ./utils/searxng.sh instance update <update
searxng>`.
.. _make node.env: .. _make node.env:
Node.js environment (``make node.env``) Node.js environment (``make node.env``)

View File

@ -292,7 +292,7 @@ content becomes smart.
files & folders origin :origin:`docs/dev/reST.rst` ``:origin:`docs/dev/reST.rst``` files & folders origin :origin:`docs/dev/reST.rst` ``:origin:`docs/dev/reST.rst```
pull request :pull:`4` ``:pull:`4``` pull request :pull:`4` ``:pull:`4```
patch :patch:`af2cae6` ``:patch:`af2cae6``` patch :patch:`af2cae6` ``:patch:`af2cae6```
PyPi package :pypi:`httpx` ``:pypi:`httpx``` PyPi package :pypi:`searx` ``:pypi:`searx```
manual page man :man:`bash` ``:man:`bash``` manual page man :man:`bash` ``:man:`bash```
intersphinx_ intersphinx_
-------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------

View File

@ -1,121 +0,0 @@
==================
Runtime Management
==================
The runtimes are managed with asdf and are activated in this project via the
`.tool-versions <.tool-versions>`_. If you have not yet installed asdf_, then
chapter :ref:`introduce asdf` may be of help to you.
.. contents::
:depth: 2
:local:
:backlinks: entry
Get started
===========
If you have asdf installed you can install the runtimes of this project by:
.. code:: bash
$ cd /path/to/searxng
$ asdf install # will install runtimes listed in .tool-versions
...
Manage Versions
===============
If you want to perform a ``test`` with special runtime versions of nodejs,
python or shellcheck, you can patch the ``.tool-versions``:
.. code:: diff
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,2 +1,2 @@
-python 3.12.0
-shellcheck 0.9.0
+python 3.11.6
+shellcheck 0.8.0
To install use ``asdf install`` again. If the runtime tools have changed, any
existing (nodejs and python) environments should be cleaned up with a ``make
clean``.
.. code:: bash
$ asdf install
...
$ make clean test
.. _introduce asdf:
Introduce asdf
==============
To `download asdf`_ and `install asdf`_:
.. code:: bash
$ git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch <version>
$ echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc
$ echo '. "$HOME/.asdf/completions/asdf.bash"' >> ~/.bashrc
Start a new shell and try to `install plugins`_:
.. code:: bash
$ asdf plugin-list-all | grep -E '(golang|python|nodejs|shellcheck).git'
golang https://github.com/asdf-community/asdf-golang.git
nodejs https://github.com/asdf-vm/asdf-nodejs.git
python https://github.com/danhper/asdf-python.git
shellcheck https://github.com/luizm/asdf-shellcheck.git
$ asdf plugin add golang https://github.com/asdf-community/asdf-golang.git
$ asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
$ asdf plugin add python https://github.com/danhper/asdf-python.git
$ asdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git
Each plugin has dependencies, to compile runtimes visit the URLs from above and
look out for the dependencies you need to install on your OS, on Debian for the
runtimes listed above you will need:
.. code:: bash
$ sudo apt update
$ sudo apt install \
dirmngr gpg curl gawk coreutils build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
With dependencies installed you can install/compile runtimes:
.. code:: bash
$ asdf install golang latest
$ asdf install nodejs latest
$ asdf install python latest
$ asdf install shellcheck latest
Python will be compiled and will take a while.
In the repository the version is defined in `.tool-versions`_. Outside the
repository, its recommended that the runtime should use the versions of the OS
(`Fallback to System Version`_) / if not already done register the system
versions global:
.. code:: bash
$ cd /
$ asdf global golang system
$ asdf global nodejs system
$ asdf global python system
$ asdf global shellcheck system
.. _asdf: https://asdf-vm.com/
.. _download asdf: https://asdf-vm.com/guide/getting-started.html#_2-download-asdf
.. _install asdf: https://asdf-vm.com/guide/getting-started.html#_3-install-asdf
.. _install plugins: https://asdf-vm.com/guide/getting-started.html#install-the-plugin
.. _Fallback to System Version: https://asdf-vm.com/manage/versions.html#fallback-to-system-version

View File

@ -69,7 +69,7 @@ Parameters
``autocomplete`` : default from :ref:`settings search` ``autocomplete`` : default from :ref:`settings search`
[ ``google``, ``dbpedia``, ``duckduckgo``, ``mwmbl``, ``startpage``, [ ``google``, ``dbpedia``, ``duckduckgo``, ``mwmbl``, ``startpage``,
``wikipedia``, ``stract``, ``swisscows``, ``qwant`` ] ``wikipedia``, ``swisscows``, ``qwant`` ]
Service which completes words as you type. Service which completes words as you type.
@ -103,14 +103,14 @@ Parameters
.. disabled by default .. disabled by default
``Hostnames_plugin``, ``Open_Access_DOI_rewrite``, ``Hostname_replace``, ``Open_Access_DOI_rewrite``,
``Vim-like_hotkeys``, ``Tor_check_plugin`` ``Vim-like_hotkeys``, ``Tor_check_plugin``
``disabled_plugins``: optional ``disabled_plugins``: optional
List of disabled plugins. List of disabled plugins.
:default: :default:
``Hostnames_plugin``, ``Open_Access_DOI_rewrite``, ``Hostname_replace``, ``Open_Access_DOI_rewrite``,
``Vim-like_hotkeys``, ``Tor_check_plugin`` ``Vim-like_hotkeys``, ``Tor_check_plugin``
:values: :values:

View File

@ -78,16 +78,6 @@ Scripts to update static data in :origin:`searx/data/`
.. automodule:: searxng_extra.update.update_pygments .. automodule:: searxng_extra.update.update_pygments
:members: :members:
.. _update_locales.py:
``update_locales.py``
=====================
:origin:`[source] <searxng_extra/update/update_locales.py>`
.. automodule:: searxng_extra.update.update_locales
:members:
``update_wikidata_units.py`` ``update_wikidata_units.py``
============================ ============================

View File

@ -17,7 +17,7 @@ If you don't trust anyone, you can set up your own, see :ref:`installation`.
- :ref:`no user tracking / no profiling <SearXNG protect privacy>` - :ref:`no user tracking / no profiling <SearXNG protect privacy>`
- script & cookies are optional - script & cookies are optional
- secure, encrypted connections - secure, encrypted connections
- :ref:`about 200 search engines <configured engines>` - :ref:`about 130 search engines <configured engines>`
- `about 60 translations <https://translate.codeberg.org/projects/searxng/searxng/>`_ - `about 60 translations <https://translate.codeberg.org/projects/searxng/searxng/>`_
- about 100 `well maintained <https://uptime.searxng.org/>`__ instances on searx.space_ - about 100 `well maintained <https://uptime.searxng.org/>`__ instances on searx.space_
- :ref:`easy integration of search engines <demo online engine>` - :ref:`easy integration of search engines <demo online engine>`

View File

@ -2,9 +2,9 @@
Why use a private instance? Why use a private instance?
=========================== ===========================
.. sidebar:: Is running my own instance worth it? .. sidebar:: Is it worth to run my own instance?
\.\.\.is a common question among SearXNG users. Before answering this \.\. is a common question among SearXNG users. Before answering this
question, see what options a SearXNG user has. question, see what options a SearXNG user has.
.. contents:: .. contents::
@ -12,13 +12,13 @@ Why use a private instance?
:local: :local:
:backlinks: entry :backlinks: entry
Public instances are open to everyone who has access to their URL. Usually, they 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 are operated by unknown parties (from the users' point of view). Private
instances can be used by a select group of people, such as a SearXNG instance for a instances can be used by a select group of people. It is for example a SearXNG of
group of friends, or a company which can be accessed through a VPN. Instances can also be group of friends or a company which can be accessed through VPN. Also it can be
single-user instances, which run locally on the user's machine. single user one which runs on the user's laptop.
To gain more insight on how these instances work, let's dive into how SearXNG To gain more insight on how these instances work let's dive into how SearXNG
protects its users. protects its users.
.. _SearXNG protect privacy: .. _SearXNG protect privacy:
@ -26,26 +26,26 @@ protects its users.
How does SearXNG protect privacy? How does SearXNG protect privacy?
================================= =================================
SearXNG protects the privacy of its users in multiple ways, regardless of the type SearXNG protects the privacy of its users in multiple ways regardless of the type
of the instance (private or public). Removal of private data from search requests of the instance (private, public). Removal of private data from search requests
comes in three forms: comes in three forms:
1. Removing private data from requests going to search services 1. removal of private data from requests going to search services
2. Not forwarding anything from third party services through search services 2. not forwarding anything from a third party services through search services
(e.g. advertisement) (e.g. advertisement)
3. Removing private data from requests going to the results pages 3. removal of private data from requests going to the result pages
Removing private data means not sending cookies to external search engines and Removing private data means not sending cookies to external search engines and
generating a random browser profile for every request. Thus, it does not matter generating a random browser profile for every request. Thus, it does not matter
if a public or private instance handles the request, because it is anonymized in if a public or private instance handles the request, because it is anonymized in
both cases. The IP address used will be the IP of the instance, but SearXNG can also be both cases. IP addresses will be the IP of the instance. But SearXNG can be
configured to use proxy or Tor. `Result proxy configured to use proxy or Tor. `Result proxy
<https://github.com/asciimoo/morty>`__ is supported, too. <https://github.com/asciimoo/morty>`__ is supported, too.
SearXNG does not serve ads or tracking content, unlike most search services. Therefore, SearXNG does not serve ads or tracking content unlike most search services. So
private data is not forwarded to third parties who might monetize it. Besides private data is not forwarded to third parties who might monetize it. Besides
protecting users from search services, both the referring page and search query are protecting users from search services, both referring page and search query are
hidden from the results pages being visited. hidden from visited result pages.
What are the consequences of using public instances? What are the consequences of using public instances?
@ -53,11 +53,11 @@ What are the consequences of using public instances?
If someone uses a public instance, they have 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 instance. This means that the user of the public instance does not know whether
their 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 abuse of Also, public instances without proper protection are more vulnerable to abusing
the search service, which may cause the external service to enforce the search service, In this case the external service in exchange returns
CAPTCHAs or to ban the IP address of the instance. Thus, search requests would return less CAPTCHAs or bans the IP of the instance. Thus, search requests return less
results. results.
I see. What about private instances? I see. What about private instances?
@ -67,10 +67,10 @@ If users run their :ref:`own instances <installation>`, everything is in their
control: the source code, logging settings and private data. Unknown instance control: the source code, logging settings and private data. Unknown instance
administrators do not have to be trusted. administrators do not have to be trusted.
Furthermore, as the default settings of their instance are editable, there is no Furthermore, as the default settings of their instance is editable, there is no
need to use cookies to tailor SearXNG to their needs and preferences will not need to use cookies to tailor SearXNG to their needs. So preferences will not be
reset to defaults when clearing browser cookies. As settings are stored on reset to defaults when clearing browser cookies. As settings are stored on
the user's computer, they will not be accessible to others as long as their computer is their computer, it will not be accessible to others as long as their computer is
not compromised. not compromised.
Conclusion Conclusion
@ -80,7 +80,7 @@ Always use an instance which is operated by people you trust. The privacy
features of SearXNG are available to users no matter what kind of instance they features of SearXNG are available to users no matter what kind of instance they
use. use.
For those on the go, or just wanting to try SearXNG for the first time, public If someone is on the go or just wants to try SearXNG for the first time public
instances are the best choice. Public instances are also making the instances are the best choices. Additionally, public instance are making a
world a better place by giving those who cannot, or do not want to, run an world a better place, because those who cannot or do not want to run an
instance access to a privacy-respecting search service. instance, have access to a privacy respecting search service.

View File

@ -12,17 +12,13 @@ Bot Detection
.. automodule:: searx.botdetection .. automodule:: searx.botdetection
:members: :members:
.. _botdetection ip_lists: .. automodule:: searx.botdetection.limiter
:members:
IP lists
========
.. automodule:: searx.botdetection.ip_lists .. automodule:: searx.botdetection.ip_lists
:members: :members:
.. _botdetection rate limit:
Rate limit Rate limit
========== ==========
@ -33,8 +29,6 @@ Rate limit
:members: :members:
.. _botdetection probe headers:
Probe HTTP headers Probe HTTP headers
================== ==================
@ -52,11 +46,3 @@ Probe HTTP headers
.. automodule:: searx.botdetection.http_user_agent .. automodule:: searx.botdetection.http_user_agent
:members: :members:
.. _botdetection config:
Config
======
.. automodule:: searx.botdetection.config
:members:

View File

@ -1,48 +0,0 @@
.. _favicons source:
=================
Favicons (source)
=================
.. contents::
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.favicons
:members:
.. _favicons.config:
Favicons Config
===============
.. automodule:: searx.favicons.config
:members:
.. _favicons.proxy:
Favicons Proxy
==============
.. automodule:: searx.favicons.proxy
:members:
.. _favicons.resolver:
Favicons Resolver
=================
.. automodule:: searx.favicons.resolvers
:members:
.. _favicons.cache:
Favicons Cache
==============
.. automodule:: searx.favicons.cache
:members:

View File

@ -13,3 +13,8 @@ Locales
:members: :members:
SearXNG's locale codes
======================
.. automodule:: searx.sxng_locales
:members:

View File

@ -1,9 +0,0 @@
.. _hostnames plugin:
================
Hostnames plugin
================
.. automodule:: searx.plugins.hostnames
:members:

View File

@ -1,9 +0,0 @@
.. _unit converter plugin:
=====================
Unit converter plugin
=====================
.. automodule:: searx.plugins.unit_converter
:members:

View File

@ -1,8 +0,0 @@
.. _searx.settings_loader:
===============
Settings Loader
===============
.. automodule:: searx.settings_loader
:members:

View File

@ -1,8 +0,0 @@
.. _sqlite db:
=========
SQLite DB
=========
.. automodule:: searx.sqlitedb
:members:

25
examples/basic_engine.py Normal file
View File

@ -0,0 +1,25 @@
categories = ['general'] # optional
def request(query, params):
'''pre-request callback
params<dict>:
method : POST/GET
headers : {}
data : {} # if method == POST
url : ''
category: 'search category'
pageno : 1 # number of the requested page
'''
params['url'] = 'https://host/%s' % query
return params
def response(resp):
'''post-response callback
resp: requests response object
'''
return [{'url': '', 'title': '', 'content': ''}]

78
manage
View File

@ -41,10 +41,10 @@ PATH="${REPO_ROOT}/node_modules/.bin:${PATH}"
PYOBJECTS="searx" PYOBJECTS="searx"
PY_SETUP_EXTRAS='[test]' PY_SETUP_EXTRAS='[test]'
GECKODRIVER_VERSION="v0.35.0" GECKODRIVER_VERSION="v0.30.0"
# SPHINXOPTS= # SPHINXOPTS=
BLACK_OPTIONS=("--target-version" "py311" "--line-length" "120" "--skip-string-normalization") BLACK_OPTIONS=("--target-version" "py311" "--line-length" "120" "--skip-string-normalization")
BLACK_TARGETS=("--exclude" "(searx/static|searx/languages.py)" "--include" 'searxng.msg|\.pyi?$' "searx" "searxng_extra" "tests") BLACK_TARGETS=("--exclude" "searx/static,searx/languages.py" "--include" 'searxng.msg|\.pyi?$' "searx" "searxng_extra" "tests")
_dev_redis_sock="/usr/local/searxng-redis/run/redis.sock" _dev_redis_sock="/usr/local/searxng-redis/run/redis.sock"
# set SEARXNG_REDIS_URL if it is not defined and "{_dev_redis_sock}" exists. # set SEARXNG_REDIS_URL if it is not defined and "{_dev_redis_sock}" exists.
@ -52,20 +52,39 @@ if [ -S "${_dev_redis_sock}" ] && [ -z "${SEARXNG_REDIS_URL}" ]; then
export SEARXNG_REDIS_URL="unix://${_dev_redis_sock}?db=0" export SEARXNG_REDIS_URL="unix://${_dev_redis_sock}?db=0"
fi fi
pylint.FILES() {
# List files tagged by comment:
#
# # lint: pylint
#
# These py files are linted by test.pylint()
grep -l -r --include \*.py '^#[[:blank:]]*lint:[[:blank:]]*pylint' searx searxng_extra tests
find . -name searxng.msg
}
YAMLLINT_FILES=() YAMLLINT_FILES=()
while IFS= read -r line; do while IFS= read -r line; do
if [ "$line" != "tests/unit/settings/syntaxerror_settings.yml" ]; then
YAMLLINT_FILES+=("$line") YAMLLINT_FILES+=("$line")
fi
done <<< "$(git ls-files './tests/*.yml' './searx/*.yml' './utils/templates/etc/searxng/*.yml')" done <<< "$(git ls-files './tests/*.yml' './searx/*.yml' './utils/templates/etc/searxng/*.yml')"
RST_FILES=( RST_FILES=(
'README.rst' 'README.rst'
) )
PYLINT_SEARXNG_DISABLE_OPTION="\
I,C,R,\
W0105,W0212,W0511,W0603,W0613,W0621,W0702,W0703,W1401,\
E1136"
PYLINT_ADDITIONAL_BUILTINS_FOR_ENGINES="traits,supported_languages,language_aliases,logger,categories"
PYLINT_OPTIONS="-m pylint -j 0 --rcfile .pylintrc"
help() { help() {
nvm.help nvm.help
cat <<EOF cat <<EOF
buildenv:
rebuild ./utils/brand.env
webapp.: webapp.:
run : run developer instance run : run developer instance
docs.: docs.:
@ -92,6 +111,8 @@ pyenv.:
uninstall : uninstall developer installation uninstall : uninstall developer installation
cmd ... : run command ... in virtualenv cmd ... : run command ... in virtualenv
OK : test if virtualenv is OK OK : test if virtualenv is OK
pypi.upload:
Upload python packages to PyPi (to test use pypi.upload.test)
format.: format.:
python : format Python code source using black python : format Python code source using black
pygments.: pygments.:
@ -134,6 +155,29 @@ webapp.run() {
SEARXNG_DEBUG=1 pyenv.cmd python -m searx.webapp SEARXNG_DEBUG=1 pyenv.cmd python -m searx.webapp
} }
buildenv() {
# settings file from repository's working tree are used by default
SEARXNG_SETTINGS_PATH="${REPO_ROOT}/searx/settings.yml"
if [ -f /etc/searx/settings.yml ]; then
err_msg "settings.yml in /etc/searx/ is deprecated, move file to folder /etc/searxng/"
fi
if [ -r '/etc/searxng/settings.yml' ]; then
if ask_yn "should settings read from: /etc/searxng/settings.yml"; then
SEARXNG_SETTINGS_PATH='/etc/searxng/settings.yml'
fi
fi
export SEARXNG_SETTINGS_PATH
(
set -e
SEARXNG_DEBUG=1 pyenv.cmd python utils/build_env.py 2>&1 \
| prefix_stdout "${_Blue}BUILDENV${_creset} "
)
return "${PIPESTATUS[0]}"
}
docker.push() { docker.push() {
docker.build push docker.build push
} }
@ -233,7 +277,7 @@ gecko.driver() {
build_msg INSTALL "geckodriver already installed" build_msg INSTALL "geckodriver already installed"
return return
fi fi
PLATFORM="$(python -c 'import platform; print(platform.system().lower(), platform.architecture()[0])')" PLATFORM="$(python3 -c 'import platform; print(platform.system().lower(), platform.architecture()[0])')"
case "$PLATFORM" in case "$PLATFORM" in
"linux 32bit" | "linux2 32bit") ARCH="linux32";; "linux 32bit" | "linux2 32bit") ARCH="linux32";;
"linux 64bit" | "linux2 64bit") ARCH="linux64";; "linux 64bit" | "linux2 64bit") ARCH="linux64";;
@ -299,8 +343,9 @@ pyenv.install() {
( set -e ( set -e
pyenv pyenv
build_msg PYENV "[install] pip install --use-pep517 --no-build-isolation -e 'searx${PY_SETUP_EXTRAS}'" build_msg PYENV "[install] pip install -e 'searx${PY_SETUP_EXTRAS}'"
"${PY_ENV_BIN}/python" -m pip install --use-pep517 --no-build-isolation -e ".${PY_SETUP_EXTRAS}" "${PY_ENV_BIN}/python" -m pip install -e ".${PY_SETUP_EXTRAS}"
buildenv
) )
local exit_val=$? local exit_val=$?
if [ ! $exit_val -eq 0 ]; then if [ ! $exit_val -eq 0 ]; then
@ -315,12 +360,31 @@ pyenv.uninstall() {
} }
pypi.upload() {
py.clean
py.build
# https://github.com/pypa/twine
pyenv.cmd twine upload "${PYDIST}"/*
}
pypi.upload.test() {
py.clean
py.build
pyenv.cmd twine upload -r testpypi "${PYDIST}"/*
}
format.python() { format.python() {
build_msg TEST "[format.python] black \$BLACK_TARGETS" build_msg TEST "[format.python] black \$BLACK_TARGETS"
pyenv.cmd black "${BLACK_OPTIONS[@]}" "${BLACK_TARGETS[@]}" pyenv.cmd black "${BLACK_OPTIONS[@]}" "${BLACK_TARGETS[@]}"
dump_return $? dump_return $?
} }
PYLINT_FILES=()
while IFS= read -r line; do
PYLINT_FILES+=("$line")
done <<< "$(pylint.FILES)"
# shellcheck disable=SC2119 # shellcheck disable=SC2119
main() { main() {

View File

@ -1,7 +1,7 @@
{ {
"dependencies": { "dependencies": {
"eslint": "^9.0.0", "eslint": "^8.18.0",
"pyright": "^1.1.329" "pyright": "^1.1.255"
}, },
"scripts": { "scripts": {
"clean": "rm -Rf node_modules package-lock.json" "clean": "rm -Rf node_modules package-lock.json"

View File

@ -1,24 +1,23 @@
mock==5.1.0 mock==5.1.0
nose2[coverage_plugin]==0.15.1 nose2[coverage_plugin]==0.13.0
cov-core==1.15.0 cov-core==1.15.0
black==24.3.0 black==22.12.0
pylint==3.3.1 pylint==2.17.5
splinter==0.21.0 splinter==0.19.0
selenium==4.25.0 selenium==4.12.0
Pallets-Sphinx-Themes==2.3.0 twine==4.0.2
Sphinx==7.4.7 Pallets-Sphinx-Themes==2.1.1
sphinx-issues==5.0.0 Sphinx<=7.1.2; python_version == '3.8'
Sphinx==7.2.6; python_version > '3.8'
sphinx-issues==3.0.1
sphinx-jinja==2.0.2 sphinx-jinja==2.0.2
sphinx-tabs==3.4.7 sphinx-tabs==3.4.1
sphinxcontrib-programoutput==0.17 sphinxcontrib-programoutput==0.17
sphinx-autobuild==2024.10.3 sphinx-autobuild==2021.3.14
sphinx-notfound-page==1.0.4 sphinx-notfound-page==1.0.0
myst-parser==3.0.1 myst-parser==2.0.0
linuxdoc==20240924 linuxdoc==20230827
aiounittest==1.4.2 aiounittest==1.4.2
yamllint==1.35.1 yamllint==1.32.0
wlc==1.15 wlc==1.13
coloredlogs==15.0.1 coloredlogs==15.0.1
docutils>=0.21.2
parameterized==0.9.0
autodoc_pydantic==2.2.0

View File

@ -1,21 +1,19 @@
certifi==2024.8.30 certifi==2023.7.22
babel==2.16.0 babel==2.12.1
flask-babel==4.0.0 flask-babel==3.1.0
flask==3.0.3 flask==2.3.3
jinja2==3.1.4 jinja2==3.1.2
lxml==5.3.0 lxml==4.9.3
pygments==2.18.0 pygments==2.16.1
python-dateutil==2.9.0.post0 python-dateutil==2.8.2
pyyaml==6.0.2 pyyaml==6.0.1
httpx[http2]==0.24.1 httpx[http2]==0.24.1
Brotli==1.1.0 Brotli==1.1.0
uvloop==0.21.0 uvloop==0.17.0
httpx-socks[asyncio]==0.7.7 httpx-socks[asyncio]==0.7.7
setproctitle==1.3.3 setproctitle==1.3.2
redis==5.0.8 redis==4.6.0
markdown-it-py==3.0.0 markdown-it-py==3.0.0
fasttext-predict==0.9.2.2 typing_extensions==4.8.0
tomli==2.0.2; python_version < '3.11' fasttext-predict==0.9.2.1
msgspec==0.18.6 pytomlpp==1.0.13
eval_type_backport; python_version < '3.9'
typer-slim==0.12.5

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring, cyclic-import # lint: pylint
# pylint: disable=missing-module-docstring
import sys import sys
import os import os
@ -103,10 +104,3 @@ if max_request_timeout is None:
logger.info('max_request_timeout=%s', repr(max_request_timeout)) logger.info('max_request_timeout=%s', repr(max_request_timeout))
else: else:
logger.info('max_request_timeout=%i second(s)', max_request_timeout) logger.info('max_request_timeout=%i second(s)', max_request_timeout)
if settings['server']['public_instance']:
logger.warning(
"Be aware you have activated features intended only for public instances. "
"This force the usage of the limiter and link_token / "
"see https://docs.searxng.org/admin/searx.limiter.html"
)

View File

@ -1,30 +1,25 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring
import sys
from os import listdir from os import listdir
from os.path import realpath, dirname, join, isdir from os.path import realpath, dirname, join, isdir
from searx.utils import load_module
from collections import defaultdict from collections import defaultdict
from searx.utils import load_module
answerers_dir = dirname(realpath(__file__)) answerers_dir = dirname(realpath(__file__))
def load_answerers(): def load_answerers():
answerers = [] # pylint: disable=redefined-outer-name answerers = []
for filename in listdir(answerers_dir): for filename in listdir(answerers_dir):
if not isdir(join(answerers_dir, filename)) or filename.startswith('_'): if not isdir(join(answerers_dir, filename)) or filename.startswith('_'):
continue continue
module = load_module('answerer.py', join(answerers_dir, filename)) module = load_module('answerer.py', join(answerers_dir, filename))
if not hasattr(module, 'keywords') or not isinstance(module.keywords, tuple) or not module.keywords: if not hasattr(module, 'keywords') or not isinstance(module.keywords, tuple) or not len(module.keywords):
sys.exit(2) exit(2)
answerers.append(module) answerers.append(module)
return answerers return answerers
def get_answerers_by_keywords(answerers): # pylint:disable=redefined-outer-name def get_answerers_by_keywords(answerers):
by_keyword = defaultdict(list) by_keyword = defaultdict(list)
for answerer in answerers: for answerer in answerers:
for keyword in answerer.keywords: for keyword in answerer.keywords:

View File

@ -1,2 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring

View File

@ -1,6 +1,3 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring
import hashlib import hashlib
import random import random
import string import string

View File

@ -1,2 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring

View File

@ -1,6 +1,3 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring
from functools import reduce from functools import reduce
from operator import mul from operator import mul
@ -20,27 +17,27 @@ def answer(query):
try: try:
args = list(map(float, parts[1:])) args = list(map(float, parts[1:]))
except: # pylint: disable=bare-except except:
return [] return []
func = parts[0] func = parts[0]
_answer = None answer = None
if func == 'min': if func == 'min':
_answer = min(args) answer = min(args)
elif func == 'max': elif func == 'max':
_answer = max(args) answer = max(args)
elif func == 'avg': elif func == 'avg':
_answer = sum(args) / len(args) answer = sum(args) / len(args)
elif func == 'sum': elif func == 'sum':
_answer = sum(args) answer = sum(args)
elif func == 'prod': elif func == 'prod':
_answer = reduce(mul, args, 1) answer = reduce(mul, args, 1)
if _answer is None: if answer is None:
return [] return []
return [{'answer': str(_answer)}] return [{'answer': str(answer)}]
# required answerer function # required answerer function

View File

@ -1,12 +1,12 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
"""This module implements functions needed for the autocompleter. """This module implements functions needed for the autocompleter.
""" """
# pylint: disable=use-dict-literal # pylint: disable=use-dict-literal
import json import json
import html from urllib.parse import urlencode
from urllib.parse import urlencode, quote_plus
import lxml import lxml
from httpx import HTTPError from httpx import HTTPError
@ -16,26 +16,17 @@ from searx.engines import (
engines, engines,
google, google,
) )
from searx.network import get as http_get, post as http_post from searx.network import get as http_get
from searx.exceptions import SearxEngineResponseException from searx.exceptions import SearxEngineResponseException
def update_kwargs(**kwargs): def get(*args, **kwargs):
if 'timeout' not in kwargs: if 'timeout' not in kwargs:
kwargs['timeout'] = settings['outgoing']['request_timeout'] kwargs['timeout'] = settings['outgoing']['request_timeout']
kwargs['raise_for_httperror'] = True kwargs['raise_for_httperror'] = True
def get(*args, **kwargs):
update_kwargs(**kwargs)
return http_get(*args, **kwargs) return http_get(*args, **kwargs)
def post(*args, **kwargs):
update_kwargs(**kwargs)
return http_post(*args, **kwargs)
def brave(query, _lang): def brave(query, _lang):
# brave search autocompleter # brave search autocompleter
url = 'https://search.brave.com/api/suggest?' url = 'https://search.brave.com/api/suggest?'
@ -154,18 +145,6 @@ def seznam(query, _lang):
] ]
def stract(query, _lang):
# stract autocompleter (beta)
url = f"https://stract.com/beta/api/autosuggest?q={quote_plus(query)}"
resp = post(url)
if not resp.ok:
return []
return [html.unescape(suggestion['raw']) for suggestion in resp.json()]
def startpage(query, sxng_locale): def startpage(query, sxng_locale):
"""Autocomplete from Startpage. Supports Startpage's languages""" """Autocomplete from Startpage. Supports Startpage's languages"""
lui = engines['startpage'].traits.get_language(sxng_locale, 'english') lui = engines['startpage'].traits.get_language(sxng_locale, 'english')
@ -244,7 +223,6 @@ backends = {
'mwmbl': mwmbl, 'mwmbl': mwmbl,
'seznam': seznam, 'seznam': seznam,
'startpage': startpage, 'startpage': startpage,
'stract': stract,
'swisscows': swisscows, 'swisscows': swisscows,
'qwant': qwant, 'qwant': qwant,
'wikipedia': wikipedia, 'wikipedia': wikipedia,

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
"""This module implements the :origin:`searxng_msg <babel.cfg>` extractor to """This module implements the :origin:`searxng_msg <babel.cfg>` extractor to
extract messages from: extract messages from:

View File

@ -1,22 +1,27 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""".. _botdetection src: """.. _botdetection src:
Implementations used for bot detection. X-Forwarded-For
===============
.. attention::
A correct setup of the HTTP request headers ``X-Forwarded-For`` and
``X-Real-IP`` is essential to be able to assign a request to an IP correctly:
- `NGINX RequestHeader`_
- `Apache RequestHeader`_
.. _NGINX RequestHeader:
https://docs.searxng.org/admin/installation-nginx.html#nginx-s-searxng-site
.. _Apache RequestHeader:
https://docs.searxng.org/admin/installation-apache.html#apache-s-searxng-site
.. autofunction:: searx.botdetection.get_real_ip
""" """
from ._helpers import dump_request from ._helpers import dump_request
from ._helpers import get_real_ip from ._helpers import get_real_ip
from ._helpers import get_network
from ._helpers import too_many_requests from ._helpers import too_many_requests
__all__ = ['dump_request', 'get_network', 'get_real_ip', 'too_many_requests']
redis_client = None
cfg = None
def init(_cfg, _redis_client):
global redis_client, cfg # pylint: disable=global-statement
redis_client = _redis_client
cfg = _cfg

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
# pylint: disable=missing-module-docstring, invalid-name # pylint: disable=missing-module-docstring, invalid-name
from __future__ import annotations from __future__ import annotations
@ -12,8 +13,8 @@ from ipaddress import (
import flask import flask
import werkzeug import werkzeug
from searx.tools import config
from searx import logger from searx import logger
from . import config
logger = logger.getChild('botdetection') logger = logger.getChild('botdetection')
@ -103,10 +104,10 @@ def get_real_ip(request: flask.Request) -> str:
if not forwarded_for: if not forwarded_for:
_log_error_only_once("X-Forwarded-For header is not set!") _log_error_only_once("X-Forwarded-For header is not set!")
else: else:
from . import cfg # pylint: disable=import-outside-toplevel, cyclic-import from .limiter import get_cfg # pylint: disable=import-outside-toplevel, cyclic-import
forwarded_for = [x.strip() for x in forwarded_for.split(',')] forwarded_for = [x.strip() for x in forwarded_for.split(',')]
x_for: int = cfg['real_ip.x_for'] # type: ignore x_for: int = get_cfg()['real_ip.x_for'] # type: ignore
forwarded_for = forwarded_for[-min(len(forwarded_for), x_for)] forwarded_for = forwarded_for[-min(len(forwarded_for), x_for)]
if not real_ip: if not real_ip:

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""" """
Method ``http_accept`` Method ``http_accept``
---------------------- ----------------------
@ -23,7 +24,7 @@ from ipaddress import (
import flask import flask
import werkzeug import werkzeug
from . import config from searx.tools import config
from ._helpers import too_many_requests from ._helpers import too_many_requests

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""" """
Method ``http_accept_encoding`` Method ``http_accept_encoding``
------------------------------- -------------------------------
@ -24,7 +25,7 @@ from ipaddress import (
import flask import flask
import werkzeug import werkzeug
from . import config from searx.tools import config
from ._helpers import too_many_requests from ._helpers import too_many_requests

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""" """
Method ``http_accept_language`` Method ``http_accept_language``
------------------------------- -------------------------------
@ -20,7 +21,7 @@ from ipaddress import (
import flask import flask
import werkzeug import werkzeug
from . import config from searx.tools import config
from ._helpers import too_many_requests from ._helpers import too_many_requests

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""" """
Method ``http_connection`` Method ``http_connection``
-------------------------- --------------------------
@ -21,7 +22,7 @@ from ipaddress import (
import flask import flask
import werkzeug import werkzeug
from . import config from searx.tools import config
from ._helpers import too_many_requests from ._helpers import too_many_requests

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""" """
Method ``http_user_agent`` Method ``http_user_agent``
-------------------------- --------------------------
@ -23,7 +24,7 @@ from ipaddress import (
import flask import flask
import werkzeug import werkzeug
from . import config from searx.tools import config
from ._helpers import too_many_requests from ._helpers import too_many_requests
@ -34,7 +35,7 @@ USER_AGENT = (
+ r'|HttpClient|Jersey|Python|libwww-perl|Ruby|SynHttpClient|UniversalFeedParser|Googlebot|GoogleImageProxy' + r'|HttpClient|Jersey|Python|libwww-perl|Ruby|SynHttpClient|UniversalFeedParser|Googlebot|GoogleImageProxy'
+ r'|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot' + r'|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot'
+ r'|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT|Sogou|Abonti|Pixray|Spinn3r|SemrushBot|Exabot' + r'|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT|Sogou|Abonti|Pixray|Spinn3r|SemrushBot|Exabot'
+ r'|ZmEu|BLEXBot|bitlybot|HeadlessChrome' + r'|ZmEu|BLEXBot|bitlybot'
# unmaintained Farside instances # unmaintained Farside instances
+ r'|' + r'|'
+ re.escape(r'Mozilla/5.0 (compatible; Farside/0.1.0; +https://farside.link)') + re.escape(r'Mozilla/5.0 (compatible; Farside/0.1.0; +https://farside.link)')

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""".. _botdetection.ip_limit: """.. _botdetection.ip_limit:
Method ``ip_limit`` Method ``ip_limit``
@ -12,7 +13,8 @@ and at least for a maximum of 10 minutes.
The :py:obj:`.link_token` method can be used to investigate whether a request is The :py:obj:`.link_token` method can be used to investigate whether a request is
*suspicious*. To activate the :py:obj:`.link_token` method in the *suspicious*. To activate the :py:obj:`.link_token` method in the
:py:obj:`.ip_limit` method add the following configuration: :py:obj:`.ip_limit` method add the following to your
``/etc/searxng/limiter.toml``:
.. code:: toml .. code:: toml
@ -44,12 +46,12 @@ from ipaddress import (
import flask import flask
import werkzeug import werkzeug
from searx.tools import config
from searx import redisdb from searx import redisdb
from searx.redislib import incr_sliding_window, drop_counter from searx.redislib import incr_sliding_window, drop_counter
from . import link_token from . import link_token
from . import config
from ._helpers import ( from ._helpers import (
too_many_requests, too_many_requests,
logger, logger,
@ -76,11 +78,11 @@ LONG_MAX = 150
LONG_MAX_SUSPICIOUS = 10 LONG_MAX_SUSPICIOUS = 10
"""Maximum suspicious requests from one IP in the :py:obj:`LONG_WINDOW`""" """Maximum suspicious requests from one IP in the :py:obj:`LONG_WINDOW`"""
API_WINDOW = 3600 API_WONDOW = 3600
"""Time (sec) before sliding window for API requests (format != html) expires.""" """Time (sec) before sliding window for API requests (format != html) expires."""
API_MAX = 4 API_MAX = 4
"""Maximum requests from one IP in the :py:obj:`API_WINDOW`""" """Maximum requests from one IP in the :py:obj:`API_WONDOW`"""
SUSPICIOUS_IP_WINDOW = 3600 * 24 * 30 SUSPICIOUS_IP_WINDOW = 3600 * 24 * 30
"""Time (sec) before sliding window for one suspicious IP expires.""" """Time (sec) before sliding window for one suspicious IP expires."""
@ -103,7 +105,7 @@ def filter_request(
return None return None
if request.args.get('format', 'html') != 'html': if request.args.get('format', 'html') != 'html':
c = incr_sliding_window(redis_client, 'ip_limit.API_WINDOW:' + network.compressed, API_WINDOW) c = incr_sliding_window(redis_client, 'ip_limit.API_WONDOW:' + network.compressed, API_WONDOW)
if c > API_MAX: if c > API_MAX:
return too_many_requests(network, "too many request in API_WINDOW") return too_many_requests(network, "too many request in API_WINDOW")

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""".. _botdetection.ip_lists: """.. _botdetection.ip_lists:
Method ``ip_lists`` Method ``ip_lists``
@ -12,7 +13,7 @@ The ``ip_lists`` method implements IP :py:obj:`block- <block_ip>` and
[botdetection.ip_lists] [botdetection.ip_lists]
pass_ip = [ pass_ip = [
'167.235.158.251', # IPv4 of check.searx.space '140.238.172.132', # IPv4 of check.searx.space
'192.168.0.0/16', # IPv4 private network '192.168.0.0/16', # IPv4 private network
'fe80::/10' # IPv6 linklocal 'fe80::/10' # IPv6 linklocal
] ]
@ -32,15 +33,15 @@ from ipaddress import (
IPv6Address, IPv6Address,
) )
from . import config from searx.tools import config
from ._helpers import logger from ._helpers import logger
logger = logger.getChild('ip_limit') logger = logger.getChild('ip_limit')
SEARXNG_ORG = [ SEARXNG_ORG = [
# https://github.com/searxng/searxng/pull/2484#issuecomment-1576639195 # https://github.com/searxng/searxng/pull/2484#issuecomment-1576639195
'167.235.158.251', # IPv4 check.searx.space '140.238.172.132', # IPv4 check.searx.space
'2a01:04f8:1c1c:8fc2::/64', # IPv6 check.searx.space '2603:c022:0:4900::/56', # IPv6 check.searx.space
] ]
"""Passlist of IPs from the SearXNG organization, e.g. `check.searx.space`.""" """Passlist of IPs from the SearXNG organization, e.g. `check.searx.space`."""

View File

@ -0,0 +1,147 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""".. _limiter src:
Limiter
=======
.. sidebar:: info
The limiter requires a :ref:`Redis <settings redis>` database.
Bot protection / IP rate limitation. The intention of rate limitation is to
limit suspicious requests from an IP. The motivation behind this is the fact
that SearXNG passes through requests from bots and is thus classified as a bot
itself. As a result, the SearXNG engine then receives a CAPTCHA or is blocked
by the search engine (the origin) in some other way.
To avoid blocking, the requests from bots to SearXNG must also be blocked, this
is the task of the limiter. To perform this task, the limiter uses the methods
from the :py:obj:`searx.botdetection`.
To enable the limiter activate:
.. code:: yaml
server:
...
limiter: true # rate limit the number of request on the instance, block some bots
and set the redis-url connection. Check the value, it depends on your redis DB
(see :ref:`settings redis`), by example:
.. code:: yaml
redis:
url: unix:///usr/local/searxng-redis/run/redis.sock?db=0
"""
from __future__ import annotations
from pathlib import Path
from ipaddress import ip_address
import flask
import werkzeug
from searx.tools import config
from searx import logger
from . import (
http_accept,
http_accept_encoding,
http_accept_language,
http_connection,
http_user_agent,
ip_limit,
ip_lists,
)
from ._helpers import (
get_network,
get_real_ip,
dump_request,
)
logger = logger.getChild('botdetection.limiter')
CFG: config.Config = None # type: ignore
LIMITER_CFG_SCHEMA = Path(__file__).parent / "limiter.toml"
"""Base configuration (schema) of the botdetection."""
LIMITER_CFG = Path('/etc/searxng/limiter.toml')
"""Local Limiter configuration."""
CFG_DEPRECATED = {
# "dummy.old.foo": "config 'dummy.old.foo' exists only for tests. Don't use it in your real project config."
}
def get_cfg() -> config.Config:
global CFG # pylint: disable=global-statement
if CFG is None:
CFG = config.Config.from_toml(LIMITER_CFG_SCHEMA, LIMITER_CFG, CFG_DEPRECATED)
return CFG
def filter_request(request: flask.Request) -> werkzeug.Response | None:
# pylint: disable=too-many-return-statements
cfg = get_cfg()
real_ip = ip_address(get_real_ip(request))
network = get_network(real_ip, cfg)
if request.path == '/healthz':
return None
# link-local
if network.is_link_local:
return None
# block- & pass- lists
#
# 1. The IP of the request is first checked against the pass-list; if the IP
# matches an entry in the list, the request is not blocked.
# 2. If no matching entry is found in the pass-list, then a check is made against
# the block list; if the IP matches an entry in the list, the request is
# blocked.
# 3. If the IP is not in either list, the request is not blocked.
match, msg = ip_lists.pass_ip(real_ip, cfg)
if match:
logger.warning("PASS %s: matched PASSLIST - %s", network.compressed, msg)
return None
match, msg = ip_lists.block_ip(real_ip, cfg)
if match:
logger.error("BLOCK %s: matched BLOCKLIST - %s", network.compressed, msg)
return flask.make_response(('IP is on BLOCKLIST - %s' % msg, 429))
# methods applied on /
for func in [
http_user_agent,
]:
val = func.filter_request(network, request, cfg)
if val is not None:
return val
# methods applied on /search
if request.path == '/search':
for func in [
http_accept,
http_accept_encoding,
http_accept_language,
http_connection,
http_user_agent,
ip_limit,
]:
val = func.filter_request(network, request, cfg)
if val is not None:
return val
logger.debug(f"OK {network}: %s", dump_request(flask.request))
return None

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
""" """
Method ``link_token`` Method ``link_token``
--------------------- ---------------------
@ -28,7 +29,7 @@ And in the HTML template from flask a stylesheet link is needed (the value of
<link rel="stylesheet" <link rel="stylesheet"
href="{{ url_for('client_token', token=link_token) }}" href="{{ url_for('client_token', token=link_token) }}"
type="text/css" > type="text/css" />
.. _X-Forwarded-For: .. _X-Forwarded-For:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
@ -82,7 +83,7 @@ def is_suspicious(network: IPv4Network | IPv6Network, request: flask.Request, re
ping_key = get_ping_key(network, request) ping_key = get_ping_key(network, request)
if not redis_client.get(ping_key): if not redis_client.get(ping_key):
logger.info("missing ping (IP: %s) / request: %s", network.compressed, ping_key) logger.warning("missing ping (IP: %s) / request: %s", network.compressed, ping_key)
return True return True
if renew: if renew:
@ -98,13 +99,15 @@ def ping(request: flask.Request, token: str):
The expire time of this ping-key is :py:obj:`PING_LIVE_TIME`. The expire time of this ping-key is :py:obj:`PING_LIVE_TIME`.
""" """
from . import redis_client, cfg # pylint: disable=import-outside-toplevel, cyclic-import from . import limiter # pylint: disable=import-outside-toplevel, cyclic-import
redis_client = redisdb.client()
if not redis_client: if not redis_client:
return return
if not token_is_valid(token): if not token_is_valid(token):
return return
cfg = limiter.get_cfg()
real_ip = ip_address(get_real_ip(request)) real_ip = ip_address(get_real_ip(request))
network = get_network(real_ip, cfg) network = get_network(real_ip, cfg)

View File

@ -1,18 +1,73 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""Compatibility with older versions""" # lint: pylint
# pyright: basic
"""Module for backward compatibility.
# pylint: disable=unused-import """
# pylint: disable=C,R
__all__ = [
"tomllib",
]
import sys __all__ = ('cached_property',)
# TOML (lib) compatibility
# ------------------------
if sys.version_info >= (3, 11): try:
import tomllib from functools import cached_property # type: ignore
else:
import tomli as tomllib except ImportError:
# cache_property has been added in py3.8 [1]
#
# To support cache_property in py3.7 the implementation from 3.8 has been
# copied here. This code can be cleanup with EOL of py3.7.
#
# [1] https://docs.python.org/3/library/functools.html#functools.cached_property
from threading import RLock
_NOT_FOUND = object()
class cached_property:
def __init__(self, func):
self.func = func
self.attrname = None
self.__doc__ = func.__doc__
self.lock = RLock()
def __set_name__(self, owner, name):
if self.attrname is None:
self.attrname = name
elif name != self.attrname:
raise TypeError(
"Cannot assign the same cached_property to two different names "
f"({self.attrname!r} and {name!r})."
)
def __get__(self, instance, owner=None):
if instance is None:
return self
if self.attrname is None:
raise TypeError("Cannot use cached_property instance without calling __set_name__ on it.")
try:
cache = instance.__dict__
except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
msg = (
f"No '__dict__' attribute on {type(instance).__name__!r} "
f"instance to cache {self.attrname!r} property."
)
raise TypeError(msg) from None
val = cache.get(self.attrname, _NOT_FOUND)
if val is _NOT_FOUND:
with self.lock:
# check if another thread filled cache while we awaited lock
val = cache.get(self.attrname, _NOT_FOUND)
if val is _NOT_FOUND:
val = self.func(instance)
try:
cache[self.attrname] = val
except TypeError:
msg = (
f"The '__dict__' attribute on {type(instance).__name__!r} instance "
f"does not support item assignment for caching {self.attrname!r} property."
)
raise TypeError(msg) from None
return val

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
"""This module holds the *data* created by:: """This module holds the *data* created by::
make data.all make data.all
@ -14,7 +15,6 @@ __all__ = [
'EXTERNAL_BANGS', 'EXTERNAL_BANGS',
'OSM_KEYS_TAGS', 'OSM_KEYS_TAGS',
'ENGINE_DESCRIPTIONS', 'ENGINE_DESCRIPTIONS',
'LOCALES',
'ahmia_blacklist_loader', 'ahmia_blacklist_loader',
] ]
@ -50,4 +50,3 @@ EXTERNAL_BANGS = _load('external_bangs.json')
OSM_KEYS_TAGS = _load('osm_keys_tags.json') OSM_KEYS_TAGS = _load('osm_keys_tags.json')
ENGINE_DESCRIPTIONS = _load('engine_descriptions.json') ENGINE_DESCRIPTIONS = _load('engine_descriptions.json')
ENGINE_TRAITS = _load('engine_traits.json') ENGINE_TRAITS = _load('engine_traits.json')
LOCALES = _load('locales.json')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,69 +0,0 @@
{
"LOCALE_NAMES": {
"af": "Afrikaans",
"ar": "العربية (Arabic)",
"bg": "Български (Bulgarian)",
"bn": "বাংলা (Bangla)",
"bo": "བོད་སྐད་ (Tibetan)",
"ca": "Català (Catalan)",
"cs": "Čeština (Czech)",
"cy": "Cymraeg (Welsh)",
"da": "Dansk (Danish)",
"de": "Deutsch (German)",
"dv": "ދިވެހި (Dhivehi)",
"el-GR": "Ελληνικά, Ελλάδα (Greek, Greece)",
"en": "English",
"eo": "Esperanto",
"es": "Español (Spanish)",
"et": "Eesti (Estonian)",
"eu": "Euskara (Basque)",
"fa-IR": "فارسی, ایران (Persian, Iran)",
"fi": "Suomi (Finnish)",
"fil": "Filipino",
"fr": "Français (French)",
"gl": "Galego (Galician)",
"he": "עברית (Hebrew)",
"hr": "Hrvatski (Croatian)",
"hu": "Magyar (Hungarian)",
"ia": "Interlingua",
"id": "Indonesia (Indonesian)",
"it": "Italiano (Italian)",
"ja": "日本語 (Japanese)",
"ko": "한국어 (Korean)",
"lt": "Lietuvių (Lithuanian)",
"lv": "Latviešu (Latvian)",
"ml": "മലയാളം (Malayalam)",
"ms": "Melayu (Malay)",
"nb-NO": "Norsk bokmål, Norge (Norwegian bokmål, Norway)",
"nl": "Nederlands (Dutch)",
"nl-BE": "Nederlands, België (Dutch, Belgium)",
"oc": "Occitan",
"pa": "ਪੰਜਾਬੀ (Punjabi)",
"pap": "Papiamento",
"pl": "Polski (Polish)",
"pt": "Português (Portuguese)",
"pt-BR": "Português, Brasil (Portuguese, Brazil)",
"ro": "Română (Romanian)",
"ru": "Русский (Russian)",
"si": "සිංහල (Sinhala)",
"sk": "Slovenčina (Slovak)",
"sl": "Slovenščina (Slovenian)",
"sr": "Српски (Serbian)",
"sv": "Svenska (Swedish)",
"szl": "Ślōnski (Silesian)",
"ta": "தமிழ் (Tamil)",
"te": "తెలుగు (Telugu)",
"th": "ไทย (Thai)",
"tr": "Türkçe (Turkish)",
"uk": "Українська (Ukrainian)",
"vi": "Tiếng việt (Vietnamese)",
"zh-HK": "中文, 中國香港特別行政區 (Chinese, Hong Kong SAR China)",
"zh-Hans-CN": "中文, 中国 (Chinese, China)",
"zh-Hant-TW": "中文, 台灣 (Chinese, Taiwan)"
},
"RTL_LOCALES": [
"ar",
"fa-IR",
"he"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
{ {
"versions": [
"117.0",
"116.0"
],
"os": [ "os": [
"Windows NT 10.0; Win64; x64", "Windows NT 10.0; Win64; x64",
"X11; Linux x86_64" "X11; Linux x86_64"
], ],
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}", "ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}"
"versions": [
"132.0",
"131.0"
]
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
"""Implementations of the framework for the SearXNG engines. """Implementations of the framework for the SearXNG engines.
.. hint:: .. hint::

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
"""Engine's traits are fetched from the origin engines and stored in a JSON file """Engine's traits are fetched from the origin engines and stored in a JSON file
in the *data folder*. Most often traits are languages and region codes and in the *data folder*. Most often traits are languages and region codes and
their mapping from SearXNG's representation to the representation in the origin their mapping from SearXNG's representation to the representation in the origin
@ -13,7 +14,8 @@ from __future__ import annotations
import json import json
import dataclasses import dataclasses
import types import types
from typing import Dict, Literal, Iterable, Union, Callable, Optional, TYPE_CHECKING from typing import Dict, Iterable, Union, Callable, Optional, TYPE_CHECKING
from typing_extensions import Literal, Self
from searx import locales from searx import locales
from searx.data import data_dir, ENGINE_TRAITS from searx.data import data_dir, ENGINE_TRAITS
@ -134,7 +136,7 @@ class EngineTraits:
return EngineTraits(**dataclasses.asdict(self)) return EngineTraits(**dataclasses.asdict(self))
@classmethod @classmethod
def fetch_traits(cls, engine: Engine) -> Union['EngineTraits', None]: def fetch_traits(cls, engine: Engine) -> Union[Self, None]:
"""Call a function ``fetch_traits(engine_traits)`` from engines namespace to fetch """Call a function ``fetch_traits(engine_traits)`` from engines namespace to fetch
and set properties from the origin engine in the object ``engine_traits``. If and set properties from the origin engine in the object ``engine_traits``. If
function does not exists, ``None`` is returned. function does not exists, ``None`` is returned.
@ -166,7 +168,7 @@ class EngineTraits:
# - name: google italian # - name: google italian
# engine: google # engine: google
# language: it # language: it
# region: it-IT # type: ignore # region: it-IT
traits = self.copy() traits = self.copy()
@ -202,7 +204,7 @@ class EngineTraitsMap(Dict[str, EngineTraits]):
json.dump(self, f, indent=2, sort_keys=True, cls=EngineTraitsEncoder) json.dump(self, f, indent=2, sort_keys=True, cls=EngineTraitsEncoder)
@classmethod @classmethod
def from_data(cls) -> 'EngineTraitsMap': def from_data(cls) -> Self:
"""Instantiate :class:`EngineTraitsMap` object from :py:obj:`ENGINE_TRAITS`""" """Instantiate :class:`EngineTraitsMap` object from :py:obj:`ENGINE_TRAITS`"""
obj = cls() obj = cls()
for k, v in ENGINE_TRAITS.items(): for k, v in ENGINE_TRAITS.items():
@ -210,7 +212,7 @@ class EngineTraitsMap(Dict[str, EngineTraits]):
return obj return obj
@classmethod @classmethod
def fetch_traits(cls, log: Callable) -> 'EngineTraitsMap': def fetch_traits(cls, log: Callable) -> Self:
from searx import engines # pylint: disable=cyclic-import, import-outside-toplevel from searx import engines # pylint: disable=cyclic-import, import-outside-toplevel
names = list(engines.engines) names = list(engines.engines)

View File

@ -1,12 +1,11 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=invalid-name """
"""1337x 1337x
""" """
from urllib.parse import quote, urljoin from urllib.parse import quote, urljoin
from lxml import html from lxml import html
from searx.utils import extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex from searx.utils import extract_text, get_torrent_size, eval_xpath, eval_xpath_list, eval_xpath_getindex
# about # about
about = { about = {
@ -40,7 +39,9 @@ def response(resp):
title = extract_text(eval_xpath(result, './td[contains(@class, "name")]/a[2]')) title = extract_text(eval_xpath(result, './td[contains(@class, "name")]/a[2]'))
seed = extract_text(eval_xpath(result, './/td[contains(@class, "seeds")]')) seed = extract_text(eval_xpath(result, './/td[contains(@class, "seeds")]'))
leech = extract_text(eval_xpath(result, './/td[contains(@class, "leeches")]')) leech = extract_text(eval_xpath(result, './/td[contains(@class, "leeches")]'))
filesize = extract_text(eval_xpath(result, './/td[contains(@class, "size")]/text()')) filesize_info = extract_text(eval_xpath(result, './/td[contains(@class, "size")]/text()'))
filesize, filesize_multiplier = filesize_info.split()
filesize = get_torrent_size(filesize, filesize_multiplier)
results.append( results.append(
{ {

Some files were not shown because too many files have changed in this diff Show More