diff --git a/scripts/dev-den-local.sh b/scripts/dev-den-local.sh index 99094169..31f9cff8 100755 --- a/scripts/dev-den-local.sh +++ b/scripts/dev-den-local.sh @@ -3,4 +3,4 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -exec "$ROOT_DIR/scripts/dev-web-local.sh" "$@" +exec bash "$ROOT_DIR/scripts/dev-web-local.sh" "$@" diff --git a/scripts/dev-web-local.sh b/scripts/dev-web-local.sh index 77632e91..45ed1089 100644 --- a/scripts/dev-web-local.sh +++ b/scripts/dev-web-local.sh @@ -4,28 +4,53 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" COMPOSE_FILE="$ROOT_DIR/packaging/docker/docker-compose.web-local.yml" PROJECT_NAME="openwork-web-local" -DEV_CMD=(pnpm --parallel --filter @openwork-ee/den-controller --filter @openwork-ee/den-web dev) # Local-dev defaults — match the MySQL container in docker-compose.web-local.yml. # These are only used when not already set in the environment or .env. : "${DATABASE_URL:=mysql://root:password@127.0.0.1:3306/openwork_den}" : "${BETTER_AUTH_SECRET:=local-dev-secret-not-for-production-use!!}" -: "${BETTER_AUTH_URL:=http://localhost:8788}" -export DATABASE_URL BETTER_AUTH_SECRET BETTER_AUTH_URL + +pick_port() { + node <<'EOF' +const net = require('net'); +const server = net.createServer(); +server.listen(0, '127.0.0.1', () => { + const { port } = server.address(); + process.stdout.write(String(port)); + server.close(); +}); +EOF +} + +port_is_free() { + local port="$1" + node -e "const net=require('net'); const server=net.createServer(); server.once('error',()=>process.exit(1)); server.once('listening',()=>server.close(()=>process.exit(0))); server.listen(${port});" +} + +choose_port() { + local preferred="$1" + if port_is_free "$preferred"; then + printf '%s\n' "$preferred" + return + fi + pick_port +} detect_web_origins() { - node <<'EOF' + local web_port="$1" + WEB_PORT="$web_port" node <<'EOF' const os = require('os'); +const port = process.env.WEB_PORT; const origins = new Set([ - 'http://localhost:3005', - 'http://127.0.0.1:3005', + `http://localhost:${port}`, + `http://127.0.0.1:${port}`, ]); for (const entries of Object.values(os.networkInterfaces())) { for (const entry of entries || []) { if (!entry || entry.internal || entry.family !== 'IPv4') continue; - origins.add(`http://${entry.address}:3005`); + origins.add(`http://${entry.address}:${port}`); } } @@ -37,10 +62,12 @@ cleanup() { local exit_code=$? trap - EXIT INT TERM - if [[ -n "${DEV_PID:-}" ]]; then - kill "$DEV_PID" >/dev/null 2>&1 || true - wait "$DEV_PID" 2>/dev/null || true - fi + for pid_var in DEN_PID WEB_PID; do + if [[ -n "${!pid_var:-}" ]]; then + kill "${!pid_var}" >/dev/null 2>&1 || true + wait "${!pid_var}" 2>/dev/null || true + fi + done docker compose -p "$PROJECT_NAME" -f "$COMPOSE_FILE" down -v >/dev/null 2>&1 || true exit "$exit_code" @@ -53,17 +80,54 @@ if ! command -v docker >/dev/null 2>&1; then exit 1 fi +DEN_API_PORT="$(choose_port "${DEN_LOCAL_API_PORT:-8788}")" +DEN_WEB_PORT="$(choose_port "${DEN_LOCAL_WEB_PORT:-3005}")" +DEN_WEB_ORIGIN="http://localhost:${DEN_WEB_PORT}" + +: "${BETTER_AUTH_URL:=$DEN_WEB_ORIGIN}" +export DATABASE_URL BETTER_AUTH_SECRET BETTER_AUTH_URL + echo "Starting local MySQL..." docker compose -p "$PROJECT_NAME" -f "$COMPOSE_FILE" down -v >/dev/null 2>&1 || true docker compose -p "$PROJECT_NAME" -f "$COMPOSE_FILE" up -d --wait mysql echo "Running Den migrations..." -pnpm --filter @openwork-ee/den-controller db:migrate +pnpm --filter @openwork-ee/den-db db:push <<'EOF' +y +EOF -WEB_CORS_ORIGINS="$(detect_web_origins)" +WEB_CORS_ORIGINS="$(detect_web_origins "$DEN_WEB_PORT")" echo "Allowing Better Auth origins: $WEB_CORS_ORIGINS" echo "Starting Den and web dev servers..." -env CORS_ORIGINS="$WEB_CORS_ORIGINS" "${DEV_CMD[@]}" & -DEV_PID=$! -wait "$DEV_PID" +echo "Den controller: http://localhost:$DEN_API_PORT" +echo "Den web: $DEN_WEB_ORIGIN" + +( + cd "$ROOT_DIR/ee/apps/den-controller" + env \ + OPENWORK_DEV_MODE=1 \ + DATABASE_URL="$DATABASE_URL" \ + BETTER_AUTH_SECRET="$BETTER_AUTH_SECRET" \ + BETTER_AUTH_URL="$BETTER_AUTH_URL" \ + CORS_ORIGINS="$WEB_CORS_ORIGINS" \ + PORT="$DEN_API_PORT" \ + sh -lc 'pnpm run build:den-db && exec pnpm exec tsx watch src/index.ts' +) & +DEN_PID=$! + +( + cd "$ROOT_DIR/ee/apps/den-web" + env \ + OPENWORK_DEV_MODE=1 \ + NEXT_PUBLIC_POSTHOG_KEY= \ + NEXT_PUBLIC_POSTHOG_API_KEY= \ + DEN_API_BASE="http://127.0.0.1:$DEN_API_PORT" \ + DEN_AUTH_FALLBACK_BASE="http://127.0.0.1:$DEN_API_PORT" \ + DEN_AUTH_ORIGIN="$DEN_WEB_ORIGIN" \ + NEXT_PUBLIC_OPENWORK_AUTH_CALLBACK_URL="$DEN_WEB_ORIGIN" \ + pnpm exec next dev --hostname 0.0.0.0 --port "$DEN_WEB_PORT" +) & +WEB_PID=$! + +wait "$DEN_PID" "$WEB_PID"