morty.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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_go.sh
  6. source "${REPO_ROOT}/utils/lib_go.sh"
  7. # shellcheck source=utils/lib_install.sh
  8. source "${REPO_ROOT}/utils/lib_install.sh"
  9. # ----------------------------------------------------------------------------
  10. # config
  11. # ----------------------------------------------------------------------------
  12. MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
  13. PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty/}"
  14. PUBLIC_URL_MORTY="${PUBLIC_URL_MORTY:-$(echo "$PUBLIC_URL" | sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}}"
  15. # shellcheck disable=SC2034
  16. MORTY_TIMEOUT=5
  17. SERVICE_NAME="morty"
  18. SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
  19. SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
  20. SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
  21. SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
  22. # shellcheck disable=SC2034
  23. SERVICE_GROUP="${SERVICE_USER}"
  24. # shellcheck disable=SC2034
  25. SERVICE_ENV_DEBUG=false
  26. GO_ENV="${SERVICE_HOME}/.go_env"
  27. GO_VERSION="go1.17.2"
  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. go.golang "${GO_VERSION}" "${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. go.version "${SERVICE_USER}"
  280. }
  281. remove_all() {
  282. rst_title "De-Install $SERVICE_NAME (service)"
  283. rst_para "\
  284. It goes without saying that this script can only be used to remove
  285. installations that were installed with this script."
  286. if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
  287. drop_service_account "${SERVICE_USER}"
  288. fi
  289. }
  290. assert_user() {
  291. rst_title "user $SERVICE_USER" section
  292. echo
  293. tee_stderr 1 <<EOF | bash | prefix_stdout
  294. useradd --shell /bin/bash --system \
  295. --home-dir "$SERVICE_HOME" \
  296. --comment 'Web content sanitizer proxy' $SERVICE_USER
  297. mkdir "$SERVICE_HOME"
  298. chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
  299. groups $SERVICE_USER
  300. EOF
  301. SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
  302. export SERVICE_HOME
  303. echo "export SERVICE_HOME=$SERVICE_HOME"
  304. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER"
  305. grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
  306. EOF
  307. }
  308. morty_is_installed() {
  309. [[ -f $SERVICE_HOME/go-apps/bin/morty ]]
  310. }
  311. install_morty() {
  312. rst_title "Install morty in user's ~/go-apps" section
  313. echo
  314. go.install github.com/asciimoo/morty@latest "${SERVICE_USER}"
  315. }
  316. update_morty() {
  317. rst_title "Update morty" section
  318. echo
  319. go.install github.com/asciimoo/morty@latest "${SERVICE_USER}"
  320. }
  321. set_service_env_debug() {
  322. # usage: set_service_env_debug [false|true]
  323. # shellcheck disable=SC2034
  324. local SERVICE_ENV_DEBUG="${1:-false}"
  325. if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
  326. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  327. fi
  328. }
  329. inspect_service() {
  330. rst_title "service status & log"
  331. cat <<EOF
  332. sourced ${DOT_CONFIG} :
  333. SERVICE_USER : ${SERVICE_USER}
  334. SERVICE_HOME : ${SERVICE_HOME}
  335. PUBLIC_URL_MORTY: : ${PUBLIC_URL_MORTY}
  336. MORTY_LISTEN: : ${MORTY_LISTEN}
  337. EOF
  338. install_log_searx_instance
  339. install_check
  340. if in_container; then
  341. lxc_suite_info
  342. else
  343. info_msg "public URL --> ${PUBLIC_URL_MORTY}"
  344. info_msg "morty URL --> http://${MORTY_LISTEN}"
  345. fi
  346. local _debug_on
  347. if ask_yn "Enable morty debug mode (needs reinstall of systemd service)?"; then
  348. enable_debug
  349. _debug_on=1
  350. else
  351. systemctl --no-pager -l status "${SERVICE_NAME}"
  352. fi
  353. echo
  354. # shellcheck disable=SC2059
  355. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  356. read -r -s -n1 -t 5
  357. echo
  358. while true; do
  359. trap break 2
  360. journalctl -f -u "${SERVICE_NAME}"
  361. done
  362. if [[ $_debug_on == 1 ]]; then
  363. FORCE_SELECTION=Y disable_debug
  364. fi
  365. return 0
  366. }
  367. enable_debug() {
  368. warn_msg "Do not enable debug in production environments!!"
  369. info_msg "Enabling debug option needs to reinstall systemd service!"
  370. set_service_env_debug true
  371. }
  372. disable_debug() {
  373. info_msg "Disabling debug option needs to reinstall systemd service!"
  374. set_service_env_debug false
  375. }
  376. set_new_key() {
  377. rst_title "Set morty key"
  378. echo
  379. MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
  380. info_msg "morty key: '${MORTY_KEY}'"
  381. warn_msg "this will need to reinstall services .."
  382. MSG="${_Green}press any [${_BCyan}KEY${_Green}] to continue // stop with [${_BCyan}CTRL-C${_creset}]" wait_key
  383. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  384. "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
  385. "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
  386. }
  387. install_apache_site() {
  388. rst_title "Install Apache site $APACHE_MORTY_SITE"
  389. rst_para "\
  390. This installs a reverse proxy (ProxyPass) into apache site (${APACHE_MORTY_SITE})"
  391. ! apache_is_installed && err_msg "Apache is not installed."
  392. if ! ask_yn "Do you really want to continue?" Yn; then
  393. return
  394. else
  395. install_apache
  396. fi
  397. apache_install_site "${APACHE_MORTY_SITE}"
  398. info_msg "testing public url .."
  399. if ! service_is_available "${PUBLIC_URL_MORTY}"; then
  400. err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
  401. fi
  402. }
  403. remove_apache_site() {
  404. rst_title "Remove Apache site $APACHE_MORTY_SITE"
  405. rst_para "\
  406. This removes apache site ${APACHE_MORTY_SITE}."
  407. ! apache_is_installed && err_msg "Apache is not installed."
  408. if ! ask_yn "Do you really want to continue?" Yn; then
  409. return
  410. fi
  411. apache_remove_site "$APACHE_MORTY_SITE"
  412. }
  413. install_nginx_site() {
  414. rst_title "Install nginx site $NGINX_MORTY_SITE"
  415. rst_para "\
  416. This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_MORTY_SITE})"
  417. ! nginx_is_installed && err_msg "nginx is not installed."
  418. if ! ask_yn "Do you really want to continue?" Yn; then
  419. return
  420. else
  421. install_nginx
  422. fi
  423. "${REPO_ROOT}/utils/searx.sh" install uwsgi
  424. # shellcheck disable=SC2034
  425. SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
  426. # shellcheck disable=SC2034
  427. SEARXNG_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARXNG_URL_PATH)
  428. nginx_install_app "${NGINX_MORTY_SITE}"
  429. info_msg "testing public url .."
  430. if ! service_is_available "${PUBLIC_URL_MORTY}"; then
  431. err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
  432. fi
  433. }
  434. remove_nginx_site() {
  435. rst_title "Remove nginx site $NGINX_MORTY_SITE"
  436. rst_para "\
  437. This removes nginx site ${NGINX_MORTY_SITE}."
  438. ! nginx_is_installed && err_msg "nginx is not installed."
  439. if ! ask_yn "Do you really want to continue?" Yn; then
  440. return
  441. fi
  442. nginx_remove_site "$NGINX_MORTY_SITE"
  443. }
  444. rst-doc() {
  445. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/morty.rst")\""
  446. echo -e "\n.. START install systemd unit"
  447. cat <<EOF
  448. .. tabs::
  449. .. group-tab:: systemd
  450. .. code:: bash
  451. EOF
  452. eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
  453. echo -e "\n.. END install systemd unit"
  454. # for DIST_NAME in ubuntu-20.04 arch fedora centos; do
  455. # (
  456. # DIST_ID=${DIST_NAME%-*}
  457. # DIST_VERS=${DIST_NAME#*-}
  458. # [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  459. # # ...
  460. # )
  461. # done
  462. }
  463. # ----------------------------------------------------------------------------
  464. main "$@"
  465. # ----------------------------------------------------------------------------