From cbd2a71a217b6fec5f0b37a85e19dfaf09f0cf68 Mon Sep 17 00:00:00 2001 From: Bob Mottram <bob@freedombone.net> Date: Sun, 4 Nov 2018 13:09:54 +0000 Subject: [PATCH] New turtl app implementation --- src/freedombone-app-turtl | 781 +++++++++++++------------------------- src/freedombone-template | 2 +- src/turtl_old | 682 +++++++++++++++++++++++++++++++++ 3 files changed, 946 insertions(+), 519 deletions(-) mode change 100755 => 100644 src/freedombone-app-turtl create mode 100755 src/turtl_old diff --git a/src/freedombone-app-turtl b/src/freedombone-app-turtl old mode 100755 new mode 100644 index 6af653323..636ab4150 --- a/src/freedombone-app-turtl +++ b/src/freedombone-app-turtl @@ -1,4 +1,5 @@ #!/bin/bash +# # _____ _ _ # | __|___ ___ ___ _| |___ _____| |_ ___ ___ ___ # | __| _| -_| -_| . | . | | . | . | | -_| @@ -6,15 +7,10 @@ # # Freedom in the Cloud # -# turtl app -# -# http://portallinux.es/instalacion-servidor-turtl-debian-8 -# http://framacloud.org/cultiver-son-jardin/installation-de-turtl/ -# # License # ======= # -# Copyright (C) 2016-2018 Bob Mottram <bob@freedombone.net> +# Copyright (C) 2018 Bob Mottram <bob@freedombone.net> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -29,38 +25,34 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -VARIANTS="full full-vim writer" +VARIANTS='full full-vim' IN_DEFAULT_INSTALL=0 +INSTALLED_ON_DEFAULT_DOMAIN=0 SHOW_ON_ABOUT=1 -NOT_ON_ARM=1 + +# whether to show the domain name in the web UI +SHOW_DOMAIN_IN_WEBADMIN=1 +NOT_ON_API=0 TURTL_DOMAIN_NAME= TURTL_CODE= -TURTL_ONION_PORT=8107 -TURTL_PORT=8181 -TURTL_REPO="https://github.com/turtl/api.git" -TURTL_COMMIT='53e00a5583f52de8f86ef380fe11c176b5738dcf' -TURTL_ADMIN_PASSWORD= -TURTL_STORAGE_LIMIT_MB=100 -TURTL_BASE_DIR=/etc/turtl - -# part of a hack to enable/disable signups -TURTL_SIGNUP_STRING='Signup a new user' -turtl_users_file=$TURTL_BASE_DIR/api/controllers/users.lisp - -TURTL_SHORT_DESCRIPTION=$'Note taking' -TURTL_DESCRIPTION=$'Note taking' -TURTL_MOBILE_APP_URL=https://turtlapp.com/releases/mobile/turtl-android-0.6.4.apk +TURTL_ONION_PORT=9473 +TURTL_REPO="https://github.com/turtl/server" +TURTL_COMMIT='475de66908c7537ddde4397e918f9da4ab2a5aea' +TURTL_PORT_INTERNAL=8181 +TURTL_APP_VERSION='0.7.2' + +# These parameters are used by the FreedomBox mobile app and web UI +TURTL_SHORT_DESCRIPTION='The secure collaborative notebook' +TURTL_DESCRIPTION='Whether its bookmarks or passwords, files or shopping lists...Turtl organizes it all and makes it easy to find later. Sync across your devices.' +TURTL_MOBILE_APP_URL="https://github.com/turtl/android/releases/download/v${TURTL_APP_VERSION}/turtl-${TURTL_APP_VERSION}-android.apk" turtl_variables=(ONION_ONLY - DEFAULT_DOMAIN_NAME - TURTL_DOMAIN_NAME - TURTL_CODE - TURTL_STORAGE_LIMIT_MB - DDNS_PROVIDER - MY_EMAIL_ADDRESS - MY_USERNAME) + TURTL_DOMAIN_NAME + TURTL_CODE + DDNS_PROVIDER + MY_USERNAME) function logging_on_turtl { echo -n '' @@ -70,20 +62,17 @@ function logging_off_turtl { echo -n '' } -function change_password_turtl { - echo -n '' -# change_username="$1" -# new_user_password="$2" -} - function remove_user_turtl { - echo -n '' -# remove_username="$1" + remove_username="$1" + + "${PROJECT_NAME}-pass" -u "$remove_username" --rmapp turtl } function add_user_turtl { -# new_username="$1" -# new_user_password="$2" + new_username="$1" + new_user_password="$2" + + "${PROJECT_NAME}-pass" -u "$new_username" -a turtl -p "$new_user_password" echo '0' } @@ -92,113 +81,67 @@ function install_interactive_turtl { ONION_ONLY='no' fi - if [[ $ONION_ONLY != "no" ]]; then - TURTL_DOMAIN_NAME='notes.local' + if [[ "$ONION_ONLY" != "no" ]]; then + TURTL_DOMAIN_NAME='turtl.local' write_config_param "TURTL_DOMAIN_NAME" "$TURTL_DOMAIN_NAME" else - function_check interactive_site_details interactive_site_details "turtl" "TURTL_DOMAIN_NAME" "TURTL_CODE" fi APP_INSTALLED=1 } -function turtl_disable_registrations { - if grep -q "$TURTL_SIGNUP_STRING" $turtl_users_file; then - if [ -f $turtl_users_file ]; then - cp $turtl_users_file $TURTL_BASE_DIR/.users.lisp - sed -i '/(route (:post "\/users") (req res)/,/(send-json res user))))/{//!d}' $turtl_users_file - sed -i 's|(send-json res user))))|())|g' $turtl_users_file - chown -R turtl:turtl $TURTL_BASE_DIR - systemctl restart turtl - fi - fi +function change_password_turtl { + curr_username="$1" + new_user_password="$2" + + read_config_param 'TURTL_DOMAIN_NAME' + + "${PROJECT_NAME}-pass" -u "$curr_username" -a turtl -p "$new_user_password" } -function turtl_enable_registrations { - if ! grep -q "$TURTL_SIGNUP_STRING" $turtl_users_file; then - if [ -f $TURTL_BASE_DIR/.users.lisp ]; then - cp $TURTL_BASE_DIR/.users.lisp $turtl_users_file - rm $TURTL_BASE_DIR/.users.lisp - chown -R turtl:turtl $TURTL_BASE_DIR - systemctl restart turtl +function turtl_create_database { + if [ -f "$IMAGE_PASSWORD_FILE" ]; then + TURTL_ADMIN_PASSWORD="$(printf "%d" "$(cat "")")" + else + if [ ! "$TURTL_ADMIN_PASSWORD" ]; then + TURTL_ADMIN_PASSWORD=$(create_password "${MINIMUM_PASSWORD_LENGTH}") fi fi -} + if [ ! "$TURTL_ADMIN_PASSWORD" ]; then + return + fi -function configure_interactive_turtl_signups { - # This implements a hack which removes or adds the function needed - # to sign up new users. It should eventually be removed once that - # capability exists within the api - - dialog --title $"Allow new turtl signups" \ - --backtitle $"Freedombone Control Panel" \ - --defaultno \ - --yesno $"\\nAllow registration of new users?" 10 60 - sel=$? - case $sel in - 0) - turtl_enable_registrations - dialog --title $"Allow new turtl signups" \ - --msgbox $"New turtl user registrations are now allowed" 6 60 - return;; - 1) - turtl_disable_registrations - dialog --title $"Disable new turtl signups" \ - --msgbox $"New turtl user registrations are now disabled" 6 60 - return;; - 255) return;; - esac + systemctl restart postgresql + run_system_query_postgresql "CREATE USER turtl WITH PASSWORD '$TURTL_ADMIN_PASSWORD';" + run_system_query_postgresql "CREATE DATABASE turtl OWNER turtl;" + run_system_query_postgresql "GRANT ALL PRIVILEGES ON DATABASE turtl to turtl;" + run_system_query_postgresql "set statement_timeout to 40000;" } -function configure_interactive_turtl_storage { - data=$(mktemp 2>/dev/null) - dialog --title $"Change storage limit" \ - --backtitle $"Freedombone Control Panel" \ - --inputbox $"Enter a storage limit in megabytes." 8 75 "$TURTL_STORAGE_LIMIT_MB" 2>"$data" - sel=$? - case $sel in - 0) - STORAGE=$(<"$data") - if [ ${#STORAGE} -gt 0 ]; then - TURTL_STORAGE_LIMIT_MB=$STORAGE - sed -i "s|defparameter *default-storage-limit*.*|defparameter *default-storage-limit* ${TURTL_STORAGE_LIMIT_MB})|g" $TURTL_BASE_DIR/api/config/config.lisp - systemctl restart turtl - dialog --title $"Change storage limit" \ - --msgbox $"Storage limit changed to ${TURTL_STORAGE_LIMIT_MB}M" 6 50 - fi - ;; - esac - rm -f "$data" +function reconfigure_turtl { + # This is used if you need to switch identity. Dump old keys and generate new ones + echo -n '' } function configure_interactive_turtl { - data=$(mktemp 2>/dev/null) - dialog --backtitle $"Freedombone Control Panel" \ - --title $"turtl app settings" \ - --radiolist $"Choose an operation:" 12 70 3 \ - 1 $"Enable/disable new user registrations" off \ - 2 $"Change storage limit" off \ - 3 $"Exit" on 2> "$data" - sel=$? - case $sel in - 1) rm -f "$data" - exit 1;; - 255) rm -f "$data" - exit 1;; - esac - case $(cat "$data") in - 1) configure_interactive_turtl_signups;; - 2) configure_interactive_turtl_storage;; - 3) rm -f "$data" - return;; - esac - rm -f "$data" -} + W=(1 $"Option 1" + 2 $"Option 2") -function reconfigure_turtl { - if [ -d $TURTL_BASE_DIR/data ]; then - rm -rf $TURTL_BASE_DIR/data/* - fi + while true + do + # shellcheck disable=SC2068 + selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"turtl" --menu $"Choose an operation, or ESC for main menu:" 14 70 3 "${W[@]}" 3>&2 2>&1 1>&3) + + if [ ! "$selection" ]; then + break + fi + case $selection in + 1) # call some function for option 1 + ;; + 2) # call some function for option 2 + ;; + esac + done } function upgrade_turtl { @@ -207,88 +150,76 @@ function upgrade_turtl { return fi - read_config_param "TURTL_DOMAIN_NAME" - - function_check set_repo_commit - set_repo_commit $TURTL_BASE_DIR/api "turtl commit" "$TURTL_COMMIT" $TURTL_REPO - - # this is used as a crude way of disabling signups and so - # should be superceded in future - if [ -f $TURTL_BASE_DIR/.users.lisp ]; then - turtl_disable_registrations + if grep -q "turtl domain" "$COMPLETION_FILE"; then + TURTL_DOMAIN_NAME=$(get_completion_param "turtl domain") fi - systemctl restart turtl - nginx_dissite $TURTL_DOMAIN_NAME - chown -R turtl:turtl $TURTL_BASE_DIR - nginx_ensite $TURTL_DOMAIN_NAME + # update to the next commit + set_repo_commit "/etc/turtl" "turtl commit" "$TURTL_COMMIT" "$TURTL_REPO" + chown -R turtl:turtl "/etc/turtl" + systemctl restart turtl } function backup_local_turtl { - read_config_param "TURTL_DOMAIN_NAME" - source_directory=$TURTL_BASE_DIR - if [ -d $source_directory ]; then - dest_directory=turtl - function_check suspend_site - suspend_site ${TURTL_DOMAIN_NAME} - - function_check backup_directory_to_usb - backup_directory_to_usb $source_directory $dest_directory - - function_check restart_site - restart_site + TURTL_DOMAIN_NAME='turtl' + if grep -q "turtl domain" "$COMPLETION_FILE"; then + TURTL_DOMAIN_NAME=$(get_completion_param "turtl domain") fi - source_directory=/var/lib/rethinkdb - if [ -d $source_directory ]; then - dest_directory=rethinkdb - function_check suspend_site - suspend_site ${TURTL_DOMAIN_NAME} - function_check backup_directory_to_usb - backup_directory_to_usb $source_directory $dest_directory + source_directory=/etc/turtl - function_check restart_site - restart_site - fi + suspend_site "${TURTL_DOMAIN_NAME}" + + systemctl stop turtl + + dest_directory=turtl + backup_directory_to_usb "$source_directory" $dest_directory + + USE_POSTGRESQL=1 + backup_database_to_usb turtl + + restart_site + systemctl start turtl } function restore_local_turtl { - read_config_param "TURTL_DOMAIN_NAME" - if [ $TURTL_DOMAIN_NAME ]; then - temp_restore_dir=/root/tempturtl - restore_directory_from_usb $temp_restore_dir turtl + if ! grep -q "turtl domain" "$COMPLETION_FILE"; then + return + fi + TURTL_DOMAIN_NAME=$(get_completion_param "turtl domain") + if [ ! "$TURTL_DOMAIN_NAME" ]; then + return + fi + suspend_site "${TURTL_DOMAIN_NAME}" + systemctl stop turtl - if [ -d ${temp_restore_dir}/etc/turtl ]; then - cp -r ${temp_restore_dir}/etc/turtl/* /etc/turtl/ - else - cp -r ${temp_restore_dir}/* /etc/turtl/ - fi - # shellcheck disable=SC2181 - if [ ! "$?" = "0" ]; then - set_user_permissions - backup_unmount_drive - exit 36723 - fi - rm -rf ${temp_restore_dir} - chown -R turtl:turtl $TURTL_BASE_DIR + temp_restore_dir=/root/tempturtl + turtl_dir=/etc/turtl - temp_restore_dir=/root/temprethinkdb - restore_directory_from_usb $temp_restore_dir rethinkdb + turtl_create_database - if [ -d ${temp_restore_dir}/var/lib/rethinkdb ]; then - cp -r ${temp_restore_dir}/var/lib/rethinkdb/* /var/lib/rethinkdb/ - else - cp -r ${temp_restore_dir}/* /var/lib/rethinkdb/ - fi + USE_POSTGRESQL=1 + restore_database turtl + if [ -d $temp_restore_dir ]; then + rm -rf $temp_restore_dir + fi - # shellcheck disable=SC2181 - if [ ! "$?" = "0" ]; then - set_user_permissions - backup_unmount_drive - exit 378324 + restore_directory_from_usb $temp_restore_dir turtl + if [ -d $temp_restore_dir ]; then + if [ -d "$temp_restore_dir$turtl_dir" ]; then + cp -rp "$temp_restore_dir$turtl_dir"/* "$turtl_dir"/ + else + if [ ! -d "$turtl_dir" ]; then + mkdir "$turtl_dir" + fi + cp -rp "$temp_restore_dir"/* "$turtl_dir"/ fi - rm -rf ${temp_restore_dir} + chown -R turtl:turtl "$turtl_dir" + rm -rf $temp_restore_dir fi + systemctl start turtl + + restart_site } function backup_remote_turtl { @@ -300,383 +231,197 @@ function restore_remote_turtl { } function remove_turtl { - if [ ! -d $TURTL_BASE_DIR ]; then - return - fi - systemctl stop turtl - systemctl disable turtl - rm /etc/systemd/system/turtl.service - systemctl daemon-reload + nginx_dissite "$TURTL_DOMAIN_NAME" + remove_certs "$TURTL_DOMAIN_NAME" - remove_rethinkdb - remove_app turtl - remove_completion_param install_turtl - sed -i '/turtl/d' "$COMPLETION_FILE" - nginx_dissite $TURTL_DOMAIN_NAME - if [ -f /etc/nginx/sites-available/$TURTL_DOMAIN_NAME ]; then - rm /etc/nginx/sites-available/$TURTL_DOMAIN_NAME + if [ -f /etc/systemd/system/turtl.service ]; then + systemctl stop turtl + systemctl disable turtl + rm /etc/systemd/system/turtl.service fi - remove_certs $TURTL_DOMAIN_NAME - function_check remove_onion_service - remove_onion_service turtl ${TURTL_ONION_PORT} - function_check remove_ddns_domain - remove_ddns_domain $TURTL_DOMAIN_NAME - rm -rf /etc/rethinkdb - rm -rf /var/lib/rethinkdb - rm -rf $TURTL_BASE_DIR - - groupdel -f turtl userdel -r turtl -} + remove_nodejs turtl -function turtl_setup { - PIDFILE=${PIDFILE:-nil} - BINDADDR=${BINDADDR:-0.0.0.0} - BINDPORT=${BINDPORT:-8181} - PROD_ERR_HANDLING=${PROD_ERR_HANDLING:-t} - if [[ $ONION_ONLY == 'no' ]]; then - FQDN=${FQDN:-$TURTL_DOMAIN_NAME} - SITE_URL=${SITE_URL:-https://$TURTL_DOMAIN_NAME} - else - FQDN=${FQDN:-$TURTL_ONION_HOSTNAME} - SITE_URL=${SITE_URL:-http://$TURTL_ONION_HOSTNAME} - fi - ADMIN_EMAIL=${ADMIN_EMAIL:-$MY_EMAIL_ADDRESS} - EMAIL_FROM=${EMAIL_FROM:-noreply@$DEFAULT_DOMAIN_NAME} - SMTP_USER=${SMTP_USER:-} - SMTP_PASS=${SMTP_PASS:-} - DISPLAY_ERRORS=${DISPLAY_ERRORS:-t} - DEFAULT_STORAGE_LIMIT=${DEFAULT_STORAGE_LIMIT:-100} - STORAGE_INVITE_CREDIT=${STORAGE_INVITE_CREDIT:-25} - if [[ $ONION_ONLY == 'no' ]]; then - LOCAL_UPLOAD_URL=${LOCAL_UPLOAD_URL:-https://$TURTL_DOMAIN_NAME} - else - LOCAL_UPLOAD_URL=${LOCAL_UPLOAD_URL:-http://$TURTL_ONION_HOSTNAME} + if [ -d "/var/www/$TURTL_DOMAIN_NAME" ]; then + rm -rf "/var/www/$TURTL_DOMAIN_NAME" fi - LOCAL_UPLOAD_PATH=${LOCAL_UPLOAD_PATH:-"$TURTL_BASE_DIR/data"} - AWS_S3_TOKEN=${AWS_S3_TOKEN:-(:token '' - :secret '' - :bucket '' - :endpoint 'https://s3.amazonaws.com')} - - # generates the config-file - cat << __ENDCONFIG__ > $TURTL_BASE_DIR/api/config/config.lisp -(in-package :turtl) -(defparameter *root* (asdf:system-relative-pathname :turtl #P"")) -(defparameter *pid-file* "${PIDFILE}") -(defvar *server-bind* "${BINDADDR}") -(defvar *server-port* ${BINDPORT}) -(defvar *db-name* "turtl") -(defvar *db-host* "127.0.0.1") -(defvar *db-port* 28015) -(defvar *production-error-handling* ${PROD_ERR_HANDLING}) -(defvar *enable-hsts-header* nil) -(defvar *site-url* "${SITE_URL}") -(defvar *api-path* "") -(defvar *admin-email* "${ADMIN_EMAIL}") -(defvar *email-from* "${EMAIL_FROM}") -(defvar *email-user* "${SMTP_USER}") -(defvar *email-pass* "${SMTP_PASS}") -(defvar *display-errors* ${DISPLAY_ERRORS}) -(defparameter *default-storage-limit* ${DEFAULT_STORAGE_LIMIT}) -(defparameter *storage-invite-credit* ${STORAGE_INVITE_CREDIT}) -(vom:config :turtl :info) -(defvar *local-upload* "${LOCAL_UPLOAD_PATH}") -(defvar *local-upload-url* "${LOCAL_UPLOAD_URL}") -(defvar *amazon-s3* "${AWS_S3_TOKEN}") -__ENDCONFIG__ - - cat $TURTL_BASE_DIR/api/config/config.footer >> $TURTL_BASE_DIR/api/config/config.lisp - - # start the turtl server - systemctl restart rethinkdb - - if [ ! -f $TURTL_BASE_DIR/quicklisp/setup.lisp ]; then - echo $"$TURTL_BASE_DIR/quicklisp/setup.lisp was not found" - exit 6238234 + if [ -f "/etc/nginx/sites-available/$TURTL_DOMAIN_NAME" ]; then + rm "/etc/nginx/sites-available/$TURTL_DOMAIN_NAME" fi - - { echo '[Unit]'; - echo 'Description=Note taking service'; - echo 'Documentation=http://turtl.it'; - echo 'Requires=network.target'; - echo 'Requires=rethinkdb.service'; - echo 'After=network.target'; - echo 'After=rethinkdb.service'; - echo ''; - echo '[Service]'; - echo 'Type=simple'; - echo 'User=turtl'; - echo "WorkingDirectory=$TURTL_BASE_DIR/api/"; } > /etc/systemd/system/turtl.service - - if [[ "$check_architecture" == *"64"* && "$check_architecture" != *"arm"* ]]; then - echo "ExecStart=$TURTL_BASE_DIR/ccl/lx86cl64 -l $TURTL_BASE_DIR/quicklisp/setup.lisp -l launch.lisp" >> /etc/systemd/system/turtl.service - else - if [[ "$check_architecture" != *"arm"* ]]; then - echo "ExecStart=$TURTL_BASE_DIR/ccl/lx86cl -l $TURTL_BASE_DIR/quicklisp/setup.lisp -l launch.lisp" >> /etc/systemd/system/turtl.service - else - echo "ExecStart=$TURTL_BASE_DIR/ccl/armcl -l $TURTL_BASE_DIR/quicklisp/setup.lisp -l launch.lisp" >> /etc/systemd/system/turtl.service - fi + drop_database_postgresql turtl + remove_onion_service turtl "${TURTL_ONION_PORT}" + if grep -q "turtl" /etc/crontab; then + sed -i "/turtl/d" /etc/crontab fi - { echo ''; - echo '[Install]'; - echo 'WantedBy=multi-user.target'; } >> /etc/systemd/system/turtl.service - chmod +x /etc/systemd/system/turtl.service + remove_app turtl + remove_completion_param install_turtl + sed -i '/turtl/d' "$COMPLETION_FILE" - chown -R turtl:turtl $TURTL_BASE_DIR - systemctl enable turtl - systemctl daemon-reload - systemctl start turtl + remove_ddns_domain "$TURTL_DOMAIN_NAME" } -function install_turtl_api { - # https://github.com/ArthurGarnier/turtl-docker - $INSTALL_PACKAGES wget libterm-readline-perl-perl gcc libuv1-dev +function install_turtl { + install_postgresql - if [ ! -d $TURTL_BASE_DIR ]; then - mkdir -p $TURTL_BASE_DIR - fi - cd "$TURTL_BASE_DIR" || exit 745726542 - mkdir cd $TURTL_BASE_DIR/data - check_architecture=$(uname -a) - - # Install ccl - if [[ "$check_architecture" != *"arm"* ]]; then - wget -P $TURTL_BASE_DIR/ ftp://ftp.clozure.com/pub/release/1.11/ccl-1.11-linuxx86.tar.gz - mkdir -p $TURTL_BASE_DIR/ccl - tar xvzf $TURTL_BASE_DIR/ccl-1.11-linuxx86.tar.gz -C $TURTL_BASE_DIR/ccl --strip-components=1 - else - wget -P $TURTL_BASE_DIR/ ftp://ftp.clozure.com/pub/release/1.11/ccl-1.11-linuxarm.tar.gz - mkdir -p $TURTL_BASE_DIR/ccl - tar xvzf $TURTL_BASE_DIR/ccl-1.11-linuxarm.tar.gz -C $TURTL_BASE_DIR/ccl --strip-components=1 + install_nodejs turtl + if [ ! "$TURTL_DOMAIN_NAME" ]; then + echo $'No domain name was given' + exit 3568356 fi - # install quicklisp - cat << __ENDCONFIG__ > $TURTL_BASE_DIR/quicklisp_install -(load (compile-file "asdf.lisp")) -(load (compile-file "quicklisp.lisp")) -(quicklisp-quickstart:install) -(ql:system-apropos "vecto") -(ql:quickload "alexandria") -(ql:quickload "babel") -(ql:quickload "blackbird") -(ql:quickload "bordeaux-threads") -(ql:quickload "cffi") -(ql:quickload "chipz") -(ql:quickload "chunga") -(ql:quickload "cl-annot") -(ql:quickload "cl-async") -(ql:quickload "cl-async-future") -(ql:quickload "cl-base64") -(ql:quickload "cl-fad") -(ql:quickload "cl-libuv") -(ql:quickload "cl-mongo-id") -(ql:quickload "cl-ppcre") -(ql:quickload "cl-rethinkdb") -(ql:quickload "cl-smtp") -(ql:quickload "cl+ssl") -(ql:quickload "cl-syntax") -(ql:quickload "cl-utilities") -(ql:quickload "cl-vectors") -(ql:quickload "do-urlencode") -(ql:quickload "drakma") -(ql:quickload "drakma-async") -(ql:quickload "event-glue") -(ql:quickload "fast-http") -(ql:quickload "fast-io") -(ql:quickload "flexi-streams") -(ql:quickload "ironclad") -(ql:quickload "jonathan") -(ql:quickload "local-time") -(ql:quickload "md5") -(ql:quickload "named-readtables") -(ql:quickload "nibbles") -(ql:quickload "proc-parse") -(ql:quickload "puri") -(ql:quickload "quri") -(ql:quickload "salza2") -(ql:quickload "secure-random") -(ql:quickload "smart-buffer") -(ql:quickload "split-sequence") -(ql:quickload "static-vectors") -(ql:quickload "trivial-backtrace") -(ql:quickload "trivial-features") -(ql:quickload "trivial-garbage") -(ql:quickload "trivial-gray-streams") -(ql:quickload "trivial-types") -(ql:quickload "usocket") -(ql:quickload "vecto") -(ql:quickload "vom") -(ql:quickload "wookie") -(ql:quickload "xmls") -(ql:quickload "xsubseq") -(ql:quickload "yason") -(ql:quickload "zpb-ttf") -(ql:quickload "zpng") -(ql:add-to-init-file) -(ccl::quit) -__ENDCONFIG__ - - if [ ! -f asdf.lisp ]; then - wget https://common-lisp.net/project/asdf/asdf.lisp + if [ -d "/var/www/$TURTL_DOMAIN_NAME/htdocs" ]; then + rm -rf "/var/www/$TURTL_DOMAIN_NAME/htdocs" fi - if [ ! -f quicklisp.lisp ]; then - wget https://beta.quicklisp.org/quicklisp.lisp + mkdir "/var/www/$TURTL_DOMAIN_NAME" + if [ -d /repos/turtl ]; then + mkdir -p "/var/www/$TURTL_DOMAIN_NAME/htdocs" + cp -r -p /repos/turtl/. "/etc/turtl" + cd "/etc/turtl" || exit 36487365 + git pull + else + mkdir "/var/www/$TURTL_DOMAIN_NAME/htdocs" + git_clone "$TURTL_REPO" "/etc/turtl" fi - if [ -d $TURTL_BASE_DIR ]; then - chown -R turtl:turtl $TURTL_BASE_DIR - fi - adduser --disabled-login --home=$TURTL_BASE_DIR --gecos 'turtl' turtl - if [ ! -d $TURTL_BASE_DIR ]; then - echo $"$TURTL_BASE_DIR directory not created" - exit 263493 + if [ ! -d "/etc/turtl" ]; then + echo $'Unable to clone turtl repo' + exit 87525 fi - groupadd turtl - chown -R turtl:turtl $TURTL_BASE_DIR + cd "/etc/turtl" || exit 3463754637 + git checkout "$TURTL_COMMIT" -b "$TURTL_COMMIT" + set_completion_param "turtl commit" "$TURTL_COMMIT" - if [[ "$check_architecture" != *"arm"* ]]; then - if [[ "$check_architecture" == *"64"* ]]; then - su -c "cat $TURTL_BASE_DIR/quicklisp_install | $TURTL_BASE_DIR/ccl/lx86cl64" - turtl - else - su -c "cat $TURTL_BASE_DIR/quicklisp_install | $TURTL_BASE_DIR/ccl/lx86cl" - turtl - fi - else - su -c "cat $TURTL_BASE_DIR/quicklisp_install | $TURTL_BASE_DIR/ccl/larmcl" - turtl + if ! npm install; then + echo $'Failed to install turtl' + exit 24682468 fi - rm $TURTL_BASE_DIR/quicklisp_install - install_rethinkdb - echo "http-port=8091" > /etc/rethinkdb/instances.d/turtl.conf - chown -R rethinkdb:rethinkdb /var/lib/rethinkdb + mkdir /etc/turtl/plugins || exit 35683 + ./scripts/init-db.sh - # install turtl API - cd "$TURTL_BASE_DIR/" || exit 6428462 + chmod g+w "/var/www/$TURTL_DOMAIN_NAME/htdocs" + chown -R www-data:www-data "/var/www/$TURTL_DOMAIN_NAME/htdocs" - if [ -d /repos/turtl ]; then - mkdir -p $TURTL_BASE_DIR/api - cp -r -p /repos/turtl/. $TURTL_BASE_DIR/api - cd "$TURTL_BASE_DIR/api" || exit 57141845 - git pull - else - git clone $TURTL_REPO $TURTL_BASE_DIR/api - fi + turtl_create_database - cd "$TURTL_BASE_DIR/api" || exit 35814614 - git checkout $TURTL_COMMIT -b $TURTL_COMMIT - set_completion_param "turtl commit" "$TURTL_COMMIT" - cd "$TURTL_BASE_DIR/quicklisp/local-projects" || exit 43618941415 - git clone git://github.com/orthecreedence/cl-hash-util - if [[ "$check_architecture" != *"arm"* ]]; then - if [[ "$check_architecture" == *"64"* ]]; then - su -c "cat '(ccl:quit)' | $TURTL_BASE_DIR/ccl/lx86cl64 -l $TURTL_BASE_DIR/quicklisp/setup.lisp" - turtl - else - su -c "cat '(ccl:quit)' | $TURTL_BASE_DIR/ccl/lx86cl -l $TURTL_BASE_DIR/quicklisp/setup.lisp" - turtl - fi + add_ddns_domain "$TURTL_DOMAIN_NAME" + + TURTL_ONION_HOSTNAME=$(add_onion_service turtl 80 "${TURTL_ONION_PORT}") + + cp /etc/turtl/config/config.yaml.default /etc/turtl/config/config.yaml + sed -i "s|connstr:.*|connstr: 'postgres://turtl:${TURTL_ADMIN_PASSWORD}@127.0.0.1:5432/turtl'|g" /etc/turtl/config/config.yaml + if [[ "$ONION_ONLY" != 'no' ]]; then + sed -i "s|api_url:.*|api_url: 'https://${TURTL_DOMAIN_NAME}'|g" /etc/turtl/config/config.yaml + sed -i "s|www_url:.*|www_url: 'https://${TURTL_DOMAIN_NAME}/web'|g" /etc/turtl/config/config.yaml else - su -c "cat '(ccl:quit)' | $TURTL_BASE_DIR/ccl/larmcl -l $TURTL_BASE_DIR/quicklisp/setup.lisp" - turtl + sed -i "s|api_url:.*|api_url: 'http://${TURTL_ONION_HOSTNAME}'|g" /etc/turtl/config/config.yaml + sed -i "s|www_url:.*|www_url: 'http://${TURTL_ONION_HOSTNAME}/web'|g" /etc/turtl/config/config.yaml fi + sed -i "s|admin:.*|admin: '${MY_EMAIL_ADDRESS}'|g" /etc/turtl/config/config.yaml + sed -i "s|info:.*|info: 'Turtl <${MY_EMAIL_ADDRESS}>'|g" /etc/turtl/config/config.yaml + sed -i "s|invites:.*|invites: '${MY_EMAIL_ADDRESS}'|g" /etc/turtl/config/config.yaml + TURTL_HASH="$(create_random_string 30)$(create_random_string 30)$(create_random_string 30)$(create_random_string 30)$(create_random_string 30)$(create_random_string 30)" + sed -i "s|secure_hash_salt:.*|secure_hash_salt: \"${TURTL_HASH}\"|g" /etc/turtl/config/config.yaml + if [ ! -d /etc/turtl/public/uploads ]; then + mkdir -p /etc/turtl/public/uploads + fi + sed -i "s|local:.*|local: '/etc/turtl/public/uploads'|g" /etc/turtl/config/config.yaml - # config - { echo '(defvar *enabled-cors-resources* "resource://turtl-at-lyonbros-dot-com"'; - echo ' "When set, will enable CORS for resource:// origins if they match the given'; - echo ' string. Entries should be comma separated (this string is passed verbatim in'; - echo ' the Access-Control-Allow-Origin header).")'; - echo '(defparameter *public-actions*'; - echo " \`((:post . ,(concatenate 'string *api-path* \"/users\"))"; - echo " (:post . ,(concatenate 'string *api-path* \"/log/error\"))"; - echo ' (:post . "/cla/sign")'; - echo ' (:get . "/ping")'; - echo ' (:get . "/admin")'; - echo " (:get . ,(cl-ppcre:create-scanner (concatenate 'string *api-path* \"/invites/codes/([0-9a-f-]+)\"))))"; - echo " \"A list of public resources/actions that do not require authentication.\")"; - echo "(defvar *analytics* '(:enabled t"; - echo ' :db "analytics"))'; } > "$TURTL_BASE_DIR/api/config/config.footer" - - cp $TURTL_BASE_DIR/asdf.lisp $TURTL_BASE_DIR/api - echo '(load (compile-file "asdf.lisp"))' > $TURTL_BASE_DIR/api/launch.lisp - echo "(pushnew \"./\" asdf:*central-registry* :test #'equal)" >> $TURTL_BASE_DIR/api/launch.lisp - echo '(load "start")' >> $TURTL_BASE_DIR/api/launch.lisp - - TURTL_ONION_HOSTNAME=$(add_onion_service turtl 80 ${TURTL_ONION_PORT}) - - turtl_setup -} - -function install_turtl_nginx { turtl_nginx_site=/etc/nginx/sites-available/$TURTL_DOMAIN_NAME - if [[ $ONION_ONLY == "no" ]]; then - function_check nginx_http_redirect - nginx_http_redirect $TURTL_DOMAIN_NAME + if [[ "$ONION_ONLY" == "no" ]]; then + nginx_http_redirect "$TURTL_DOMAIN_NAME" "index index.html" { echo 'server {'; echo ' listen 443 ssl;'; echo ' #listen [::]:443 ssl;'; - echo " server_name ${TURTL_DOMAIN_NAME};"; - echo ''; - echo ' # Security'; } >> "$turtl_nginx_site" - function_check nginx_ssl - nginx_ssl $TURTL_DOMAIN_NAME + echo " server_name $TURTL_DOMAIN_NAME;"; + echo ''; } >> "$turtl_nginx_site" + nginx_compress "$TURTL_DOMAIN_NAME" + echo '' >> "$turtl_nginx_site" + echo ' # Security' >> "$turtl_nginx_site" + nginx_ssl "$TURTL_DOMAIN_NAME" - function_check nginx_security_options - nginx_security_options $TURTL_DOMAIN_NAME + nginx_security_options "$TURTL_DOMAIN_NAME" { echo ' add_header Strict-Transport-Security max-age=15768000;'; echo ''; - echo ' # Logs'; - echo ' access_log /dev/null;'; - echo ' error_log /dev/null;'; + echo ' access_log /dev/null;'; + echo ' error_log /dev/null;'; + echo ''; + echo " root /var/www/$TURTL_DOMAIN_NAME/htdocs;"; echo ''; + echo ' index index.html;'; + echo ' # Location'; echo ' location / {'; } >> "$turtl_nginx_site" - function_check nginx_limits - nginx_limits $TURTL_DOMAIN_NAME '15m' - { echo " proxy_pass http://localhost:${TURTL_PORT}/;"; - echo " proxy_set_header Host \$host;"; - echo ' proxy_buffering off;'; + nginx_limits "$TURTL_DOMAIN_NAME" '15m' + { echo " proxy_pass http://localhost:$TURTL_PORT_INTERNAL;"; + echo ' }'; + echo ''; + echo ' location ^~ /web {'; echo ' }'; echo '}'; } >> "$turtl_nginx_site" else - echo -n '' > $turtl_nginx_site + echo -n '' > "$turtl_nginx_site" fi { echo 'server {'; - echo " listen 127.0.0.1:${TURTL_ONION_PORT};"; - echo ' port_in_redirect off;'; - echo " server_name ${TURTL_ONION_HOSTNAME};"; - echo ''; } >> $turtl_nginx_site - function_check nginx_security_options - nginx_security_options $TURTL_DOMAIN_NAME + echo " listen 127.0.0.1:$TURTL_ONION_PORT default_server;"; + echo " server_name $TURTL_ONION_HOSTNAME;"; + echo ''; } >> "$turtl_nginx_site" + nginx_compress "$TURTL_DOMAIN_NAME" + echo '' >> "$turtl_nginx_site" + nginx_security_options "$TURTL_DOMAIN_NAME" { echo ''; - echo ' # Logs'; - echo ' access_log /dev/null;'; - echo ' error_log /dev/null;'; + echo ' access_log /dev/null;'; + echo ' error_log /dev/null;'; + echo ''; + echo " root /var/www/$TURTL_DOMAIN_NAME/htdocs;"; + echo ''; + echo ' index index.html;'; + echo ' # Location'; + echo ' location / {'; } >> "$turtl_nginx_site" + nginx_limits "$TURTL_DOMAIN_NAME" '15m' + { echo " proxy_pass http://localhost:$TURTL_PORT_INTERNAL;"; + echo ' }'; echo ''; - echo ' location / {'; } >> $turtl_nginx_site - function_check nginx_limits - nginx_limits $TURTL_DOMAIN_NAME '15m' - { echo " proxy_pass http://localhost:${TURTL_PORT}/;"; - echo " proxy_set_header Host \$host;"; - echo ' proxy_buffering off;'; + echo ' location ^~ /web {'; echo ' }'; - echo '}'; } >> $turtl_nginx_site + echo '}'; } >> "$turtl_nginx_site" - function_check add_ddns_domain - add_ddns_domain $TURTL_DOMAIN_NAME + adduser --system --home="/etc/turtl" --group turtl - set_completion_param "turtl domain" "$TURTL_DOMAIN_NAME" + { echo '[Unit]'; + echo 'Description=turtl'; + echo 'After=syslog.target'; + echo 'After=network.target'; + echo "Documentation=$TURTL_REPO"; + echo ''; + echo '[Service]'; + echo 'Type=simple'; + echo 'User=turtl'; + echo 'Group=turtl'; + echo 'WorkingDirectory=/etc/turtl'; + echo 'ExecStart=/usr/local/bin/npm server.js'; + echo 'Environment=USER=turtl'; + echo 'Restart=always'; + echo 'StandardError=syslog'; + echo ''; + echo '[Install]'; + echo 'WantedBy=multi-user.target'; } > "/etc/systemd/system/turtl.service" + systemctl enable turtl + chown -R turtl:turtl "/etc/turtl" + systemctl start turtl - function_check create_site_certificate - create_site_certificate $TURTL_DOMAIN_NAME 'yes' + create_site_certificate "$TURTL_DOMAIN_NAME" 'yes' - function_check nginx_ensite - nginx_ensite $TURTL_DOMAIN_NAME + nginx_ensite "$TURTL_DOMAIN_NAME" systemctl restart nginx -} -function install_turtl { - install_turtl_api - install_turtl_nginx + "${PROJECT_NAME}-pass" -u "$MY_USERNAME" -a turtl -p "$TURTL_ADMIN_PASSWORD" + set_completion_param "turtl domain" "$TURTL_DOMAIN_NAME" APP_INSTALLED=1 } + +# NOTE: deliberately there is no "exit 0" diff --git a/src/freedombone-template b/src/freedombone-template index aa450ca96..920b96be5 100755 --- a/src/freedombone-template +++ b/src/freedombone-template @@ -375,7 +375,7 @@ if [[ "$database_type" == "mariadb" || "$database_type" == "mysql" || "$database fi if [[ "$database_type" == "postgres"* ]]; then echo ' systemctl restart postgresql' - echo " run_system_query_postgresql \"CREATE USER peertube WITH PASSWORD '\$${app_name_upper}_ADMIN_PASSWORD';\"" + echo " run_system_query_postgresql \"CREATE USER ${app_name} WITH PASSWORD '\$${app_name_upper}_ADMIN_PASSWORD';\"" echo " run_system_query_postgresql \"CREATE DATABASE ${app_name} OWNER ${app_name};\"" echo " run_system_query_postgresql \"GRANT ALL PRIVILEGES ON DATABASE ${app_name} to ${app_name};\"" echo " run_system_query_postgresql \"set statement_timeout to 40000;\"" diff --git a/src/turtl_old b/src/turtl_old new file mode 100755 index 000000000..6af653323 --- /dev/null +++ b/src/turtl_old @@ -0,0 +1,682 @@ +#!/bin/bash +# _____ _ _ +# | __|___ ___ ___ _| |___ _____| |_ ___ ___ ___ +# | __| _| -_| -_| . | . | | . | . | | -_| +# |__| |_| |___|___|___|___|_|_|_|___|___|_|_|___| +# +# Freedom in the Cloud +# +# turtl app +# +# http://portallinux.es/instalacion-servidor-turtl-debian-8 +# http://framacloud.org/cultiver-son-jardin/installation-de-turtl/ +# +# License +# ======= +# +# Copyright (C) 2016-2018 Bob Mottram <bob@freedombone.net> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +VARIANTS="full full-vim writer" + +IN_DEFAULT_INSTALL=0 +SHOW_ON_ABOUT=1 +NOT_ON_ARM=1 + +TURTL_DOMAIN_NAME= +TURTL_CODE= +TURTL_ONION_PORT=8107 +TURTL_PORT=8181 +TURTL_REPO="https://github.com/turtl/api.git" +TURTL_COMMIT='53e00a5583f52de8f86ef380fe11c176b5738dcf' +TURTL_ADMIN_PASSWORD= +TURTL_STORAGE_LIMIT_MB=100 +TURTL_BASE_DIR=/etc/turtl + +# part of a hack to enable/disable signups +TURTL_SIGNUP_STRING='Signup a new user' +turtl_users_file=$TURTL_BASE_DIR/api/controllers/users.lisp + +TURTL_SHORT_DESCRIPTION=$'Note taking' +TURTL_DESCRIPTION=$'Note taking' +TURTL_MOBILE_APP_URL=https://turtlapp.com/releases/mobile/turtl-android-0.6.4.apk + +turtl_variables=(ONION_ONLY + DEFAULT_DOMAIN_NAME + TURTL_DOMAIN_NAME + TURTL_CODE + TURTL_STORAGE_LIMIT_MB + DDNS_PROVIDER + MY_EMAIL_ADDRESS + MY_USERNAME) + +function logging_on_turtl { + echo -n '' +} + +function logging_off_turtl { + echo -n '' +} + +function change_password_turtl { + echo -n '' +# change_username="$1" +# new_user_password="$2" +} + +function remove_user_turtl { + echo -n '' +# remove_username="$1" +} + +function add_user_turtl { +# new_username="$1" +# new_user_password="$2" + echo '0' +} + +function install_interactive_turtl { + if [ ! "$ONION_ONLY" ]; then + ONION_ONLY='no' + fi + + if [[ $ONION_ONLY != "no" ]]; then + TURTL_DOMAIN_NAME='notes.local' + write_config_param "TURTL_DOMAIN_NAME" "$TURTL_DOMAIN_NAME" + else + function_check interactive_site_details + interactive_site_details "turtl" "TURTL_DOMAIN_NAME" "TURTL_CODE" + fi + APP_INSTALLED=1 +} + +function turtl_disable_registrations { + if grep -q "$TURTL_SIGNUP_STRING" $turtl_users_file; then + if [ -f $turtl_users_file ]; then + cp $turtl_users_file $TURTL_BASE_DIR/.users.lisp + sed -i '/(route (:post "\/users") (req res)/,/(send-json res user))))/{//!d}' $turtl_users_file + sed -i 's|(send-json res user))))|())|g' $turtl_users_file + chown -R turtl:turtl $TURTL_BASE_DIR + systemctl restart turtl + fi + fi +} + +function turtl_enable_registrations { + if ! grep -q "$TURTL_SIGNUP_STRING" $turtl_users_file; then + if [ -f $TURTL_BASE_DIR/.users.lisp ]; then + cp $TURTL_BASE_DIR/.users.lisp $turtl_users_file + rm $TURTL_BASE_DIR/.users.lisp + chown -R turtl:turtl $TURTL_BASE_DIR + systemctl restart turtl + fi + fi +} + +function configure_interactive_turtl_signups { + # This implements a hack which removes or adds the function needed + # to sign up new users. It should eventually be removed once that + # capability exists within the api + + dialog --title $"Allow new turtl signups" \ + --backtitle $"Freedombone Control Panel" \ + --defaultno \ + --yesno $"\\nAllow registration of new users?" 10 60 + sel=$? + case $sel in + 0) + turtl_enable_registrations + dialog --title $"Allow new turtl signups" \ + --msgbox $"New turtl user registrations are now allowed" 6 60 + return;; + 1) + turtl_disable_registrations + dialog --title $"Disable new turtl signups" \ + --msgbox $"New turtl user registrations are now disabled" 6 60 + return;; + 255) return;; + esac +} + +function configure_interactive_turtl_storage { + data=$(mktemp 2>/dev/null) + dialog --title $"Change storage limit" \ + --backtitle $"Freedombone Control Panel" \ + --inputbox $"Enter a storage limit in megabytes." 8 75 "$TURTL_STORAGE_LIMIT_MB" 2>"$data" + sel=$? + case $sel in + 0) + STORAGE=$(<"$data") + if [ ${#STORAGE} -gt 0 ]; then + TURTL_STORAGE_LIMIT_MB=$STORAGE + sed -i "s|defparameter *default-storage-limit*.*|defparameter *default-storage-limit* ${TURTL_STORAGE_LIMIT_MB})|g" $TURTL_BASE_DIR/api/config/config.lisp + systemctl restart turtl + dialog --title $"Change storage limit" \ + --msgbox $"Storage limit changed to ${TURTL_STORAGE_LIMIT_MB}M" 6 50 + fi + ;; + esac + rm -f "$data" +} + +function configure_interactive_turtl { + data=$(mktemp 2>/dev/null) + dialog --backtitle $"Freedombone Control Panel" \ + --title $"turtl app settings" \ + --radiolist $"Choose an operation:" 12 70 3 \ + 1 $"Enable/disable new user registrations" off \ + 2 $"Change storage limit" off \ + 3 $"Exit" on 2> "$data" + sel=$? + case $sel in + 1) rm -f "$data" + exit 1;; + 255) rm -f "$data" + exit 1;; + esac + case $(cat "$data") in + 1) configure_interactive_turtl_signups;; + 2) configure_interactive_turtl_storage;; + 3) rm -f "$data" + return;; + esac + rm -f "$data" +} + +function reconfigure_turtl { + if [ -d $TURTL_BASE_DIR/data ]; then + rm -rf $TURTL_BASE_DIR/data/* + fi +} + +function upgrade_turtl { + CURR_TURTL_COMMIT=$(get_completion_param "turtl commit") + if [[ "$CURR_TURTL_COMMIT" == "$TURTL_COMMIT" ]]; then + return + fi + + read_config_param "TURTL_DOMAIN_NAME" + + function_check set_repo_commit + set_repo_commit $TURTL_BASE_DIR/api "turtl commit" "$TURTL_COMMIT" $TURTL_REPO + + # this is used as a crude way of disabling signups and so + # should be superceded in future + if [ -f $TURTL_BASE_DIR/.users.lisp ]; then + turtl_disable_registrations + fi + systemctl restart turtl + + nginx_dissite $TURTL_DOMAIN_NAME + chown -R turtl:turtl $TURTL_BASE_DIR + nginx_ensite $TURTL_DOMAIN_NAME +} + +function backup_local_turtl { + read_config_param "TURTL_DOMAIN_NAME" + source_directory=$TURTL_BASE_DIR + if [ -d $source_directory ]; then + dest_directory=turtl + function_check suspend_site + suspend_site ${TURTL_DOMAIN_NAME} + + function_check backup_directory_to_usb + backup_directory_to_usb $source_directory $dest_directory + + function_check restart_site + restart_site + fi + source_directory=/var/lib/rethinkdb + if [ -d $source_directory ]; then + dest_directory=rethinkdb + function_check suspend_site + suspend_site ${TURTL_DOMAIN_NAME} + + function_check backup_directory_to_usb + backup_directory_to_usb $source_directory $dest_directory + + function_check restart_site + restart_site + fi +} + +function restore_local_turtl { + read_config_param "TURTL_DOMAIN_NAME" + if [ $TURTL_DOMAIN_NAME ]; then + temp_restore_dir=/root/tempturtl + restore_directory_from_usb $temp_restore_dir turtl + + if [ -d ${temp_restore_dir}/etc/turtl ]; then + cp -r ${temp_restore_dir}/etc/turtl/* /etc/turtl/ + else + cp -r ${temp_restore_dir}/* /etc/turtl/ + fi + # shellcheck disable=SC2181 + if [ ! "$?" = "0" ]; then + set_user_permissions + backup_unmount_drive + exit 36723 + fi + rm -rf ${temp_restore_dir} + chown -R turtl:turtl $TURTL_BASE_DIR + + temp_restore_dir=/root/temprethinkdb + restore_directory_from_usb $temp_restore_dir rethinkdb + + if [ -d ${temp_restore_dir}/var/lib/rethinkdb ]; then + cp -r ${temp_restore_dir}/var/lib/rethinkdb/* /var/lib/rethinkdb/ + else + cp -r ${temp_restore_dir}/* /var/lib/rethinkdb/ + fi + + # shellcheck disable=SC2181 + if [ ! "$?" = "0" ]; then + set_user_permissions + backup_unmount_drive + exit 378324 + fi + rm -rf ${temp_restore_dir} + fi +} + +function backup_remote_turtl { + echo -n '' +} + +function restore_remote_turtl { + echo -n '' +} + +function remove_turtl { + if [ ! -d $TURTL_BASE_DIR ]; then + return + fi + systemctl stop turtl + systemctl disable turtl + rm /etc/systemd/system/turtl.service + systemctl daemon-reload + + remove_rethinkdb + remove_app turtl + remove_completion_param install_turtl + sed -i '/turtl/d' "$COMPLETION_FILE" + nginx_dissite $TURTL_DOMAIN_NAME + if [ -f /etc/nginx/sites-available/$TURTL_DOMAIN_NAME ]; then + rm /etc/nginx/sites-available/$TURTL_DOMAIN_NAME + fi + remove_certs $TURTL_DOMAIN_NAME + function_check remove_onion_service + remove_onion_service turtl ${TURTL_ONION_PORT} + function_check remove_ddns_domain + remove_ddns_domain $TURTL_DOMAIN_NAME + rm -rf /etc/rethinkdb + rm -rf /var/lib/rethinkdb + rm -rf $TURTL_BASE_DIR + + groupdel -f turtl + userdel -r turtl +} + + +function turtl_setup { + PIDFILE=${PIDFILE:-nil} + BINDADDR=${BINDADDR:-0.0.0.0} + BINDPORT=${BINDPORT:-8181} + PROD_ERR_HANDLING=${PROD_ERR_HANDLING:-t} + if [[ $ONION_ONLY == 'no' ]]; then + FQDN=${FQDN:-$TURTL_DOMAIN_NAME} + SITE_URL=${SITE_URL:-https://$TURTL_DOMAIN_NAME} + else + FQDN=${FQDN:-$TURTL_ONION_HOSTNAME} + SITE_URL=${SITE_URL:-http://$TURTL_ONION_HOSTNAME} + fi + ADMIN_EMAIL=${ADMIN_EMAIL:-$MY_EMAIL_ADDRESS} + EMAIL_FROM=${EMAIL_FROM:-noreply@$DEFAULT_DOMAIN_NAME} + SMTP_USER=${SMTP_USER:-} + SMTP_PASS=${SMTP_PASS:-} + DISPLAY_ERRORS=${DISPLAY_ERRORS:-t} + DEFAULT_STORAGE_LIMIT=${DEFAULT_STORAGE_LIMIT:-100} + STORAGE_INVITE_CREDIT=${STORAGE_INVITE_CREDIT:-25} + if [[ $ONION_ONLY == 'no' ]]; then + LOCAL_UPLOAD_URL=${LOCAL_UPLOAD_URL:-https://$TURTL_DOMAIN_NAME} + else + LOCAL_UPLOAD_URL=${LOCAL_UPLOAD_URL:-http://$TURTL_ONION_HOSTNAME} + fi + LOCAL_UPLOAD_PATH=${LOCAL_UPLOAD_PATH:-"$TURTL_BASE_DIR/data"} + AWS_S3_TOKEN=${AWS_S3_TOKEN:-(:token '' + :secret '' + :bucket '' + :endpoint 'https://s3.amazonaws.com')} + + # generates the config-file + cat << __ENDCONFIG__ > $TURTL_BASE_DIR/api/config/config.lisp +(in-package :turtl) +(defparameter *root* (asdf:system-relative-pathname :turtl #P"")) +(defparameter *pid-file* "${PIDFILE}") +(defvar *server-bind* "${BINDADDR}") +(defvar *server-port* ${BINDPORT}) +(defvar *db-name* "turtl") +(defvar *db-host* "127.0.0.1") +(defvar *db-port* 28015) +(defvar *production-error-handling* ${PROD_ERR_HANDLING}) +(defvar *enable-hsts-header* nil) +(defvar *site-url* "${SITE_URL}") +(defvar *api-path* "") +(defvar *admin-email* "${ADMIN_EMAIL}") +(defvar *email-from* "${EMAIL_FROM}") +(defvar *email-user* "${SMTP_USER}") +(defvar *email-pass* "${SMTP_PASS}") +(defvar *display-errors* ${DISPLAY_ERRORS}) +(defparameter *default-storage-limit* ${DEFAULT_STORAGE_LIMIT}) +(defparameter *storage-invite-credit* ${STORAGE_INVITE_CREDIT}) +(vom:config :turtl :info) +(defvar *local-upload* "${LOCAL_UPLOAD_PATH}") +(defvar *local-upload-url* "${LOCAL_UPLOAD_URL}") +(defvar *amazon-s3* "${AWS_S3_TOKEN}") +__ENDCONFIG__ + + cat $TURTL_BASE_DIR/api/config/config.footer >> $TURTL_BASE_DIR/api/config/config.lisp + + # start the turtl server + systemctl restart rethinkdb + + if [ ! -f $TURTL_BASE_DIR/quicklisp/setup.lisp ]; then + echo $"$TURTL_BASE_DIR/quicklisp/setup.lisp was not found" + exit 6238234 + fi + + { echo '[Unit]'; + echo 'Description=Note taking service'; + echo 'Documentation=http://turtl.it'; + echo 'Requires=network.target'; + echo 'Requires=rethinkdb.service'; + echo 'After=network.target'; + echo 'After=rethinkdb.service'; + echo ''; + echo '[Service]'; + echo 'Type=simple'; + echo 'User=turtl'; + echo "WorkingDirectory=$TURTL_BASE_DIR/api/"; } > /etc/systemd/system/turtl.service + + if [[ "$check_architecture" == *"64"* && "$check_architecture" != *"arm"* ]]; then + echo "ExecStart=$TURTL_BASE_DIR/ccl/lx86cl64 -l $TURTL_BASE_DIR/quicklisp/setup.lisp -l launch.lisp" >> /etc/systemd/system/turtl.service + else + if [[ "$check_architecture" != *"arm"* ]]; then + echo "ExecStart=$TURTL_BASE_DIR/ccl/lx86cl -l $TURTL_BASE_DIR/quicklisp/setup.lisp -l launch.lisp" >> /etc/systemd/system/turtl.service + else + echo "ExecStart=$TURTL_BASE_DIR/ccl/armcl -l $TURTL_BASE_DIR/quicklisp/setup.lisp -l launch.lisp" >> /etc/systemd/system/turtl.service + fi + fi + { echo ''; + echo '[Install]'; + echo 'WantedBy=multi-user.target'; } >> /etc/systemd/system/turtl.service + chmod +x /etc/systemd/system/turtl.service + + chown -R turtl:turtl $TURTL_BASE_DIR + systemctl enable turtl + systemctl daemon-reload + systemctl start turtl +} + +function install_turtl_api { + # https://github.com/ArthurGarnier/turtl-docker + $INSTALL_PACKAGES wget libterm-readline-perl-perl gcc libuv1-dev + + if [ ! -d $TURTL_BASE_DIR ]; then + mkdir -p $TURTL_BASE_DIR + fi + cd "$TURTL_BASE_DIR" || exit 745726542 + mkdir cd $TURTL_BASE_DIR/data + check_architecture=$(uname -a) + + # Install ccl + if [[ "$check_architecture" != *"arm"* ]]; then + wget -P $TURTL_BASE_DIR/ ftp://ftp.clozure.com/pub/release/1.11/ccl-1.11-linuxx86.tar.gz + mkdir -p $TURTL_BASE_DIR/ccl + tar xvzf $TURTL_BASE_DIR/ccl-1.11-linuxx86.tar.gz -C $TURTL_BASE_DIR/ccl --strip-components=1 + else + wget -P $TURTL_BASE_DIR/ ftp://ftp.clozure.com/pub/release/1.11/ccl-1.11-linuxarm.tar.gz + mkdir -p $TURTL_BASE_DIR/ccl + tar xvzf $TURTL_BASE_DIR/ccl-1.11-linuxarm.tar.gz -C $TURTL_BASE_DIR/ccl --strip-components=1 + fi + + # install quicklisp + cat << __ENDCONFIG__ > $TURTL_BASE_DIR/quicklisp_install +(load (compile-file "asdf.lisp")) +(load (compile-file "quicklisp.lisp")) +(quicklisp-quickstart:install) +(ql:system-apropos "vecto") +(ql:quickload "alexandria") +(ql:quickload "babel") +(ql:quickload "blackbird") +(ql:quickload "bordeaux-threads") +(ql:quickload "cffi") +(ql:quickload "chipz") +(ql:quickload "chunga") +(ql:quickload "cl-annot") +(ql:quickload "cl-async") +(ql:quickload "cl-async-future") +(ql:quickload "cl-base64") +(ql:quickload "cl-fad") +(ql:quickload "cl-libuv") +(ql:quickload "cl-mongo-id") +(ql:quickload "cl-ppcre") +(ql:quickload "cl-rethinkdb") +(ql:quickload "cl-smtp") +(ql:quickload "cl+ssl") +(ql:quickload "cl-syntax") +(ql:quickload "cl-utilities") +(ql:quickload "cl-vectors") +(ql:quickload "do-urlencode") +(ql:quickload "drakma") +(ql:quickload "drakma-async") +(ql:quickload "event-glue") +(ql:quickload "fast-http") +(ql:quickload "fast-io") +(ql:quickload "flexi-streams") +(ql:quickload "ironclad") +(ql:quickload "jonathan") +(ql:quickload "local-time") +(ql:quickload "md5") +(ql:quickload "named-readtables") +(ql:quickload "nibbles") +(ql:quickload "proc-parse") +(ql:quickload "puri") +(ql:quickload "quri") +(ql:quickload "salza2") +(ql:quickload "secure-random") +(ql:quickload "smart-buffer") +(ql:quickload "split-sequence") +(ql:quickload "static-vectors") +(ql:quickload "trivial-backtrace") +(ql:quickload "trivial-features") +(ql:quickload "trivial-garbage") +(ql:quickload "trivial-gray-streams") +(ql:quickload "trivial-types") +(ql:quickload "usocket") +(ql:quickload "vecto") +(ql:quickload "vom") +(ql:quickload "wookie") +(ql:quickload "xmls") +(ql:quickload "xsubseq") +(ql:quickload "yason") +(ql:quickload "zpb-ttf") +(ql:quickload "zpng") +(ql:add-to-init-file) +(ccl::quit) +__ENDCONFIG__ + + if [ ! -f asdf.lisp ]; then + wget https://common-lisp.net/project/asdf/asdf.lisp + fi + if [ ! -f quicklisp.lisp ]; then + wget https://beta.quicklisp.org/quicklisp.lisp + fi + + if [ -d $TURTL_BASE_DIR ]; then + chown -R turtl:turtl $TURTL_BASE_DIR + fi + adduser --disabled-login --home=$TURTL_BASE_DIR --gecos 'turtl' turtl + if [ ! -d $TURTL_BASE_DIR ]; then + echo $"$TURTL_BASE_DIR directory not created" + exit 263493 + fi + + groupadd turtl + chown -R turtl:turtl $TURTL_BASE_DIR + + if [[ "$check_architecture" != *"arm"* ]]; then + if [[ "$check_architecture" == *"64"* ]]; then + su -c "cat $TURTL_BASE_DIR/quicklisp_install | $TURTL_BASE_DIR/ccl/lx86cl64" - turtl + else + su -c "cat $TURTL_BASE_DIR/quicklisp_install | $TURTL_BASE_DIR/ccl/lx86cl" - turtl + fi + else + su -c "cat $TURTL_BASE_DIR/quicklisp_install | $TURTL_BASE_DIR/ccl/larmcl" - turtl + fi + rm $TURTL_BASE_DIR/quicklisp_install + + install_rethinkdb + echo "http-port=8091" > /etc/rethinkdb/instances.d/turtl.conf + chown -R rethinkdb:rethinkdb /var/lib/rethinkdb + + # install turtl API + cd "$TURTL_BASE_DIR/" || exit 6428462 + + if [ -d /repos/turtl ]; then + mkdir -p $TURTL_BASE_DIR/api + cp -r -p /repos/turtl/. $TURTL_BASE_DIR/api + cd "$TURTL_BASE_DIR/api" || exit 57141845 + git pull + else + git clone $TURTL_REPO $TURTL_BASE_DIR/api + fi + + cd "$TURTL_BASE_DIR/api" || exit 35814614 + git checkout $TURTL_COMMIT -b $TURTL_COMMIT + set_completion_param "turtl commit" "$TURTL_COMMIT" + cd "$TURTL_BASE_DIR/quicklisp/local-projects" || exit 43618941415 + git clone git://github.com/orthecreedence/cl-hash-util + if [[ "$check_architecture" != *"arm"* ]]; then + if [[ "$check_architecture" == *"64"* ]]; then + su -c "cat '(ccl:quit)' | $TURTL_BASE_DIR/ccl/lx86cl64 -l $TURTL_BASE_DIR/quicklisp/setup.lisp" - turtl + else + su -c "cat '(ccl:quit)' | $TURTL_BASE_DIR/ccl/lx86cl -l $TURTL_BASE_DIR/quicklisp/setup.lisp" - turtl + fi + else + su -c "cat '(ccl:quit)' | $TURTL_BASE_DIR/ccl/larmcl -l $TURTL_BASE_DIR/quicklisp/setup.lisp" - turtl + fi + + # config + { echo '(defvar *enabled-cors-resources* "resource://turtl-at-lyonbros-dot-com"'; + echo ' "When set, will enable CORS for resource:// origins if they match the given'; + echo ' string. Entries should be comma separated (this string is passed verbatim in'; + echo ' the Access-Control-Allow-Origin header).")'; + echo '(defparameter *public-actions*'; + echo " \`((:post . ,(concatenate 'string *api-path* \"/users\"))"; + echo " (:post . ,(concatenate 'string *api-path* \"/log/error\"))"; + echo ' (:post . "/cla/sign")'; + echo ' (:get . "/ping")'; + echo ' (:get . "/admin")'; + echo " (:get . ,(cl-ppcre:create-scanner (concatenate 'string *api-path* \"/invites/codes/([0-9a-f-]+)\"))))"; + echo " \"A list of public resources/actions that do not require authentication.\")"; + echo "(defvar *analytics* '(:enabled t"; + echo ' :db "analytics"))'; } > "$TURTL_BASE_DIR/api/config/config.footer" + + cp $TURTL_BASE_DIR/asdf.lisp $TURTL_BASE_DIR/api + echo '(load (compile-file "asdf.lisp"))' > $TURTL_BASE_DIR/api/launch.lisp + echo "(pushnew \"./\" asdf:*central-registry* :test #'equal)" >> $TURTL_BASE_DIR/api/launch.lisp + echo '(load "start")' >> $TURTL_BASE_DIR/api/launch.lisp + + TURTL_ONION_HOSTNAME=$(add_onion_service turtl 80 ${TURTL_ONION_PORT}) + + turtl_setup +} + +function install_turtl_nginx { + turtl_nginx_site=/etc/nginx/sites-available/$TURTL_DOMAIN_NAME + if [[ $ONION_ONLY == "no" ]]; then + function_check nginx_http_redirect + nginx_http_redirect $TURTL_DOMAIN_NAME + { echo 'server {'; + echo ' listen 443 ssl;'; + echo ' #listen [::]:443 ssl;'; + echo " server_name ${TURTL_DOMAIN_NAME};"; + echo ''; + echo ' # Security'; } >> "$turtl_nginx_site" + function_check nginx_ssl + nginx_ssl $TURTL_DOMAIN_NAME + + function_check nginx_security_options + nginx_security_options $TURTL_DOMAIN_NAME + + { echo ' add_header Strict-Transport-Security max-age=15768000;'; + echo ''; + echo ' # Logs'; + echo ' access_log /dev/null;'; + echo ' error_log /dev/null;'; + echo ''; + echo ' location / {'; } >> "$turtl_nginx_site" + function_check nginx_limits + nginx_limits $TURTL_DOMAIN_NAME '15m' + { echo " proxy_pass http://localhost:${TURTL_PORT}/;"; + echo " proxy_set_header Host \$host;"; + echo ' proxy_buffering off;'; + echo ' }'; + echo '}'; } >> "$turtl_nginx_site" + else + echo -n '' > $turtl_nginx_site + fi + { echo 'server {'; + echo " listen 127.0.0.1:${TURTL_ONION_PORT};"; + echo ' port_in_redirect off;'; + echo " server_name ${TURTL_ONION_HOSTNAME};"; + echo ''; } >> $turtl_nginx_site + function_check nginx_security_options + nginx_security_options $TURTL_DOMAIN_NAME + { echo ''; + echo ' # Logs'; + echo ' access_log /dev/null;'; + echo ' error_log /dev/null;'; + echo ''; + echo ' location / {'; } >> $turtl_nginx_site + function_check nginx_limits + nginx_limits $TURTL_DOMAIN_NAME '15m' + { echo " proxy_pass http://localhost:${TURTL_PORT}/;"; + echo " proxy_set_header Host \$host;"; + echo ' proxy_buffering off;'; + echo ' }'; + echo '}'; } >> $turtl_nginx_site + + function_check add_ddns_domain + add_ddns_domain $TURTL_DOMAIN_NAME + + set_completion_param "turtl domain" "$TURTL_DOMAIN_NAME" + + function_check create_site_certificate + create_site_certificate $TURTL_DOMAIN_NAME 'yes' + + function_check nginx_ensite + nginx_ensite $TURTL_DOMAIN_NAME + + systemctl restart nginx +} + +function install_turtl { + install_turtl_api + install_turtl_nginx + + APP_INSTALLED=1 +} -- GitLab