#!/bin/bash # System preflight checks. set -euo pipefail detect_system_arch() { local raw raw="$(uname -m)" case "$raw" in aarch64|arm64|armv7l|armv8*) SYSTEM_ARCH="arm" USE_ARM_POSTGIS=true POSTGIS_IMAGE="imresamu/postgis:16-3.5-alpine" ;; x86_64|amd64) SYSTEM_ARCH="amd64" USE_ARM_POSTGIS=false POSTGIS_IMAGE="postgis/postgis:16-3.5" ;; *) SYSTEM_ARCH="$raw" USE_ARM_POSTGIS=false POSTGIS_IMAGE="postgis/postgis:16-3.5" ;; esac } verify_postgis_image() { local image="$1" if docker manifest inspect "$image" &>/dev/null; then return 0 fi log_warning "Could not verify manifest for $image" return 1 } write_postgis_override() { local target="${1:-docker-compose.override.yml}" if [[ "$USE_ARM_POSTGIS" != true ]]; then return 0 fi log_info "ARM architecture detected — using PostGIS image: $POSTGIS_IMAGE" cat > "$target" << EOF # Auto-generated by AdventureLog installer for ARM hosts services: db: image: ${POSTGIS_IMAGE} EOF log_success "Wrote $target" } check_dependencies() { print_section "Dependencies" local missing=() command -v curl &>/dev/null || missing+=("curl") command -v docker &>/dev/null || missing+=("docker") if ! docker compose version &>/dev/null 2>&1 && ! command -v docker-compose &>/dev/null; then missing+=("docker-compose") fi if [[ ${#missing[@]} -gt 0 ]]; then log_error "Missing required tools: ${missing[*]}" log_muted "Install Docker: https://docs.docker.com/get-docker/" exit 1 fi log_success "Docker, Compose, and curl are available" } check_docker_status() { if [[ "${DRY_RUN:-false}" == true ]]; then log_muted "[dry-run] Skipping Docker daemon check" return 0 fi log_info "Checking Docker daemon" if ! docker info &>/dev/null; then log_error "Docker is not running — start Docker and try again" exit 1 fi if ! docker info 2>/dev/null | grep -q "Username:" && [[ "$(id -u)" -ne 0 ]]; then if ! groups 2>/dev/null | grep -q docker; then log_warning "Your user is not in the docker group — you may need sudo" fi fi log_success "Docker daemon is running" } check_memory() { local avail_kb avail_mb if [[ -r /proc/meminfo ]]; then avail_kb="$(awk '/MemAvailable/ {print $2}' /proc/meminfo)" if [[ -n "$avail_kb" && "$avail_kb" =~ ^[0-9]+$ ]]; then avail_mb=$((avail_kb / 1024)) if (( avail_mb < 2048 )); then log_warning "Available RAM is ~${avail_mb}MB — first boot needs ~2GB for world data import" if tui_confirm "Enable SKIP_WORLD_DATA=1 to skip geography import on first boot?" "y"; then SKIP_WORLD_DATA="1" append_env_line "SKIP_WORLD_DATA=1" fi fi fi elif [[ "$OSTYPE" == "darwin"* ]]; then log_info "Could not auto-detect RAM on macOS — ensure at least 2GB is allocated to Docker" fi } check_disk_space() { local target="${1:-.}" local check_path="$target" if [[ ! -e "$check_path" ]]; then check_path="." fi local avail if command -v df &>/dev/null; then avail="$(df -BG "$check_path" 2>/dev/null | awk 'NR==2 {gsub(/G/,"",$4); print $4}')" || avail="" if [[ -n "$avail" && "$avail" =~ ^[0-9]+$ ]] && (( avail < 5 )); then log_warning "Less than 5GB free disk space in $check_path" fi fi } check_port_in_use() { local port="$1" local name="$2" local in_use=false if command -v ss &>/dev/null; then ss -ln 2>/dev/null | grep -q ":${port} " && in_use=true elif command -v netstat &>/dev/null; then netstat -ln 2>/dev/null | grep -q ":${port} " && in_use=true fi if [[ "$in_use" == true ]]; then log_warning "Port $port ($name) appears to be in use" tui_confirm "Continue anyway?" "n" || exit 0 fi } detect_existing_install() { local dir="${1:-$INSTALL_DIR}" if [[ -f "$dir/.env" ]] || [[ -f "$dir/.env.advanced" ]] || [[ -f "$dir/.env.aio" ]]; then return 0 fi if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qE '^(adventurelog|adventurelog-aio|adventurelog-frontend|adventurelog-backend)$'; then return 0 fi return 1 } detect_setup_type_from_install() { local dir="${1:-$INSTALL_DIR}" if [[ -f "$dir/.env.aio" ]]; then SETUP_TYPE="standard" elif [[ -f "$dir/.env.advanced" ]]; then SETUP_TYPE="advanced" elif [[ -f "$dir/.env" ]]; then if grep -qE '^(ORIGIN|PUBLIC_URL|FRONTEND_URL)=' "$dir/.env" 2>/dev/null; then SETUP_TYPE="advanced" else SETUP_TYPE="standard" fi fi resolve_compose_settings } run_preflight() { print_step_header 1 "$WIZARD_TOTAL" "System checks" check_dependencies check_docker_status detect_system_arch log_success "Architecture: $SYSTEM_ARCH" log_muted "PostGIS image: $POSTGIS_IMAGE" check_memory check_disk_space "$INSTALL_DIR" } run_port_checks() { if [[ "$SETUP_TYPE" == "standard" ]]; then check_port_in_use "$HOST_PORT" "AdventureLog" else check_port_in_use "$FRONTEND_PORT" "frontend" check_port_in_use "$BACKEND_PORT" "backend" fi } prompt_postgis_override() { if [[ "$USE_ARM_POSTGIS" == true ]]; then return 0 fi if tui_confirm "Use custom PostGIS image? (default: $POSTGIS_IMAGE)" "n"; then POSTGIS_IMAGE="$(prompt_with_default "PostGIS image" "$POSTGIS_IMAGE")" USE_ARM_POSTGIS=true fi }