morty.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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. touch $GO_ENV
  306. grep -qFs -- 'source "$GO_ENV"' ~/.profile || echo 'source "$GO_ENV"' >> ~/.profile
  307. EOF
  308. }
  309. morty_is_installed() {
  310. [[ -f $SERVICE_HOME/go-apps/bin/morty ]]
  311. }
  312. install_morty() {
  313. rst_title "Install morty in user's ~/go-apps" section
  314. echo
  315. go.install github.com/asciimoo/morty@latest "${SERVICE_USER}"
  316. }
  317. update_morty() {
  318. rst_title "Update morty" section
  319. echo
  320. go.install github.com/asciimoo/morty@latest "${SERVICE_USER}"
  321. }
  322. set_service_env_debug() {
  323. # usage: set_service_env_debug [false|true]
  324. # shellcheck disable=SC2034
  325. local SERVICE_ENV_DEBUG="${1:-false}"
  326. if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
  327. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  328. fi
  329. }
  330. inspect_service() {
  331. rst_title "service status & log"
  332. cat <<EOF
  333. sourced ${DOT_CONFIG} :
  334. SERVICE_USER : ${SERVICE_USER}
  335. SERVICE_HOME : ${SERVICE_HOME}
  336. PUBLIC_URL_MORTY: : ${PUBLIC_URL_MORTY}
  337. MORTY_LISTEN: : ${MORTY_LISTEN}
  338. EOF
  339. install_log_searx_instance
  340. install_check
  341. if in_container; then
  342. lxc_suite_info
  343. else
  344. info_msg "public URL --> ${PUBLIC_URL_MORTY}"
  345. info_msg "morty URL --> http://${MORTY_LISTEN}"
  346. fi
  347. local _debug_on
  348. if ask_yn "Enable morty debug mode (needs reinstall of systemd service)?"; then
  349. enable_debug
  350. _debug_on=1
  351. else
  352. systemctl --no-pager -l status "${SERVICE_NAME}"
  353. fi
  354. echo
  355. # shellcheck disable=SC2059
  356. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  357. read -r -s -n1 -t 5
  358. echo
  359. while true; do
  360. trap break 2
  361. journalctl -f -u "${SERVICE_NAME}"
  362. done
  363. if [[ $_debug_on == 1 ]]; then
  364. FORCE_SELECTION=Y disable_debug
  365. fi
  366. return 0
  367. }
  368. enable_debug() {
  369. warn_msg "Do not enable debug in production environments!!"
  370. info_msg "Enabling debug option needs to reinstall systemd service!"
  371. set_service_env_debug true
  372. }
  373. disable_debug() {
  374. info_msg "Disabling debug option needs to reinstall systemd service!"
  375. set_service_env_debug false
  376. }
  377. set_new_key() {
  378. rst_title "Set morty key"
  379. echo
  380. MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
  381. info_msg "morty key: '${MORTY_KEY}'"
  382. warn_msg "this will need to reinstall services .."
  383. MSG="${_Green}press any [${_BCyan}KEY${_Green}] to continue // stop with [${_BCyan}CTRL-C${_creset}]" wait_key
  384. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  385. "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
  386. "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
  387. }
  388. install_apache_site() {
  389. rst_title "Install Apache site $APACHE_MORTY_SITE"
  390. rst_para "\
  391. This installs a reverse proxy (ProxyPass) into apache site (${APACHE_MORTY_SITE})"
  392. ! apache_is_installed && err_msg "Apache is not installed."
  393. if ! ask_yn "Do you really want to continue?" Yn; then
  394. return
  395. else
  396. install_apache
  397. fi
  398. apache_install_site "${APACHE_MORTY_SITE}"
  399. info_msg "testing public url .."
  400. if ! service_is_available "${PUBLIC_URL_MORTY}"; then
  401. err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
  402. fi
  403. }
  404. remove_apache_site() {
  405. rst_title "Remove Apache site $APACHE_MORTY_SITE"
  406. rst_para "\
  407. This removes apache site ${APACHE_MORTY_SITE}."
  408. ! apache_is_installed && err_msg "Apache is not installed."
  409. if ! ask_yn "Do you really want to continue?" Yn; then
  410. return
  411. fi
  412. apache_remove_site "$APACHE_MORTY_SITE"
  413. }
  414. install_nginx_site() {
  415. rst_title "Install nginx site $NGINX_MORTY_SITE"
  416. rst_para "\
  417. This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_MORTY_SITE})"
  418. ! nginx_is_installed && err_msg "nginx is not installed."
  419. if ! ask_yn "Do you really want to continue?" Yn; then
  420. return
  421. else
  422. install_nginx
  423. fi
  424. "${REPO_ROOT}/utils/searx.sh" install uwsgi
  425. # shellcheck disable=SC2034
  426. SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
  427. # shellcheck disable=SC2034
  428. SEARXNG_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARXNG_URL_PATH)
  429. nginx_install_app "${NGINX_MORTY_SITE}"
  430. info_msg "testing public url .."
  431. if ! service_is_available "${PUBLIC_URL_MORTY}"; then
  432. err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
  433. fi
  434. }
  435. remove_nginx_site() {
  436. rst_title "Remove nginx site $NGINX_MORTY_SITE"
  437. rst_para "\
  438. This removes nginx site ${NGINX_MORTY_SITE}."
  439. ! nginx_is_installed && err_msg "nginx is not installed."
  440. if ! ask_yn "Do you really want to continue?" Yn; then
  441. return
  442. fi
  443. nginx_remove_site "$NGINX_MORTY_SITE"
  444. }
  445. rst-doc() {
  446. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/morty.rst")\""
  447. echo -e "\n.. START install systemd unit"
  448. cat <<EOF
  449. .. tabs::
  450. .. group-tab:: systemd
  451. .. code:: bash
  452. EOF
  453. eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
  454. echo -e "\n.. END install systemd unit"
  455. # for DIST_NAME in ubuntu-20.04 arch fedora centos; do
  456. # (
  457. # DIST_ID=${DIST_NAME%-*}
  458. # DIST_VERS=${DIST_NAME#*-}
  459. # [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  460. # # ...
  461. # )
  462. # done
  463. }
  464. # ----------------------------------------------------------------------------
  465. main "$@"
  466. # ----------------------------------------------------------------------------