#!/bin/bash # DcRouter Installer Script # Installs the self-extracting Linux binary by default, or builds the NodeNext # source package when --source is specified. # # Usage: # Binary install: # curl -sSL https://code.foss.global/serve.zone/dcrouter/raw/branch/main/install.sh | sudo bash # # Source install: # curl -sSL https://code.foss.global/serve.zone/dcrouter/raw/branch/main/install.sh | sudo bash -s -- --source # # Options: # -h, --help Show this help message # --version VERSION Install a specific tag/version (e.g. vX.Y.Z) # --install-dir DIR Installation directory (default: /opt/dcrouter) # --binary Install release binary (default) # --source Clone the tag and build the NodeNext package locally set -euo pipefail SHOW_HELP=0 SPECIFIED_VERSION="" INSTALL_DIR="/opt/dcrouter" INSTALL_MODE="binary" GITEA_BASE_URL="https://code.foss.global" GITEA_REPO="serve.zone/dcrouter" SERVICE_NAME="dcrouter" BIN_DIR="/usr/local/bin" while [[ $# -gt 0 ]]; do case "$1" in -h|--help) SHOW_HELP=1 shift ;; --version) if [[ $# -lt 2 ]]; then echo "Error: --version requires a value" exit 1 fi SPECIFIED_VERSION="$2" shift 2 ;; --install-dir) if [[ $# -lt 2 ]]; then echo "Error: --install-dir requires a value" exit 1 fi INSTALL_DIR="$2" shift 2 ;; --binary) INSTALL_MODE="binary" shift ;; --source) INSTALL_MODE="source" shift ;; *) echo "Unknown option: $1" echo "Use -h or --help for usage information" exit 1 ;; esac done if [[ $SHOW_HELP -eq 1 ]]; then echo "DcRouter Installer Script" echo "Installs DcRouter as a self-extracting binary or NodeNext source build." echo "" echo "Usage: $0 [options]" echo "" echo "Options:" echo " -h, --help Show this help message" echo " --version VERSION Install a specific tag/version (e.g. vX.Y.Z)" echo " --install-dir DIR Installation directory (default: /opt/dcrouter)" echo " --binary Install release binary (default)" echo " --source Clone the tag and build the NodeNext package locally" echo "" echo "Examples:" echo " curl -sSL https://code.foss.global/serve.zone/dcrouter/raw/branch/main/install.sh | sudo bash" echo " curl -sSL https://code.foss.global/serve.zone/dcrouter/raw/branch/main/install.sh | sudo bash -s -- --source" echo " curl -sSL https://code.foss.global/serve.zone/dcrouter/raw/branch/main/install.sh | sudo bash -s -- --version vX.Y.Z" exit 0 fi if [[ "$EUID" -ne 0 ]]; then echo "Please run as root (sudo bash install.sh or pipe to sudo bash)" exit 1 fi case "$INSTALL_DIR" in ""|"/") echo "Error: unsafe install directory: $INSTALL_DIR" exit 1 ;; esac require_command() { if ! command -v "$1" >/dev/null 2>&1; then echo "Error: required command not found: $1" exit 1 fi } ensure_pnpm() { if command -v pnpm >/dev/null 2>&1; then return fi if command -v corepack >/dev/null 2>&1; then corepack enable fi if ! command -v pnpm >/dev/null 2>&1; then echo "Error: pnpm is required for --source installs. Install Node.js with corepack/pnpm first." exit 1 fi } make_executable_if_present() { if [[ -f "$1" ]]; then chmod 0755 "$1" fi } get_latest_version() { echo "Fetching latest release version from Gitea..." >&2 local api_url="${GITEA_BASE_URL}/api/v1/repos/${GITEA_REPO}/releases/latest" local response if ! response=$(curl -fsSL "$api_url" 2>/dev/null); then echo "Error: Failed to fetch latest release information from Gitea API" >&2 echo "URL: $api_url" >&2 exit 1 fi local version version=$(printf '%s' "$response" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') if [[ -z "$version" ]]; then echo "Error: Could not determine latest version from API response" >&2 exit 1 fi echo "$version" } detect_binary_name() { local os local arch os=$(uname -s) arch=$(uname -m) if [[ "$os" != "Linux" ]]; then echo "Error: binary installer currently supports Linux only. Use --source for this platform." >&2 exit 1 fi case "$arch" in x86_64|amd64) echo "dcrouter-linux-x64" ;; aarch64|arm64) echo "dcrouter-linux-arm64" ;; *) echo "Error: unsupported architecture for binary install: $arch. Use --source." >&2 exit 1 ;; esac } echo "================================================" echo " DcRouter Installation Script" echo "================================================" echo "" require_command curl require_command sed if [[ -n "$SPECIFIED_VERSION" ]]; then VERSION="$SPECIFIED_VERSION" echo "Installing specified version: $VERSION" else VERSION=$(get_latest_version) echo "Installing latest version: $VERSION" fi echo "Install mode: $INSTALL_MODE" echo "" SOURCE_REF="$VERSION" REPO_URL="${GITEA_BASE_URL}/${GITEA_REPO}.git" TEMP_DIR=$(mktemp -d) SOURCE_DIR="$TEMP_DIR/source" BACKUP_DIR="" SERVICE_WAS_RUNNING=0 SERVICE_STOPPED=0 SYSTEMD_AVAILABLE=0 cleanup_temp() { rm -rf "$TEMP_DIR" } trap cleanup_temp EXIT if command -v systemctl >/dev/null 2>&1; then SYSTEMD_AVAILABLE=1 if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then SERVICE_WAS_RUNNING=1 fi fi restore_previous_installation() { if [[ -n "$BACKUP_DIR" && -d "$BACKUP_DIR" ]]; then echo "Restoring previous installation from $BACKUP_DIR..." rm -rf "$INSTALL_DIR" || true mv "$BACKUP_DIR" "$INSTALL_DIR" || true if [[ -f "$INSTALL_DIR/dcrouter" ]]; then mkdir -p "$BIN_DIR" || true ln -sf "$INSTALL_DIR/dcrouter" "$BIN_DIR/dcrouter" || true elif [[ -f "$INSTALL_DIR/cli.js" ]]; then mkdir -p "$BIN_DIR" || true ln -sf "$INSTALL_DIR/cli.js" "$BIN_DIR/dcrouter" || true fi fi } restart_previous_service_on_error() { if [[ $SERVICE_STOPPED -eq 1 && $SYSTEMD_AVAILABLE -eq 1 ]]; then echo "Installation failed after stopping DcRouter; restarting previous service..." systemctl start "$SERVICE_NAME" || true fi } handle_install_error() { trap - ERR restore_previous_installation restart_previous_service_on_error } trap handle_install_error ERR stop_service_if_running() { if [[ $SERVICE_WAS_RUNNING -eq 1 && $SYSTEMD_AVAILABLE -eq 1 ]] && systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then echo "Stopping DcRouter service..." systemctl stop "$SERVICE_NAME" SERVICE_STOPPED=1 fi } move_previous_installation() { mkdir -p "$(dirname "$INSTALL_DIR")" if [[ -d "$INSTALL_DIR" ]]; then BACKUP_DIR="${INSTALL_DIR}.previous.$$" echo "Moving previous installation to $BACKUP_DIR" mv "$INSTALL_DIR" "$BACKUP_DIR" fi } install_source_build() { require_command git require_command node ensure_pnpm echo "Cloning DcRouter source from $REPO_URL ($SOURCE_REF)..." git clone --depth 1 --branch "$SOURCE_REF" "$REPO_URL" "$SOURCE_DIR" echo "Installing dependencies..." pnpm --dir "$SOURCE_DIR" install --frozen-lockfile echo "Building DcRouter..." pnpm --dir "$SOURCE_DIR" run build echo "Validating built CLI..." node "$SOURCE_DIR/cli.js" --version >/dev/null stop_service_if_running move_previous_installation echo "Installing source build to $INSTALL_DIR" mv "$SOURCE_DIR" "$INSTALL_DIR" make_executable_if_present "$INSTALL_DIR/cli.js" make_executable_if_present "$INSTALL_DIR/cli.ts.js" make_executable_if_present "$INSTALL_DIR/cli.child.js" mkdir -p "$BIN_DIR" ln -sf "$INSTALL_DIR/cli.js" "$BIN_DIR/dcrouter" } install_release_binary() { local binary_name local download_url local temp_file binary_name=$(detect_binary_name) download_url="${GITEA_BASE_URL}/${GITEA_REPO}/releases/download/${VERSION}/${binary_name}" temp_file="$TEMP_DIR/$binary_name" echo "Downloading DcRouter binary: $download_url" curl -fSL "$download_url" -o "$temp_file" chmod 0755 "$temp_file" echo "Validating downloaded binary..." "$temp_file" --version >/dev/null stop_service_if_running move_previous_installation echo "Installing binary to $INSTALL_DIR" mkdir -p "$INSTALL_DIR" install -m 0755 "$temp_file" "$INSTALL_DIR/dcrouter" mkdir -p "$BIN_DIR" ln -sf "$INSTALL_DIR/dcrouter" "$BIN_DIR/dcrouter" } if [[ "$INSTALL_MODE" == "source" ]]; then install_source_build else install_release_binary fi echo "Symlink created: $BIN_DIR/dcrouter" if ! "$BIN_DIR/dcrouter" --version >/dev/null; then echo "Error: Installed DcRouter CLI failed validation" restore_previous_installation restart_previous_service_on_error exit 1 fi if [[ -n "$BACKUP_DIR" && -d "$BACKUP_DIR" ]]; then rm -rf "$BACKUP_DIR" fi if [[ $SERVICE_WAS_RUNNING -eq 1 && $SYSTEMD_AVAILABLE -eq 1 ]]; then echo "Restarting DcRouter service..." systemctl restart "$SERVICE_NAME" SERVICE_STOPPED=0 echo "Service restarted successfully." echo "" fi trap - ERR echo "================================================" echo " DcRouter Installation Complete!" echo "================================================" echo "" echo "Installation details:" echo " Install directory: $INSTALL_DIR" echo " Symlink location: $BIN_DIR/dcrouter" echo " Version: $VERSION" echo " Mode: $INSTALL_MODE" echo "" echo "Get started:" echo "" echo " dcrouter --version" echo " dcrouter --help" echo ""