#!/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' Self-hosted Mermaid Editor
HTML # 4. Caddy config (HTTP on $PORT) log "Configuring Caddy..." cat > /etc/caddy/Caddyfile </dev/null || true ufw allow $PORT/tcp 2>/dev/null || true # 7. Wait + check log "Waiting for Caddy to start..." sleep 5 if curl -sf "http://localhost:$PORT" >/dev/null; then log "Mermaid Editor is live!" else warn "Caddy failed — check 'rc-service caddy status' and 'journalctl -u caddy'" exit 1 fi # 8. Output summary IP=$(hostname -I 2>/dev/null | cut -d' ' -f1) if [[ -z "$IP" ]]; then IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}') fi log "URL: ${BLUE}http://${IP}:${PORT}${NC}" log "Root password: ${YELLOW}$ROOT_PASSWORD${NC}" log "SSH: ssh root@${IP}" EOF # Push & run install script pct push $LXC_ID /tmp/mermaid-install.sh /root/mermaid-install.sh || die "Failed to push script" pct exec $LXC_ID -- bash /root/mermaid-install.sh "$LXC_PORT" "$LXC_ROOT_PASSWORD" || die "Install failed" # Cleanup rm -f /tmp/mermaid-install.sh # === 3. Final Summary === IP_ADDR=$(pct exec $LXC_ID -- hostname -I 2>/dev/null | cut -d' ' -f1) [[ -z "$IP_ADDR" ]] && IP_ADDR=$(pct exec $LXC_ID -- ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}') log "Mermaid Editor LXC ($LXC_ID) ready!" log "URL: ${BLUE}http://${IP_ADDR}:${LXC_PORT}${NC}" log "Root password: ${YELLOW}$LXC_ROOT_PASSWORD${NC}"