mirror of
https://github.com/ancsemi/Haven.git
synced 2026-10-26 08:38:20 +00:00
205 lines
8.9 KiB
Bash
205 lines
8.9 KiB
Bash
#!/usr/bin/env bash
|
|
# ═══════════════════════════════════════════════════════════
|
|
# Haven: Cross-Platform Launcher (Linux / macOS)
|
|
# Usage: chmod +x start.sh && ./start.sh
|
|
# ═══════════════════════════════════════════════════════════
|
|
set -e
|
|
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
BOLD='\033[1m'
|
|
|
|
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
cd "$DIR"
|
|
|
|
# ── Data directory (~/.haven) ──────────────────────────────
|
|
HAVEN_DATA="${HAVEN_DATA_DIR:-$HOME/.haven}"
|
|
mkdir -p "$HAVEN_DATA"
|
|
|
|
echo ""
|
|
echo -e "${GREEN}${BOLD} ========================================${NC}"
|
|
echo -e "${GREEN}${BOLD} HAVEN: Private Chat Server${NC}"
|
|
echo -e "${GREEN}${BOLD} ========================================${NC}"
|
|
echo ""
|
|
|
|
# ── Check Node.js ──────────────────────────────────────────
|
|
if ! command -v node &> /dev/null; then
|
|
echo -e "${RED} [ERROR] Node.js is not installed.${NC}"
|
|
echo " Haven needs Node.js 22 or newer. Install it from https://nodejs.org or:"
|
|
echo " Ubuntu/Debian: curl -fsSL https://deb.nodesource.com/setup_26.x | sudo -E bash -"
|
|
echo " sudo apt install nodejs"
|
|
echo " macOS (brew): brew install node"
|
|
echo " Fedora: sudo dnf install nodejs"
|
|
echo " Arch: sudo pacman -S nodejs npm"
|
|
echo " (Plain 'apt install nodejs' gives Node 18 on Ubuntu 24.04, which is too old.)"
|
|
exit 1
|
|
fi
|
|
|
|
NODE_VER=$(node -v | sed 's/v//' | cut -d. -f1)
|
|
echo " [✓] Node.js $(node -v) detected"
|
|
|
|
if [ "$NODE_VER" -lt 22 ]; then
|
|
echo -e "${RED} [ERROR] Haven needs Node.js 22 or newer. You have $(node -v).${NC}"
|
|
echo " Install a current version from https://nodejs.org or NodeSource:"
|
|
echo " Ubuntu/Debian: curl -fsSL https://deb.nodesource.com/setup_26.x | sudo -E bash -"
|
|
echo " sudo apt install nodejs"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$NODE_VER" -ge 27 ]; then
|
|
echo -e "${YELLOW} [!] Node.js v${NODE_VER} detected. Haven is tested on Node 22-26.${NC}"
|
|
echo " Continuing. Native modules are verified functionally below."
|
|
elif [ "$NODE_VER" -ge 24 ]; then
|
|
echo " [*] Node.js v${NODE_VER}: verifying native modules load (below)."
|
|
fi
|
|
|
|
# ── Install dependencies ───────────────────────────────────
|
|
# Also after an update that changed them: npm records what it installed in
|
|
# node_modules/.package-lock.json, so a newer package-lock.json means new or
|
|
# changed dependencies to fetch.
|
|
if [ ! -d "node_modules" ]; then
|
|
echo " [*] First run: installing dependencies..."
|
|
npm install
|
|
echo ""
|
|
elif [ package-lock.json -nt node_modules/.package-lock.json ]; then
|
|
echo " [*] Dependencies changed, updating..."
|
|
npm install
|
|
echo ""
|
|
fi
|
|
|
|
# ── Functional gate: the version number matters less than whether the
|
|
# native module actually loads on THIS node. Same check the Windows
|
|
# launcher uses. A load failure gets one rebuild attempt first.
|
|
if ! node -e "require('better-sqlite3')" &> /dev/null; then
|
|
echo -e "${YELLOW} [!] better-sqlite3 failed to load on Node $(node -v), rebuilding...${NC}"
|
|
npm rebuild better-sqlite3 || true
|
|
if ! node -e "require('better-sqlite3')" &> /dev/null; then
|
|
echo -e "${RED} [ERROR] better-sqlite3 cannot load on Node $(node -v).${NC}"
|
|
echo " Fix options:"
|
|
echo " - Install Node 26 LTS (https://nodejs.org/), or"
|
|
echo " - Install C++ build tools and re-run:"
|
|
echo " Ubuntu/Debian: sudo apt install build-essential python3"
|
|
echo " Fedora: sudo dnf group install 'Development Tools'"
|
|
echo " then: npm rebuild better-sqlite3"
|
|
exit 1
|
|
fi
|
|
echo " [✓] Native modules rebuilt successfully"
|
|
fi
|
|
echo " [✓] Native modules OK"
|
|
|
|
# ── Create .env in data directory if missing ───────────────
|
|
if [ ! -f "$HAVEN_DATA/.env" ]; then
|
|
if [ -f ".env.example" ]; then
|
|
cp .env.example "$HAVEN_DATA/.env"
|
|
echo -e "${YELLOW} [!] Created .env in $HAVEN_DATA. Edit it before going live!${NC}"
|
|
else
|
|
echo -e "${YELLOW} [!] No .env file found. Server will use defaults.${NC}"
|
|
fi
|
|
fi
|
|
|
|
# ── Generate SSL certs in data directory if missing (skip if FORCE_HTTP=true) ──
|
|
if [ "${FORCE_HTTP:-false}" = "true" ]; then
|
|
echo " [*] FORCE_HTTP=true, skipping SSL certificate generation"
|
|
elif [ ! -f "$HAVEN_DATA/certs/cert.pem" ]; then
|
|
echo " [*] Generating self-signed SSL certificate..."
|
|
# Made with Node (scripts/gen-cert.js), the same as Start Haven.bat, so
|
|
# OpenSSL is not needed and its progress output stays off the screen.
|
|
HAVEN_DATA_DIR="$HAVEN_DATA" node scripts/gen-cert.js || true
|
|
|
|
if [ -f "$HAVEN_DATA/certs/cert.pem" ]; then
|
|
echo " [✓] SSL cert generated"
|
|
else
|
|
echo -e "${RED} [!] SSL certificate generation failed. See the output above.${NC}"
|
|
echo " Haven will run in HTTP mode."
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# ── Read PORT from .env (default 3000) ─────────────────────
|
|
HAVEN_PORT="${PORT:-3000}"
|
|
if [ -f "$HAVEN_DATA/.env" ]; then
|
|
ENV_PORT=$(grep -E '^PORT=' "$HAVEN_DATA/.env" 2>/dev/null | head -1 | cut -d= -f2)
|
|
if [ -n "$ENV_PORT" ]; then
|
|
HAVEN_PORT="$ENV_PORT"
|
|
fi
|
|
fi
|
|
|
|
echo " [*] Data directory: $HAVEN_DATA"
|
|
echo " [*] Starting Haven server..."
|
|
echo ""
|
|
|
|
# ── Start server ───────────────────────────────────────────
|
|
node server.js &
|
|
SERVER_PID=$!
|
|
|
|
# ── Wait for readiness ─────────────────────────────────────
|
|
# The probe runs on node itself: it is guaranteed present (it runs the
|
|
# server), unlike curl/wget which minimal server images often lack. The
|
|
# old curl-only probe silently failed on such systems every second for
|
|
# 15s and then KILLED a perfectly healthy server (the frontend was
|
|
# already serving) with a misleading "failed to start".
|
|
#
|
|
# Two rules now:
|
|
# 1. A dead server process is the only real startup failure, detected
|
|
# immediately, with a pointer at the real log.
|
|
# 2. A probe failure alone NEVER kills a live server. If readiness
|
|
# can't be confirmed after the window, we say so and leave it up.
|
|
probe_url() {
|
|
node -e 'const u=process.argv[1];const m=u.indexOf("https")===0?require("https"):require("http");const q=m.get(u,{rejectUnauthorized:false,timeout:1500},r=>{r.resume();process.exit(0)});q.on("error",()=>process.exit(1));q.on("timeout",()=>{q.destroy();process.exit(1)});' "$1" 2>/dev/null
|
|
}
|
|
|
|
PROBE_OK=0
|
|
for i in $(seq 1 30); do
|
|
sleep 1
|
|
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
|
echo ""
|
|
echo -e "${RED} [ERROR] The server process exited during startup.${NC}"
|
|
echo " The real reason is printed above, or in: $HAVEN_DATA/crash.log"
|
|
echo " Common causes: port ${HAVEN_PORT} already in use (another Haven still"
|
|
echo " running?), a broken install, or a bad .env value."
|
|
exit 1
|
|
fi
|
|
if probe_url "https://localhost:${HAVEN_PORT}/api/health" || \
|
|
probe_url "http://localhost:${HAVEN_PORT}/api/health"; then
|
|
PROBE_OK=1
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$PROBE_OK" -ne 1 ]; then
|
|
echo -e "${YELLOW} [!] Could not confirm readiness on port ${HAVEN_PORT} after 30s,${NC}"
|
|
echo -e "${YELLOW} but the server process is running, so leaving it up.${NC}"
|
|
echo " If it never becomes reachable, check $HAVEN_DATA/crash.log,"
|
|
echo " the PORT in $HAVEN_DATA/.env, and any firewall on ${HAVEN_PORT}."
|
|
fi
|
|
|
|
PORT=${HAVEN_PORT}
|
|
|
|
echo -e "${GREEN}${BOLD} ========================================${NC}"
|
|
echo -e "${GREEN}${BOLD} Haven is LIVE on port ${PORT}${NC}"
|
|
echo -e "${GREEN}${BOLD} ========================================${NC}"
|
|
echo ""
|
|
echo " Local: https://localhost:${PORT}"
|
|
echo " LAN: https://YOUR_LOCAL_IP:${PORT}"
|
|
echo " Remote: https://YOUR_PUBLIC_IP:${PORT}"
|
|
echo ""
|
|
echo " First time? Browser will show a certificate warning."
|
|
echo " Click 'Advanced' → 'Proceed' (self-signed cert)."
|
|
echo ""
|
|
|
|
# ── Open browser (platform-specific) ──────────────────────
|
|
if command -v xdg-open &> /dev/null; then
|
|
xdg-open "https://localhost:${PORT}" 2>/dev/null &
|
|
elif command -v open &> /dev/null; then
|
|
open "https://localhost:${PORT}" 2>/dev/null &
|
|
fi
|
|
|
|
echo " Press Ctrl+C to stop the server."
|
|
echo ""
|
|
|
|
# Keep alive: clean shutdown on Ctrl+C
|
|
trap "echo ''; echo ' Shutting down Haven...'; kill $SERVER_PID 2>/dev/null; exit 0" INT TERM
|
|
wait $SERVER_PID
|