#!/usr/bin/env bash # Absolute DB v9.5.1 # Copyright (c) 2024-2026 D.H.Maree. All rights reserved. # Author/Creator: David H Maree # Owner: D.H.Maree (ABN 21 498 105 915) — sole IP holder # Licensed to: SupportCALL AU — primary distributor # SPDX-License-Identifier: BSL-1.1 # https://absolutedb.com/ # https://downloads.absolutedb.com/install-docker.sh # # Deploys Absolute DB as a Docker container with optional docker-compose setup. # Requires Docker Engine 20.10+ or Docker Desktop. # # Usage: # bash install-docker.sh # bash install-docker.sh --no-service # create compose file but don't start # bash install-docker.sh --no-test # skip connection verification # bash install-docker.sh --uninstall # remove container and optionally volumes # bash install-docker.sh --prefix=/myapp # directory for docker-compose.yml # bash install-docker.sh --branch=9.0.2 # specific image tag # # Copyright 2024-2026 SupportCALL AU & D.H.Maree # SPDX-License-Identifier: BSL-1.1 set -euo pipefail # ── Constants ───────────────────────────────────────────────────────────────── VERSION="9.5.1" REPO="https://github.com/supportcall/AbsoluteDB.git" IMAGE_NAME="absolutedb/absdb" IMAGE_TAG="${VERSION}" CONTAINER_NAME="absdb" VOLUME_NAME="absdb_data" PG_PORT=5433 REST_PORT=8080 GRPC_PORT=9090 COMPOSE_DIR="${PWD}" LOG_FILE="/tmp/absdb-docker-install-$$.log" MIN_DOCKER_MAJOR=20 # ── Colour helpers ──────────────────────────────────────────────────────────── GRN='\033[0;32m'; BLU='\033[0;34m'; YLW='\033[0;33m' RED='\033[0;31m'; CYN='\033[0;36m'; NC='\033[0m'; BOLD='\033[1m' info() { echo -e "${BLU}[INFO]${NC} $*"; } ok() { echo -e "${GRN}[ OK ]${NC} $*"; } warn() { echo -e "${YLW}[WARN]${NC} $*"; } step() { echo -e "\n${BOLD}${CYN}$*${NC}"; } die() { echo -e "\n${RED}${BOLD}[FAIL]${NC} $*" >&2 echo -e "${YLW}${BOLD}Troubleshooting guide:${NC}" >&2 echo -e " 1. Full install log: ${LOG_FILE}" >&2 echo -e " 2. Re-run with verbose output: bash -x install-docker.sh" >&2 echo -e " 3. Docker daemon not running?" >&2 echo -e " Linux: sudo systemctl start docker && sudo systemctl enable docker" >&2 echo -e " macOS: Open Docker Desktop application" >&2 echo -e " Windows: Start Docker Desktop from system tray" >&2 echo -e " 4. Permission denied on Docker socket?" >&2 echo -e " sudo usermod -aG docker \$USER then log out and back in" >&2 echo -e " 5. Port conflicts?" >&2 echo -e " Check: ss -tlnp | grep -E '${PG_PORT}|${REST_PORT}|${GRPC_PORT}'" >&2 echo -e " Or: netstat -tlnp | grep -E '${PG_PORT}|${REST_PORT}|${GRPC_PORT}'" >&2 echo -e " 6. Image pull failed?" >&2 echo -e " Check network/firewall. Try: docker pull ${IMAGE_NAME}:${IMAGE_TAG}" >&2 echo -e " 7. Volume permission issues?" >&2 echo -e " docker volume rm ${VOLUME_NAME} && re-run" >&2 echo -e " 8. Low disk space for image?" >&2 echo -e " Check: docker system df" >&2 echo -e " Clean: docker system prune" >&2 echo -e " 9. Container exits immediately?" >&2 echo -e " Check: docker logs ${CONTAINER_NAME}" >&2 echo -e " 10. See full docs: https://absolutedb.com/docs/docker" >&2 exit 1 } # Redirect all output to log file while keeping terminal output exec > >(tee -a "${LOG_FILE}") 2>&1 # ── Argument parsing ────────────────────────────────────────────────────────── OPT_NO_SERVICE=0 OPT_NO_TEST=0 OPT_UNINSTALL=0 for a in "$@"; do case "$a" in --no-service) OPT_NO_SERVICE=1 ;; --no-test) OPT_NO_TEST=1 ;; --uninstall) OPT_UNINSTALL=1 ;; --prefix=*) COMPOSE_DIR="${a#--prefix=}" ;; --branch=*) IMAGE_TAG="${a#--branch=}" ;; --help|-h) echo "Usage: bash install-docker.sh [OPTIONS]" echo " --no-service Create compose file but do not start container" echo " --no-test Skip connection verification after start" echo " --uninstall Remove container, image, and optionally volumes" echo " --prefix= Directory to create docker-compose.yml (default: \$PWD)" echo " --branch= Docker image tag to use (default: ${VERSION})" exit 0 ;; *) warn "Unknown option: $a (ignored)" ;; esac done # ── Banner ──────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}${GRN}╔══════════════════════════════════════════════════════════╗${NC}" echo -e "${BOLD}${GRN}║ Absolute DB v${VERSION} — Docker Installer ║${NC}" echo -e "${BOLD}${GRN}╚══════════════════════════════════════════════════════════╝${NC}" echo "" echo -e " Image : ${BOLD}${IMAGE_NAME}:${IMAGE_TAG}${NC}" echo -e " Container : ${BOLD}${CONTAINER_NAME}${NC}" echo -e " Volume : ${BOLD}${VOLUME_NAME}${NC}" echo -e " Compose : ${BOLD}${COMPOSE_DIR}/docker-compose.yml${NC}" echo -e " Ports : ${BOLD}${PG_PORT} (PG) / ${REST_PORT} (REST) / ${GRPC_PORT} (gRPC)${NC}" echo -e " Log : ${BOLD}${LOG_FILE}${NC}" echo "" # ── Step 1: Check Docker installation ───────────────────────────────────────── step "Step 1/4: Checking Docker" DOCKER_CMD="" if command -v docker &>/dev/null; then DOCKER_CMD="docker" elif command -v podman &>/dev/null; then # Podman is Docker-compatible (rootless, daemonless) DOCKER_CMD="podman" info "Podman detected — using as Docker-compatible runtime." fi if [ -z "${DOCKER_CMD}" ]; then warn "Docker is not installed." echo "" echo -e "${BOLD} Install Docker:${NC}" echo "" # Detect OS for install instructions OS_TYPE=$(uname -s) if [ "${OS_TYPE}" = "Linux" ]; then if [ -f /etc/debian_version ] || grep -qi debian /etc/os-release 2>/dev/null || grep -qi ubuntu /etc/os-release 2>/dev/null; then echo -e " ${CYN}Ubuntu / Debian:${NC}" echo " sudo apt-get update" echo " sudo apt-get install -y docker.io docker-compose-plugin" echo " sudo systemctl enable --now docker" echo " sudo usermod -aG docker \$USER # log out then back in" echo "" fi if [ -f /etc/redhat-release ] || grep -qi "rhel\|centos\|fedora\|rocky\|alma" /etc/os-release 2>/dev/null; then echo -e " ${CYN}RHEL / CentOS / Fedora / Rocky:${NC}" echo " sudo dnf install -y docker" echo " sudo systemctl enable --now docker" echo " sudo usermod -aG docker \$USER # log out then back in" echo "" fi echo -e " ${CYN}Official Docker Engine (all Linux distros):${NC}" echo " curl -fsSL https://get.docker.com | bash" echo "" elif [ "${OS_TYPE}" = "Darwin" ]; then echo -e " ${CYN}macOS:${NC}" echo " 1. Download Docker Desktop: https://www.docker.com/products/docker-desktop" echo " 2. Open the .dmg and drag to Applications" echo " 3. Launch Docker Desktop from Applications" echo " 4. Wait for Docker to start (whale icon in menu bar)" echo "" echo " Alternatively via Homebrew:" echo " brew install --cask docker" echo "" fi die "Docker is required. Install it using the instructions above, then re-run this script." fi # Verify Docker daemon is running _docker_err=$(docker info 2>&1 || true) if ! docker info &>/dev/null 2>&1; then OS_TYPE=$(uname -s) if echo "${_docker_err}" | grep -qi "permission denied"; then echo -e "${RED}Permission denied accessing Docker socket.${NC}" >&2 echo " Your user is not in the 'docker' group." >&2 echo " Fix: sudo usermod -aG docker \$(whoami) && newgrp docker" >&2 echo " Then re-run this installer." >&2 die "Docker permission denied. Add your user to the docker group." else echo -e "${RED}Docker daemon is not running.${NC}" >&2 if [ "${OS_TYPE}" = "Linux" ]; then echo " Start it: sudo systemctl start docker" >&2 echo " Enable on boot: sudo systemctl enable docker" >&2 elif [ "${OS_TYPE}" = "Darwin" ]; then echo " Start Docker Desktop from the Applications folder or menu bar." >&2 fi die "Docker daemon is not running. Start Docker and try again." fi fi # Check Docker version DOCKER_VERSION=$(docker --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+' | head -1 || echo "0.0") DOCKER_MAJOR=$(echo "${DOCKER_VERSION}" | cut -d. -f1) if [ "${DOCKER_MAJOR:-0}" -lt "${MIN_DOCKER_MAJOR}" ] 2>/dev/null; then warn "Docker ${DOCKER_VERSION} is older than the recommended version (${MIN_DOCKER_MAJOR}.x+)." warn "Consider upgrading: https://docs.docker.com/engine/install/" else ok "Docker ${DOCKER_VERSION}" fi # Check disk space AVAIL_KB=$(df -k "${HOME}" 2>/dev/null | awk 'NR==2 {print $4}' || echo 999999) if [ "${AVAIL_KB:-0}" -lt 2097152 ]; then warn "Less than 2 GB disk space available. Docker image requires ~200 MB, plus data volume." warn "Check: docker system df && docker system prune" else ok "Disk space: $(( ${AVAIL_KB:-0} / 1024 )) MB available" fi # ── Uninstall ───────────────────────────────────────────────────────────────── if [ "${OPT_UNINSTALL}" -eq 1 ]; then step "Uninstalling Absolute DB Docker deployment..." docker stop "${CONTAINER_NAME}" 2>/dev/null && ok "Container stopped" || true docker rm "${CONTAINER_NAME}" 2>/dev/null && ok "Container removed" || true docker rmi "${IMAGE_NAME}:${IMAGE_TAG}" 2>/dev/null && ok "Image removed" || true rm -f "${COMPOSE_DIR}/docker-compose.yml" echo "" read -r -p " Remove persistent data volume '${VOLUME_NAME}'? [y/N] " CONFIRM || CONFIRM="n" if [ "${CONFIRM}" = "y" ] || [ "${CONFIRM}" = "Y" ]; then docker volume rm "${VOLUME_NAME}" 2>/dev/null && ok "Volume ${VOLUME_NAME} removed" || warn "Volume not found" else warn "Volume '${VOLUME_NAME}' retained. Remove manually: docker volume rm ${VOLUME_NAME}" fi ok "Absolute DB Docker deployment removed." exit 0 fi # ── Step 2: Pull or build image ──────────────────────────────────────────────── step "Step 2/4: Getting Absolute DB Docker image" # Check if image already exists locally if docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" &>/dev/null 2>&1; then ok "Image ${IMAGE_NAME}:${IMAGE_TAG} already present locally" info "To force a fresh pull: docker rmi ${IMAGE_NAME}:${IMAGE_TAG}" else info "Pulling ${IMAGE_NAME}:${IMAGE_TAG} from Docker Hub..." if docker pull "${IMAGE_NAME}:${IMAGE_TAG}" 2>/dev/null; then ok "Image pulled: ${IMAGE_NAME}:${IMAGE_TAG}" else warn "Pull from Docker Hub failed — attempting to build from source..." info "Cloning source repository for local build..." BUILD_DIR=$(mktemp -d) _attempt=1; _delay=4 while [ "${_attempt}" -le 3 ]; do git clone --depth=1 "${REPO}" "${BUILD_DIR}/AbsoluteDB" 2>&1 && break if [ "${_attempt}" -lt 3 ]; then warn "Clone attempt ${_attempt}/3 failed — retrying in ${_delay}s..." sleep "${_delay}"; _delay=$(( _delay * 2 )) else die "git clone failed after 3 attempts. Install git or check network: sudo apt-get install git" fi _attempt=$(( _attempt + 1 )) done info "Building Docker image (this may take 5-15 minutes)..." docker build \ --tag "${IMAGE_NAME}:${IMAGE_TAG}" \ --label "version=${VERSION}" \ --label "build-date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --file "${BUILD_DIR}/AbsoluteDB/deploy/docker/Dockerfile" \ "${BUILD_DIR}/AbsoluteDB" || die "docker build failed. Check Dockerfile in repository." rm -rf "${BUILD_DIR}" ok "Image built locally: ${IMAGE_NAME}:${IMAGE_TAG}" fi fi # Verify image IMAGE_SIZE=$(docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" --format='{{.Size}}' 2>/dev/null || echo 0) IMAGE_SIZE_MB=$(( IMAGE_SIZE / 1048576 )) ok "Image size: ${IMAGE_SIZE_MB} MB" # ── Step 3: Create docker-compose.yml ───────────────────────────────────────── step "Step 3/4: Creating deployment configuration" mkdir -p "${COMPOSE_DIR}" COMPOSE_FILE="${COMPOSE_DIR}/docker-compose.yml" if [ -f "${COMPOSE_FILE}" ]; then cp "${COMPOSE_FILE}" "${COMPOSE_FILE}.bak.$(date +%Y%m%d-%H%M%S)" info "Existing docker-compose.yml backed up" fi cat > "${COMPOSE_FILE}" </dev/null 2>&1; then info "Stopping existing ${CONTAINER_NAME} container..." docker stop "${CONTAINER_NAME}" 2>/dev/null || true docker rm "${CONTAINER_NAME}" 2>/dev/null || true fi # Check for port conflicts before starting PORT_CONFLICT=0 for PORT in "${PG_PORT}" "${REST_PORT}" "${GRPC_PORT}"; do _port_in_use=false if (ss -tlnp 2>/dev/null | grep -qE ":${PORT}[[:space:]]|:${PORT}$") 2>/dev/null || \ (netstat -tlnp 2>/dev/null | grep -qE ":${PORT}[[:space:]]|:${PORT}$") 2>/dev/null || \ (lsof -iTCP:"${PORT}" -sTCP:LISTEN &>/dev/null 2>/dev/null); then _port_in_use=true fi if [ "${_port_in_use}" = true ]; then warn "Port ${PORT} is already in use. Another process may be listening." PORT_CONFLICT=1 fi done if [ "${PORT_CONFLICT}" -eq 1 ]; then warn "Port conflict detected. The container may fail to start." warn "Check: ss -tlnp | grep -E '${PG_PORT}|${REST_PORT}|${GRPC_PORT}'" fi info "Starting ${CONTAINER_NAME} container..." docker run -d \ --name "${CONTAINER_NAME}" \ --restart unless-stopped \ -p "${PG_PORT}:5433" \ -p "${REST_PORT}:8080" \ -p "${GRPC_PORT}:9090" \ -v "${VOLUME_NAME}:/var/lib/absdb" \ -e "ABSDB_LOG_LEVEL=info" \ "${IMAGE_NAME}:${IMAGE_TAG}" || die "docker run failed. Check: docker logs ${CONTAINER_NAME}" ok "Container started: ${CONTAINER_NAME}" # Wait for container to be healthy info "Waiting for Absolute DB to initialise..." STARTED=0 for i in $(seq 1 30); do sleep 1 CONTAINER_STATUS=$(docker inspect "${CONTAINER_NAME}" --format='{{.State.Status}}' 2>/dev/null || echo "unknown") HEALTH_STATUS=$(docker inspect "${CONTAINER_NAME}" --format='{{.State.Health.Status}}' 2>/dev/null || echo "none") if [ "${CONTAINER_STATUS}" = "running" ]; then # Use Docker's built-in healthcheck status — avoids need for curl inside container if [ "${HEALTH_STATUS}" = "healthy" ]; then ok "Absolute DB is healthy (${i}s)" STARTED=1 break fi # Fallback: try REST API from host if healthcheck not yet reporting HEALTH=$(curl -sf --connect-timeout 2 "http://localhost:${REST_PORT}/health" 2>/dev/null || true) if echo "${HEALTH}" | grep -q '"healthy":true\|"status"'; then ok "Absolute DB REST API responding (${i}s)" STARTED=1 break fi elif [ "${CONTAINER_STATUS}" = "exited" ]; then docker logs "${CONTAINER_NAME}" --tail 20 >&2 die "Container exited unexpectedly. Check logs above." fi done if [ "${STARTED}" -eq 0 ]; then warn "Container did not become healthy within 30 seconds." warn " Container status: $(docker inspect ${CONTAINER_NAME} --format='{{.State.Status}}' 2>/dev/null || echo 'unknown')" warn " Check logs: docker logs ${CONTAINER_NAME}" fi # Verify from host if [ "${OPT_NO_TEST}" -eq 0 ]; then echo "" info "Verifying connectivity from host..." # REST API REST_OK=0 HEALTH=$(curl -sf --connect-timeout 5 "http://localhost:${REST_PORT}/health" 2>/dev/null || true) if echo "${HEALTH}" | grep -q '"status"'; then ok "REST API: http://localhost:${REST_PORT}/health → ${HEALTH}" REST_OK=1 else warn "REST API not responding on port ${REST_PORT}" fi # PostgreSQL connection (if psql available) if command -v psql &>/dev/null; then PSQL_OK=$(PGPASSWORD="" psql -h localhost -p "${PG_PORT}" -U absdb \ -c "SELECT 'connected' AS status" -t 2>/dev/null | xargs || true) if echo "${PSQL_OK}" | grep -q "connected"; then ok "PostgreSQL wire: localhost:${PG_PORT} → connected" else warn "PostgreSQL connection test failed (psql may need credentials)" fi fi fi # Show container status echo "" info "Container status:" docker ps --filter "name=${CONTAINER_NAME}" --format " {{.Names}}\t{{.Status}}\t{{.Ports}}" fi # ── Final summary ───────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}${GRN}╔══════════════════════════════════════════════════════════╗${NC}" echo -e "${BOLD}${GRN}║ Absolute DB v${VERSION} — Docker deployment ready! ║${NC}" echo -e "${BOLD}${GRN}╚══════════════════════════════════════════════════════════╝${NC}" echo "" echo -e "${BOLD} Quick start:${NC}" echo "" echo -e " ${CYN}REST API:${NC}" echo " curl http://localhost:${REST_PORT}/health" echo " curl -X POST http://localhost:${REST_PORT}/sql \\" echo " -H 'Content-Type: application/json' \\" echo " -d '{\"sql\":\"SELECT 1+1 AS result\"}'" echo "" echo -e " ${CYN}Web console:${NC}" echo " http://localhost:${REST_PORT}" echo "" echo -e " ${CYN}PostgreSQL wire (psql):${NC}" echo " psql -h localhost -p ${PG_PORT}" echo "" echo -e " ${CYN}gRPC:${NC}" echo " grpcurl -plaintext localhost:${GRPC_PORT} list" echo "" echo -e " ${CYN}docker run (standalone):${NC}" echo " docker run -d --name ${CONTAINER_NAME} \\" echo " -p ${PG_PORT}:5433 -p ${REST_PORT}:8080 -p ${GRPC_PORT}:9090 \\" echo " -v ${VOLUME_NAME}:/var/lib/absdb \\" echo " ${IMAGE_NAME}:${IMAGE_TAG}" echo "" echo -e " ${CYN}docker compose (from ${COMPOSE_DIR}):${NC}" echo " docker compose up -d # start" echo " docker compose down # stop" echo " docker compose logs -f # follow logs" echo " docker compose pull # update image" echo "" echo -e " ${CYN}Container management:${NC}" echo " docker ps # check running containers" echo " docker logs ${CONTAINER_NAME} # view logs" echo " docker logs ${CONTAINER_NAME} -f # follow logs" echo " docker exec -it ${CONTAINER_NAME} sh # open shell inside container" echo " docker stop ${CONTAINER_NAME} # stop" echo " docker start ${CONTAINER_NAME} # start" echo " docker restart ${CONTAINER_NAME} # restart" echo "" echo -e " ${CYN}Persistent volume:${NC}" echo " docker volume ls # list volumes" echo " docker volume inspect ${VOLUME_NAME} # inspect volume" echo " docker volume rm ${VOLUME_NAME} # WARNING: deletes all data" echo "" echo -e " ${CYN}Update to new version:${NC}" echo " docker pull ${IMAGE_NAME}:latest" echo " docker stop ${CONTAINER_NAME} && docker rm ${CONTAINER_NAME}" echo " # Re-run docker run command above (data is preserved in volume)" echo "" # ── Production vs Testing prompt ───────────────────────────────────────────── if [ -t 0 ] && [ -t 1 ]; then echo "" echo -e " ${BOLD}Is this Docker installation for Production or Testing/Evaluation?${NC}" echo "" echo -e " ${CYN}1)${NC} Production — container starts clean, no sample data" echo -e " ${CYN}2)${NC} Testing — optionally generate tier-sized sample data" echo "" INSTALL_TYPE="" while true; do read -rp " Enter choice [1/2] (default: 1): " _it _it="${_it:-1}" case "${_it}" in 1|production|prod) INSTALL_TYPE="production"; break ;; 2|testing|test) INSTALL_TYPE="testing"; break ;; *) echo " Enter 1 or 2." ;; esac done if [ "${INSTALL_TYPE}" = "testing" ]; then echo "" read -rp " Generate sample/test data inside the container? [y/N]: " _wd _wd="${_wd:-n}" if [[ "${_wd,,}" == "y" || "${_wd,,}" == "yes" ]]; then echo "" echo -e " ${BOLD}Select tier size:${NC}" echo -e " ${CYN}1)${NC} Community — ${YLW}2 GB${NC}" echo -e " ${CYN}2)${NC} SME — ${YLW}3 GB${NC}" echo -e " ${CYN}3)${NC} Professional — ${YLW}10 GB${NC}" echo -e " ${CYN}4)${NC} Enterprise — ${YLW}20 GB${NC}" echo "" DATA_TIER="" while true; do read -rp " Enter tier [1-4 or name]: " _dt _dt="${_dt,,}" case "${_dt}" in 1|community) DATA_TIER="community"; break ;; 2|sme) DATA_TIER="sme"; break ;; 3|pro|professional) DATA_TIER="pro"; break ;; 4|enterprise) DATA_TIER="enterprise"; break ;; *) echo " Enter 1–4 or community/sme/pro/enterprise." ;; esac done echo "" echo " Generating ${DATA_TIER} test data inside container..." echo " This may take several minutes for larger tiers." # Wait for container to be fully ready sleep 3 docker exec "${CONTAINER_NAME}" \ python3 /app/tools/gen_test_data.py \ --tier "${DATA_TIER}" \ --host 127.0.0.1 \ --port 5433 \ --dbname absdb \ --drop-existing \ 2>/dev/null \ || echo " [WARN] Data gen failed — ensure container is running." echo " To regenerate: docker exec ${CONTAINER_NAME} python3 /app/tools/gen_test_data.py --tier ${DATA_TIER} --drop-existing" fi else echo " Production install — container starts clean." echo " To generate test data later:" echo " docker exec ${CONTAINER_NAME} python3 /app/tools/gen_test_data.py" fi fi echo -e "${BOLD} Next steps:${NC}" echo "" echo -e " ${YLW}1. Host firewall:${NC} Docker maps container ports to the host." echo -e " If your host has a firewall, ensure these ports are open for remote access:" echo -e " ${DIM}PostgreSQL wire:${NC} ${PG_PORT} ${DIM}REST API + Web console:${NC} ${REST_PORT}" echo -e " ${DIM}gRPC / HTTP/2:${NC} ${GRPC_PORT} ${DIM}Redis RESP3:${NC} 6379" echo "" echo -e " ${YLW}2. Open the web management console:${NC}" echo -e " ${CYN}http://localhost:${REST_PORT}/console${NC}" echo "" echo -e " ${YLW}3. Enable TLS for production:${NC}" echo -e " See ${DIM}https://absolutedb.com/docs/tls${NC}" echo "" echo -e " ${CYN}Uninstall:${NC}" echo " bash install-docker.sh --uninstall" echo "" echo -e " ${CYN}Docs:${NC} https://absolutedb.com/docs/docker" echo -e " ${CYN}Support:${NC} https://absolutedb.com/support" echo ""