#!/bin/bash # ct/mermaidjs.sh - Create and configure a self-hosted Mermaid.js editor LXC set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${GREEN}[$(date +%H:%M:%S)]${NC} $1"; } warn() { echo -e "${YELLOW}[$(date +%H:%M:%S)]${NC} $1"; } die() { echo -e "${RED}[$(date +%H:%M:%S)]${NC} $1"; exit 1; } # Ensure root on Proxmox VE if [[ $EUID -ne 0 ]] || ! command -v pct &>/dev/null; then die "This script must be run as root on a Proxmox VE host" fi # === Configuration Defaults === LXC_ID="" LXC_HOSTNAME="mermaid" LXC_MEMORY=512 LXC_CORES=1 LXC_DISK="local-lvm" LXC_TEMPLATE="local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst" LXC_ROOT_PASSWORD="mermaid" LXC_IP="" LXC_NETMASK="24" LXC_GATEWAY="" LXC_BRIDGE="vmbr0" LXC_PORT="80" # HTTP port — change if needed (e.g., 8080) # Auto-increment VMID get_next_id() { local max=0 for id in $(pct list | awk 'NR>1 {print $1}'); do ((id > max)) && max=$id done echo $((max + 1)) } log "=== Mermaid.js Interactive Editor LXC Setup (Debian 12) ===" log "This creates a container with a live-editable Mermaid editor (offline-capable)." # === Interactive Prompts === read -rp "Hostname (default: mermaid): " HOSTNAME_INPUT [[ -n "$HOSTNAME_INPUT" ]] && LXC_HOSTNAME="$HOSTNAME_INPUT" read -rp "Container ID (default: $(get_next_id)): " ID_INPUT LXC_ID=${ID_INPUT:-$(get_next_id)} read -rp "Memory (MB, default: 512): " MEM_INPUT LXC_MEMORY=${MEM_INPUT:-512} read -rp "CPU Cores (default: 1): " CORES_INPUT LXC_CORES=${CORES_INPUT:-1} read -rp "Root Password (default: mermaid): " PASS_INPUT LXC_ROOT_PASSWORD=${PASS_INPUT:-mermaid} read -rp "Disk Storage Pool (default: local-lvm): " DISK_INPUT LXC_DISK=${DISK_INPUT:-local-lvm} read -rp "Network Bridge (default: vmbr0): " BRIDGE_INPUT LXC_BRIDGE=${BRIDGE_INPUT:-vmbr0} read -rp "HTTP Port (default: 80): " PORT_INPUT LXC_PORT=${PORT_INPUT:-80} read -rp "IP Address (e.g., 192.168.1.60; leave blank for DHCP): " IP_INPUT LXC_IP="$IP_INPUT" if [[ -n "$LXC_IP" ]]; then read -rp "Netmask (default: 24): " NETMASK_INPUT LXC_NETMASK=${NETMASK_INPUT:-24} read -rp "Gateway (blank for none): " GW_INPUT LXC_GATEWAY="$GW_INPUT" fi # === 1. Create LXC Container === log "Creating Debian 12 LXC container $LXC_ID ($LXC_HOSTNAME)..." # Build NET_CONFIG safely BEFORE pct create if [[ -n "$LXC_IP" && -n "$LXC_GATEWAY" ]]; then NET0_ARGS="name=eth0,bridge=$LXC_BRIDGE,ip=$LXC_IP/$LXC_NETMASK,gw=$LXC_GATEWAY" elif [[ -z "$LXC_GATEWAY" && -n "$LXC_IP" ]]; then NET0_ARGS="name=eth0,bridge=$LXC_BRIDGE,ip=dhcp" else NET0_ARGS="name=eth0,bridge=$LXC_BRIDGE,ip=dhcp" fi echo "DEBUG: NET0_ARGS = $NET0_ARGS" pct create $LXC_ID $LXC_TEMPLATE \ -hostname "$LXC_HOSTNAME" \ -memory "$LXC_MEMORY" \ -cores "$LXC_CORES" \ -net0 "$NET0_ARGS" \ -rootfs "$LXC_DISK:2" \ -ostype debian \ -password "$LXC_ROOT_PASSWORD" \ -onboot 1 \ -features nesting=1 log "Starting container..." pct start $LXC_ID log "Waiting for container to be reachable (max 60s)..." for i in {1..60}; do if pct exec $LXC_ID -- sh -c "ping -c1 127.0.0.1 >/dev/null 2>&1"; then break fi sleep 1 done # === 2. Prepare and Deploy Install Script === log "Deploying Mermaid editor + Caddy..." cat > /tmp/mermaid-install.sh <<'EOF' #!/bin/bash set -euo pipefail # === Define functions FIRST === GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${GREEN}[$(date +%H:%M:%S)]${NC} $1"; } warn() { echo -e "${YELLOW}[$(date +%H:%M:%S)]${NC} $1"; } die() { echo -e "${RED}[$(date +%H:%M:%S)]${NC} $1"; exit 1; } PORT="${1:-80}" ROOT_PASSWORD="${2:-mermaid}" # === Now run operations === log "Installing dependencies..." apt-get install -y -qq \ wget \ ufw \ curl \ caddy \ apt-transport-https \ ca-certificates # 2. Download Mermaid (offline bundled) log "Downloading Mermaid v10.6.1 (offline)..." mkdir -p /var/www/mermaid wget -q https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js -O /var/www/mermaid/mermaid.min.js # 3. Create the editor HTML (dark theme, live preview) log "Creating Mermaid editor interface..." cat > /var/www/mermaid/index.html <<'HTML'