- 迁移 → 管理员初始化 → 前端构建 → API+worker 后台启动 → 健康检查 → 自动打开页面
- 支持 --stop 停止、--no-open 跳过打开浏览器;端口已在服务时直接复用
- 未设 DATABASE_URL 时使用 SQLite 轻量库并给出验收提示
- 修复 bash 3.2(macOS 自带)将紧贴 $VAR 的多字节字符并入变量名的问题:
全角括号内的变量一律用 ${VAR} 显式界定
- README 本地运行章节补充快速启动说明
This commit is contained in:
@@ -33,6 +33,14 @@
|
|||||||
|
|
||||||
需要 Node.js 22、uv、Python 3.12、PostgreSQL 17。所有示例从仓库根目录执行。
|
需要 Node.js 22、uv、Python 3.12、PostgreSQL 17。所有示例从仓库根目录执行。
|
||||||
|
|
||||||
|
快速启动本地页面(迁移、管理员初始化、前端构建、API 与导入 worker 一条命令完成,健康检查通过后自动打开 `http://127.0.0.1:8000`;`Ctrl-C` 或 `scripts/dev_up.sh --stop` 停止,`--no-open` 跳过自动打开浏览器):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/dev_up.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
未设置 `DATABASE_URL` 时该脚本使用 SQLite 轻量开发库 `var/db.sqlite3`,仅限页面测试与开发回归;金额聚合与财务验收需按下方配置 PostgreSQL。手动分步流程如下:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv sync --frozen --python 3.12
|
uv sync --frozen --python 3.12
|
||||||
npm ci --registry=https://registry.npmjs.org
|
npm ci --registry=https://registry.npmjs.org
|
||||||
|
|||||||
Executable
+124
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 一键启动本地开发环境:迁移 → 初始化管理员 → 构建前端 → 启动 API+worker → 打开页面。
|
||||||
|
# 默认使用 SQLite 轻量开发库(var/db.sqlite3);设置 DATABASE_URL 则使用 PostgreSQL。
|
||||||
|
# Ctrl-C 或 --stop 停止本脚本启动的进程。
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
PORT="${PORT:-8000}"
|
||||||
|
SERVER_PID_FILE="var/dev-server.pid"
|
||||||
|
WORKER_PID_FILE="var/dev-worker.pid"
|
||||||
|
SERVER_LOG="var/dev-server.log"
|
||||||
|
WORKER_LOG="var/dev-worker.log"
|
||||||
|
|
||||||
|
die() { echo "错误:$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
detect_python() {
|
||||||
|
if [ -x ".venv/bin/python" ]; then
|
||||||
|
PY=".venv/bin/python"
|
||||||
|
elif command -v uv >/dev/null 2>&1; then
|
||||||
|
PY="uv run python"
|
||||||
|
else
|
||||||
|
die "未找到 .venv/bin/python 或 uv,请先安装依赖(uv sync)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
read_pidfile() { [ -f "$1" ] && cat "$1" || true; }
|
||||||
|
|
||||||
|
stop_all() {
|
||||||
|
local stopped=0 pid
|
||||||
|
for file in "$SERVER_PID_FILE" "$WORKER_PID_FILE"; do
|
||||||
|
pid="$(read_pidfile "$file")"
|
||||||
|
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
stopped=1
|
||||||
|
fi
|
||||||
|
rm -f "$file"
|
||||||
|
done
|
||||||
|
[ "$stopped" = 1 ] && echo "已停止本地开发服务" || echo "没有正在运行的开发服务"
|
||||||
|
}
|
||||||
|
|
||||||
|
health_ok() { curl -fsS --max-time 2 "http://127.0.0.1:$PORT/api/health" >/dev/null 2>&1; }
|
||||||
|
|
||||||
|
open_page() {
|
||||||
|
local url="http://127.0.0.1:$PORT/"
|
||||||
|
if command -v open >/dev/null 2>&1; then open "$url"
|
||||||
|
elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
--stop)
|
||||||
|
stop_all
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
"" | --no-open) ;;
|
||||||
|
*) die "未知参数:$1(支持 --stop / --no-open)" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
detect_python
|
||||||
|
mkdir -p var
|
||||||
|
|
||||||
|
if health_ok; then
|
||||||
|
echo "已在 $PORT 端口运行,直接打开页面"
|
||||||
|
[ "${1:-}" != "--no-open" ] && open_page
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${DATABASE_URL:-}" ]; then
|
||||||
|
echo "未设置 DATABASE_URL,使用 SQLite 轻量开发库 var/db.sqlite3(仅限页面测试与开发回归)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "执行数据库迁移…"
|
||||||
|
$PY manage.py migrate --no-input >/dev/null
|
||||||
|
|
||||||
|
if [ ! -f var/local-access.txt ]; then
|
||||||
|
echo "初始化本地管理员…"
|
||||||
|
PYTHONPATH="$ROOT" $PY scripts/init_local.py
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d frontend/dist ]; then
|
||||||
|
echo "构建前端(首次)…"
|
||||||
|
npm run build >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "启动 worker(日志 ${WORKER_LOG})…"
|
||||||
|
nohup $PY manage.py import_worker >"$WORKER_LOG" 2>&1 &
|
||||||
|
echo $! >"$WORKER_PID_FILE"
|
||||||
|
|
||||||
|
echo "启动 API 服务…"
|
||||||
|
nohup $PY manage.py runserver "127.0.0.1:$PORT" --noreload >"$SERVER_LOG" 2>&1 &
|
||||||
|
echo $! >"$SERVER_PID_FILE"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
stop_all >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup INT TERM
|
||||||
|
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
if health_ok; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ -n "$(read_pidfile "$SERVER_PID_FILE")" ] && ! kill -0 "$(read_pidfile "$SERVER_PID_FILE")" 2>/dev/null; then
|
||||||
|
tail -20 "$SERVER_LOG" >&2
|
||||||
|
die "API 服务启动失败(完整日志:${SERVER_LOG})"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
health_ok || { tail -20 "$SERVER_LOG" >&2; die "健康检查超时(日志:${SERVER_LOG})"; }
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "本地开发环境已就绪:http://127.0.0.1:$PORT"
|
||||||
|
[ -f var/local-access.txt ] && echo "登录凭据:var/local-access.txt(用户名 admin,权限 0600)"
|
||||||
|
if [ -z "${DATABASE_URL:-}" ]; then
|
||||||
|
echo "提示:当前为 SQLite 轻量模式;高精度金额聚合与生产验收请按 README 配置 PostgreSQL。"
|
||||||
|
fi
|
||||||
|
echo "日志:${SERVER_LOG} / ${WORKER_LOG} · Ctrl-C 或 scripts/dev_up.sh --stop 停止"
|
||||||
|
echo
|
||||||
|
|
||||||
|
[ "${1:-}" != "--no-open" ] && open_page
|
||||||
|
|
||||||
|
wait "$(read_pidfile "$SERVER_PID_FILE")" || true
|
||||||
|
cleanup
|
||||||
Reference in New Issue
Block a user