Update mermaids.sh

This commit is contained in:
Dan 2026-04-16 00:13:19 +08:00
parent d2f9a7388a
commit e9149c49ff

View File

@ -1,4 +1,6 @@
#!/bin/bash #!/bin/bash
# ct/mermaidjs.sh - Create and configure a self-hosted Mermaid.js editor LXC
set -euo pipefail set -euo pipefail
RED='\033[0;31m' RED='\033[0;31m'
@ -11,171 +13,255 @@ log() { echo -e "${GREEN}[$(date +%H:%M:%S)]${NC} $1"; }
warn() { echo -e "${YELLOW}[$(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; } die() { echo -e "${RED}[$(date +%H:%M:%S)]${NC} $1"; exit 1; }
if [[ $EUID -ne 0 ]] || [[ ! -f /etc/pve/nodes ]]; then # 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" die "This script must be run as root on a Proxmox VE host"
fi fi
LXC_HOSTNAME="mermaid" # === Configuration Defaults ===
LXC_ID="" LXC_ID=""
LXC_MEMORY=256 LXC_HOSTNAME="mermaid"
LXC_MEMORY=512
LXC_CORES=1 LXC_CORES=1
LXC_DISK="local-lvm" LXC_DISK="local-lvm"
LXC_TEMPLATE="local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst" LXC_TEMPLATE="local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst"
LXC_ROOT_PASSWORD="mermaid" LXC_ROOT_PASSWORD="mermaid"
LXC_IP="" LXC_IP=""
LXC_NETMASK="24" LXC_NETMASK="24"
LXC_GATEWAY="" LXC_GATEWAY=""
LXC_BRIDGE="vmbr0" LXC_BRIDGE="vmbr0"
LXC_PORT="80" # HTTP port — change if needed (e.g., 8080)
# Auto-increment VMID
get_next_id() { get_next_id() {
local max=0 local max=0
for id in $(pct list | awk 'NR>1 {print $1}'); do ((id > max)) && max=$id; done for id in $(pct list | awk 'NR>1 {print $1}'); do
((id > max)) && max=$id
done
echo $((max + 1)) echo $((max + 1))
} }
log "=== Mermaid Editor LXC Setup (Alpine + Caddy) ===" 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 read -rp "Hostname (default: mermaid): " HOSTNAME_INPUT
[[ -n "$HOSTNAME_INPUT" ]] && LXC_HOSTNAME="$HOSTNAME_INPUT" [[ -n "$HOSTNAME_INPUT" ]] && LXC_HOSTNAME="$HOSTNAME_INPUT"
read -rp "Container ID (default: $(get_next_id)): " ID_INPUT read -rp "Container ID (default: $(get_next_id)): " ID_INPUT
LXC_ID=${ID_INPUT:-$(get_next_id)} LXC_ID=${ID_INPUT:-$(get_next_id)}
read -rp "Memory (MB, default: 256): " MEM_INPUT
LXC_MEMORY=${MEM_INPUT:-256} read -rp "Memory (MB, default: 512): " MEM_INPUT
LXC_MEMORY=${MEM_INPUT:-512}
read -rp "CPU Cores (default: 1): " CORES_INPUT read -rp "CPU Cores (default: 1): " CORES_INPUT
LXC_CORES=${CORES_INPUT:-1} LXC_CORES=${CORES_INPUT:-1}
read -rp "Root Password (default: mermaid): " PASS_INPUT read -rp "Root Password (default: mermaid): " PASS_INPUT
LXC_ROOT_PASSWORD=${PASS_INPUT:-mermaid} LXC_ROOT_PASSWORD=${PASS_INPUT:-mermaid}
read -rp "Disk Pool (default: local-lvm): " DISK_INPUT
read -rp "Disk Storage Pool (default: local-lvm): " DISK_INPUT
LXC_DISK=${DISK_INPUT:-local-lvm} LXC_DISK=${DISK_INPUT:-local-lvm}
read -rp "Bridge (default: vmbr0): " BRIDGE_INPUT
read -rp "Network Bridge (default: vmbr0): " BRIDGE_INPUT
LXC_BRIDGE=${BRIDGE_INPUT:-vmbr0} LXC_BRIDGE=${BRIDGE_INPUT:-vmbr0}
read -rp "IP (blank for DHCP): " IP_INPUT
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" LXC_IP="$IP_INPUT"
[[ -n "$LXC_IP" ]] && {
if [[ -n "$LXC_IP" ]]; then
read -rp "Netmask (default: 24): " NETMASK_INPUT read -rp "Netmask (default: 24): " NETMASK_INPUT
LXC_NETMASK=${NETMASK_INPUT:-24} LXC_NETMASK=${NETMASK_INPUT:-24}
read -rp "Gateway (blank): " GW_INPUT read -rp "Gateway (blank for none): " GW_INPUT
LXC_GATEWAY="$GW_INPUT" LXC_GATEWAY="$GW_INPUT"
}
NET0_ARGS="name=eth0,bridge=$LXC_BRIDGE"
if [[ -n "$LXC_IP" ]]; then
NET0_ARGS="$NET0_ARGS,ip=$LXC_IP/$LXC_NETMASK"
[[ -n "$LXC_GATEWAY" ]] && NET0_ARGS="$NET0_ARGS,gw=$LXC_GATEWAY"
fi fi
# Fail fast if bridge or disk don't exist # === 1. Create LXC Container ===
pvesm status | grep -q "$LXC_DISK" || die "Storage pool '$LXC_BRIDGE' not found" log "Creating Debian 12 LXC container $LXC_ID ($LXC_HOSTNAME)..."
ip link show "$LXC_BRIDGE" >/dev/null 2>&1 || die "Bridge '$LXC_BRIDGE' not found"
log "Creating LXC container $LXC_ID ($LXC_HOSTNAME)..." # Build network config safely
pct create "$LXC_ID" "$LXC_TEMPLATE" \ if [[ -n "$LXC_IP" ]]; then
-hostname "$LXC_HOSTNAME" \ if [[ -z "$LXC_GATEWAY" ]]; then
-memory "$LXC_MEMORY" \ warn "IP provided without gateway — using DHCP for gateway"
-cores "$LXC_CORES" \ NET_CONFIG="dhcp=1"
-net0 "$NET0_ARGS" \ else
-rootfs "$LXC_DISK:1" \ NET_CONFIG="ip=$LXC_IP/$LXC_NETMASK,gw=$LXC_GATEWAY"
fi
else
NET_CONFIG="dhcp=1"
fi
pct create $LXC_ID $LXC_TEMPLATE \
-hostname $LXC_HOSTNAME \
-memory $LXC_MEMORY \
-cores $LXC_CORES \
-net0 name=eth0,bridge=$LXC_BRIDGE,$NET_CONFIG \
-rootfs $LXC_DISK:2 \
-ostype debian \ -ostype debian \
-password "$LXC_ROOT_PASSWORD" \ -password $LXC_ROOT_PASSWORD \
-onboot 1 -onboot 1 \
-features nesting=1 \
-unprivileged 0
log "Starting container..." log "Starting container..."
pct start "$LXC_ID" pct start $LXC_ID
log "Waiting for container to be reachable..."
for i in {1..40}; do log "Waiting for container to be reachable (max 60s)..."
pct exec "$LXC_ID" -- sh -c "ping -c1 127.0.0.1 >/dev/null 2>&1" && break 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 sleep 1
done done
log "Deploying Mermaid editor (offline + interactive UI)..." # === 2. Prepare and Deploy Install Script ===
pct exec "$LXC_ID" -- sh -c " log "Deploying Mermaid editor + Caddy..."
set -euo pipefail
GREEN='\033[0;32m'; NC='\033[0m'
log() { echo -e \"\${GREEN}[\$(date +%H:%M:%S)]\${NC} \$1\"; }
# 1. Update + install deps cat > /tmp/mermaid-install.sh <<'EOF'
log 'Updating system...' #!/bin/bash
apk update >/dev/null 2>&1 set -euo pipefail
apk add caddy curl >/dev/null 2>&1
# 2. Download Mermaid (offline) GREEN='\033[0;32m'
log 'Downloading Mermaid v10.6.1...' YELLOW='\033[1;33m'
mkdir -p /var/www/mermaid BLUE='\033[0;34m'
wget -q -O /var/www/mermaid/mermaid.min.js \ NC='\033[0m'
https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js
# 3. Create editor HTML (split editor + preview) log() { echo -e "${GREEN}[$(date +%H:%M:%S)]${NC} $1"; }
log 'Creating interactive editor interface...'
cat > /var/www/mermaid/index.html <<'HTMLEOF' PORT="${1:-80}"
# 1. Update & install Caddy + deps
log "Updating system..."
apt-get update -qq
apt-get install -y -qq wget ufw
# 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'
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset='utf-8'> <meta charset="utf-8">
<title>Self-hosted Mermaid Editor</title> <title>Self-hosted Mermaid Editor</title>
<script src='/mermaid.min.js'></script> <script src="/mermaid.min.js"></script>
<style> <style>
body { margin:0; display:grid; grid-template-columns:1fr 1fr; height:100vh; } body {
textarea { margin: 0;
width:100%; height:100%; padding:1rem; font-family:monospace; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size:14px; border:none; resize:none; display: grid;
background:#2d2d2d; color:#f8f8f2; grid-template-columns: 1fr 1fr;
height: 100vh;
background: #f0f0f0;
} }
#preview { padding:1rem; overflow:auto; border-left:1px solid #ddd; background:#fff; } textarea {
.mermaid { max-width:90%; margin:0 auto; } width: 100%;
height: 100%;
padding: 1rem;
font-family: 'JetBrains Mono', Consolas, monospace;
font-size: 14px;
border: none;
resize: none;
background: #2d2d2d;
color: #f8f8f2;
}
#preview {
padding: 1rem;
overflow: auto;
border-left: 1px solid #ccc;
background: #fff;
}
.mermaid { max-width: 90%; margin: 0 auto; }
</style> </style>
<script> <script>
mermaid.initialize({startOnLoad:false,securityLevel:'loose'}); mermaid.initialize({startOnLoad: false, securityLevel: 'loose'});
const editor = document.querySelector('textarea'); const editor = document.querySelector('textarea');
const preview = document.getElementById('preview'); const preview = document.getElementById('preview');
editor.addEventListener('input', () => { editor.addEventListener('input', () => {
const code = editor.value.trim(); const code = editor.value.trim();
if (code) { if (code) {
mermaid.render('diagram', code, svg => preview.innerHTML = svg); mermaid.render('diagram', code, svg => preview.innerHTML = svg);
} else { } else {
preview.innerHTML = '<p style=\"color:#999;\">Type Mermaid code here...</p>'; preview.innerHTML = '<p style="color:#999;">Type Mermaid code here...</p>';
} }
}); });
editor.value = 'graph TD\n A[Start] --> B{Decision?}\n B -->|Yes| C[Do A]\n B -->|No| D[Do B]\n C --> E\n D --> E';
editor.dispatchEvent(new Event('input')); // Initial render
editor.value = `graph TD
A[Start] --> B{Decision?}
B -->|Yes| C[Do A]
B -->|No| D[Do B]
C --> E[End]
D --> E`;
input();
</script> </script>
</head> </head>
<body> <body>
<textarea spellcheck='false'></textarea> <textarea spellcheck="false"></textarea>
<div id='preview'></div> <div id="preview"></div>
</body> </body>
</html> </html>
HTMLEOF HTML
# 4. Caddy config (HTTP) # 4. Caddy config (HTTP on $PORT)
log 'Configuring Caddy...' log "Configuring Caddy..."
cat > /etc/caddy/Caddyfile <<'CADDYEOF' cat > /etc/caddy/Caddyfile <<CADDY
:80 { :$PORT {
root * /var/www/mermaid root * /var/www/mermaid
file_server file_server
log { output file /var/log/caddy/access.log } log {
output file /var/log/caddy/access.log
}
} }
CADDYEOF CADDY
# 5. Start Caddy # 5. Start & enable Caddy
rc-service caddy start >/dev/null 2>&1 rc-service caddy start
rc-update add caddy >/dev/null 2>&1 rc-update add caddy
sleep 3
if curl -sf http://localhost >/dev/null; then # 6. Firewall
log 'Mermaid Editor is live!' ufw --force enable 2>/dev/null || true
else ufw allow $PORT/tcp 2>/dev/null || true
log 'Caddy may still be starting — check rc-service caddy status'
fi
# 6. Summary # 7. Wait + check
IP=$(hostname -I 2>/dev/null | cut -d' ' -f1) log "Waiting for Caddy to start..."
[[ -z \"$IP\" ]] && IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}') sleep 5
log \"URL: ${BLUE}http://${IP}${NC}\" if curl -sf "http://localhost:$PORT" >/dev/null; then
log \"Root password: ${YELLOW}$LXC_ROOT_PASSWORD${NC}\" log "Mermaid Editor is live!"
" else
warn "Caddy failed — check 'rc-service caddy status' and 'journalctl -u caddy'"
exit 1
fi
IP=$(pct exec "$LXC_ID" -- sh -c 'hostname -I 2>/dev/null | cut -d" " -f1') # 8. Output summary
[[ -z "$IP" ]] && IP=$(pct exec "$LXC_ID" -- sh -c "ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'") 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}$LXC_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" || 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 "Mermaid Editor LXC ($LXC_ID) ready!"
log "URL: ${BLUE}http://${IP}${NC}" log "URL: ${BLUE}http://${IP_ADDR}:${LXC_PORT}${NC}"
log "Root password: ${YELLOW}$LXC_ROOT_PASSWORD${NC}" log "Root password: ${YELLOW}$LXC_ROOT_PASSWORD${NC}"