#!/usr/bin/env bash # Start the API (port 3000) and Flutter web dev server (port 8080) with merged logs. # Usage: ./startup.sh set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" API_PORT=3000 WEB_PORT=8080 if [ -f "$ROOT/scripts/flutter-env.sh" ]; then # shellcheck source=/dev/null source "$ROOT/scripts/flutter-env.sh" fi log() { printf '%s\n' "$*"; } kill_port() { local port=$1 if command -v fuser >/dev/null 2>&1; then if fuser "${port}/tcp" >/dev/null 2>&1; then log "Stopping process(es) on port ${port}..." fuser -k "${port}/tcp" >/dev/null 2>&1 || true sleep 0.5 fi return fi if ! command -v lsof >/dev/null 2>&1; then warn "Cannot free port ${port}: install fuser or lsof" return fi local pids pids="$(lsof -t -iTCP:"${port}" -sTCP:LISTEN 2>/dev/null || true)" if [ -n "$pids" ]; then log "Stopping process(es) on port ${port}: ${pids}" # shellcheck disable=SC2086 kill ${pids} 2>/dev/null || true sleep 0.5 # shellcheck disable=SC2086 kill -9 ${pids} 2>/dev/null || true fi } warn() { printf 'WARN: %s\n' "$*" >&2; } prefix_lines() { local tag=$1 while IFS= read -r line || [ -n "${line:-}" ]; do printf '[%s] %s\n' "$tag" "$line" done } cleanup() { trap - INT TERM EXIT log "Shutting down..." if [ -n "${API_PID:-}" ]; then kill "$API_PID" 2>/dev/null || true; fi if [ -n "${WEB_PID:-}" ]; then kill "$WEB_PID" 2>/dev/null || true; fi kill_port "$API_PORT" kill_port "$WEB_PORT" wait 2>/dev/null || true } trap cleanup INT TERM EXIT log "Freeing ports ${API_PORT} (API) and ${WEB_PORT} (web)..." kill_port "$API_PORT" kill_port "$WEB_PORT" if [ ! -f "$ROOT/server/.env" ]; then warn "Missing server/.env — copy server/.env.example and configure it first." fi log "Starting API on http://localhost:${API_PORT} ..." ( cd "$ROOT/server" dart run bin/server.dart 2>&1 | prefix_lines api ) & API_PID=$! # Build a static debug web bundle and serve it via a plain HTTP server. # This avoids `flutter run -d web-server`'s DWDS session pinning, which causes # blank pages when the browser is closed and reopened. # # Set SKIP_WEB_BUILD=1 to skip the build (useful when iterating on server-only # changes and the existing build/web is fine to keep serving). if [ "${SKIP_WEB_BUILD:-0}" != "1" ]; then log "Building Flutter web (debug)..." ( cd "$ROOT" flutter build web --debug --source-maps \ --dart-define=API_BASE_URL="http://localhost:${API_PORT}" \ 2>&1 | prefix_lines web-build ) else log "SKIP_WEB_BUILD=1 — reusing existing build/web bundle." fi log "Starting web app on http://localhost:${WEB_PORT} ..." ( cd "$ROOT" dart run scripts/web_static_server.dart build/web "${WEB_PORT}" \ 2>&1 | prefix_lines web ) & WEB_PID=$! log "Both services running. Press Ctrl+C to stop." log " API: http://localhost:${API_PORT}" log " Web: http://localhost:${WEB_PORT}" log "" wait -n 2>/dev/null || wait || true exit_code=$? cleanup exit "$exit_code"