为把数据库从单文件 SQLite 迁到 MySQL 5.7 铺路。本期不改后端: DB_HOST 为空时仍走 SQLite,本地开发无感。 - config.py: 新增 DEPLOY_ENV(默认 dev)与 DB_HOST/PORT/USER/PASSWORD/NAME/ CHARSET/COLLATION、DATABASE_URL、两个逃生阀(DB_ALLOW_ENV_MISMATCH / DB_ALLOW_SQLITE_FALLBACK) - core/db_config.py(新增): URI 组装;按方言分叉的引擎参数(utf8mb4、 pool_pre_ping、pool_recycle=1800、READ COMMITTED、STRICT_TRANS_TABLES); 连接探活;app_meta 方言中立读写(MySQL 里 key 是保留字,需反引号) - 防混库三层: ①库名与环境绑定(dev→auto_control_dev / prod→auto_control) ②库标签 app_meta.deployment_env 与 .env 声明比对 ③启动横幅打印当前库 (生产用 WARNING 级)。不符直接拒绝启动并说明两边分别是什么 - web_server.py: 硬编码 sqlite URI → db_config;配置错在装配期就 exit 2; init_db 之后跑库标签校验 + 横幅 - core/models.py: PRAGMA 监听器加 sqlite 类型守卫——它挂在 Engine 基类上, MySQL 连接执行 PRAGMA 会直接导致建连失败 - requirements.txt 加 PyMySQL;scripts/start.sh 依赖守卫加 pymysql, 并在 exec 前打印 DEPLOY_ENV/DB_NAME/DB_HOST - .env.example 新增「数据库」段;DEVELOPMENT.md §4.1/4.2、DEPLOY.md §2.2 同步 验证: 用 DATABASE_URL 指向 users.db 的一致快照副本跑通主要只读接口 (health/devices/jobs/pool/groups/discovery/summary 全 200,app_meta 读写正常); DEPLOY_ENV=prod 且无 DB_HOST 时退出码 2;开 DB_ALLOW_SQLITE_FALLBACK 后可回退。
85 lines
4.1 KiB
Bash
Executable File
85 lines
4.1 KiB
Bash
Executable File
#!/bin/bash
|
||
# 容器启动脚本:依赖安装(仅首次/缺失时)+ 环境修复 + 启动 web_server。
|
||
# compose 的 python-app 服务 command 指向本脚本(不要在 compose 里再跑 pip,
|
||
# 否则每次容器重启都重装依赖——实测每轮重新下载 opencv 5.0(73MB) 并破坏 cv2)。
|
||
#
|
||
# 为什么需要环境修复(必须在 pip install 之后):
|
||
# rapidocr_onnxruntime 的依赖声明是 opencv-python(GUI 版),pip 解析依赖时
|
||
# 会强制装回它;而 GUI 版依赖 X11 库(libxcb),python:slim 容器里 import cv2
|
||
# 直接崩溃 → RapidOCR 不可用 → 条件判断的 OCR 步骤抛异常 → 任务失败退出。
|
||
# 因此在安装完 requirements 后,最后强制卸载 GUI 版并装 headless(幂等)。
|
||
# 本地 Mac 有 GUI 环境不受影响。
|
||
|
||
# ---- 依赖就绪守卫:flask/u2/uiautodev/rapidocr/cv2 全部可用则跳过安装 ----
|
||
# (容器重启秒级启动;只有首次部署或依赖缺失时才走完整安装)
|
||
python3 - <<'PY'
|
||
import importlib.util, sys
|
||
mods = ("flask", "flask_login", "flask_sqlalchemy", "apscheduler",
|
||
"uiautomator2", "uiautodev", "rapidocr_onnxruntime", "PIL",
|
||
"fastmcp", "pymysql")
|
||
if all(importlib.util.find_spec(m) for m in mods):
|
||
try:
|
||
import cv2
|
||
ok = hasattr(cv2, "__version__") and cv2.__file__ is not None
|
||
except Exception:
|
||
ok = False
|
||
sys.exit(0 if ok else 1)
|
||
sys.exit(1)
|
||
PY
|
||
if [ $? -ne 0 ]; then
|
||
echo "[start] 依赖缺失或 cv2 异常,执行安装与环境修复(首次启动较慢)..."
|
||
if [ -f requirements.txt ]; then
|
||
pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
|
||
fi
|
||
|
||
# 环境修复:最后强制 headless(覆盖 rapidocr 拉回的 GUI 版)
|
||
pip uninstall -y -q opencv-python 2>/dev/null || true
|
||
pip install -q -i https://pypi.tuna.tsinghua.edu.cn/simple "opencv-python-headless>=4.8,<5" # 5.x wheel 无 cv2 模块,锁定 4.x
|
||
|
||
# pip 24.0 解压 abi3 wheel 偶发丢根文件(site-packages/cv2 只剩子目录,
|
||
# cv2.abi3.so / __init__.py 缺失 → import cv2 成空壳,OCR 全挂)。
|
||
# 手动解压兜底在部分环境修不好(解压后仍无 cv2.__version__),
|
||
# 实测 pip --force-reinstall 完整安装有效(220 验证 cv2 4.14.0 恢复)。
|
||
python3 - <<'PY'
|
||
import subprocess, sys
|
||
|
||
def cv2_ok():
|
||
try:
|
||
import cv2
|
||
return hasattr(cv2, "__version__") and cv2.__file__ is not None
|
||
except Exception:
|
||
return False
|
||
|
||
if cv2_ok():
|
||
sys.exit(0)
|
||
print("cv2 异常,pip 强制重装 opencv-python-headless...")
|
||
r = subprocess.run(["pip", "install", "--force-reinstall", "-q",
|
||
"-i", "https://pypi.tuna.tsinghua.edu.cn/simple",
|
||
"opencv-python-headless>=4.8,<5"])
|
||
if r.returncode == 0:
|
||
import cv2
|
||
print(f"cv2 {cv2.__version__} 重装修复完成")
|
||
sys.exit(r.returncode)
|
||
PY
|
||
else
|
||
echo "[start] 依赖已就绪(cv2 $(python3 -c 'import cv2; print(cv2.__version__)' 2>/dev/null)),跳过安装"
|
||
fi
|
||
|
||
# 启动 MCP server(后台,AI 控制台/外部客户端经 127.0.0.1:8033 调用)。
|
||
# web_server.py 内 agent 依赖它,容器重启必须自动拉起(MCP_ENABLED=0 可关)。
|
||
# 平台账号:MCP_PLATFORM_USER 默认 admin;密码优先取环境变量(compose/.env),
|
||
# 兜底 admin123 与部署时的手动启动参数一致。
|
||
if [ -f mcp_server/mcp_server.py ] && [ "${MCP_ENABLED:-1}" = "1" ]; then
|
||
echo "[start] 启动 MCP server..."
|
||
MCP_ALLOW_WRITE=1 \
|
||
MCP_PLATFORM_USER="${MCP_PLATFORM_USER:-admin}" \
|
||
MCP_PLATFORM_PASS="${MCP_PLATFORM_PASS:-admin123}" \
|
||
MCP_AUDIT_FILE="${MCP_AUDIT_FILE:-/tmp/mcp_audit.log}" \
|
||
python3 -m mcp_server.mcp_server > /tmp/mcp_server.log 2>&1 &
|
||
fi
|
||
|
||
# 启动前把"连的是哪个库"打在最显眼的位置(web_server 启动横幅是权威,
|
||
# 这里再报一次是防止日志只截到容器入口这几行时看不出环境)
|
||
echo "[start] DEPLOY_ENV=${DEPLOY_ENV:-<未设置,默认 dev>} DB_NAME=${DB_NAME:-<未设置>} DB_HOST=${DB_HOST:-<未设置>}"
|
||
exec python -u web_server.py
|