#!/usr/bin/env bash # ────────────────────────────────────────────────────────────── # IndeeHub — Full-stack development launcher # # Starts everything you need for local development: # 1. Infrastructure (Postgres, Redis, MinIO) — Homebrew or Docker # 2. Local Nostr relay (nak) — optional # 3. Backend API (NestJS on port 4000) # 4. Frontend dev server (Vite on port 5174) # # Usage: # bash scripts/dev.sh # start everything # bash scripts/dev.sh --no-docker # skip Docker (use Homebrew services) # bash scripts/dev.sh --no-seed # skip Nostr relay seeding # # Press Ctrl+C to stop everything cleanly. # ────────────────────────────────────────────────────────────── set -e # ── Configuration ──────────────────────────────────────────── RELAY_PORT=7777 RELAY_URL="ws://localhost:$RELAY_PORT" VITE_PORT=5174 BACKEND_PORT=4000 ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" BACKEND_DIR="$ROOT_DIR/backend" SKIP_DOCKER=false SKIP_SEED=false for arg in "$@"; do case "$arg" in --no-docker) SKIP_DOCKER=true ;; --no-seed) SKIP_SEED=true ;; esac done # ── Colours ────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # No Colour # ── PID tracking for cleanup ──────────────────────────────── PIDS=() MINIO_PID="" cleanup() { echo "" echo -e "${YELLOW}Shutting down all services...${NC}" for pid in "${PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null fi done # Stop MinIO if we started it if [ -n "$MINIO_PID" ] && kill -0 "$MINIO_PID" 2>/dev/null; then kill "$MINIO_PID" 2>/dev/null fi # Stop Docker infrastructure (if we used Docker) if [ "$SKIP_DOCKER" = false ] && command -v docker &>/dev/null; then echo -e "${CYAN}Stopping Docker services...${NC}" docker compose -f "$ROOT_DIR/docker-compose.dev.yml" stop postgres redis minio minio-init mailpit 2>/dev/null || true fi # Kill anything still on relay port lsof -ti :$RELAY_PORT 2>/dev/null | xargs kill -9 2>/dev/null || true echo -e "${GREEN}All services stopped.${NC}" exit 0 } trap cleanup SIGINT SIGTERM EXIT # ── Prefixed output helper ─────────────────────────────────── prefix_output() { local label="$1" local colour="$2" while IFS= read -r line; do echo -e "${colour}[${label}]${NC} $line" done } # ── Pre-flight checks ─────────────────────────────────────── echo -e "${BOLD}${CYAN}" echo "╔══════════════════════════════════════════╗" echo "║ IndeeHub Dev Environment ║" echo "╚══════════════════════════════════════════╝" echo -e "${NC}" # Check required tools MISSING=() command -v node &>/dev/null || MISSING+=("node") command -v npm &>/dev/null || MISSING+=("npm") if [ ${#MISSING[@]} -gt 0 ]; then echo -e "${RED}Missing required tools: ${MISSING[*]}${NC}" echo "Install them and try again." exit 1 fi HAS_NAK=true if ! command -v nak &>/dev/null; then HAS_NAK=false echo -e "${YELLOW}Warning: 'nak' not installed. Skipping local Nostr relay.${NC}" echo -e "${YELLOW}Install with: brew install nak${NC}" echo "" fi HAS_DOCKER=false if command -v docker &>/dev/null; then HAS_DOCKER=true fi # ═════════════════════════════════════════════════════════════ # STEP 1 — Infrastructure (Homebrew or Docker) # ═════════════════════════════════════════════════════════════ if [ "$SKIP_DOCKER" = true ] || [ "$HAS_DOCKER" = false ]; then echo -e "${CYAN}[1/4] Starting infrastructure via Homebrew...${NC}" # Postgres if command -v pg_isready &>/dev/null || [ -f /opt/homebrew/opt/postgresql@15/bin/pg_isready ]; then PG_ISREADY="${PG_ISREADY:-$(command -v pg_isready 2>/dev/null || echo /opt/homebrew/opt/postgresql@15/bin/pg_isready)}" if $PG_ISREADY -h localhost -p 5432 -q 2>/dev/null; then echo -e "${GREEN} Postgres is already running.${NC}" else echo -e "${CYAN} Starting Postgres...${NC}" brew services start postgresql@15 2>/dev/null || true sleep 2 fi else echo -e "${RED} Postgres not found. Install with: brew install postgresql@15${NC}" exit 1 fi # Redis if command -v redis-cli &>/dev/null; then if redis-cli ping 2>/dev/null | grep -q PONG; then echo -e "${GREEN} Redis is already running.${NC}" else echo -e "${CYAN} Starting Redis...${NC}" brew services start redis 2>/dev/null || true sleep 1 fi else echo -e "${RED} Redis not found. Install with: brew install redis${NC}" exit 1 fi # MinIO if command -v minio &>/dev/null; then if curl -s -o /dev/null http://localhost:9000/minio/health/live 2>/dev/null; then echo -e "${GREEN} MinIO is already running.${NC}" else echo -e "${CYAN} Starting MinIO...${NC}" mkdir -p /tmp/minio-data MINIO_ROOT_USER=minioadmin MINIO_ROOT_PASSWORD=minioadmin123 \ minio server /tmp/minio-data --console-address ":9001" --address ":9000" \ > /tmp/minio.log 2>&1 & MINIO_PID=$! sleep 2 # Create buckets if mc is available if command -v mc &>/dev/null; then mc alias set local http://localhost:9000 minioadmin minioadmin123 2>/dev/null || true mc mb local/indeedhub-private --ignore-existing 2>/dev/null || true mc mb local/indeedhub-public --ignore-existing 2>/dev/null || true mc anonymous set download local/indeedhub-public 2>/dev/null || true echo -e "${GREEN} MinIO buckets configured.${NC}" fi fi else echo -e "${YELLOW} MinIO not found (optional). Install with: brew install minio/stable/minio${NC}" fi # Ensure the Postgres database exists PSQL="${PSQL:-$(command -v psql 2>/dev/null || echo /opt/homebrew/opt/postgresql@15/bin/psql)}" CREATEDB="${CREATEDB:-$(command -v createdb 2>/dev/null || echo /opt/homebrew/opt/postgresql@15/bin/createdb)}" CREATEUSER="${CREATEUSER:-$(command -v createuser 2>/dev/null || echo /opt/homebrew/opt/postgresql@15/bin/createuser)}" # Create user if not exists if ! $PSQL -U "$(whoami)" -d postgres -tc "SELECT 1 FROM pg_roles WHERE rolname='indeedhub'" 2>/dev/null | grep -q 1; then $CREATEUSER -U "$(whoami)" --superuser indeedhub 2>/dev/null || true $PSQL -U "$(whoami)" -d postgres -c "ALTER USER indeedhub WITH PASSWORD 'indeedhub_dev_2026';" 2>/dev/null || true fi # Create database if not exists if ! $PSQL -U "$(whoami)" -d postgres -tc "SELECT 1 FROM pg_database WHERE datname='indeedhub'" 2>/dev/null | grep -q 1; then $CREATEDB -U "$(whoami)" -O indeedhub indeedhub 2>/dev/null || true fi echo -e "${GREEN} Homebrew infrastructure is ready.${NC}" else echo -e "${CYAN}[1/4] Starting Docker infrastructure...${NC}" docker compose -f "$ROOT_DIR/docker-compose.dev.yml" up -d \ postgres redis minio minio-init mailpit 2>&1 | prefix_output "docker" "$CYAN" echo -e "${CYAN} Waiting for Postgres...${NC}" for i in $(seq 1 30); do if docker compose -f "$ROOT_DIR/docker-compose.dev.yml" exec -T postgres pg_isready -U indeedhub -q 2>/dev/null; then echo -e "${GREEN} Postgres is ready.${NC}" break fi if [ "$i" -eq 30 ]; then echo -e "${RED} Postgres did not become healthy in time.${NC}" exit 1 fi sleep 1 done echo -e "${GREEN} Docker infrastructure is up.${NC}" fi # ═════════════════════════════════════════════════════════════ # STEP 2 — Local Nostr relay # ═════════════════════════════════════════════════════════════ if [ "$HAS_NAK" = true ]; then echo -e "${CYAN}[2/4] Starting local Nostr relay on port $RELAY_PORT...${NC}" # Kill existing process on the port if lsof -i :$RELAY_PORT -P &>/dev/null; then lsof -ti :$RELAY_PORT | xargs kill -9 2>/dev/null || true sleep 1 fi nak serve --port $RELAY_PORT > /dev/null 2>&1 & PIDS+=($!) # Wait for relay for i in $(seq 1 20); do if curl -s -o /dev/null http://localhost:$RELAY_PORT 2>/dev/null; then echo -e "${GREEN} Relay is ready at $RELAY_URL${NC}" break fi sleep 0.5 done # Seed relay if [ "$SKIP_SEED" = false ]; then if [ -f "$ROOT_DIR/scripts/seed-profiles.ts" ]; then echo -e "${CYAN} Seeding test profiles...${NC}" (cd "$ROOT_DIR" && npx tsx scripts/seed-profiles.ts 2>&1 | prefix_output "seed" "$CYAN") || true fi if [ -f "$ROOT_DIR/scripts/seed-activity.ts" ]; then echo -e "${CYAN} Seeding activity...${NC}" (cd "$ROOT_DIR" && npx tsx scripts/seed-activity.ts 2>&1 | prefix_output "seed" "$CYAN") || true fi fi else echo -e "${YELLOW}[2/4] Skipping Nostr relay (nak not installed).${NC}" fi # ═════════════════════════════════════════════════════════════ # STEP 3 — Backend API # ═════════════════════════════════════════════════════════════ echo -e "${CYAN}[3/4] Starting backend API on port $BACKEND_PORT...${NC}" # Override Docker service hostnames → localhost so the backend # can reach services from the host machine. export DATABASE_HOST=localhost export QUEUE_HOST=localhost export S3_ENDPOINT=http://localhost:9000 export SMTP_HOST=localhost (cd "$BACKEND_DIR" && npm run start:dev 2>&1 | prefix_output "api" "$GREEN") & PIDS+=($!) # Wait for the API to respond echo -e "${CYAN} Waiting for API...${NC}" for i in $(seq 1 60); do if curl -s -o /dev/null "http://localhost:$BACKEND_PORT" 2>/dev/null; then echo -e "${GREEN} Backend API is ready at http://localhost:$BACKEND_PORT${NC}" break fi if [ "$i" -eq 60 ]; then echo -e "${YELLOW} Backend is still starting (check logs above for errors).${NC}" fi sleep 2 done # ═════════════════════════════════════════════════════════════ # STEP 4 — Frontend dev server # ═════════════════════════════════════════════════════════════ echo "" echo -e "${BOLD}${GREEN}" echo "════════════════════════════════════════════" echo " All services launching! " echo "" echo " Frontend: http://localhost:$VITE_PORT" echo " Backend: http://localhost:$BACKEND_PORT" echo " MinIO: http://localhost:9001" [ "$HAS_NAK" = true ] && echo " Relay: $RELAY_URL" echo "" echo " Mock mode: OFF (VITE_USE_MOCK_DATA=false)" echo " BTCPay: ${BTCPAY_URL:-from backend/.env}" echo "" echo " Press Ctrl+C to stop everything" echo "════════════════════════════════════════════" echo -e "${NC}" # Run Vite in the foreground so Ctrl+C propagates cleanly cd "$ROOT_DIR" npx vite --port $VITE_PORT 2>&1 | prefix_output "vite" "$YELLOW"