diff --git a/scripts/restore-appfactory.sh b/scripts/restore-appfactory.sh index 3b99092..a2142e3 100755 --- a/scripts/restore-appfactory.sh +++ b/scripts/restore-appfactory.sh @@ -1,116 +1,163 @@ #!/usr/bin/env bash set -euo pipefail -CONFIG_FILE="/opt/appfactory/config/appfactory.env" +APPFACTORY_ROOT="/opt/appfactory" +BACKUP_FILE="${1:-}" +TARGET_USER="${2:-${SUDO_USER:-$USER}}" -if [ ! -f "$CONFIG_FILE" ]; then - echo "Missing config file: $CONFIG_FILE" +fail() { + echo "[restore][FAIL] $*" >&2 exit 1 -fi +} -source "$CONFIG_FILE" +ok() { + echo "[restore][OK] $*" +} -FORCE="false" -BACKUP_FILE="" +log() { + echo "[restore] $*" +} -if [ "${1:-}" = "--force" ]; then - FORCE="true" - BACKUP_FILE="${2:-}" -else - BACKUP_FILE="${1:-}" -fi +usage() { + cat </dev/null 2>&1; then + fail "Target user does not exist: $TARGET_USER" fi -echo "Restoring backup..." -tar -xzf "$BACKUP_FILE" -C / +command -v tar >/dev/null 2>&1 || fail "tar missing" +command -v docker >/dev/null 2>&1 || fail "docker missing" -echo "Fixing ownership..." -chown -R "$APP_USER:$APP_GROUP" "$APPFACTORY_DIR" -chown -R "$APP_USER:$APP_GROUP" "$WORKSPACE_DIR" -chmod 600 "$APPFACTORY_DIR/config/appfactory.env" || true +APPFACTORY_UID="$(id -u "$TARGET_USER")" +APPFACTORY_GID="$(id -g "$TARGET_USER")" -echo "Starting core services..." -cd "$APPFACTORY_DIR/deploy" -docker compose -f docker-compose.yml up -d - -TOOLS_DIR="$WORKSPACE_DIR/appfactory-tools" - -if [ -d "$TOOLS_DIR" ]; then - cd "$TOOLS_DIR" - - ./scripts/generate-apps-compose.sh || true - ./scripts/generate-caddyfile.sh || true - ./scripts/generate-apps-page.sh || true - - for app_dir in "$WORKSPACE_DIR"/*; do - if [ -f "$app_dir/app.yml" ]; then - app_id="$(basename "$app_dir")" - echo "Rebuilding app: $app_id" - ./scripts/deploy-app.sh "$app_id" || true - fi - done +if [ ! -S /var/run/docker.sock ]; then + fail "Docker socket missing: /var/run/docker.sock" fi -echo "Final compose up..." -cd "$APPFACTORY_DIR/deploy" -docker compose -f docker-compose.yml -f apps-compose.yml up -d +APPFACTORY_DOCKER_GID="$(stat -c '%g' /var/run/docker.sock)" -echo "" -echo "DONE" -echo "Emergency backup:" -echo "$EMERGENCY_BACKUP_FILE" +log "Backup: $BACKUP_FILE" +log "Target user: $TARGET_USER" +log "APPFACTORY_UID=$APPFACTORY_UID" +log "APPFACTORY_GID=$APPFACTORY_GID" +log "APPFACTORY_DOCKER_GID=$APPFACTORY_DOCKER_GID" + +log "Stopping existing AppFactory containers if present" +docker ps -a --format '{{.Names}}' | grep -E '^(appfactory-|.*-service$|test-|microsoft-365-service)' | while read -r container; do + [ -n "$container" ] || continue + docker rm -f "$container" >/dev/null 2>&1 || true + echo "[restore] Removed container: $container" +done + +log "Removing existing AppFactory root" +rm -rf "$APPFACTORY_ROOT" + +log "Recreating AppFactory root" +mkdir -p "$APPFACTORY_ROOT" + +log "Extracting backup" +cd / +tar -xzf "$BACKUP_FILE" + +if [ ! -d "$APPFACTORY_ROOT" ]; then + fail "Backup did not create $APPFACTORY_ROOT" +fi + +log "Ensuring required directories" +mkdir -p \ + "$APPFACTORY_ROOT/apps" \ + "$APPFACTORY_ROOT/backups" \ + "$APPFACTORY_ROOT/config" \ + "$APPFACTORY_ROOT/data" \ + "$APPFACTORY_ROOT/deploy" \ + "$APPFACTORY_ROOT/gateway" \ + "$APPFACTORY_ROOT/services" \ + "$APPFACTORY_ROOT/templates" \ + "$APPFACTORY_ROOT/tools" \ + "$APPFACTORY_ROOT/workspace" + +ENV_FILE="$APPFACTORY_ROOT/config/appfactory.env" + +if [ ! -f "$ENV_FILE" ]; then + fail "Missing env file after restore: $ENV_FILE" +fi + +log "Updating runtime ids in appfactory.env" + +set_env_value() { + local key="$1" + local value="$2" + + if grep -q "^${key}=" "$ENV_FILE"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE" + else + echo "${key}=${value}" >> "$ENV_FILE" + fi +} + +set_env_value "APPFACTORY_UID" "$APPFACTORY_UID" +set_env_value "APPFACTORY_GID" "$APPFACTORY_GID" +set_env_value "APPFACTORY_DOCKER_GID" "$APPFACTORY_DOCKER_GID" + +grep -q '^APPFACTORY_ENABLE_HTTPS=' "$ENV_FILE" || echo 'APPFACTORY_ENABLE_HTTPS=false' >> "$ENV_FILE" +grep -q '^APPFACTORY_DOMAIN=' "$ENV_FILE" || echo 'APPFACTORY_DOMAIN=' >> "$ENV_FILE" +grep -q '^APPFACTORY_PORTAL_DOMAIN=' "$ENV_FILE" || echo 'APPFACTORY_PORTAL_DOMAIN=' >> "$ENV_FILE" +grep -q '^APPFACTORY_GITEA_DOMAIN=' "$ENV_FILE" || echo 'APPFACTORY_GITEA_DOMAIN=' >> "$ENV_FILE" +grep -q '^APPFACTORY_REGISTRY_DOMAIN=' "$ENV_FILE" || echo 'APPFACTORY_REGISTRY_DOMAIN=' >> "$ENV_FILE" +grep -q '^GOOGLE_CLIENT_ID=' "$ENV_FILE" || echo 'GOOGLE_CLIENT_ID=' >> "$ENV_FILE" +grep -q '^GOOGLE_CLIENT_SECRET=' "$ENV_FILE" || echo 'GOOGLE_CLIENT_SECRET=' >> "$ENV_FILE" +grep -q '^GOOGLE_REDIRECT_URI=' "$ENV_FILE" || echo 'GOOGLE_REDIRECT_URI=' >> "$ENV_FILE" + +log "Fixing ownership" +chown -R "$APPFACTORY_UID:$APPFACTORY_GID" "$APPFACTORY_ROOT" + +log "Fixing script permissions" +find "$APPFACTORY_ROOT/workspace/appfactory-tools" -type f -name '*.sh' -exec chmod +x {} \; 2>/dev/null || true + +BOOTSTRAP="$APPFACTORY_ROOT/workspace/appfactory-tools/maintenance/bootstrap-v2.sh" +PREFLIGHT="$APPFACTORY_ROOT/workspace/appfactory-tools/maintenance/preflight-check.sh" +GENERATE_CADDY="$APPFACTORY_ROOT/workspace/appfactory-tools/scripts/generate-caddyfile.sh" + +[ -f "$BOOTSTRAP" ] || fail "Missing bootstrap script: $BOOTSTRAP" +[ -f "$PREFLIGHT" ] || fail "Missing preflight script: $PREFLIGHT" + +chmod +x "$BOOTSTRAP" "$PREFLIGHT" +[ -f "$GENERATE_CADDY" ] && chmod +x "$GENERATE_CADDY" + +log "Running bootstrap repair" +sudo -u "$TARGET_USER" "$BOOTSTRAP" repair + +if [ -f "$GENERATE_CADDY" ]; then + log "Generating Caddyfile" + sudo -u "$TARGET_USER" "$GENERATE_CADDY" || true +fi + +log "Running bootstrap deploy" +sudo -u "$TARGET_USER" "$BOOTSTRAP" deploy + +log "Running final preflight" +sudo -u "$TARGET_USER" "$PREFLIGHT" + +echo +echo "APPFACTORY RESTORE: READY"