#!/bin/sh # /usr/libexec/rpcd/vhost-manager # RPCD backend for VHost Manager module # Provides ubus interface: luci.vhost-manager . /lib/functions.sh . /usr/share/libubox/jshn.sh # Configuration UCI_CONFIG="vhost_manager" NGINX_VHOSTS_DIR="/etc/nginx/conf.d" ACME_DIR="/etc/acme" # Helper: Check if nginx is running nginx_running() { pgrep -x nginx > /dev/null 2>&1 } # Helper: Get nginx status get_nginx_status() { if nginx_running; then echo "running" else echo "stopped" fi } # Helper: Count vhosts count_vhosts() { if [ -d "$NGINX_VHOSTS_DIR" ]; then ls -1 "$NGINX_VHOSTS_DIR"/*.conf 2>/dev/null | wc -l else echo "0" fi } # Helper: List vhosts list_vhosts() { json_add_array "vhosts" if [ -d "$NGINX_VHOSTS_DIR" ]; then for conf in "$NGINX_VHOSTS_DIR"/*.conf; do [ -f "$conf" ] || continue # Extract server_name from config SERVER_NAME=$(grep -m1 "server_name" "$conf" | awk '{print $2}' | tr -d ';') PROXY_PASS=$(grep -m1 "proxy_pass" "$conf" | awk '{print $2}' | tr -d ';') SSL_ENABLED="false" if grep -q "ssl_certificate" "$conf"; then SSL_ENABLED="true" fi json_add_object "" json_add_string "name" "$(basename "$conf" .conf)" json_add_string "domain" "$SERVER_NAME" json_add_string "backend" "$PROXY_PASS" json_add_boolean "ssl" "$SSL_ENABLED" json_add_boolean "enabled" 1 json_close_object done fi json_close_array } # Helper: Get SSL certificates list_certificates() { json_add_array "certificates" if [ -d "$ACME_DIR" ]; then for cert_dir in "$ACME_DIR"/*/; do [ -d "$cert_dir" ] || continue DOMAIN=$(basename "$cert_dir") CERT_FILE="$cert_dir/fullchain.cer" if [ -f "$CERT_FILE" ]; then # Get expiry date EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_FILE" 2>/dev/null | cut -d= -f2) json_add_object "" json_add_string "domain" "$DOMAIN" json_add_string "expiry" "$EXPIRY" json_add_boolean "valid" 1 json_close_object fi done fi json_close_array } # Initialize JSON json_init case "$1" in list) # List available methods json_add_object "status" json_close_object json_add_object "get_vhosts" json_close_object json_add_object "get_vhost" json_add_string "name" "string" json_close_object json_add_object "add_vhost" json_add_string "domain" "string" json_add_string "backend" "string" json_add_boolean "ssl" false json_close_object json_add_object "delete_vhost" json_add_string "name" "string" json_close_object json_add_object "get_certificates" json_close_object json_add_object "request_certificate" json_add_string "domain" "string" json_close_object json_add_object "reload_nginx" json_close_object json_add_object "test_config" json_close_object json_dump ;; call) case "$2" in status) # Return module status json_add_string "module" "vhost-manager" json_add_string "version" "2.0.0" json_add_string "nginx_status" "$(get_nginx_status)" json_add_boolean "nginx_running" $(nginx_running && echo 1 || echo 0) json_add_int "vhost_count" "$(count_vhosts)" json_add_string "config_dir" "$NGINX_VHOSTS_DIR" json_add_string "acme_dir" "$ACME_DIR" # Check nginx version if command -v nginx > /dev/null 2>&1; then NGINX_VERSION=$(nginx -v 2>&1 | cut -d/ -f2) json_add_string "nginx_version" "$NGINX_VERSION" fi json_dump ;; get_vhosts) # Return list of vhosts list_vhosts json_dump ;; get_vhost) # Get single vhost details read -r input json_load "$input" json_get_var vhost_name name CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf" if [ -f "$CONF_FILE" ]; then json_add_boolean "found" 1 json_add_string "name" "$vhost_name" json_add_string "config" "$(cat "$CONF_FILE")" else json_add_boolean "found" 0 json_add_string "error" "VHost not found" fi json_dump ;; add_vhost) # Add new vhost read -r input json_load "$input" json_get_var domain domain json_get_var backend backend json_get_var ssl ssl # Validate if [ -z "$domain" ] || [ -z "$backend" ]; then json_init json_add_boolean "success" 0 json_add_string "error" "Domain and backend are required" json_dump exit 0 fi # Create config VHOST_NAME=$(echo "$domain" | tr '.' '-') CONF_FILE="$NGINX_VHOSTS_DIR/${VHOST_NAME}.conf" mkdir -p "$NGINX_VHOSTS_DIR" cat > "$CONF_FILE" << NGINX_EOF server { listen 80; listen [::]:80; server_name $domain; location / { proxy_pass $backend; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; } } NGINX_EOF json_init json_add_boolean "success" 1 json_add_string "message" "VHost created" json_add_string "config_file" "$CONF_FILE" json_dump ;; delete_vhost) # Delete vhost read -r input json_load "$input" json_get_var vhost_name name CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf" if [ -f "$CONF_FILE" ]; then rm -f "$CONF_FILE" json_init json_add_boolean "success" 1 json_add_string "message" "VHost deleted" else json_init json_add_boolean "success" 0 json_add_string "error" "VHost not found" fi json_dump ;; get_certificates) # List SSL certificates list_certificates json_dump ;; request_certificate) # Request Let's Encrypt certificate read -r input json_load "$input" json_get_var domain domain if [ -z "$domain" ]; then json_init json_add_boolean "success" 0 json_add_string "error" "Domain is required" json_dump exit 0 fi # Check if acme.sh is available if command -v acme.sh > /dev/null 2>&1; then # Request certificate (async - just start the process) acme.sh --issue -d "$domain" --webroot /www --keylength ec-256 & json_init json_add_boolean "success" 1 json_add_string "message" "Certificate request started" json_add_string "domain" "$domain" else json_init json_add_boolean "success" 0 json_add_string "error" "acme.sh not installed" fi json_dump ;; reload_nginx) # Reload nginx configuration if nginx -t > /dev/null 2>&1; then /etc/init.d/nginx reload json_add_boolean "success" 1 json_add_string "message" "Nginx reloaded" else json_add_boolean "success" 0 json_add_string "error" "Configuration test failed" json_add_string "details" "$(nginx -t 2>&1)" fi json_dump ;; test_config) # Test nginx configuration TEST_OUTPUT=$(nginx -t 2>&1) TEST_RESULT=$? if [ $TEST_RESULT -eq 0 ]; then json_add_boolean "valid" 1 json_add_string "message" "Configuration OK" else json_add_boolean "valid" 0 json_add_string "error" "$TEST_OUTPUT" fi json_dump ;; *) # Unknown method json_add_int "error" -32601 json_add_string "message" "Method not found: $2" json_dump ;; esac ;; *) echo "Usage: $0 {list|call}" exit 1 ;; esac