pulse-zax/scripts/install-pulse-clean.sh

200 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
log() { echo "[pulse-install] $*"; }
die() { echo "[pulse-install] ERROR: $*" >&2; exit 1; }
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
die "Run as root (or with sudo)."
fi
read -r -p "Reinstall from scratch (will delete /opt/pulse and ALL volumes)? [y/N]: " WIPE
WIPE="${WIPE:-N}"
PULSE_DIR="/opt/pulse"
if [[ "${WIPE}" =~ ^[Yy]$ ]]; then
log "Stopping and removing existing Pulse stack (if any)..."
if [ -d "${PULSE_DIR}" ]; then
cd "${PULSE_DIR}" || true
if [ -f docker-compose.su pawse.yml ]; then
true
fi
if [ -f docker-compose-supabase.yml ]; then
COMPOSE_FILES="-f docker-compose-supabase.yml"
if [ -f docker-compose.override.yml ]; then
COMPOSE_FILES="$COMPOSE_FILES -f docker-compose.override.yml"
fi
if [ -f docker-compose.noise.yml ]; then
COMPOSE_FILES="$COMPOSE_FILES -f docker-compose.noise.yml"
fi
docker compose $COMPOSE_FILES down -v --remove-orphans || true
fi
fi
rm -rf "${PULSE_DIR}"
fi
read -r -p "Domain for Pulse (e.g. chat.example.com): " DOMAIN
if [ -z "${DOMAIN}" ]; then
die "Domain is required."
fi
read -r -p "Enable Caddy HTTPS proxy? [y/N]: " ENABLE_CADDY
ENABLE_CADDY="${ENABLE_CADDY:-N}"
if [[ "${ENABLE_CADDY}" =~ ^[Yy]$ ]]; then
read -r -p "TLS email (for Let's Encrypt): " TLS_EMAIL
if [ -z "${TLS_EMAIL}" ]; then
die "TLS email is required when enabling Caddy."
fi
fi
read -r -p "Postgres password (leave blank to generate): " POSTGRES_PASSWORD
if [ -z "${POSTGRES_PASSWORD}" ]; then
POSTGRES_PASSWORD="$(openssl rand -hex 16)"
log "Generated Postgres password: ${POSTGRES_PASSWORD}"
fi
read -r -p "Pulse port [4991]: " PULSE_PORT
PULSE_PORT="${PULSE_PORT:-4991}"
read -r -p "Public IP (optional; leave blank to auto-detect): " PUBLIC_IP
if [ -z "${PUBLIC_IP}" ]; then
PUBLIC_IP="$(curl -fsSL https://api.ipify.org || true)"
if [ -n "${PUBLIC_IP}" ]; then
log "Detected public IP: ${PUBLIC_IP}"
else
log "Public IP not set."
fi
fi
log "Installing base packages..."
apt-get update -y
apt-get install -y ca-certificates curl gnupg git ufw openssl
if ! command -v docker >/dev/null 2>&1; then
log "Installing Docker..."
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
. /etc/os-release
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian ${VERSION_CODENAME} stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update -y
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
fi
if ! command -v bun >/dev/null 2>&1; then
log "Installing Bun..."
curl -fsSL https://bun.sh/install | bash
fi
export BUN_INSTALL="${BUN_INSTALL:-/root/.bun}"
export PATH="${BUN_INSTALL}/bin:${PATH}"
log "Cloning Pulse..."
mkdir -p "${PULSE_DIR}"
if [ -d "${PULSE_DIR}/.git" ]; then
git -C "${PULSE_DIR}" fetch --all --prune
git -C "${PULSE_DIR}" reset --hard origin/main
else
git clone https://github.com/plsechat/pulse-chat.git "${PULSE_DIR}"
fi
cd "${PULSE_DIR}"
log "Generating JWT and Supabase keys..."
KEY_OUTPUT="$(bun docker/generate-keys.ts)"
JWT_SECRET="$(echo "${KEY_OUTPUT}" | grep -E '^JWT_SECRET=' | head -n1 | cut -d'=' -f2-)"
SUPABASE_ANON_KEY="$(echo "${KEY_OUTPUT}" | grep -E '^SUPABASE_ANON_KEY=' | head -n1 | cut -d'=' -f2-)"
SUPABASE_SERVICE_ROLE_KEY="$(echo "${KEY_OUTPUT}" | grep -E '^SUPABASE_SERVICE_ROLE_KEY=' | head -n1 | cut -d'=' -f2-)"
if [ -z "${JWT_SECRET}" ] || [ -z "${SUPABASE_ANON_KEY}" ] || [ -z "${SUPABASE_SERVICE_ROLE_KEY}" ]; then
die "Failed to parse generated keys."
fi
log "Writing .env..."
cp -f .env.supabase.example .env
sed -i \
-e "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=${POSTGRES_PASSWORD}|" \
-e "s|^JWT_SECRET=.*|JWT_SECRET=${JWT_SECRET}|" \
-e "s|^SUPABASE_ANON_KEY=.*|SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}|" \
-e "s|^SUPABASE_SERVICE_ROLE_KEY=.*|SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY}|" \
-e "s|^SITE_URL=.*|SITE_URL=https://${DOMAIN}|" \
.env
if [ -n "${PUBLIC_IP}" ]; then
if grep -q '^PUBLIC_IP=' .env; then
sed -i "s|^PUBLIC_IP=.*|PUBLIC_IP=${PUBLIC_IP}|" .env
else
echo "PUBLIC_IP=${PUBLIC_IP}" >> .env
fi
fi
if grep -q '^PULSE_PORT=' .env; then
sed -i "s|^PULSE_PORT=.*|PULSE_PORT=${PULSE_PORT}|" .env
else
echo "PULSE_PORT=${PULSE_PORT}" >> .env
fi
if [[ "${ENABLE_CADDY}" =~ ^[Yy]$ ]]; then
log "Writing Caddyfile and compose override..."
cat > "${PULSE_DIR}/Caddyfile" <<EOF
${DOMAIN} {
encode gzip
reverse_proxy pulse:4991
tls ${TLS_EMAIL}
}
EOF
cat > "${PULSE_DIR}/docker-compose.override.yml" <<'EOF'
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
EOF
else
rm -f "${PULSE_DIR}/docker-compose.override.yml" "${PULSE_DIR}/Caddyfile" || true
fi
log "Configuring firewall (ufw)..."
ufw allow OpenSSH >/dev/null 2>&1 || true
if [[ "${ENABLE_CADDY}" =~ ^[Yy]$ ]]; then
ufw allow 80/tcp >/dev/null 2>&1 || true
ufw allow 443/tcp >/dev/null 2>&1 || true
ufw allow 443/udp >/dev/null 2>&1 || true
else
ufw allow "${PULSE_PORT}/tcp" >/dev/null 2>&1 || true
fi
ufw allow 40000:40020/tcp >/dev/null 2>&1 || true
ufw allow 40000:40020/udp >/dev/null 2>&1 || true
ufw --force enable >/dev/null 2>&1 || true
log "Starting Pulse (Supabase stack)..."
COMPOSE_FILES="-f docker-compose-supabase.yml"
if [ -f docker-compose.override.yml ]; then
COMPOSE_FILES="$COMPOSE_FILES -f docker-compose.override.yml"
fi
docker compose $COMPOSE_FILES up -d --build
log "Done."
if [[ "${ENABLE_CADDY}" =~ ^[Yy]$ ]]; then
log "Pulse: https://${DOMAIN}"
else
log "Pulse: http://${DOMAIN}:${PULSE_PORT}"
fi
log "If this is the first run, watch logs to get the security token:"
log " docker logs -f pulse | head -n 200"