200 lines
6.0 KiB
Bash
Executable File
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"
|