120 lines
3.1 KiB
Bash
Executable File
120 lines
3.1 KiB
Bash
Executable File
#!/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
|
|
)
|
|
if [ -x "$ROOT/scripts/copy_flutter_js_source_map.sh" ]; then
|
|
"$ROOT/scripts/copy_flutter_js_source_map.sh" "$ROOT/build/web" \
|
|
2>&1 | prefix_lines web-build
|
|
fi
|
|
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"
|