morty.sh 16 KB

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