246 lines
5.2 KiB
Bash
Executable File
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
|