#!/bin/bash # # Web Host Management Script # Manages podman-compose based web hosts on 127.0.0.1 # Each root domain has its own compose file in /docker/web-hosts/domains// # set -e # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" HOSTS_DIR="$(dirname "$SCRIPT_DIR")" DOMAINS_DIR="$HOSTS_DIR/domains" NGINX_STARTUP="/docker/www/startup.sh" log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[OK]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_header() { echo -e "${CYAN}=== $1 ===${NC}"; } # Get domain directory get_domain_dir() { local domain=$1 echo "$DOMAINS_DIR/$domain" } # Check if domain exists domain_exists() { local domain=$1 local domain_dir=$(get_domain_dir "$domain") [ -d "$domain_dir" ] && [ -f "$domain_dir/compose.yaml" ] } # List all domains list_domains() { log_header "Configured Domains" echo "" if [ ! -d "$DOMAINS_DIR" ] || [ -z "$(ls -A "$DOMAINS_DIR" 2>/dev/null)" ]; then log_warn "No domains configured yet." echo "" echo "To create a new domain, run:" echo " $0 create " return fi for domain_dir in "$DOMAINS_DIR"/*/; do if [ -d "$domain_dir" ]; then domain=$(basename "$domain_dir") compose_file="$domain_dir/compose.yaml" if [ -f "$compose_file" ]; then # Check running status cd "$domain_dir" running_count=$(podman-compose ps -q 2>/dev/null | wc -l) if [ "$running_count" -gt 0 ]; then status="${GREEN}RUNNING${NC} ($running_count containers)" else status="${YELLOW}STOPPED${NC}" fi echo -e " $domain: $status" # Show services if [ -f "$compose_file" ]; then services=$(grep -E "^\s+[a-zA-Z0-9_-]+:\s*$" "$compose_file" | sed 's/://g' | xargs) if [ -n "$services" ]; then echo " Services: $services" fi fi echo "" fi fi done } # Start a domain start_domain() { local domain=$1 if [ -z "$domain" ]; then log_error "Usage: $0 start " return 1 fi if ! domain_exists "$domain"; then log_error "Domain '$domain' not found. Create it first with: $0 create $domain" return 1 fi local domain_dir=$(get_domain_dir "$domain") log_info "Starting $domain..." cd "$domain_dir" podman-compose up -d log_success "$domain started" # Show running containers podman-compose ps } # Stop a domain stop_domain() { local domain=$1 if [ -z "$domain" ]; then log_error "Usage: $0 stop " return 1 fi if ! domain_exists "$domain"; then log_error "Domain '$domain' not found." return 1 fi local domain_dir=$(get_domain_dir "$domain") log_info "Stopping $domain..." cd "$domain_dir" podman-compose down log_success "$domain stopped" } # Restart a domain restart_domain() { local domain=$1 if [ -z "$domain" ]; then log_error "Usage: $0 restart " return 1 fi stop_domain "$domain" sleep 1 start_domain "$domain" } # Show domain status status_domain() { local domain=$1 if [ -z "$domain" ]; then # Show all domains list_domains return fi if ! domain_exists "$domain"; then log_error "Domain '$domain' not found." return 1 fi local domain_dir=$(get_domain_dir "$domain") log_header "Status: $domain" cd "$domain_dir" podman-compose ps } # Show domain logs logs_domain() { local domain=$1 local service=$2 if [ -z "$domain" ]; then log_error "Usage: $0 logs [service]" return 1 fi if ! domain_exists "$domain"; then log_error "Domain '$domain' not found." return 1 fi local domain_dir=$(get_domain_dir "$domain") cd "$domain_dir" if [ -n "$service" ]; then podman-compose logs -f "$service" else podman-compose logs -f fi } # Create a new domain create_domain() { local domain=$1 local port=$2 if [ -z "$domain" ]; then log_error "Usage: $0 create [starting_port]" log_error "Example: $0 create example.com 9000" return 1 fi # Default starting port port=${port:-9000} local domain_dir=$(get_domain_dir "$domain") if [ -d "$domain_dir" ]; then log_warn "Domain '$domain' already exists at $domain_dir" return 1 fi log_info "Creating domain: $domain" # Create directory structure mkdir -p "$domain_dir"/{www,data,config} # Create compose.yaml template cat > "$domain_dir/compose.yaml" << EOF # Compose file for $domain # All services bind to 127.0.0.1 only (routed via nginx reverse proxy) # # Port assignments for $domain: # - Main site ($domain): $port # - API (api.$domain): $((port + 1)) # - Add more subdomains as needed # services: # Main website www: image: docker.io/nginx:stable-alpine container_name: ${domain//./-}-www restart: unless-stopped ports: - "127.0.0.1:${port}:80" volumes: - ./www:/usr/share/nginx/html:ro # healthcheck: # test: ["CMD", "curl", "-f", "http://localhost/"] # interval: 30s # timeout: 10s # retries: 3 # Uncomment to add API subdomain # api: # image: your-api-image # container_name: ${domain//./-}-api # restart: unless-stopped # ports: # - "127.0.0.1:$((port + 1)):8080" # volumes: # - ./data:/app/data # environment: # - NODE_ENV=production # Shared network (optional) # networks: # default: # name: ${domain//./-}-network EOF # Create default index.html cat > "$domain_dir/www/index.html" << EOF Welcome to $domain

