#!/usr/bin/env bash set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/_common.sh" # Ensure .env exists. if [ ! -f "$ROOT/.env" ]; then if [ -f "$ROOT/.env.example" ]; then cp "$ROOT/.env.example" "$ROOT/.env" c_yellow "Created .env from .env.example." set -a; . "$ROOT/.env"; set +a else c_yellow "No .env or .env.example found; using built-in defaults." fi fi start_backend() { c_dim "Starting backend (bind ${BIND_HOST}:${BACKEND_PORT}) …" nohup python -m uvicorn researchlink.api.app:app \ --host "$BIND_HOST" --port "$BACKEND_PORT" --log-level info \ > "$BACKEND_LOG" 2>&1 & echo $! > "$BACKEND_PID" } # ── Backend: idempotent + health-verified ──────────────────────────────────── if pid_alive "$BACKEND_PID"; then if http_ok "$HEALTH_URL/api/health"; then c_yellow "Backend already running and healthy (pid $(cat "$BACKEND_PID"))." else c_yellow "Backend pid alive but not responding — restarting." kill "$(cat "$BACKEND_PID")" 2>/dev/null || true; sleep 1; start_backend fi elif port_in_use "127.0.0.1" "$BACKEND_PORT"; then c_red "Port ${BACKEND_PORT} is in use by another process." c_dim "Free it, or set RESEARCHLINK_BACKEND_PORT in .env, then run 'make start' again." exit 1 else start_backend fi if ! wait_health "$HEALTH_URL/api/health" 40; then c_red "Backend failed to become healthy. Last log lines:" tail -n 20 "$BACKEND_LOG" 2>/dev/null || true exit 1 fi # ── Optional Node frontend (only if one exists) ────────────────────────────── FRONTEND_URL="${BACKEND_URL}/" if [ -f "$FRONTEND_DIR/package.json" ]; then if pid_alive "$FRONTEND_PID"; then c_yellow "Frontend already running (pid $(cat "$FRONTEND_PID"))." elif port_in_use "127.0.0.1" "$FRONTEND_PORT"; then c_red "Frontend port ${FRONTEND_PORT} in use — set RESEARCHLINK_FRONTEND_PORT." else c_dim "Starting frontend dev server …" ( cd "$FRONTEND_DIR" && nohup npm run dev -- --host "$FRONTEND_HOST" --port "$FRONTEND_PORT" \ > "$FRONTEND_LOG" 2>&1 & echo $! > "$FRONTEND_PID" ) FRONTEND_URL="http://${ADV_HOST}:${FRONTEND_PORT}" fi else c_dim "No separate frontend project — the UI is served by the backend." fi # ── Verify every service endpoint (on loopback) ────────────────────────────── echo c_green "Services:" all_ok=1 for path in /api/health /api/config /api/llm/status /api/papers; do if http_ok "${HEALTH_URL}${path}"; then c_green " ✓ ${path}" else c_red " ✗ ${path}"; all_ok=0 fi done echo c_green "Research-Link-AI is running." echo " Open in your browser : $FRONTEND_URL" echo " Backend / API base : $BACKEND_URL (API: $BACKEND_URL/api)" if [ "$ADV_HOST" != "127.0.0.1" ]; then echo " Local (on server) : http://127.0.0.1:${BACKEND_PORT}/" fi echo " Binding : ${BIND_HOST}:${BACKEND_PORT} (all interfaces)" echo " LLM mode : $LLM_MODE model: $LLM_MODEL $(is_external_mode && echo '(external provider)' || echo '(offline / local)')" if is_external_mode; then echo " API key : $(key_configured)"; fi echo " Logs : $BACKEND_LOG" echo c_dim "Reachable from other machines at ${BACKEND_URL} (ensure the port is open in the firewall)." c_dim "Stop with: make stop · Status: make status · Logs: make logs" [ "$all_ok" -eq 1 ] || { c_yellow "Some endpoints did not respond — see 'make logs'."; exit 1; }