Files
auto_control/scripts/start.sh
T
butubb f22263ab45 feat: 数据库连接层改造——库目标由 .env 装配 + 环境防呆(迁 MySQL 第一步)
为把数据库从单文件 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 后可回退。
2026-09-13 10:27:14 +08:00

85 lines
4.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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