feat: add employee management, backend health check, and UI improvements

This commit is contained in:
2026-05-07 11:50:10 +08:00
parent a5db09f41e
commit c00db75c13
59 changed files with 3926 additions and 5796 deletions

133
start.sh
View File

@@ -1,6 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
export MSYS_NO_PATHCONV=1
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="$SCRIPT_DIR/.env"
ENV_EXAMPLE_FILE="$SCRIPT_DIR/.env.example"
@@ -31,6 +33,69 @@ set +a
SERVER_STARTUP_TIMEOUT="${SERVER_STARTUP_TIMEOUT:-300}"
SETUP_COMPLETED="${SETUP_COMPLETED:-false}"
server_probe_url() {
echo "http://${SERVER_HOST:-127.0.0.1}:${SERVER_PORT:-8000}${API_V1_PREFIX:-/api/v1}/health"
}
server_smoke_url() {
echo "http://${SERVER_HOST:-127.0.0.1}:${SERVER_PORT:-8000}${API_V1_PREFIX:-/api/v1}/employees/meta"
}
server_probe_python() {
if [ -x "$SCRIPT_DIR/server/.venv/Scripts/python.exe" ]; then
echo "$SCRIPT_DIR/server/.venv/Scripts/python.exe"
return 0
fi
if [ -x "$SCRIPT_DIR/server/.venv/bin/python" ]; then
echo "$SCRIPT_DIR/server/.venv/bin/python"
return 0
fi
return 1
}
probe_server_health() {
local probe_url="${1:-$(server_probe_url)}"
local probe_python=""
if probe_python="$(server_probe_python)"; then
"$probe_python" -c "import json, sys, urllib.request; data = json.load(urllib.request.urlopen(sys.argv[1], timeout=2)); raise SystemExit(0 if data.get('status') == 'ok' else 1)" "$probe_url" >/dev/null 2>&1
return $?
fi
if command -v curl >/dev/null 2>&1; then
curl --silent --fail --max-time 2 "$probe_url" | grep -q '"status"[[:space:]]*:[[:space:]]*"ok"'
return $?
fi
return 1
}
probe_server_smoke() {
local probe_url="${1:-$(server_smoke_url)}"
local probe_python=""
if probe_python="$(server_probe_python)"; then
"$probe_python" -c "import sys, urllib.request; response = urllib.request.urlopen(sys.argv[1], timeout=3); raise SystemExit(0 if response.status == 200 else 1)" "$probe_url" >/dev/null 2>&1
return $?
fi
if command -v curl >/dev/null 2>&1; then
curl --silent --fail --max-time 3 "$probe_url" >/dev/null 2>&1
return $?
fi
return 1
}
probe_server_ready() {
local health_url="${1:-$(server_probe_url)}"
local smoke_url="${2:-$(server_smoke_url)}"
probe_server_health "$health_url" && probe_server_smoke "$smoke_url"
}
prepare_web() {
info "Preparing web dependencies..."
(
@@ -69,12 +134,14 @@ start_setup_web() {
start_all() {
local server_pid=""
local started_server=false
local probe_url=""
local smoke_url=""
prepare_web
prepare_server
cleanup() {
if [ -n "$server_pid" ] && kill -0 "$server_pid" 2>/dev/null; then
if [ "$started_server" = true ] && [ -n "$server_pid" ] && kill -0 "$server_pid" 2>/dev/null; then
warn "Stopping FastAPI server..."
kill "$server_pid" 2>/dev/null || true
wait "$server_pid" 2>/dev/null || true
@@ -83,49 +150,61 @@ start_all() {
trap cleanup EXIT INT TERM
info "Starting FastAPI server..."
(
cd "$SCRIPT_DIR/server"
./start.sh start
) &
server_pid=$!
probe_url="$(server_probe_url)"
smoke_url="$(server_smoke_url)"
wait_for_server() {
local base_url="http://${SERVER_HOST:-127.0.0.1}:${SERVER_PORT:-8000}${API_V1_PREFIX:-/api/v1}/bootstrap"
if probe_server_ready "$probe_url" "$smoke_url"; then
warn "FastAPI is already ready at $probe_url. Reusing the existing backend process."
elif probe_server_health "$probe_url"; then
error "An existing backend process is responding at $probe_url, but the smoke check failed at $smoke_url. Stop the old FastAPI process and rerun ./start.sh."
else
info "Starting FastAPI server..."
(
cd "$SCRIPT_DIR/server"
./start.sh start
) &
server_pid=$!
started_server=true
fi
wait_for_server_ready() {
local attempt=1
local max_attempts="$SERVER_STARTUP_TIMEOUT"
if ! command -v curl >/dev/null 2>&1; then
warn "curl not found, skipping backend readiness check."
return 0
fi
info "Waiting for FastAPI bootstrap endpoint..."
info "Waiting for FastAPI readiness before starting the web frontend..."
while [ "$attempt" -le "$max_attempts" ]; do
if ! kill -0 "$server_pid" 2>/dev/null; then
wait "$server_pid" 2>/dev/null || true
error "FastAPI process exited before becoming ready. Run ./server/start.sh start directly to inspect the backend error."
fi
if curl --silent --fail "$base_url" >/dev/null 2>&1; then
info "FastAPI bootstrap endpoint is ready."
if probe_server_ready "$probe_url" "$smoke_url"; then
info "FastAPI is ready. Starting web frontend next."
return 0
fi
if [ $((attempt % 15)) -eq 0 ]; then
warn "FastAPI is still starting. First run may take longer while .venv and dependencies are prepared."
if [ "$started_server" = true ] && ! kill -0 "$server_pid" 2>/dev/null; then
if probe_server_ready "$probe_url" "$smoke_url"; then
warn "FastAPI is already available at $probe_url. Continuing with the existing process."
started_server=false
server_pid=""
return 0
fi
wait "$server_pid" 2>/dev/null || true
error "FastAPI process exited before becoming ready. Run ./server/start.sh start directly to inspect the backend error."
fi
sleep 1
attempt=$((attempt + 1))
done
error "FastAPI did not become ready within ${SERVER_STARTUP_TIMEOUT}s: $base_url"
if probe_server_health "$probe_url"; then
error "FastAPI answered health checks at $probe_url, but the smoke check failed at $smoke_url. The running backend is stale or incompatible."
fi
error "FastAPI did not become ready within ${SERVER_STARTUP_TIMEOUT}s. Inspect server/logs/app.log."
}
wait_for_server
wait_for_server_ready
prepare_web
info "Starting web frontend..."
cd "$SCRIPT_DIR/web"
./start.sh start