#!/usr/bin/env sh set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) PROJECT_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) SOURCE_ENV="$PROJECT_ROOT/.env" COMPOSE_SOURCE="$PROJECT_ROOT/docker-compose.yml" BACKUP_TEMPLATE="$SCRIPT_DIR/backup_db.sh" NGINX_TEMPLATE="$SCRIPT_DIR/nginx/moving-helper.nginx.template" NGINX_SITE_NAME="moving-helper-nginx" CRON_MARKER="# moving-helper-backup" require_command() { if ! command -v "$1" >/dev/null 2>&1; then echo "Missing required command: $1" >&2 exit 1 fi } run_as_root() { if [ "$(id -u)" -eq 0 ]; then "$@" elif command -v sudo >/dev/null 2>&1; then sudo "$@" else echo "This step requires root privileges, but sudo is not available: $*" >&2 exit 1 fi } escape_sed_replacement() { printf '%s' "$1" | sed 's/[|&]/\\&/g' } render_template() { src=$1 dst=$2 host_domain_escaped=$(escape_sed_replacement "$HOST_DOMAIN") ssl_path_escaped=$(escape_sed_replacement "$SSL_PATH") app_port_escaped=$(escape_sed_replacement "$APP_PORT") app_dir_escaped=$(escape_sed_replacement "$APP_DIR") backup_dir_escaped=$(escape_sed_replacement "$BACKUP_DIR") sed \ -e "s|__HOST_DOMAIN__|$host_domain_escaped|g" \ -e "s|__SSL_PATH__|$ssl_path_escaped|g" \ -e "s|__APP_PORT__|$app_port_escaped|g" \ -e "s|__APP_DIR__|$app_dir_escaped|g" \ -e "s|__BACKUP_DIR__|$backup_dir_escaped|g" \ "$src" > "$dst" } if [ ! -f "$SOURCE_ENV" ]; then echo "Missing $SOURCE_ENV" >&2 echo "Create it first: cp .env.example .env" >&2 exit 1 fi require_command docker require_command crontab if ! docker compose version >/dev/null 2>&1; then echo "The docker compose plugin is not available in the current environment" >&2 exit 1 fi set -a . "$SOURCE_ENV" set +a HOST_DOMAIN=${HOST_DOMAIN:-} SSL_PATH=${SSL_PATH:-} APP_DIR=${APP_DIR:-$HOME/.local/share/moving-helper} BACKUP_DIR=${BACKUP_DIR:-$HOME/.local/backup/moving-helper} APP_PORT=${APP_PORT:-10000} DATA_DIR=${DATA_DIR:-./data} if [ -z "$HOST_DOMAIN" ]; then echo "HOST_DOMAIN is not configured" >&2 exit 1 fi if [ -z "$SSL_PATH" ]; then echo "SSL_PATH is not configured" >&2 exit 1 fi mkdir -p "$APP_DIR" "$BACKUP_DIR" "$APP_DIR/logs" case "$DATA_DIR" in /*) mkdir -p "$DATA_DIR" ;; *) mkdir -p "$APP_DIR/$DATA_DIR" ;; esac cp "$COMPOSE_SOURCE" "$APP_DIR/docker-compose.yml" cp "$SOURCE_ENV" "$APP_DIR/.env" rendered_backup=$(mktemp) rendered_nginx=$(mktemp) trap 'rm -f "$rendered_backup" "$rendered_nginx"' EXIT INT TERM render_template "$BACKUP_TEMPLATE" "$rendered_backup" install -m 0755 "$rendered_backup" "$APP_DIR/backup_db.sh" render_template "$NGINX_TEMPLATE" "$rendered_nginx" run_as_root install -d /etc/nginx/sites-available /etc/nginx/sites-enabled run_as_root install -m 0644 "$rendered_nginx" "/etc/nginx/sites-available/$NGINX_SITE_NAME" run_as_root ln -sfn "/etc/nginx/sites-available/$NGINX_SITE_NAME" "/etc/nginx/sites-enabled/$NGINX_SITE_NAME" run_as_root nginx -t if command -v systemctl >/dev/null 2>&1; then run_as_root systemctl reload nginx else run_as_root service nginx reload fi ( cd "$APP_DIR" docker compose pull web docker compose up -d ) cron_tmp=$(mktemp) existing_cron=$(mktemp) trap 'rm -f "$rendered_backup" "$rendered_nginx" "$cron_tmp" "$existing_cron"' EXIT INT TERM crontab -l 2>/dev/null > "$existing_cron" || true grep -v "moving-helper-backup" "$existing_cron" > "$cron_tmp" || true printf '10 2 * * * %s/backup_db.sh >> %s/logs/backup.log 2>&1 %s\n' "$APP_DIR" "$APP_DIR" "$CRON_MARKER" >> "$cron_tmp" crontab "$cron_tmp" echo "Installation complete." echo "- Application directory: $APP_DIR" echo "- Backup directory: $BACKUP_DIR" echo "- Nginx config: /etc/nginx/sites-available/$NGINX_SITE_NAME" echo "- URL: https://$HOST_DOMAIN" echo "- Scheduled backup: daily at 02:10 via the current user's crontab"