49a5452141
This commit adds the first complete local-network deployment path for the project. It normalizes the runtime contract around a fixed container listener on 0.0.0.0:10000, binds the published compose port to 127.0.0.1, and keeps the image/build workflow aligned with the released container image. It also introduces an installation script, an nginx reverse-proxy template, and a safer SQLite backup flow based on sqlite3 .backup with retention and optional rclone upload support. Deployment-oriented configuration has been consolidated into .env.example, repository-local .env files are now ignored, and the deployment scripts are executable. In addition, the frontend mixed-content issue is fixed by switching the stylesheet reference to a root-relative static path, with tests updated to cover the regression. README guidance has been expanded to document the new install, nginx, backup, and restore conventions.
137 lines
3.8 KiB
Bash
Executable File
137 lines
3.8 KiB
Bash
Executable File
#!/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" |