#!/usr/bin/env bash # GroupChat2 Production Build & Run Script # Builds the client, compiles the server in release mode, and runs it. # The Rust server serves the static client files directly — no Vite needed. # # Usage: # ./prod.sh # Build + run # ./prod.sh build # Build only (no run) # ./prod.sh run # Run only (skip build, assumes already built) set -e ROOT="$(cd "$(dirname "$0")" && pwd)" # Colors MAGENTA='\033[0;35m' YELLOW='\033[0;33m' CYAN='\033[0;36m' RED='\033[0;31m' GREEN='\033[0;32m' GRAY='\033[0;90m' NC='\033[0m' MODE="${1:-all}" # all | build | run # ─── Preflight checks ─────────────────────────────────────────────── check_env() { if [ ! -f "$ROOT/server/.env" ]; then echo -e "${RED}[prod] Missing server/.env file!${NC}" echo -e "${GRAY} Copy server/.env.example and fill in your keys:${NC}" echo -e "${GRAY} cp server/.env.example server/.env${NC}" exit 1 fi # Source .env to validate required vars set -a source "$ROOT/server/.env" set +a if [ -z "$OPENROUTER_API_KEY" ] || [ "$OPENROUTER_API_KEY" = "your-openrouter-api-key-here" ]; then echo -e "${RED}[prod] OPENROUTER_API_KEY not set in server/.env${NC}" exit 1 fi if [ -z "$BRAVE_API_KEY" ] || [ "$BRAVE_API_KEY" = "your-brave-api-key-here" ]; then echo -e "${RED}[prod] BRAVE_API_KEY not set in server/.env${NC}" exit 1 fi if [ -z "$JWT_SECRET" ] || [ "$JWT_SECRET" = "dev-secret-change-me" ]; then echo -e "${YELLOW}[prod] WARNING: Using default JWT_SECRET — set a real secret for production!${NC}" fi } # ─── Build ─────────────────────────────────────────────────────────── build() { echo -e "${MAGENTA}[prod] Building GroupChat2 for production...${NC}" echo "" # 1. Install client deps if needed if [ ! -d "$ROOT/client/node_modules" ]; then echo -e "${CYAN}[prod] Installing client dependencies...${NC}" (cd "$ROOT/client" && npm install) fi # 2. Build client (Vite → dist/) echo -e "${CYAN}[prod] Building client...${NC}" (cd "$ROOT/client" && npm run build) echo -e "${GREEN}[prod] Client built → client/dist/${NC}" # 3. Build server in release mode echo -e "${YELLOW}[prod] Building server (release)...${NC}" (cd "$ROOT/server" && cargo build --release) echo -e "${GREEN}[prod] Server built → server/target/release/groupchat-server${NC}" echo "" echo -e "${GREEN}[prod] Build complete!${NC}" } # ─── Run ───────────────────────────────────────────────────────────── run() { check_env # Verify build artifacts exist if [ ! -f "$ROOT/server/target/release/groupchat-server" ]; then echo -e "${RED}[prod] Server binary not found. Run './prod.sh build' first.${NC}" exit 1 fi if [ ! -d "$ROOT/client/dist" ]; then echo -e "${RED}[prod] Client dist not found. Run './prod.sh build' first.${NC}" exit 1 fi BIND_ADDR="${BIND_ADDR:-0.0.0.0:3001}" echo "" echo -e " ${MAGENTA}GroupChat2 Production${NC}" echo -e " ${GREEN}Domain: https://gchat.tud.ink${NC}" echo -e " ${YELLOW}Bind: ${BIND_ADDR}${NC}" echo -e " ${GRAY}Press Ctrl+C to stop${NC}" echo "" # Source .env so the server binary picks up the vars set -a source "$ROOT/server/.env" set +a export STATIC_DIR="$ROOT/client/dist" export BIND_ADDR export RUST_LOG="${RUST_LOG:-info}" cleanup() { echo "" echo -e "${RED}[prod] Shutting down...${NC}" kill "$SERVER_PID" 2>/dev/null || true lsof -ti:3001 2>/dev/null | xargs kill -9 2>/dev/null || true echo -e "${RED}[prod] Stopped.${NC}" exit 0 } trap cleanup INT TERM # Run the release binary "$ROOT/server/target/release/groupchat-server" & SERVER_PID=$! wait "$SERVER_PID" } # ─── Main ──────────────────────────────────────────────────────────── case "$MODE" in build) build ;; run) run ;; all|"") build run ;; *) echo "Usage: $0 [build|run]" echo " (no args) Build and run" echo " build Build only" echo " run Run only (must build first)" exit 1 ;; esac