Files
wordcloud/backend/start-dev.sh
T
broccoli f8a907e7c5 Add floating canvas panels, theme settings, and SVG line-spacing analysis.
Canvas Studio now uses dockable floating panels, app settings/help navigation, and improved SVG export; the backend adds an SVG line-spacing analysis API with SciPy acceleration and new design templates.
2026-07-13 22:01:01 +08:00

125 lines
4.9 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
VENV_DIR="$ROOT_DIR/.venv"
BACKEND_PORT="${BACKEND_PORT:-8000}"
# ── find python ──────────────────────────────────────────
PYTHON=""
for cmd in python3.13 python3.12 python3.11 python3.10 python3.9 python3; do
if command -v "$cmd" >/dev/null 2>&1; then
PYTHON="$cmd"
break
fi
done
[[ -n "$PYTHON" ]] || { echo "[ERROR] 找不到 python3"; exit 1; }
# verify version >= 3.9
if ! "$PYTHON" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3,9) else 1)'; then
echo "[ERROR] 需要 Python 3.9+"; exit 1
fi
# ── deps check & install ─────────────────────────────────
DEPS_OK=0
if "$PYTHON" -c "
import importlib.util, sys
mods = ('fastapi','uvicorn','pandas','PIL','numpy','matplotlib','pydantic','openpyxl')
for m in mods:
if importlib.util.find_spec(m) is None:
sys.exit(1)
" 2>/dev/null; then
DEPS_OK=1
fi
if [[ "$DEPS_OK" == "0" ]]; then
# try venv first
if [[ -f "$VENV_DIR/bin/python" ]] && "$VENV_DIR/bin/python" --version >/dev/null 2>&1; then
PYTHON="$VENV_DIR/bin/python"
if "$PYTHON" -c "
import importlib.util, sys
for m in ('fastapi','uvicorn','pandas','PIL','numpy','matplotlib','pydantic','openpyxl'):
if importlib.util.find_spec(m) is None: sys.exit(1)
" 2>/dev/null; then
DEPS_OK=1
fi
fi
fi
if [[ "$DEPS_OK" == "0" ]]; then
echo "[INFO] 安装依赖 (首次或缺失) ..."
# create venv if needed
if [[ ! -f "$VENV_DIR/bin/python" ]]; then
"$PYTHON" -m venv "$VENV_DIR"
fi
PYTHON="$VENV_DIR/bin/python"
"$PYTHON" -m pip install -q --upgrade pip
"$PYTHON" -m pip install -q fastapi uvicorn python-multipart pydantic pandas openpyxl pillow numpy matplotlib
fi
# SciPy is an optional accelerator for line-spacing analysis. The production
# requirements include it, but local dev environments may already have it in
# the base interpreter while the project venv does not. Reuse that copy when
# the Python ABI matches so startup does not require a network install.
EXTRA_SCIPY_SITE=""
if ! "$PYTHON" -c 'import scipy' >/dev/null 2>&1; then
PYTHON_ABI=$("$PYTHON" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
for cmd in python3.13 python3.12 python3.11 python3.10 python3.9 python3; do
if command -v "$cmd" >/dev/null 2>&1; then
SCIPY_SITE=$("$cmd" -c '
import pathlib
import scipy
import sys
expected = "'"$PYTHON_ABI"'"
current = f"{sys.version_info.major}.{sys.version_info.minor}"
print(pathlib.Path(scipy.__file__).parents[1] if current == expected else "")
' 2>/dev/null || true)
if [[ -n "$SCIPY_SITE" ]]; then
EXTRA_SCIPY_SITE="$SCIPY_SITE"
echo "[OK] 使用外部 SciPy 加速线距计算: $SCIPY_SITE"
break
fi
fi
done
fi
# ── C++ extension ────────────────────────────────────────
EXT=$($PYTHON -c 'import importlib.machinery; print(importlib.machinery.EXTENSION_SUFFIXES[0])')
EWC_SO="$ROOT_DIR/EfficientWordCloud/efficient_wordcloud/ewc_core${EXT}"
EWC_CPP="$ROOT_DIR/EfficientWordCloud/efficient_wordcloud/src/ewc_core.cpp"
if [[ ! -f "$EWC_SO" ]] || [[ "$EWC_CPP" -nt "$EWC_SO" ]]; then
echo "[INFO] 编译 ewc_core ..."
(cd "$ROOT_DIR/EfficientWordCloud" && "$PYTHON" setup.py build_ext --inplace >/dev/null)
echo "[OK] 编译完成"
else
echo "[OK] ewc_core 就绪"
fi
# ── ensure runtime dirs ──────────────────────────────────
mkdir -p "$ROOT_DIR/service_workspace" "$ROOT_DIR/service_assets" "$ROOT_DIR/service_projects"
# ── port: auto-kill occupant ─────────────────────────────
if ss -tln 2>/dev/null | grep -q ":$BACKEND_PORT "; then
echo "[INFO] 端口 $BACKEND_PORT 被占用,正在释放 ..."
fuser -k "$BACKEND_PORT/tcp" >/dev/null 2>&1 || true
sleep 1
fi
# ── start ────────────────────────────────────────────────
echo "[INFO] 启动后端 http://0.0.0.0:${BACKEND_PORT}"
WORDCLOUD_SCIPY_SITE="$EXTRA_SCIPY_SITE" \
PYTHONPATH="$ROOT_DIR/EfficientWordCloud" \
"$PYTHON" -m uvicorn service.app:app \
--app-dir "$ROOT_DIR" \
--host 0.0.0.0 --port "$BACKEND_PORT" \
--reload &
PID=$!
echo "[OK] PID: $PID"
echo "[OK] API: http://0.0.0.0:${BACKEND_PORT}/docs"
trap 'echo; echo "[INFO] 停止 ..."; kill $PID 2>/dev/null || true; wait $PID 2>/dev/null || true; echo "[OK] 已停止"; exit 0' INT TERM
wait "$PID"