Files
prop-ai-hr/scripts/dev.sh
T

246 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PIDS=()
usage() {
cat <<'EOF'
Usage: ./scripts/dev.sh [start|stop|restart|status|--reset]
Starts MySQL, Redis, MinIO, Qdrant, backend, admin frontend and legacy mobile fallback.
start start local demo services
stop stop backend and frontend dev processes
restart stop backend and frontend, then start again
status show local demo process status
--reset rebuild local demo database before starting services
EOF
}
cleanup() {
trap - INT TERM EXIT
for pid in "${PIDS[@]}"; do
kill "$pid" >/dev/null 2>&1 || true
done
}
ensure_docker() {
if docker info >/dev/null 2>&1; then
return
fi
if [[ "$(uname -s)" == "Darwin" ]] && [[ -d /Applications/Docker.app ]]; then
echo "Docker daemon is not running. Opening Docker Desktop..."
open -a Docker
for _ in {1..90}; do
if docker info >/dev/null 2>&1; then
echo "Docker daemon is ready."
return
fi
sleep 2
done
fi
echo "Docker daemon is not running. Start Docker Desktop, then run ./scripts/dev.sh again." >&2
exit 1
}
ensure_port_free() {
local port="$1"
if lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1; then
echo "Port $port is already in use:" >&2
lsof -nP -iTCP:"$port" -sTCP:LISTEN >&2
echo "Stop the existing process, then run ./scripts/dev.sh again." >&2
exit 1
fi
}
stop_port() {
local port="$1"
local pids
pids="$(lsof -tiTCP:"$port" -sTCP:LISTEN || true)"
if [[ -z "$pids" ]]; then
echo "Port $port is not in use."
return
fi
echo "Stopping port $port: $pids"
kill $pids >/dev/null 2>&1 || true
for _ in {1..20}; do
if ! lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1; then
return
fi
sleep 0.2
done
kill -9 $pids >/dev/null 2>&1 || true
}
stop_portless_route() {
local name="$1"
local pids
if ! command -v portless >/dev/null 2>&1; then
return
fi
pids="$(portless list 2>/dev/null | sed -nE "s#.*https://${name}[.]localhost.*[(]pid ([0-9]+)[)].*#\\1#p" || true)"
if [[ -z "$pids" ]]; then
echo "Portless route $name is not running."
return
fi
echo "Stopping portless route $name: $pids"
kill $pids >/dev/null 2>&1 || true
for _ in {1..20}; do
if ! portless list 2>/dev/null | grep -q "https://${name}[.]localhost"; then
return
fi
sleep 0.2
done
kill -9 $pids >/dev/null 2>&1 || true
}
stop_launchctl_job() {
local label="$1"
local domain="gui/$(id -u)/$label"
if ! command -v launchctl >/dev/null 2>&1; then
return
fi
if ! launchctl print "$domain" >/dev/null 2>&1; then
return
fi
echo "Stopping launchctl job $label"
launchctl bootout "$domain" >/dev/null 2>&1 || true
}
using_portless() {
[[ "${PORTLESS:-1}" != "0" ]] && command -v portless >/dev/null 2>&1
}
using_portless_backend() {
using_portless
}
using_portless_admin() {
using_portless
}
backend_url() {
if using_portless_backend; then
echo "https://wygj-api.localhost/"
else
echo "http://127.0.0.1:8080/"
fi
}
admin_url() {
if using_portless_admin; then
echo "https://wygj-admin.localhost/"
else
echo "http://127.0.0.1:5173/"
fi
}
stop_dev() {
stop_launchctl_job local.wygj.backend.dev
stop_portless_route wygj-api
stop_port 8080
stop_portless_route wygj-admin
stop_port 5173
stop_port 5174
}
status() {
docker compose -f docker-compose.dev.yml ps
lsof -nP -iTCP:8080 -sTCP:LISTEN || true
lsof -nP -iTCP:5173 -sTCP:LISTEN || true
lsof -nP -iTCP:5174 -sTCP:LISTEN || true
if command -v portless >/dev/null 2>&1; then
portless list || true
fi
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
COMMAND="${1:-start}"
RESET=false
case "$COMMAND" in
start)
;;
--reset)
COMMAND="start"
RESET=true
;;
stop)
cd "$ROOT_DIR"
stop_dev
exit 0
;;
restart)
cd "$ROOT_DIR"
stop_dev
COMMAND="start"
;;
status)
cd "$ROOT_DIR"
status
exit 0
;;
*)
usage
exit 1
;;
esac
cd "$ROOT_DIR"
ensure_docker
if ! using_portless_backend; then
ensure_port_free 8080
fi
if ! using_portless_admin; then
ensure_port_free 5173
fi
ensure_port_free 5174
if [[ "$RESET" == true ]]; then
./scripts/reset-dev-db.sh
else
docker compose -f docker-compose.dev.yml up -d mysql redis qdrant minio minio-init
fi
trap cleanup INT TERM EXIT
if using_portless_backend; then
portless alias --remove wygj-api >/dev/null 2>&1 || true
portless run --name wygj-api ./scripts/dev-backend.sh &
else
./scripts/dev-backend.sh &
fi
PIDS+=("$!")
./scripts/dev-frontend.sh &
PIDS+=("$!")
./scripts/dev-mobile.sh &
PIDS+=("$!")
echo "Backend: $(backend_url)"
echo "Admin: $(admin_url)"
echo "Legacy mobile fallback: http://127.0.0.1:5174/h5/user"
echo "Current user side: https://wygj-mobile-uni.localhost/h5/ (run: cd mobile-uni && portless run --name wygj-mobile-uni npm run dev)"
echo "Qdrant: http://127.0.0.1:6333/"
echo "Press Ctrl+C to stop backend, admin frontend and legacy mobile frontend."
while :; do
for pid in "${PIDS[@]}"; do
if ! kill -0 "$pid" >/dev/null 2>&1; then
wait "$pid"
exit $?
fi
done
sleep 2
done