$domain

Your site is ready to be configured.

EOF # Create README cat > "$domain_dir/README.md" << EOF # $domain ## Directory Structure \`\`\` $domain/ ├── compose.yaml # Podman compose configuration ├── www/ # Static website files ├── data/ # Persistent data volumes ├── config/ # Configuration files └── README.md # This file \`\`\` ## Port Assignment - Main site ($domain): \`127.0.0.1:$port\` - API (api.$domain): \`127.0.0.1:$((port + 1))\` (if enabled) ## Commands \`\`\`bash # Start the domain hostctl start $domain # Stop the domain hostctl stop $domain # View logs hostctl logs $domain # Check status hostctl status $domain \`\`\` ## Nginx Configuration After starting the containers, add nginx configuration: \`\`\`bash sudo /docker/www/startup.sh add-domain $domain $port \`\`\` EOF log_success "Domain created: $domain_dir" echo "" echo "Next steps:" echo " 1. Edit $domain_dir/compose.yaml to configure services" echo " 2. Add your website files to $domain_dir/www/" echo " 3. Start with: $0 start $domain" echo " 4. Add nginx config: sudo /docker/www/startup.sh add-domain $domain $port" } # Remove a domain remove_domain() { local domain=$1 local force=$2 if [ -z "$domain" ]; then log_error "Usage: $0 remove [--force]" return 1 fi if ! domain_exists "$domain"; then log_error "Domain '$domain' not found." return 1 fi local domain_dir=$(get_domain_dir "$domain") # Check if running cd "$domain_dir" if [ "$(podman-compose ps -q 2>/dev/null | wc -l)" -gt 0 ]; then if [ "$force" != "--force" ]; then log_error "Domain '$domain' is running. Stop it first or use --force" return 1 fi log_warn "Force stopping $domain..." podman-compose down fi if [ "$force" != "--force" ]; then echo -n "Are you sure you want to remove $domain? This will delete all files! (y/N) " read -r confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then log_info "Cancelled." return 0 fi fi rm -rf "$domain_dir" log_success "Domain '$domain' removed." } # Start all domains start_all() { log_header "Starting All Domains" for domain_dir in "$DOMAINS_DIR"/*/; do if [ -d "$domain_dir" ] && [ -f "$domain_dir/compose.yaml" ]; then domain=$(basename "$domain_dir") start_domain "$domain" echo "" fi done log_success "All domains started" } # Stop all domains stop_all() { log_header "Stopping All Domains" for domain_dir in "$DOMAINS_DIR"/*/; do if [ -d "$domain_dir" ] && [ -f "$domain_dir/compose.yaml" ]; then domain=$(basename "$domain_dir") stop_domain "$domain" echo "" fi done log_success "All domains stopped" } # Show usage usage() { echo "Web Host Management Script" echo "" echo "Usage: $0 [options]" echo "" echo "Commands:" echo " list List all configured domains" echo " status [domain] Show status (all or specific domain)" echo " start Start a domain" echo " stop Stop a domain" echo " restart Restart a domain" echo " logs [svc] View logs for a domain" echo "" echo " create [port] Create a new domain (default port: 9000)" echo " remove Remove a domain" echo "" echo " start-all Start all domains" echo " stop-all Stop all domains" echo "" echo "Examples:" echo " $0 create example.com 9000" echo " $0 start example.com" echo " $0 logs example.com www" echo " $0 status" echo "" echo "Directory structure:" echo " $DOMAINS_DIR//compose.yaml" } # Main entry point case "${1:-help}" in list) list_domains ;; status) status_domain "$2" ;; start) start_domain "$2" ;; stop) stop_domain "$2" ;; restart) restart_domain "$2" ;; logs) logs_domain "$2" "$3" ;; create) create_domain "$2" "$3" ;; remove|delete) remove_domain "$2" "$3" ;; start-all) start_all ;; stop-all) stop_all ;; help|--help|-h|"") usage ;; *) log_error "Unknown command: $1" usage exit 1 ;; esac