diff --git a/src/freedombone-app-hackmd b/src/freedombone-app-hackmd new file mode 100755 index 00000000..c2a50e91 --- /dev/null +++ b/src/freedombone-app-hackmd @@ -0,0 +1,484 @@ +#!/bin/bash +# +# _____ _ _ +# | __|___ ___ ___ _| |___ _____| |_ ___ ___ ___ +# | __| _| -_| -_| . | . | | . | . | | -_| +# |__| |_| |___|___|___|___|_|_|_|___|___|_|_|___| +# +# Freedom in the Cloud +# +# License +# ======= +# +# Copyright (C) 2018 Bob Mottram +# +# 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 . + +VARIANTS='full full-vim' + +IN_DEFAULT_INSTALL=0 +SHOW_ON_ABOUT=1 + +HACKMD_DOMAIN_NAME= +HACKMD_CODE= +HACKMD_ONION_PORT=9052 +HACKMD_REPO="https://github.com/hackmdio/hackmd" +HACKMD_COMMIT='c71361467d6eee6519b050fb5c40fc32520a19a8' +HACKMD_PORT_INTERNAL=TODO + +hackmd_variables=(ONION_ONLY + HACKMD_DOMAIN_NAME + HACKMD_CODE + DDNS_PROVIDER + MY_USERNAME) + +function logging_on_hackmd { + echo -n '' +} + +function logging_off_hackmd { + echo -n '' +} + +function remove_user_hackmd { + remove_username="$1" + + "${PROJECT_NAME}-pass" -u "$remove_username" --rmapp hackmd +} + +function add_user_hackmd { + new_username="$1" + new_user_password="$2" + + "${PROJECT_NAME}-pass" -u "$new_username" -a hackmd -p "$new_user_password" + echo '0' +} + +function install_interactive_hackmd { + if [ ! "$ONION_ONLY" ]; then + ONION_ONLY='no' + fi + + if [[ "$ONION_ONLY" != "no" ]]; then + HACKMD_DOMAIN_NAME='hackmd.local' + write_config_param "HACKMD_DOMAIN_NAME" "$HACKMD_DOMAIN_NAME" + else + interactive_site_details "hackmd" "HACKMD_DOMAIN_NAME" "HACKMD_CODE" + fi + APP_INSTALLED=1 +} + +function change_password_hackmd { + curr_username="$1" + new_user_password="$2" + + read_config_param 'HACKMD_DOMAIN_NAME' + + "${PROJECT_NAME}-pass" -u "$curr_username" -a hackmd -p "$new_user_password" +} + +function hackmd_create_database { + if [ -f $IMAGE_PASSWORD_FILE ]; then + HACKMD_ADMIN_PASSWORD="$(printf "%d" "$(cat "")")" + else + if [ ! $HACKMD_ADMIN_PASSWORD ]; then + HACKMD_ADMIN_PASSWORD="$(create_password ${MINIMUM_PASSWORD_LENGTH})" + fi + fi + if [ ! $HACKMD_ADMIN_PASSWORD ]; then + return + fi + + create_database hackmd "$HACKMD_ADMIN_PASSWORD" $MY_USERNAME +} + +function reconfigure_hackmd { + # This is used if you need to switch identity. Dump old keys and generate new ones + echo -n '' +} + +function configure_interactive_hackmd { + W=(1 $"Option 1" + 2 $"Option 2") + + while true + do + # shellcheck disable=SC2068 + selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"hackmd" --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_hackmd { + CURR_HACKMD_COMMIT=$(get_completion_param "hackmd commit") + if [[ "$CURR_HACKMD_COMMIT" == "$HACKMD_COMMIT" ]]; then + return + fi + + if grep -q "hackmd domain" "$COMPLETION_FILE"; then + HACKMD_DOMAIN_NAME=$(get_completion_param "hackmd domain") + fi + + # update to the next commit + set_repo_commit "/etc/hackmd" "hackmd commit" "$HACKMD_COMMIT" "$HACKMD_REPO" + chown -R hackmd:hackmd "/etc/hackmd" + systemctl restart hackmd +} + +function backup_local_hackmd { + HACKMD_DOMAIN_NAME='hackmd' + if grep -q "hackmd domain" "$COMPLETION_FILE"; then + HACKMD_DOMAIN_NAME=$(get_completion_param "hackmd domain") + fi + + source_directory=/etc/hackmd + + suspend_site "${HACKMD_DOMAIN_NAME}" + + systemctl stop hackmd + + dest_directory=hackmd + backup_directory_to_usb "$source_directory" $dest_directory + + backup_database_to_usb hackmd + + restart_site + systemctl start hackmd +} + +function restore_local_hackmd { + if ! grep -q "hackmd domain" "$COMPLETION_FILE"; then + return + fi + HACKMD_DOMAIN_NAME=$(get_completion_param "hackmd domain") + if [ ! "$HACKMD_DOMAIN_NAME" ]; then + return + fi + suspend_site "${HACKMD_DOMAIN_NAME}" + systemctl stop hackmd + + temp_restore_dir=/root/temphackmd + hackmd_dir=/etc/hackmd + + hackmd_create_database + + restore_database hackmd + if [ -d $temp_restore_dir ]; then + rm -rf $temp_restore_dir + fi + + restore_directory_from_usb $temp_restore_dir hackmd + if [ -d $temp_restore_dir ]; then + if [ -d "$temp_restore_dir$hackmd_dir" ]; then + cp -rp "$temp_restore_dir$hackmd_dir"/* "$hackmd_dir"/ + else + if [ ! -d "$hackmd_dir" ]; then + mkdir "$hackmd_dir" + fi + cp -rp "$temp_restore_dir"/* "$hackmd_dir"/ + fi + chown -R hackmd:hackmd "$hackmd_dir" + rm -rf $temp_restore_dir + fi + systemctl start hackmd + + restart_site +} + +function backup_remote_hackmd { + HACKMD_DOMAIN_NAME='hackmd' + if grep -q "hackmd domain" "$COMPLETION_FILE"; then + HACKMD_DOMAIN_NAME=$(get_completion_param "hackmd domain") + fi + + source_directory=/etc/hackmd + + suspend_site "${HACKMD_DOMAIN_NAME}" + systemctl stop hackmd + + dest_directory=hackmd + backup_directory_to_friend "$source_directory" $dest_directory + backup_database_to_friend hackmd + + + systemctl start hackmd + + restart_site +} + +function restore_remote_hackmd { + if ! grep -q "hackmd domain" "$COMPLETION_FILE"; then + return + fi + HACKMD_DOMAIN_NAME=$(get_completion_param "hackmd domain") + if [ ! "$HACKMD_DOMAIN_NAME" ]; then + return + fi + suspend_site "${HACKMD_DOMAIN_NAME}" + systemctl stop hackmd + + temp_restore_dir=/root/temphackmd + hackmd_dir=/etc/hackmd + + hackmd_create_database + + restore_database_from_friend hackmd + if [ -d "$temp_restore_dir" ]; then + rm -rf $temp_restore_dir + fi + + restore_directory_from_friend $temp_restore_dir hackmd + if [ -d $temp_restore_dir ]; then + if [ -d "$temp_restore_dir$hackmd_dir" ]; then + cp -rp "$temp_restore_dir$hackmd_dir"/* "$hackmd_dir"/ + else + if [ ! -d "$hackmd_dir" ]; then + mkdir "$hackmd_dir" + fi + cp -rp $temp_restore_dir/* "$hackmd_dir"/ + fi + chown -R hackmd:hackmd "$hackmd_dir" + rm -rf $temp_restore_dir + fi + systemctl start hackmd + + restart_site +} + +function remove_hackmd { + nginx_dissite "$HACKMD_DOMAIN_NAME" + remove_certs "$HACKMD_DOMAIN_NAME" + + if [ -f /etc/systemd/system/hackmd.service ]; then + systemctl stop hackmd + systemctl disable hackmd + rm /etc/systemd/system/hackmd.service + fi + userdel -r hackmd + remove_nodejs hackmd + + + if [ -d "/var/www/$HACKMD_DOMAIN_NAME" ]; then + rm -rf "/var/www/$HACKMD_DOMAIN_NAME" + fi + if [ -f "/etc/nginx/sites-available/$HACKMD_DOMAIN_NAME" ]; then + rm "/etc/nginx/sites-available/$HACKMD_DOMAIN_NAME" + fi + drop_database hackmd + remove_onion_service hackmd "${HACKMD_ONION_PORT}" + if grep -q "hackmd" /etc/crontab; then + sed -i "/hackmd/d" /etc/crontab + fi + remove_app hackmd + remove_completion_param install_hackmd + sed -i '/hackmd/d' "$COMPLETION_FILE" + + remove_ddns_domain "$HACKMD_DOMAIN_NAME" +} + +function hackmd_create_config { + { echo '{'; + echo ' "production": {'; + echo ' "domain": "localhost",'; + echo ' "hsts": {'; + echo ' "enable": true,'; + echo ' "maxAgeSeconds": "31536000",'; + echo ' "includeSubdomains": true,'; + echo ' "preload": true'; + echo ' },'; + echo ' "csp": {'; + echo ' "enable": true,'; + echo ' "directives": {'; + echo ' },'; + echo ' "upgradeInsecureRequests": "auto",'; + echo ' "addDefaults": true,'; + echo ' "addDisqus": true,'; + echo ' "addGoogleAnalytics": true'; + echo ' },'; + echo ' "db": {'; + echo " \"username\": \"root\","; + echo " \"password\": \"$MARIADB_PASSWORD\","; + echo ' "database": "hackmd",'; + echo ' "host": "localhost",'; + echo ' "port": "5432",'; + echo ' "dialect": "mysql"'; + echo ' },'; + echo ' "github": {'; + echo ' "clientID": "change this",'; + echo ' "clientSecret": "change this"'; + echo ' },'; + echo ' "gitlab": {'; + echo ' "baseURL": "change this",'; + echo ' "clientID": "change this",'; + echo ' "clientSecret": "change this",'; + echo ' "scope": "use read_user scope for auth user only or remove this property if you need gitlab snippet import/export support (will result to be default scope api)"'; + echo ' }'; + echo ' }'; + echo '}'; } > config.json +} + +function install_hackmd { + install_mariadb + + get_mariadb_password + + install_nodejs hackmd + if [ ! "$HACKMD_DOMAIN_NAME" ]; then + echo $'No domain name was given' + exit 3568356 + fi + + if [ -d "/var/www/$HACKMD_DOMAIN_NAME/htdocs" ]; then + rm -rf "/var/www/$HACKMD_DOMAIN_NAME/htdocs" + fi + if [ -d /repos/hackmd ]; then + mkdir "/var/www/$HACKMD_DOMAIN_NAME/htdocs" + cp -r -p /repos/hackmd/. "/etc/hackmd" + cd "/etc/hackmd" || exit 36487365 + git pull + else + git_clone "$HACKMD_REPO" "/etc/hackmd" + fi + + if [ ! -d "/etc/hackmd" ]; then + echo $'Unable to clone hackmd repo' + exit 87525 + fi + + cd "/etc/hackmd" || exit 3463754637 + git checkout "$HACKMD_COMMIT" -b "$HACKMD_COMMIT" + set_completion_param "hackmd commit" "$HACKMD_COMMIT" + + if [ ! -f bin/setup ]; then + echo $'No setup file found' + exit 36587356 + fi + chmod +x bin/setup + ./bin/setup + hackmd_create_config + npm run build + + chmod g+w "/var/www/$HACKMD_DOMAIN_NAME/htdocs" + chown -R www-data:www-data "/var/www/$HACKMD_DOMAIN_NAME/htdocs" + + hackmd_create_database + + add_ddns_domain "$HACKMD_DOMAIN_NAME" + + HACKMD_ONION_HOSTNAME=$(add_onion_service hackmd 80 "${HACKMD_ONION_PORT}") + + hackmd_nginx_site=/etc/nginx/sites-available/$HACKMD_DOMAIN_NAME + if [[ "$ONION_ONLY" == "no" ]]; then + nginx_http_redirect "$HACKMD_DOMAIN_NAME" "index index.html" + { echo 'server {'; + echo ' listen 443 ssl;'; + echo ' #listen [::]:443 ssl;'; + echo " server_name $HACKMD_DOMAIN_NAME;"; + echo ''; } >> "$hackmd_nginx_site" + nginx_compress "$HACKMD_DOMAIN_NAME" + echo '' >> "$hackmd_nginx_site" + echo ' # Security' >> "$hackmd_nginx_site" + nginx_ssl "$HACKMD_DOMAIN_NAME" + + nginx_security_options "$HACKMD_DOMAIN_NAME" + + { echo ' add_header Strict-Transport-Security max-age=15768000;'; + echo ''; + echo ' access_log /dev/null;'; + echo ' error_log /dev/null;'; + echo ''; + echo " root /var/www/$HACKMD_DOMAIN_NAME/htdocs;"; + echo ''; + echo ' index index.html;'; + echo ' # Location'; + echo ' location / {'; } >> "$hackmd_nginx_site" + nginx_limits "$HACKMD_DOMAIN_NAME" '15m' + { echo " proxy_pass http://localhost:$HACKMD_PORT_INTERNAL;"; + echo ' }'; + echo '}'; } >> "$hackmd_nginx_site" + else + echo -n '' > "$hackmd_nginx_site" + fi + { echo 'server {'; + echo " listen 127.0.0.1:$HACKMD_ONION_PORT default_server;"; + echo " server_name $HACKMD_ONION_HOSTNAME;"; + echo ''; } >> "$hackmd_nginx_site" + nginx_compress "$HACKMD_DOMAIN_NAME" + echo '' >> "$hackmd_nginx_site" + nginx_security_options "$HACKMD_DOMAIN_NAME" + { echo ''; + echo ' access_log /dev/null;'; + echo ' error_log /dev/null;'; + echo ''; + echo " root /var/www/$HACKMD_DOMAIN_NAME/htdocs;"; + echo ''; + echo ' index index.html;'; + echo ' # Location'; + echo ' location / {'; } >> "$hackmd_nginx_site" + nginx_limits "$HACKMD_DOMAIN_NAME" '15m' + { echo " proxy_pass http://localhost:$HACKMD_PORT_INTERNAL;"; + echo ' }'; + echo '}'; } >> "$hackmd_nginx_site" + + adduser --system --home="/etc/hackmd" --group hackmd + + + + { echo '[Unit]'; + echo 'Description=hackmd'; + echo 'After=syslog.target'; + echo 'After=network.target'; + echo "Documentation=$HACKMD_REPO"; + echo ''; + echo '[Service]'; + echo 'Type=simple'; + echo 'User=hackmd'; + echo 'Group=hackmd'; + echo 'WorkingDirectory=/etc/hackmd'; + echo 'ExecStart=/usr/local/bin/npm start'; + echo 'ExecStop=/usr/local/bin/npm stop'; + echo 'Environment=USER=hackmd'; + echo 'Restart=always'; + echo 'StandardError=syslog'; + echo ''; + echo '[Install]'; + echo 'WantedBy=multi-user.target'; } >> "/etc/systemd/system/hackmd.service" + systemctl enable hackmd + chown -R hackmd:hackmd "/etc/hackmd" + systemctl start hackmd + + create_site_certificate "$HACKMD_DOMAIN_NAME" 'yes' + + nginx_ensite "$HACKMD_DOMAIN_NAME" + + systemctl restart mariadb + + systemctl restart nginx + + "${PROJECT_NAME}-pass" -u "$MY_USERNAME" -a hackmd -p "$HACKMD_ADMIN_PASSWORD" + set_completion_param "hackmd domain" "$HACKMD_DOMAIN_NAME" + + APP_INSTALLED=1 +} + +# NOTE: deliberately there is no "exit 0"