#!/bin/bash set -euo pipefail headline() { printf "\n== %s ==\n" "$1" printf '%*s\n' "${#1}" '' | tr ' ' '-' } info() { printf "[INFO] %s\n" "$1" } warn() { printf "[WARN] %s\n" "$1" } error_msg() { printf "[ERROR] %s\n" "$1" >&2 } prompt() { local message=$1 printf "? %s " "$message" } headline "Pre-flight Checks" if [[ $(id -u) -ne 0 ]]; then error_msg "Please run as root" exit 1 fi if ! command -v docker >/dev/null 2>&1; then error_msg 'Docker is not installed' info 'You can install docker with: curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh' exit 1 fi if ! command -v curl >/dev/null 2>&1; then error_msg 'curl is required but not installed.' exit 1 fi info "All required binaries detected." headline "Directory Layout" for dir in \ /etc/coswarm \ /etc/coswarm/certs \ /opt/coswarm \ /opt/coswarm/certs \ /opt/coswarm/backup do mkdir -p "$dir" chmod 777 "$dir" info "Ensured $dir exists with open permissions" done headline "Network & Domain" if ! IP=$(curl -fsS4 icanhazip.com); then error_msg 'Unable to determine public IP address.' exit 1 fi info "Detected public IP: $IP" echo "Please enter the domain name of your server" echo "Don't add www. (ex: example.com)" while true; do prompt "Enter domain name:" read -r domain [[ -n "$domain" ]] && break warn "Domain name cannot be empty." done info "Using domain: $domain" while true; do prompt "Enter coswarm subdomain:" read -r subdomain [[ -n "$subdomain" ]] && break warn "Subdomain cannot be empty." done info "Using subdomain: $subdomain" headline "Verification Method" warn 'We have two ways to verify your domain:' echo "1. http verification" echo "2. tls verification" warn 'If you choose tls verification and your domain behind cloudflare, switch DNS to DNS-only mode.' warn 'If you choose http verification behind cloudflare, no DNS change is required.' while true; do prompt "Use HTTP verification? (y/n):" read -r http http=${http,,} if [[ "$http" == "y" || "$http" == "n" ]]; then break fi warn "Please enter y or n." done headline "Admin Contact" while true; do prompt "Enter admin email:" read -r email echo if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then info "Email address $email is valid." break fi error_msg "Email address $email is invalid. Please try again." done info "Default password is 'admin' (change after installation)." headline "Deployment" info "Coswarm Installing ..." swarm_state=$(docker info --format '{{.Swarm.LocalNodeState}}' 2>/dev/null || echo 'inactive') if [[ "$swarm_state" != "active" ]]; then info "Initializing Docker Swarm..." docker swarm init --advertise-addr "$IP" else warn "Docker Swarm already initialized. Skipping init." fi if ! docker network ls --format '{{.Name}}' | grep -qx 'cloud'; then info "Creating encrypted overlay network 'cloud'..." docker network create --opt encrypted -d overlay --attachable cloud else warn "Docker network 'cloud' already exists." fi info "Pulling required container images..." for image in kintsdev/coswarm kintsdev/coswarm-proxy postgres:14.6; do docker pull "$image" done if docker service ls --format '{{.Name}}' | grep -qx 'coswarm'; then error_msg "Docker service 'coswarm' already exists. Remove it if you want a fresh install." exit 1 fi info "Creating coswarm service..." docker service create --name coswarm --network cloud \ --mount 'type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock' \ --mount 'type=bind,src=/opt/coswarm/,dst=/opt/coswarm/,readonly' \ -e CSW_DOMAIN="$domain" \ -e CSW_SUBDOMAIN="$subdomain" \ -e CSW_ADMIN_EMAIL="$email" \ -e HTTP_VERIFY="$http" \ -p 3000:3000 kintsdev/coswarm headline "Success" info "Coswarm Installed Successfully" info "Coswarm will be ready for use in ~1 minute." info "Access Coswarm at https://$subdomain.$domain"