searxng.sh 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: AGPL-3.0-or-later
  3. # shellcheck disable=SC2001
  4. # Script options from the environment:
  5. SEARXNG_UWSGI_USE_SOCKET="${SEARXNG_UWSGI_USE_SOCKET:-true}"
  6. # shellcheck source=utils/lib.sh
  7. source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
  8. # shellcheck source=utils/lib_redis.sh
  9. source "$(dirname "${BASH_SOURCE[0]}")/lib_redis.sh"
  10. # shellcheck source=utils/lib_valkey.sh
  11. source "$(dirname "${BASH_SOURCE[0]}")/lib_valkey.sh"
  12. # shellcheck source=utils/brand.sh
  13. source "${REPO_ROOT}/utils/brand.sh"
  14. SERVICE_NAME="searxng"
  15. SERVICE_USER="searxng"
  16. SERVICE_HOME="/usr/local/searxng"
  17. SERVICE_GROUP="searxng"
  18. SEARXNG_SRC="${SERVICE_HOME}/searxng-src"
  19. # shellcheck disable=SC2034
  20. SEARXNG_STATIC="${SEARXNG_SRC}/searx/static"
  21. SEARXNG_PYENV="${SERVICE_HOME}/searx-pyenv"
  22. SEARXNG_SETTINGS_PATH="/etc/searxng/settings.yml"
  23. SEARXNG_UWSGI_APP="searxng.ini"
  24. SEARXNG_INTERNAL_HTTP="${SEARXNG_BIND_ADDRESS}:${SEARXNG_PORT}"
  25. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  26. SEARXNG_UWSGI_SOCKET="${SERVICE_HOME}/run/socket"
  27. else
  28. SEARXNG_UWSGI_SOCKET=
  29. fi
  30. # SEARXNG_URL: the public URL of the instance (https://example.org/searxng). The
  31. # value is taken from environment ${SEARXNG_URL} in ./utils/brand.env. This
  32. # variable is an empty string if server.base_url in the settings.yml is set to
  33. # 'false'.
  34. SEARXNG_URL="${SEARXNG_URL:-http://$(uname -n)/searxng}"
  35. SEARXNG_URL="${SEARXNG_URL%/}" # if exists, remove trailing slash
  36. if in_container; then
  37. # hint: Linux containers do not have DNS entries, lets use IPs
  38. SEARXNG_URL="http://$(primary_ip)/searxng"
  39. fi
  40. SEARXNG_URL_PATH="$(echo "${SEARXNG_URL}" | sed -e 's,^.*://[^/]*\(/.*\),\1,g')"
  41. [[ "${SEARXNG_URL_PATH}" == "${SEARXNG_URL}" ]] && SEARXNG_URL_PATH=/
  42. # Apache settings
  43. APACHE_SEARXNG_SITE="searxng.conf"
  44. # nginx settings
  45. NGINX_SEARXNG_SITE="searxng.conf"
  46. # apt packages
  47. SEARXNG_PACKAGES_debian="\
  48. python3-dev python3-babel python3-venv python-is-python3
  49. uwsgi uwsgi-plugin-python3
  50. git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev"
  51. SEARXNG_BUILD_PACKAGES_debian="\
  52. graphviz imagemagick texlive-xetex librsvg2-bin
  53. texlive-latex-recommended texlive-extra-utils fonts-dejavu
  54. latexmk shellcheck"
  55. # pacman packages
  56. SEARXNG_PACKAGES_arch="\
  57. python python-pip python-lxml python-babel
  58. uwsgi uwsgi-plugin-python
  59. git base-devel libxml2"
  60. SEARXNG_BUILD_PACKAGES_arch="\
  61. graphviz imagemagick texlive-bin extra/librsvg
  62. texlive-core texlive-latexextra ttf-dejavu shellcheck"
  63. # dnf packages
  64. SEARXNG_PACKAGES_fedora="\
  65. python python-pip python-lxml python-babel python3-devel
  66. uwsgi uwsgi-plugin-python3
  67. git @development-tools libxml2 openssl"
  68. SEARXNG_BUILD_PACKAGES_fedora="\
  69. graphviz graphviz-gd ImageMagick librsvg2-tools
  70. texlive-xetex-bin texlive-collection-fontsrecommended
  71. texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
  72. dejavu-sans-mono-fonts ShellCheck"
  73. case $DIST_ID-$DIST_VERS in
  74. ubuntu-18.04)
  75. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian}"
  76. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"
  77. APACHE_PACKAGES="$APACHE_PACKAGES libapache2-mod-proxy-uwsgi"
  78. ;;
  79. ubuntu-*|debian-*)
  80. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian} python-is-python3"
  81. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"
  82. ;;
  83. arch-*)
  84. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_arch}"
  85. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_arch}"
  86. ;;
  87. fedora-*)
  88. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_fedora}"
  89. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_fedora}"
  90. ;;
  91. esac
  92. _service_prefix=" ${_Yellow}|${SERVICE_USER}|${_creset} "
  93. # ----------------------------------------------------------------------------
  94. usage() {
  95. # ----------------------------------------------------------------------------
  96. # shellcheck disable=SC1117
  97. cat <<EOF
  98. usage:
  99. $(basename "$0") install [all|user|pyenv|settings|uwsgi|valkey|nginx|apache|searxng-src|packages|buildhost]
  100. $(basename "$0") remove [all|user|pyenv|settings|uwsgi|valkey|nginx|apache]
  101. $(basename "$0") instance [cmd|update|check|localtest|inspect]
  102. install|remove:
  103. all : complete (de-) installation of the SearXNG service
  104. user : service user '${SERVICE_USER}' (${SERVICE_HOME})
  105. pyenv : virtualenv (python) in ${SEARXNG_PYENV}
  106. settings : settings from ${SEARXNG_SETTINGS_PATH}
  107. uwsgi : SearXNG's uWSGI app ${SEARXNG_UWSGI_APP}
  108. nginx : HTTP site ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}
  109. apache : HTTP site ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}
  110. install:
  111. valkey : install a local valkey server
  112. remove:
  113. redis : remove a local redis server ${REDIS_HOME}/run/redis.sock
  114. install:
  115. searxng-src : clone ${GIT_URL} into ${SEARXNG_SRC}
  116. packages : installs packages from OS package manager required by SearXNG
  117. buildhost : installs packages from OS package manager required by a SearXNG buildhost
  118. instance:
  119. update : update SearXNG instance (git fetch + reset & update settings.yml)
  120. check : run checks from utils/searxng_check.py in the active installation
  121. inspect : run some small tests and inspect SearXNG's server status and log
  122. get_setting : get settings value from running SearXNG instance
  123. cmd : run command in SearXNG instance's environment (e.g. bash)
  124. EOF
  125. searxng.instance.env
  126. [[ -n ${1} ]] && err_msg "$1"
  127. }
  128. searxng.instance.env() {
  129. echo "uWSGI:"
  130. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  131. echo " SEARXNG_UWSGI_SOCKET : ${SEARXNG_UWSGI_SOCKET}"
  132. else
  133. echo " SEARXNG_INTERNAL_HTTP: ${SEARXNG_INTERNAL_HTTP}"
  134. fi
  135. cat <<EOF
  136. environment:
  137. GIT_URL : ${GIT_URL}
  138. GIT_BRANCH : ${GIT_BRANCH}
  139. SEARXNG_URL : ${SEARXNG_URL}
  140. SEARXNG_PORT : ${SEARXNG_PORT}
  141. SEARXNG_BIND_ADDRESS : ${SEARXNG_BIND_ADDRESS}
  142. EOF
  143. }
  144. main() {
  145. case $1 in
  146. install|remove|instance)
  147. nginx_distro_setup
  148. apache_distro_setup
  149. uWSGI_distro_setup
  150. required_commands \
  151. sudo systemctl install git wget curl \
  152. || exit
  153. ;;
  154. esac
  155. local _usage="unknown or missing $1 command $2"
  156. case $1 in
  157. --getenv) var="$2"; echo "${!var}"; exit 0;;
  158. --cmd) shift; "$@";;
  159. -h|--help) usage; exit 0;;
  160. install)
  161. sudo_or_exit
  162. case $2 in
  163. all) searxng.install.all;;
  164. user) searxng.install.user;;
  165. pyenv) searxng.install.pyenv;;
  166. searxng-src) searxng.install.clone;;
  167. settings) searxng.install.settings;;
  168. uwsgi) searxng.install.uwsgi;;
  169. packages) searxng.install.packages;;
  170. buildhost) searxng.install.buildhost;;
  171. nginx) searxng.nginx.install;;
  172. apache) searxng.apache.install;;
  173. valkey) searxng.install.valkey;;
  174. *) usage "$_usage"; exit 42;;
  175. esac
  176. ;;
  177. remove)
  178. sudo_or_exit
  179. case $2 in
  180. all) searxng.remove.all;;
  181. user) drop_service_account "${SERVICE_USER}";;
  182. pyenv) searxng.remove.pyenv;;
  183. settings) searxng.remove.settings;;
  184. uwsgi) searxng.remove.uwsgi;;
  185. apache) searxng.apache.remove;;
  186. remove) searxng.nginx.remove;;
  187. valkey) searxng.remove.valkey;;
  188. redis) searxng.remove.redis;;
  189. *) usage "$_usage"; exit 42;;
  190. esac
  191. ;;
  192. instance)
  193. case $2 in
  194. update)
  195. sudo_or_exit
  196. searxng.instance.update
  197. ;;
  198. check)
  199. sudo_or_exit
  200. searxng.instance.self.call searxng.check
  201. ;;
  202. inspect)
  203. sudo_or_exit
  204. searxng.instance.inspect
  205. ;;
  206. cmd)
  207. sudo_or_exit
  208. shift; shift; searxng.instance.exec "$@"
  209. ;;
  210. get_setting)
  211. shift; shift; searxng.instance.get_setting "$@"
  212. ;;
  213. call)
  214. # call a function in instance's environment
  215. shift; shift; searxng.instance.self.call "$@"
  216. ;;
  217. _call)
  218. shift; shift; "$@"
  219. ;;
  220. *) usage "$_usage"; exit 42;;
  221. esac
  222. ;;
  223. *)
  224. local cmd="$1"
  225. _type="$(type -t "$cmd")"
  226. if [ "$_type" != 'function' ]; then
  227. usage "unknown or missing command $1"
  228. exit 42
  229. else
  230. "$cmd" "$@"
  231. fi
  232. ;;
  233. esac
  234. }
  235. searxng.install.all() {
  236. rst_title "SearXNG installation" part
  237. local valkey_url
  238. rst_title "SearXNG"
  239. searxng.install.packages
  240. wait_key 10
  241. searxng.install.user
  242. wait_key 10
  243. searxng.install.clone
  244. wait_key
  245. searxng.install.pyenv
  246. wait_key
  247. searxng.install.settings
  248. wait_key
  249. searxng.instance.localtest
  250. wait_key
  251. searxng.install.uwsgi
  252. wait_key
  253. rst_title "Valkey DB"
  254. searxng.install.valkey.db
  255. rst_title "HTTP Server"
  256. searxng.install.http.site
  257. rst_title "Finalize installation"
  258. if ask_yn "Do you want to run some checks?" Yn; then
  259. searxng.instance.self.call searxng.check
  260. fi
  261. }
  262. searxng.install.valkey.db() {
  263. local valkey_url
  264. valkey_url=$(searxng.instance.get_setting valkey.url)
  265. if [ "${valkey_url}" = "False" ]; then
  266. rst_para "valkey DB connector is not configured in your instance"
  267. else
  268. rst_para "\
  269. In your instance, valkey DB connector is configured at:
  270. ${valkey_url}
  271. "
  272. if searxng.instance.exec python -c "from searx import valkeydb; valkeydb.initialize() or exit(42)"; then
  273. info_msg "SearXNG instance is able to connect valkey DB."
  274. return
  275. fi
  276. fi
  277. if ! [[ ${valkey_url} = valkey://localhost:6379/* ]]; then
  278. err_msg "SearXNG instance can't connect valkey DB / check valkey & your settings"
  279. return
  280. fi
  281. rst_para ".. but this valkey DB is not installed yet."
  282. if ask_yn "Do you want to install the valkey DB now?" Yn; then
  283. searxng.install.valkey
  284. uWSGI_restart "$SEARXNG_UWSGI_APP"
  285. fi
  286. }
  287. searxng.install.http.site() {
  288. if apache_is_installed; then
  289. info_msg "Apache is installed on this host."
  290. if ask_yn "Do you want to install a reverse proxy" Yn; then
  291. searxng.apache.install
  292. fi
  293. elif nginx_is_installed; then
  294. info_msg "Nginx is installed on this host."
  295. if ask_yn "Do you want to install a reverse proxy" Yn; then
  296. searxng.nginx.install
  297. fi
  298. else
  299. info_msg "Don't forget to install HTTP site."
  300. fi
  301. }
  302. searxng.remove.all() {
  303. local valkey_url
  304. rst_title "De-Install SearXNG (service)"
  305. if ! ask_yn "Do you really want to deinstall SearXNG?"; then
  306. return
  307. fi
  308. valkey_url=$(searxng.instance.get_setting valkey.url)
  309. if ! [[ ${valkey_url} = unix://${VALKEY_HOME}/run/valkey.sock* ]]; then
  310. searxng.remove.valkey
  311. fi
  312. searxng.remove.uwsgi
  313. drop_service_account "${SERVICE_USER}"
  314. searxng.remove.settings
  315. wait_key
  316. if service_is_available "${SEARXNG_URL}"; then
  317. MSG="** Don't forget to remove your public site! (${SEARXNG_URL}) **" wait_key 10
  318. fi
  319. }
  320. searxng.install.user() {
  321. rst_title "SearXNG -- install user" section
  322. echo
  323. if getent passwd "${SERVICE_USER}" > /dev/null; then
  324. echo "user already exists"
  325. return 0
  326. fi
  327. tee_stderr 1 <<EOF | bash | prefix_stdout
  328. useradd --shell /bin/bash --system \
  329. --home-dir "${SERVICE_HOME}" \
  330. --comment 'Privacy-respecting metasearch engine' ${SERVICE_USER}
  331. mkdir "${SERVICE_HOME}"
  332. chown -R "${SERVICE_GROUP}:${SERVICE_GROUP}" "${SERVICE_HOME}"
  333. groups ${SERVICE_USER}
  334. EOF
  335. }
  336. searxng.install.packages() {
  337. TITLE="SearXNG -- install packages" pkg_install "${SEARXNG_PACKAGES}"
  338. }
  339. searxng.install.buildhost() {
  340. TITLE="SearXNG -- install buildhost packages" pkg_install \
  341. "${SEARXNG_PACKAGES} ${SEARXNG_BUILD_PACKAGES}"
  342. }
  343. searxng.install.clone() {
  344. rst_title "Clone SearXNG sources" section
  345. if ! service_account_is_available "${SERVICE_USER}"; then
  346. die 42 "To clone SearXNG, first install user ${SERVICE_USER}."
  347. fi
  348. echo
  349. if ! sudo -i -u "${SERVICE_USER}" ls -d "$REPO_ROOT" > /dev/null; then
  350. die 42 "user '${SERVICE_USER}' missed read permission: $REPO_ROOT"
  351. fi
  352. # SERVICE_HOME="$(sudo -i -u "${SERVICE_USER}" echo \$HOME 2>/dev/null)"
  353. if [[ ! "${SERVICE_HOME}" ]]; then
  354. err_msg "to clone SearXNG sources, user ${SERVICE_USER} hast to be created first"
  355. return 42
  356. fi
  357. if [[ ! $(git show-ref "refs/heads/${GIT_BRANCH}") ]]; then
  358. warn_msg "missing local branch ${GIT_BRANCH}"
  359. info_msg "create local branch ${GIT_BRANCH} from start point: origin/${GIT_BRANCH}"
  360. git branch "${GIT_BRANCH}" "origin/${GIT_BRANCH}"
  361. fi
  362. if [[ ! $(git rev-parse --abbrev-ref HEAD) == "${GIT_BRANCH}" ]]; then
  363. warn_msg "take into account, installing branch $GIT_BRANCH while current branch is $(git rev-parse --abbrev-ref HEAD)"
  364. fi
  365. # export SERVICE_HOME
  366. # clone repo and add a safe.directory entry to git's system config / see
  367. # https://github.com/searxng/searxng/issues/1251
  368. git config --system --add safe.directory "${REPO_ROOT}/.git"
  369. git_clone "$REPO_ROOT" "${SEARXNG_SRC}" \
  370. "$GIT_BRANCH" "${SERVICE_USER}"
  371. git config --system --add safe.directory "${SEARXNG_SRC}"
  372. pushd "${SEARXNG_SRC}" > /dev/null
  373. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  374. cd "${SEARXNG_SRC}"
  375. git remote set-url origin ${GIT_URL}
  376. git config user.email "${ADMIN_EMAIL}"
  377. git config user.name "${ADMIN_NAME}"
  378. git config --list
  379. EOF
  380. popd > /dev/null
  381. }
  382. searxng.install.link_src() {
  383. rst_title "link SearXNG's sources to: $2" chapter
  384. echo
  385. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  386. mv -f "${SEARXNG_SRC}" "${SEARXNG_SRC}.backup"
  387. ln -s "${2}" "${SEARXNG_SRC}"
  388. ls -ld /usr/local/searxng/searxng-src
  389. EOF
  390. echo
  391. uWSGI_restart "$SEARXNG_UWSGI_APP"
  392. }
  393. searxng.install.pyenv() {
  394. rst_title "Create virtualenv (python)" section
  395. echo
  396. if [[ ! -f "${SEARXNG_SRC}/manage" ]]; then
  397. die 42 "To create pyenv for SearXNG, first install searxng-src."
  398. fi
  399. info_msg "create pyenv in ${SEARXNG_PYENV}"
  400. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  401. rm -rf "${SEARXNG_PYENV}"
  402. python -m venv "${SEARXNG_PYENV}"
  403. grep -qFs -- 'source ${SEARXNG_PYENV}/bin/activate' ~/.profile \
  404. || echo 'source ${SEARXNG_PYENV}/bin/activate' >> ~/.profile
  405. EOF
  406. info_msg "inspect python's virtual environment"
  407. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  408. command -v python && python --version
  409. EOF
  410. wait_key
  411. info_msg "install needed python packages"
  412. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  413. pip install -U pip
  414. pip install -U setuptools
  415. pip install -U wheel
  416. pip install -U pyyaml
  417. cd ${SEARXNG_SRC}
  418. pip install --use-pep517 --no-build-isolation -e .
  419. EOF
  420. }
  421. searxng.remove.pyenv() {
  422. rst_title "Remove virtualenv (python)" section
  423. if ! ask_yn "Do you really want to drop ${SEARXNG_PYENV} ?"; then
  424. return
  425. fi
  426. info_msg "remove pyenv activation from ~/.profile"
  427. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  428. grep -v 'source ${SEARXNG_PYENV}/bin/activate' ~/.profile > ~/.profile.##
  429. mv ~/.profile.## ~/.profile
  430. EOF
  431. rm -rf "${SEARXNG_PYENV}"
  432. }
  433. searxng.install.settings() {
  434. rst_title "install ${SEARXNG_SETTINGS_PATH}" section
  435. if ! [[ -f "${SEARXNG_SRC}/.git/config" ]]; then
  436. die "Before install settings, first install SearXNG."
  437. fi
  438. mkdir -p "$(dirname "${SEARXNG_SETTINGS_PATH}")"
  439. DEFAULT_SELECT=1 \
  440. install_template --no-eval \
  441. "${SEARXNG_SETTINGS_PATH}" \
  442. "${SERVICE_USER}" "${SERVICE_GROUP}"
  443. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "root"
  444. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"
  445. EOF
  446. }
  447. searxng.remove.settings() {
  448. rst_title "remove ${SEARXNG_SETTINGS_PATH}" section
  449. if ask_yn "Do you want to delete the SearXNG settings?" Yn; then
  450. rm -f "${SEARXNG_SETTINGS_PATH}"
  451. fi
  452. }
  453. searxng.check() {
  454. rst_title "SearXNG checks" section
  455. "${SEARXNG_PYENV}/bin/python" "${SEARXNG_SRC}/utils/searxng_check.py"
  456. }
  457. searxng.instance.update() {
  458. rst_title "Update SearXNG instance"
  459. rst_para "fetch from $GIT_URL and reset to origin/$GIT_BRANCH"
  460. tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  461. cd ${SEARXNG_SRC}
  462. git fetch origin "$GIT_BRANCH"
  463. git reset --hard "origin/$GIT_BRANCH"
  464. pip install -U pip
  465. pip install -U setuptools
  466. pip install -U wheel
  467. pip install -U pyyaml
  468. pip install -U --use-pep517 --no-build-isolation -e .
  469. EOF
  470. rst_para "update instance's settings.yml from ${SEARXNG_SETTINGS_PATH}"
  471. DEFAULT_SELECT=2 \
  472. install_template --no-eval \
  473. "${SEARXNG_SETTINGS_PATH}" \
  474. "${SERVICE_USER}" "${SERVICE_GROUP}"
  475. sudo -H -i <<EOF
  476. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"
  477. EOF
  478. uWSGI_restart "${SEARXNG_UWSGI_APP}"
  479. }
  480. searxng.install.uwsgi() {
  481. rst_title "SearXNG (install uwsgi)"
  482. install_uwsgi
  483. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  484. searxng.install.uwsgi.socket
  485. else
  486. searxng.install.uwsgi.http
  487. fi
  488. }
  489. searxng.install.uwsgi.http() {
  490. rst_para "Install ${SEARXNG_UWSGI_APP} at: http://${SEARXNG_INTERNAL_HTTP}"
  491. uWSGI_install_app "${SEARXNG_UWSGI_APP}"
  492. if ! searxng.uwsgi.available; then
  493. err_msg "URL http://${SEARXNG_INTERNAL_HTTP} not available, check SearXNG & uwsgi setup!"
  494. fi
  495. }
  496. searxng.install.uwsgi.socket() {
  497. rst_para "Install ${SEARXNG_UWSGI_APP} using socket at: ${SEARXNG_UWSGI_SOCKET}"
  498. mkdir -p "$(dirname "${SEARXNG_UWSGI_SOCKET}")"
  499. chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "$(dirname "${SEARXNG_UWSGI_SOCKET}")"
  500. case $DIST_ID-$DIST_VERS in
  501. fedora-*)
  502. # Fedora runs uWSGI in emperor-tyrant mode: in Tyrant mode the
  503. # Emperor will run the vassal using the UID/GID of the vassal
  504. # configuration file [1] (user and group of the app .ini file).
  505. # [1] https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html#tyrant-mode-secure-multi-user-hosting
  506. uWSGI_install_app --variant=socket "${SEARXNG_UWSGI_APP}" "${SERVICE_USER}" "${SERVICE_GROUP}"
  507. ;;
  508. *)
  509. uWSGI_install_app --variant=socket "${SEARXNG_UWSGI_APP}"
  510. ;;
  511. esac
  512. sleep 5
  513. if ! searxng.uwsgi.available; then
  514. err_msg "uWSGI socket not available at: ${SEARXNG_UWSGI_SOCKET}"
  515. fi
  516. }
  517. searxng.uwsgi.available() {
  518. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  519. [[ -S "${SEARXNG_UWSGI_SOCKET}" ]]
  520. exit_val=$?
  521. if [[ $exit_val = 0 ]]; then
  522. info_msg "uWSGI socket is located at: ${SEARXNG_UWSGI_SOCKET}"
  523. fi
  524. else
  525. service_is_available "http://${SEARXNG_INTERNAL_HTTP}"
  526. exit_val=$?
  527. fi
  528. return "$exit_val"
  529. }
  530. searxng.remove.uwsgi() {
  531. rst_title "Remove SearXNG's uWSGI app (${SEARXNG_UWSGI_APP})" section
  532. echo
  533. uWSGI_remove_app "${SEARXNG_UWSGI_APP}"
  534. }
  535. searxng.remove.redis() {
  536. rst_title "SearXNG (remove redis)"
  537. redis.rmgrp "${SERVICE_USER}"
  538. redis.remove
  539. }
  540. searxng.install.valkey() {
  541. rst_title "SearXNG (install valkey)"
  542. valkey.install
  543. }
  544. searxng.instance.localtest() {
  545. rst_title "Test SearXNG instance locally" section
  546. rst_para "Activate debug mode, start a minimal SearXNG "\
  547. "service and debug a HTTP request/response cycle."
  548. if service_is_available "http://${SEARXNG_INTERNAL_HTTP}" &>/dev/null; then
  549. err_msg "URL/port http://${SEARXNG_INTERNAL_HTTP} is already in use, you"
  550. err_msg "should stop that service before starting local tests!"
  551. if ! ask_yn "Continue with local tests?"; then
  552. return
  553. fi
  554. fi
  555. echo
  556. searxng.instance.debug.on
  557. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  558. export SEARXNG_SETTINGS_PATH="${SEARXNG_SETTINGS_PATH}"
  559. cd ${SEARXNG_SRC}
  560. timeout 10 python searx/webapp.py &
  561. sleep 3
  562. curl --location --verbose --head --insecure ${SEARXNG_INTERNAL_HTTP}
  563. EOF
  564. echo
  565. searxng.instance.debug.off
  566. }
  567. searxng.install.http.pre() {
  568. if ! searxng.uwsgi.available; then
  569. rst_para "\
  570. To install uWSGI use::
  571. $(basename "$0") install uwsgi
  572. "
  573. die 42 "SearXNG's uWSGI app not available"
  574. fi
  575. if ! searxng.instance.exec python -c "from searx import valkeydb; valkeydb.initialize() or exit(42)"; then
  576. rst_para "\
  577. The configured valkey DB is not available: If your server is public to the
  578. internet, you should setup a bot protection to block excessively bot queries.
  579. Bot protection requires a valkey DB. About bot protection visit the official
  580. SearXNG documentation and query for the word 'limiter'.
  581. "
  582. fi
  583. }
  584. searxng.apache.install() {
  585. rst_title "Install Apache site ${APACHE_SEARXNG_SITE}"
  586. rst_para "\
  587. This installs SearXNG's uWSGI app as apache site. The apache site is located at:
  588. ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}."
  589. searxng.install.http.pre
  590. if ! apache_is_installed; then
  591. err_msg "Apache packages are not installed"
  592. if ! ask_yn "Do you really want to continue and install apache packages?" Yn; then
  593. return
  594. else
  595. FORCE_SELECTION=Y install_apache
  596. fi
  597. else
  598. info_msg "Apache packages are installed [OK]"
  599. fi
  600. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  601. apache_install_site --variant=socket "${APACHE_SEARXNG_SITE}"
  602. else
  603. apache_install_site "${APACHE_SEARXNG_SITE}"
  604. fi
  605. if ! service_is_available "${SEARXNG_URL}"; then
  606. err_msg "Public service at ${SEARXNG_URL} is not available!"
  607. fi
  608. }
  609. searxng.apache.remove() {
  610. rst_title "Remove Apache site ${APACHE_SEARXNG_SITE}"
  611. rst_para "\
  612. This removes apache site ${APACHE_SEARXNG_SITE}::
  613. ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}"
  614. ! apache_is_installed && err_msg "Apache is not installed."
  615. if ! ask_yn "Do you really want to continue?" Yn; then
  616. return
  617. fi
  618. apache_remove_site "${APACHE_SEARXNG_SITE}"
  619. }
  620. searxng.nginx.install() {
  621. rst_title "Install nginx site ${NGINX_SEARXNG_SITE}"
  622. rst_para "\
  623. This installs SearXNG's uWSGI app as Nginx site. The Nginx site is located at:
  624. ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE} and requires a uWSGI."
  625. searxng.install.http.pre
  626. if ! nginx_is_installed ; then
  627. err_msg "Nginx packages are not installed"
  628. if ! ask_yn "Do you really want to continue and install Nginx packages?" Yn; then
  629. return
  630. else
  631. FORCE_SELECTION=Y install_nginx
  632. fi
  633. else
  634. info_msg "Nginx packages are installed [OK]"
  635. fi
  636. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  637. nginx_install_app --variant=socket "${NGINX_SEARXNG_SITE}"
  638. else
  639. nginx_install_app "${NGINX_SEARXNG_SITE}"
  640. fi
  641. if ! service_is_available "${SEARXNG_URL}"; then
  642. err_msg "Public service at ${SEARXNG_URL} is not available!"
  643. fi
  644. }
  645. searxng.nginx.remove() {
  646. rst_title "Remove Nginx site ${NGINX_SEARXNG_SITE}"
  647. rst_para "\
  648. This removes Nginx site ${NGINX_SEARXNG_SITE}::
  649. ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}"
  650. ! nginx_is_installed && err_msg "Nginx is not installed."
  651. if ! ask_yn "Do you really want to continue?" Yn; then
  652. return
  653. fi
  654. nginx_remove_app "${NGINX_SEARXNG_SITE}"
  655. }
  656. searxng.instance.exec() {
  657. if ! service_account_is_available "${SERVICE_USER}"; then
  658. die 42 "can't execute: instance does not exist (missed account ${SERVICE_USER})"
  659. fi
  660. sudo -H -i -u "${SERVICE_USER}" \
  661. SEARXNG_UWSGI_USE_SOCKET="${SEARXNG_UWSGI_USE_SOCKET}" \
  662. "$@"
  663. }
  664. searxng.instance.self.call() {
  665. # wrapper to call a function in instance's environment
  666. info_msg "wrapper: utils/searxng.sh instance _call $*"
  667. searxng.instance.exec "${SEARXNG_SRC}/utils/searxng.sh" instance _call "$@"
  668. }
  669. searxng.instance.get_setting() {
  670. searxng.instance.exec python <<EOF
  671. from searx import get_setting
  672. print(get_setting('$1'))
  673. EOF
  674. }
  675. searxng.instance.debug.on() {
  676. warn_msg "Do not enable debug in a production environment!"
  677. info_msg "try to enable debug mode ..."
  678. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
  679. cd ${SEARXNG_SRC}
  680. sed -i -e "s/debug: false/debug: true/g" "$SEARXNG_SETTINGS_PATH"
  681. EOF
  682. uWSGI_restart "$SEARXNG_UWSGI_APP"
  683. }
  684. searxng.instance.debug.off() {
  685. info_msg "try to disable debug mode ..."
  686. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
  687. cd ${SEARXNG_SRC}
  688. sed -i -e "s/debug: true/debug: false/g" "$SEARXNG_SETTINGS_PATH"
  689. EOF
  690. uWSGI_restart "$SEARXNG_UWSGI_APP"
  691. }
  692. searxng.instance.inspect() {
  693. rst_title "Inspect SearXNG instance"
  694. echo
  695. searxng.instance.self.call _searxng.instance.inspect
  696. local _debug_on
  697. if ask_yn "Enable SearXNG debug mode?"; then
  698. searxng.instance.debug.on
  699. _debug_on=1
  700. fi
  701. echo
  702. case $DIST_ID-$DIST_VERS in
  703. ubuntu-*|debian-*)
  704. # For uWSGI debian uses the LSB init process; for each configuration
  705. # file new uWSGI daemon instance is started with additional option.
  706. service uwsgi status "${SERVICE_NAME}"
  707. ;;
  708. arch-*)
  709. systemctl --no-pager -l status "uwsgi@${SERVICE_NAME%.*}"
  710. ;;
  711. fedora-*)
  712. systemctl --no-pager -l status uwsgi
  713. ;;
  714. esac
  715. echo -e "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  716. read -r -s -n1 -t 5
  717. echo
  718. while true; do
  719. trap break 2
  720. case $DIST_ID-$DIST_VERS in
  721. ubuntu-*|debian-*) tail -f "/var/log/uwsgi/app/${SERVICE_NAME%.*}.log" ;;
  722. arch-*) journalctl -f -u "uwsgi@${SERVICE_NAME%.*}" ;;
  723. fedora-*) journalctl -f -u uwsgi ;;
  724. esac
  725. done
  726. if [[ $_debug_on == 1 ]]; then
  727. searxng.instance.debug.off
  728. fi
  729. return 0
  730. }
  731. _searxng.instance.inspect() {
  732. searxng.instance.env
  733. if in_container; then
  734. # shellcheck source=utils/lxc-searxng.env
  735. source "${REPO_ROOT}/utils/lxc-searxng.env"
  736. lxc_suite_info
  737. fi
  738. MSG="${_Green}[${_BCyan}CTRL-C${_Green}] to stop or [${_BCyan}KEY${_Green}] to continue${_creset}"
  739. if ! searxng.uwsgi.available; then
  740. err_msg "SearXNG's uWSGI app not available"
  741. wait_key
  742. fi
  743. if ! service_is_available "${SEARXNG_URL}"; then
  744. err_msg "Public service at ${SEARXNG_URL} is not available!"
  745. wait_key
  746. fi
  747. }
  748. searxng.doc.rst() {
  749. local APACHE_SITES_AVAILABLE="/etc/apache2/sites-available"
  750. local NGINX_APPS_AVAILABLE="/etc/nginx/default.apps-available"
  751. local debian="${SEARXNG_PACKAGES_debian}"
  752. local arch="${SEARXNG_PACKAGES_arch}"
  753. local fedora="${SEARXNG_PACKAGES_fedora}"
  754. local debian_build="${SEARXNG_BUILD_PACKAGES_debian}"
  755. local arch_build="${SEARXNG_BUILD_PACKAGES_arch}"
  756. local fedora_build="${SEARXNG_BUILD_PACKAGES_fedora}"
  757. debian="$(echo "${debian}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  758. arch="$(echo "${arch}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  759. fedora="$(echo "${fedora}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  760. debian_build="$(echo "${debian_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  761. arch_build="$(echo "${arch_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  762. fedora_build="$(echo "${fedora_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  763. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  764. uwsgi_variant=':socket'
  765. else
  766. uwsgi_variant=':socket'
  767. fi
  768. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searxng.rst")\""
  769. # I use ubuntu-20.04 here to demonstrate that versions are also supported,
  770. # normally debian-* and ubuntu-* are most the same.
  771. for DIST_NAME in ubuntu-20.04 arch fedora; do
  772. (
  773. DIST_ID=${DIST_NAME%-*}
  774. DIST_VERS=${DIST_NAME#*-}
  775. [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  776. uWSGI_distro_setup
  777. echo -e "\n.. START searxng uwsgi-description $DIST_NAME"
  778. case $DIST_ID-$DIST_VERS in
  779. ubuntu-*|debian-*) cat <<EOF
  780. .. code:: bash
  781. # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
  782. # For uWSGI debian uses the LSB init process, this might be changed
  783. # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
  784. create ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}
  785. enable: sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP} ${uWSGI_APPS_ENABLED}/
  786. start: sudo -H service uwsgi start ${SEARXNG_UWSGI_APP%.*}
  787. restart: sudo -H service uwsgi restart ${SEARXNG_UWSGI_APP%.*}
  788. stop: sudo -H service uwsgi stop ${SEARXNG_UWSGI_APP%.*}
  789. disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  790. EOF
  791. ;;
  792. arch-*) cat <<EOF
  793. .. code:: bash
  794. # systemd --> /usr/lib/systemd/system/uwsgi@.service
  795. # For uWSGI archlinux uses systemd template units, see
  796. # - http://0pointer.de/blog/projects/instances.html
  797. # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
  798. create: ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  799. enable: sudo -H systemctl enable uwsgi@${SEARXNG_UWSGI_APP%.*}
  800. start: sudo -H systemctl start uwsgi@${SEARXNG_UWSGI_APP%.*}
  801. restart: sudo -H systemctl restart uwsgi@${SEARXNG_UWSGI_APP%.*}
  802. stop: sudo -H systemctl stop uwsgi@${SEARXNG_UWSGI_APP%.*}
  803. disable: sudo -H systemctl disable uwsgi@${SEARXNG_UWSGI_APP%.*}
  804. EOF
  805. ;;
  806. fedora-*|centos-7) cat <<EOF
  807. .. code:: bash
  808. # systemd --> /usr/lib/systemd/system/uwsgi.service
  809. # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
  810. # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
  811. create: ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  812. restart: sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  813. disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  814. EOF
  815. ;;
  816. esac
  817. echo -e ".. END searxng uwsgi-description $DIST_NAME"
  818. local _show_cursor="" # prevent from prefix_stdout's trailing show-cursor
  819. echo -e "\n.. START searxng uwsgi-appini $DIST_NAME"
  820. echo ".. code:: bash"
  821. echo
  822. eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}${uwsgi_variant}")\"" | prefix_stdout " "
  823. echo -e "\n.. END searxng uwsgi-appini $DIST_NAME"
  824. echo -e "\n.. START nginx socket"
  825. echo ".. code:: nginx"
  826. echo
  827. eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}:socket")\"" | prefix_stdout " "
  828. echo -e "\n.. END nginx socket"
  829. echo -e "\n.. START nginx http"
  830. echo ".. code:: nginx"
  831. echo
  832. eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}")\"" | prefix_stdout " "
  833. echo -e "\n.. END nginx http"
  834. echo -e "\n.. START apache socket"
  835. echo ".. code:: apache"
  836. echo
  837. eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}:socket")\"" | prefix_stdout " "
  838. echo -e "\n.. END apache socket"
  839. echo -e "\n.. START apache http"
  840. echo ".. code:: apache"
  841. echo
  842. eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}")\"" | prefix_stdout " "
  843. echo -e "\n.. END apache http"
  844. )
  845. done
  846. }
  847. # ----------------------------------------------------------------------------
  848. main "$@"
  849. # ----------------------------------------------------------------------------