morty.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: AGPL-3.0-or-later
  3. # shellcheck source=utils/lib.sh
  4. source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
  5. # shellcheck source=utils/lib_install.sh
  6. source "${REPO_ROOT}/utils/lib_install.sh"
  7. # ----------------------------------------------------------------------------
  8. # config
  9. # ----------------------------------------------------------------------------
  10. MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
  11. PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty/}"
  12. PUBLIC_URL_MORTY="${PUBLIC_URL_MORTY:-$(echo "$PUBLIC_URL" | sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}}"
  13. # shellcheck disable=SC2034
  14. MORTY_TIMEOUT=5
  15. SERVICE_NAME="morty"
  16. SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
  17. SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
  18. SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
  19. SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
  20. # shellcheck disable=SC2034
  21. SERVICE_GROUP="${SERVICE_USER}"
  22. # shellcheck disable=SC2034
  23. SERVICE_ENV_DEBUG=false
  24. GO_ENV="${SERVICE_HOME}/.go_env"
  25. GO_VERSION="go1.17.2"
  26. GO_PKG_URL="https://golang.org/dl/${GO_VERSION}.linux-amd64.tar.gz"
  27. GO_TAR=$(basename "$GO_PKG_URL")
  28. # shellcheck disable=SC2034
  29. CONFIG_FILES=()
  30. # Apache Settings
  31. APACHE_MORTY_SITE="morty.conf"
  32. NGINX_MORTY_SITE="morty.conf"
  33. # ----------------------------------------------------------------------------
  34. usage() {
  35. # ----------------------------------------------------------------------------
  36. # shellcheck disable=SC1117
  37. cat <<EOF
  38. usage::
  39. $(basename "$0") shell
  40. $(basename "$0") install [all|check|user]
  41. $(basename "$0") update [morty]
  42. $(basename "$0") remove [all]
  43. $(basename "$0") activate [service]
  44. $(basename "$0") deactivate [service]
  45. $(basename "$0") inspect [service]
  46. $(basename "$0") option [debug-on|debug-off|new-key]
  47. $(basename "$0") apache [install|remove]
  48. $(basename "$0") nginx [install|remove]
  49. $(basename "$0") info [searx]
  50. shell
  51. start interactive shell from user ${SERVICE_USER}
  52. install / remove
  53. :all: complete setup of morty service
  54. :user: add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
  55. install
  56. :check: check the morty installation
  57. update morty
  58. Update morty installation ($SERVICE_HOME)
  59. activate service
  60. activate and start service daemon (systemd unit)
  61. deactivate service
  62. stop and deactivate service daemon (systemd unit)
  63. inspect service
  64. show service status and log
  65. option
  66. set one of the available options
  67. :new-key: set new morty key
  68. apache : ${PUBLIC_URL_MORTY}
  69. :install: apache site with a reverse proxy (ProxyPass)
  70. :remove: apache site ${APACHE_MORTY_SITE}
  71. nginx (${PUBLIC_URL_MORTY})
  72. :install: nginx site with a reverse proxy (ProxyPass)
  73. :remove: nginx site ${NGINX_MORTY_SITE}
  74. ----
  75. sourced ${DOT_CONFIG} :
  76. SERVICE_USER : ${SERVICE_USER}
  77. SERVICE_HOME : ${SERVICE_HOME}
  78. PUBLIC_URL_MORTY: : ${PUBLIC_URL_MORTY}
  79. MORTY_LISTEN: : ${MORTY_LISTEN}
  80. EOF
  81. install_log_searx_instance
  82. if in_container; then
  83. # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
  84. for ip in $(global_IPs) ; do
  85. if [[ $ip =~ .*:.* ]]; then
  86. echo " container URL (IPv6): http://[${ip#*|}]:3000/"
  87. else
  88. # IPv4:
  89. echo " container URL (IPv4): http://${ip#*|}:3000/"
  90. fi
  91. done
  92. fi
  93. echo
  94. info_searx
  95. [[ -n ${1} ]] && err_msg "$1"
  96. }
  97. info_searx() {
  98. # shellcheck disable=SC1117
  99. cat <<EOF
  100. To activate result and image proxy in SearXNG read:
  101. https://searxng.github.io/searxng/admin/morty.html
  102. Check settings in file ${SEARXNG_SETTINGS_PATH} ...
  103. result_proxy:
  104. url : ${PUBLIC_URL_MORTY}
  105. server:
  106. image_proxy : True
  107. EOF
  108. }
  109. main() {
  110. required_commands \
  111. sudo install git wget curl \
  112. || exit
  113. local _usage="ERROR: unknown or missing $1 command $2"
  114. case $1 in
  115. --getenv) var="$2"; echo "${!var}"; exit 0;;
  116. -h|--help) usage; exit 0;;
  117. shell)
  118. sudo_or_exit
  119. interactive_shell "${SERVICE_USER}"
  120. ;;
  121. inspect)
  122. case $2 in
  123. service)
  124. sudo_or_exit
  125. inspect_service
  126. ;;
  127. *) usage "$_usage"; exit 42;;
  128. esac ;;
  129. install)
  130. rst_title "$SERVICE_NAME" part
  131. sudo_or_exit
  132. case $2 in
  133. all) install_all ;;
  134. check)
  135. rst_title "Check morty installation" part
  136. install_check
  137. ;;
  138. user) assert_user ;;
  139. *) usage "$_usage"; exit 42;;
  140. esac ;;
  141. update)
  142. sudo_or_exit
  143. case $2 in
  144. morty) update_morty ;;
  145. *) usage "$_usage"; exit 42;;
  146. esac ;;
  147. remove)
  148. sudo_or_exit
  149. case $2 in
  150. all) remove_all;;
  151. user) drop_service_account "${SERVICE_USER}" ;;
  152. *) usage "$_usage"; exit 42;;
  153. esac ;;
  154. activate)
  155. sudo_or_exit
  156. case $2 in
  157. service) systemd_activate_service "${SERVICE_NAME}" ;;
  158. *) usage "$_usage"; exit 42;;
  159. esac ;;
  160. deactivate)
  161. sudo_or_exit
  162. case $2 in
  163. service) systemd_deactivate_service "${SERVICE_NAME}" ;;
  164. *) usage "$_usage"; exit 42;;
  165. esac ;;
  166. apache)
  167. sudo_or_exit
  168. case $2 in
  169. install) install_apache_site ;;
  170. remove) remove_apache_site ;;
  171. *) usage "$_usage"; exit 42;;
  172. esac ;;
  173. nginx)
  174. sudo_or_exit
  175. case $2 in
  176. install) install_nginx_site ;;
  177. remove) remove_nginx_site ;;
  178. *) usage "$_usage"; exit 42;;
  179. esac ;;
  180. info)
  181. case $2 in
  182. searx) info_searx ;;
  183. *) usage "$_usage"; exit 42;;
  184. esac ;;
  185. option)
  186. sudo_or_exit
  187. case $2 in
  188. new-key) set_new_key ;;
  189. debug-on) enable_debug ;;
  190. debug-off) disable_debug ;;
  191. *) usage "$_usage"; exit 42;;
  192. esac ;;
  193. doc) rst-doc ;;
  194. *) usage "ERROR: unknown or missing command $1"; exit 42;;
  195. esac
  196. }
  197. install_all() {
  198. MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
  199. rst_title "Install $SERVICE_NAME (service)"
  200. assert_user
  201. wait_key
  202. install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
  203. wait_key
  204. install_morty
  205. wait_key
  206. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  207. wait_key
  208. if ! service_is_available "http://${MORTY_LISTEN}" ; then
  209. err_msg "Morty is not listening on: http://${MORTY_LISTEN}"
  210. fi
  211. if apache_is_installed; then
  212. info_msg "Apache is installed on this host."
  213. if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
  214. install_apache_site
  215. fi
  216. elif nginx_is_installed; then
  217. info_msg "nginx is installed on this host."
  218. if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
  219. install_nginx_site
  220. fi
  221. fi
  222. info_searx
  223. if ask_yn "Add image and result proxy to SearXNG settings.yml?" Yn; then
  224. "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
  225. "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
  226. fi
  227. if ask_yn "Do you want to inspect the installation?" Ny; then
  228. inspect_service
  229. fi
  230. }
  231. install_check() {
  232. if service_account_is_available "$SERVICE_USER"; then
  233. info_msg "service account $SERVICE_USER available."
  234. else
  235. err_msg "service account $SERVICE_USER not available!"
  236. fi
  237. if go_is_available "$SERVICE_USER"; then
  238. info_msg "~$SERVICE_USER: go is installed"
  239. else
  240. err_msg "~$SERVICE_USER: go is not installed"
  241. fi
  242. if morty_is_installed; then
  243. info_msg "~$SERVICE_USER: morty app is installed"
  244. else
  245. err_msg "~$SERVICE_USER: morty app is not installed!"
  246. fi
  247. if ! service_is_available "http://${MORTY_LISTEN}" ; then
  248. err_msg "Morty is not listening on: http://${MORTY_LISTEN}"
  249. echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
  250. wait_key
  251. fi
  252. if ! service_is_available "${PUBLIC_URL_MORTY}"; then
  253. warn_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
  254. if ! in_container; then
  255. warn_msg "Check if public name is correct and routed or use the public IP from above."
  256. fi
  257. fi
  258. if [[ "${GO_VERSION}" > "$(go_version)" ]]; then
  259. warn_msg "golang ($(go_version)) needs to be $GO_VERSION at least"
  260. else
  261. info_msg "golang $(go_version) is installed (min needed is: $GO_VERSION)"
  262. fi
  263. }
  264. go_version(){
  265. sudo -i -u "$SERVICE_USER" <<EOF
  266. go version | cut -d' ' -f 3
  267. EOF
  268. }
  269. remove_all() {
  270. rst_title "De-Install $SERVICE_NAME (service)"
  271. rst_para "\
  272. It goes without saying that this script can only be used to remove
  273. installations that were installed with this script."
  274. if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
  275. drop_service_account "${SERVICE_USER}"
  276. fi
  277. }
  278. assert_user() {
  279. rst_title "user $SERVICE_USER" section
  280. echo
  281. tee_stderr 1 <<EOF | bash | prefix_stdout
  282. useradd --shell /bin/bash --system \
  283. --home-dir "$SERVICE_HOME" \
  284. --comment 'Web content sanitizer proxy' $SERVICE_USER
  285. mkdir "$SERVICE_HOME"
  286. chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
  287. groups $SERVICE_USER
  288. EOF
  289. SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
  290. export SERVICE_HOME
  291. echo "export SERVICE_HOME=$SERVICE_HOME"
  292. cat > "$GO_ENV" <<EOF
  293. export GOPATH=\$HOME/go-apps
  294. export PATH=\$PATH:\$HOME/local/go/bin:\$GOPATH/bin
  295. EOF
  296. echo "Environment $GO_ENV has been setup."
  297. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER"
  298. grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
  299. EOF
  300. }
  301. morty_is_installed() {
  302. [[ -f $SERVICE_HOME/go-apps/bin/morty ]]
  303. }
  304. _svcpr=" ${_Yellow}|${SERVICE_USER}|${_creset} "
  305. install_morty() {
  306. rst_title "Install morty in user's ~/go-apps" section
  307. echo
  308. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  309. go get -v -u github.com/asciimoo/morty
  310. EOF
  311. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  312. cd \$GOPATH/src/github.com/asciimoo/morty
  313. go test
  314. go test -benchmem -bench .
  315. EOF
  316. }
  317. update_morty() {
  318. rst_title "Update morty" section
  319. echo
  320. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  321. go get -v -u github.com/asciimoo/morty
  322. EOF
  323. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  324. cd \$GOPATH/src/github.com/asciimoo/morty
  325. go test
  326. go test -benchmem -bench .
  327. EOF
  328. }
  329. set_service_env_debug() {
  330. # usage: set_service_env_debug [false|true]
  331. # shellcheck disable=SC2034
  332. local SERVICE_ENV_DEBUG="${1:-false}"
  333. if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
  334. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  335. fi
  336. }
  337. inspect_service() {
  338. rst_title "service status & log"
  339. cat <<EOF
  340. sourced ${DOT_CONFIG} :
  341. SERVICE_USER : ${SERVICE_USER}
  342. SERVICE_HOME : ${SERVICE_HOME}
  343. PUBLIC_URL_MORTY: : ${PUBLIC_URL_MORTY}
  344. MORTY_LISTEN: : ${MORTY_LISTEN}
  345. EOF
  346. install_log_searx_instance
  347. install_check
  348. if in_container; then
  349. lxc_suite_info
  350. else
  351. info_msg "public URL --> ${PUBLIC_URL_MORTY}"
  352. info_msg "morty URL --> http://${MORTY_LISTEN}"
  353. fi
  354. local _debug_on
  355. if ask_yn "Enable morty debug mode (needs reinstall of systemd service)?"; then
  356. enable_debug
  357. _debug_on=1
  358. else
  359. systemctl --no-pager -l status "${SERVICE_NAME}"
  360. fi
  361. echo
  362. # shellcheck disable=SC2059
  363. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  364. read -r -s -n1 -t 5
  365. echo
  366. while true; do
  367. trap break 2
  368. journalctl -f -u "${SERVICE_NAME}"
  369. done
  370. if [[ $_debug_on == 1 ]]; then
  371. FORCE_SELECTION=Y disable_debug
  372. fi
  373. return 0
  374. }
  375. enable_debug() {
  376. warn_msg "Do not enable debug in production environments!!"
  377. info_msg "Enabling debug option needs to reinstall systemd service!"
  378. set_service_env_debug true
  379. }
  380. disable_debug() {
  381. info_msg "Disabling debug option needs to reinstall systemd service!"
  382. set_service_env_debug false
  383. }
  384. set_new_key() {
  385. rst_title "Set morty key"
  386. echo
  387. MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
  388. info_msg "morty key: '${MORTY_KEY}'"
  389. warn_msg "this will need to reinstall services .."
  390. MSG="${_Green}press any [${_BCyan}KEY${_Green}] to continue // stop with [${_BCyan}CTRL-C${_creset}]" wait_key
  391. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  392. "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
  393. "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
  394. }
  395. install_apache_site() {
  396. rst_title "Install Apache site $APACHE_MORTY_SITE"
  397. rst_para "\
  398. This installs a reverse proxy (ProxyPass) into apache site (${APACHE_MORTY_SITE})"
  399. ! apache_is_installed && err_msg "Apache is not installed."
  400. if ! ask_yn "Do you really want to continue?" Yn; then
  401. return
  402. else
  403. install_apache
  404. fi
  405. apache_install_site "${APACHE_MORTY_SITE}"
  406. info_msg "testing public url .."
  407. if ! service_is_available "${PUBLIC_URL_MORTY}"; then
  408. err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
  409. fi
  410. }
  411. remove_apache_site() {
  412. rst_title "Remove Apache site $APACHE_MORTY_SITE"
  413. rst_para "\
  414. This removes apache site ${APACHE_MORTY_SITE}."
  415. ! apache_is_installed && err_msg "Apache is not installed."
  416. if ! ask_yn "Do you really want to continue?" Yn; then
  417. return
  418. fi
  419. apache_remove_site "$APACHE_MORTY_SITE"
  420. }
  421. install_nginx_site() {
  422. rst_title "Install nginx site $NGINX_MORTY_SITE"
  423. rst_para "\
  424. This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_MORTY_SITE})"
  425. ! nginx_is_installed && err_msg "nginx is not installed."
  426. if ! ask_yn "Do you really want to continue?" Yn; then
  427. return
  428. else
  429. install_nginx
  430. fi
  431. "${REPO_ROOT}/utils/searx.sh" install uwsgi
  432. # shellcheck disable=SC2034
  433. SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
  434. # shellcheck disable=SC2034
  435. SEARXNG_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARXNG_URL_PATH)
  436. nginx_install_app "${NGINX_MORTY_SITE}"
  437. info_msg "testing public url .."
  438. if ! service_is_available "${PUBLIC_URL_MORTY}"; then
  439. err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
  440. fi
  441. }
  442. remove_nginx_site() {
  443. rst_title "Remove nginx site $NGINX_MORTY_SITE"
  444. rst_para "\
  445. This removes nginx site ${NGINX_MORTY_SITE}."
  446. ! nginx_is_installed && err_msg "nginx is not installed."
  447. if ! ask_yn "Do you really want to continue?" Yn; then
  448. return
  449. fi
  450. nginx_remove_site "$NGINX_MORTY_SITE"
  451. }
  452. rst-doc() {
  453. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/morty.rst")\""
  454. echo -e "\n.. START install systemd unit"
  455. cat <<EOF
  456. .. tabs::
  457. .. group-tab:: systemd
  458. .. code:: bash
  459. EOF
  460. eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
  461. echo -e "\n.. END install systemd unit"
  462. # for DIST_NAME in ubuntu-20.04 arch fedora centos; do
  463. # (
  464. # DIST_ID=${DIST_NAME%-*}
  465. # DIST_VERS=${DIST_NAME#*-}
  466. # [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  467. # # ...
  468. # )
  469. # done
  470. }
  471. # ----------------------------------------------------------------------------
  472. main "$@"
  473. # ----------------------------------------------------------------------------