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.
125 lines
4.9 KiB
Bash
Executable File
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"
|