#!/usr/bin/env bash set -Eeuo pipefail APP_NAME="corp-address-book" CONTAINER_NAME="corporate-address-book" INSTALL_DIR="/opt/corp-address-book" IMAGE_NAME="corp-address-book:latest" IMAGE_URL="https://git.h0melab.ru/fabritsky/corp-address-book/raw/branch/docker-image/corp-address-book-latest.tar.gz" IMAGE_ARCHIVE="${INSTALL_DIR}/corp-address-book-latest.tar.gz" HOST_PORT="8180" CONTAINER_PORT="3000" ENV_CREATED="false" GENERATED_ADMIN_PASSWORD="" log() { printf '\n[%s] %s\n' "$(date +'%H:%M:%S')" "$*" } die() { printf '\nERROR: %s\n' "$*" >&2 exit 1 } need_root() { if [ "${EUID:-$(id -u)}" -ne 0 ]; then die "Run this installer as root, for example: curl -fsSL ... | sudo bash" fi } install_base_packages() { log "Installing required packages" export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y ca-certificates curl gnupg openssl } install_docker_if_needed() { if command -v docker >/dev/null 2>&1; then log "Docker is already installed" else log "Installing Docker Engine" install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg . /etc/os-release arch="$(dpkg --print-architecture)" codename="${VERSION_CODENAME:-}" [ -n "$codename" ] || die "Cannot determine Ubuntu codename." printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu %s stable\n' "$arch" "$codename" \ > /etc/apt/sources.list.d/docker.list export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io fi systemctl enable --now docker } prepare_env_and_data() { log "Preparing persistent files" mkdir -p "${INSTALL_DIR}/data" if [ ! -f "${INSTALL_DIR}/.env" ]; then jwt_secret="$(openssl rand -hex 32 2>/dev/null || date +%s%N)" admin_password="$(openssl rand -base64 24 2>/dev/null | tr -d '\n' || date +%s%N)" GENERATED_ADMIN_PASSWORD="$admin_password" ENV_CREATED="true" umask 077 cat > "${INSTALL_DIR}/.env" </dev/null 2>&1; then die "Loaded archive did not provide ${IMAGE_NAME}." fi } replace_container() { log "Starting ${CONTAINER_NAME} on port ${HOST_PORT}" if docker container inspect "$CONTAINER_NAME" >/dev/null 2>&1; then docker stop "$CONTAINER_NAME" >/dev/null docker rm "$CONTAINER_NAME" >/dev/null fi docker run -d \ --name "$CONTAINER_NAME" \ --restart unless-stopped \ --env-file "${INSTALL_DIR}/.env" \ -p "${HOST_PORT}:${CONTAINER_PORT}" \ -v "${INSTALL_DIR}/data:/app/data" \ "$IMAGE_NAME" >/dev/null sleep 5 if ! curl -fsSI "http://127.0.0.1:${HOST_PORT}/" >/dev/null; then docker logs --tail=80 "$CONTAINER_NAME" || true die "Health check failed on http://127.0.0.1:${HOST_PORT}/." fi } print_result() { server_ip="$(hostname -I 2>/dev/null | awk '{print $1}')" [ -n "${server_ip:-}" ] || server_ip="SERVER_IP" log "Installation completed" printf 'URL: http://%s:%s/\n' "$server_ip" "$HOST_PORT" printf 'Admin login: admin\n' if [ "$ENV_CREATED" = "true" ]; then printf 'Admin password: %s\n' "$GENERATED_ADMIN_PASSWORD" else printf 'Admin password: unchanged; read it from %s/.env on the server.\n' "$INSTALL_DIR" fi } main() { need_root install_base_packages install_docker_if_needed prepare_env_and_data download_and_load_image replace_container print_result } main "$@"