181 lines
5.6 KiB
Bash
181 lines
5.6 KiB
Bash
#!/bin/bash
|
|
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; }
|
|
|
|
if [[ $EUID -ne 0 ]] || [[ ! -f /etc/pve/nodes ]]; then
|
|
die "This script must be run as root on a Proxmox VE host"
|
|
fi
|
|
|
|
LXC_HOSTNAME="mermaid"
|
|
LXC_ID=""
|
|
LXC_MEMORY=256
|
|
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"
|
|
|
|
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 Editor LXC Setup (Alpine + Caddy) ==="
|
|
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: 256): " MEM_INPUT
|
|
LXC_MEMORY=${MEM_INPUT:-256}
|
|
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 Pool (default: local-lvm): " DISK_INPUT
|
|
LXC_DISK=${DISK_INPUT:-local-lvm}
|
|
read -rp "Bridge (default: vmbr0): " BRIDGE_INPUT
|
|
LXC_BRIDGE=${BRIDGE_INPUT:-vmbr0}
|
|
read -rp "IP (blank for DHCP): " IP_INPUT
|
|
LXC_IP="$IP_INPUT"
|
|
[[ -n "$LXC_IP" ]] && {
|
|
read -rp "Netmask (default: 24): " NETMASK_INPUT
|
|
LXC_NETMASK=${NETMASK_INPUT:-24}
|
|
read -rp "Gateway (blank): " 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
|
|
|
|
# Fail fast if bridge or disk don't exist
|
|
pvesm status | grep -q "$LXC_DISK" || die "Storage pool '$LXC_BRIDGE' not found"
|
|
ip link show "$LXC_BRIDGE" >/dev/null 2>&1 || die "Bridge '$LXC_BRIDGE' not found"
|
|
|
|
log "Creating LXC container $LXC_ID ($LXC_HOSTNAME)..."
|
|
pct create "$LXC_ID" "$LXC_TEMPLATE" \
|
|
-hostname "$LXC_HOSTNAME" \
|
|
-memory "$LXC_MEMORY" \
|
|
-cores "$LXC_CORES" \
|
|
-net0 "$NET0_ARGS" \
|
|
-rootfs "$LXC_DISK:1" \
|
|
-ostype debian \
|
|
-password "$LXC_ROOT_PASSWORD" \
|
|
-onboot 1
|
|
|
|
log "Starting container..."
|
|
pct start "$LXC_ID"
|
|
log "Waiting for container to be reachable..."
|
|
for i in {1..40}; do
|
|
pct exec "$LXC_ID" -- sh -c "ping -c1 127.0.0.1 >/dev/null 2>&1" && break
|
|
sleep 1
|
|
done
|
|
|
|
log "Deploying Mermaid editor (offline + interactive UI)..."
|
|
pct exec "$LXC_ID" -- sh -c "
|
|
set -euo pipefail
|
|
GREEN='\033[0;32m'; NC='\033[0m'
|
|
log() { echo -e \"\${GREEN}[\$(date +%H:%M:%S)]\${NC} \$1\"; }
|
|
|
|
# 1. Update + install deps
|
|
log 'Updating system...'
|
|
apk update >/dev/null 2>&1
|
|
apk add caddy curl >/dev/null 2>&1
|
|
|
|
# 2. Download Mermaid (offline)
|
|
log 'Downloading Mermaid v10.6.1...'
|
|
mkdir -p /var/www/mermaid
|
|
wget -q -O /var/www/mermaid/mermaid.min.js \
|
|
https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js
|
|
|
|
# 3. Create editor HTML (split editor + preview)
|
|
log 'Creating interactive editor interface...'
|
|
cat > /var/www/mermaid/index.html <<'HTMLEOF'
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset='utf-8'>
|
|
<title>Self-hosted Mermaid Editor</title>
|
|
<script src='/mermaid.min.js'></script>
|
|
<style>
|
|
body { margin:0; display:grid; grid-template-columns:1fr 1fr; height:100vh; }
|
|
textarea {
|
|
width:100%; height:100%; padding:1rem; font-family:monospace;
|
|
font-size:14px; border:none; resize:none;
|
|
background:#2d2d2d; color:#f8f8f2;
|
|
}
|
|
#preview { padding:1rem; overflow:auto; border-left:1px solid #ddd; background:#fff; }
|
|
.mermaid { max-width:90%; margin:0 auto; }
|
|
</style>
|
|
<script>
|
|
mermaid.initialize({startOnLoad:false,securityLevel:'loose'});
|
|
const editor = document.querySelector('textarea');
|
|
const preview = document.getElementById('preview');
|
|
editor.addEventListener('input', () => {
|
|
const code = editor.value.trim();
|
|
if (code) {
|
|
mermaid.render('diagram', code, svg => preview.innerHTML = svg);
|
|
} else {
|
|
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'));
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<textarea spellcheck='false'></textarea>
|
|
<div id='preview'></div>
|
|
</body>
|
|
</html>
|
|
HTMLEOF
|
|
|
|
# 4. Caddy config (HTTP)
|
|
log 'Configuring Caddy...'
|
|
cat > /etc/caddy/Caddyfile <<'CADDYEOF'
|
|
:80 {
|
|
root * /var/www/mermaid
|
|
file_server
|
|
log { output file /var/log/caddy/access.log }
|
|
}
|
|
CADDYEOF
|
|
|
|
# 5. Start Caddy
|
|
rc-service caddy start >/dev/null 2>&1
|
|
rc-update add caddy >/dev/null 2>&1
|
|
sleep 3
|
|
|
|
if curl -sf http://localhost >/dev/null; then
|
|
log 'Mermaid Editor is live!'
|
|
else
|
|
log 'Caddy may still be starting — check rc-service caddy status'
|
|
fi
|
|
|
|
# 6. Summary
|
|
IP=$(hostname -I 2>/dev/null | cut -d' ' -f1)
|
|
[[ -z \"$IP\" ]] && IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
|
|
log \"URL: ${BLUE}http://${IP}${NC}\"
|
|
log \"Root password: ${YELLOW}$LXC_ROOT_PASSWORD${NC}\"
|
|
"
|
|
|
|
IP=$(pct exec "$LXC_ID" -- sh -c 'hostname -I 2>/dev/null | cut -d" " -f1')
|
|
[[ -z "$IP" ]] && IP=$(pct exec "$LXC_ID" -- sh -c "ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'")
|
|
|
|
log "Mermaid Editor LXC ($LXC_ID) ready!"
|
|
log "URL: ${BLUE}http://${IP}${NC}"
|
|
log "Root password: ${YELLOW}$LXC_ROOT_PASSWORD${NC}" |