Files
auto_control/config.py
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

126 lines
6.2 KiB
Python
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.
"""项目根配置:只放核心程序级配置。
任务自身的参数(观看时长、点赞开关、滑动速度等)不在这里,
放各自 tasks/xxx.py 顶部,保持每个任务自包含、互不干扰。
改 STF 地址/token、adb 路径、web 端口都改这里。
安全:STF_TOKEN / WEB_SECRET_KEY 建议用环境变量注入(生产不要提交到 git)。
支持 `.env` 文件(项目根目录),也可直接 `export STF_TOKEN=xxx`。
"""
import os
# 可选加载 .env 文件(不存在则忽略)
_env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
if os.path.exists(_env_path):
try:
with open(_env_path, encoding="utf-8") as _f:
for _line in _f:
_line = _line.strip()
if _line and not _line.startswith("#") and "=" in _line:
_k, _v = _line.split("=", 1)
os.environ.setdefault(_k.strip(), _v.strip())
except OSError:
pass
def _env(key, default):
"""读环境变量,未设置则返回默认值。"""
return os.environ.get(key, default)
# ================== STF 配置(已废弃,阶段 3 摘除 STF 后仅保留供历史参考) ==================
STF_URL = _env("STF_URL", "")
STF_TOKEN = _env("STF_TOKEN", "") # 写入 .env,不要提交到 git
# ================== adb 路径 ==================
# adb 二进制及依赖统一放在 bin/adb/ 下
# Windows: adb.exe(依赖同目录的 dll)
# Linux: adb(需 chmod +x,Linux 版二进制)
# 代码自动识别操作系统,用户只需把对应平台的 adb 放进 bin/adb/
import sys as _sys
_ADB_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin", "adb")
if _sys.platform == "win32":
ADB_PATH = os.path.join(_ADB_DIR, "adb.exe")
else:
# Linux / macOS:用 bin/adb/adb(无 .exe 后缀)
# 部署后需执行 chmod +x bin/adb/adb
ADB_PATH = os.path.join(_ADB_DIR, "adb")
# ================== 数据库 ==================
# 平台支持 MySQL(正式用法)与 SQLite(仅作回退,见 core/db_config.py)。
#
# DEPLOY_ENV 声明"这套配置连的是哪个环境的库",与库名一一绑定:
# dev → auto_control_dev(本地开发,可随意折腾)
# prod → auto_control (正式数据)
# 启动时会校验「.env 声明的环境」与「目标库登记的环境」是否一致,
# 不一致直接拒绝启动——防止把开发配置连到生产库上。
DEPLOY_ENV = _env("DEPLOY_ENV", "dev").strip().lower()
DB_HOST = _env("DB_HOST", "") # 为空则回退 SQLite(生产禁止静默回退)
DB_PORT = int(_env("DB_PORT", "3306"))
DB_USER = _env("DB_USER", "")
DB_PASSWORD = _env("DB_PASSWORD", "") # 写入 .env,不要提交到 git
DB_NAME = _env("DB_NAME", "")
DB_CHARSET = _env("DB_CHARSET", "utf8mb4")
# 排序规则用 _bin(逐码点比较,等价 SQLite 的大小写敏感语义)。
# 用默认的 utf8mb4_general_ci 会让 Admin/admin、Phone1/phone1 被判重复。
DB_COLLATION = _env("DB_COLLATION", "utf8mb4_bin")
# 完整连接串(优先级最高,迁移/回归脚本用它临时指向别的库)
DATABASE_URL = _env("DATABASE_URL", "")
# 逃生阀(默认关;仅在你明确知道自己在干什么时打开)
DB_ALLOW_ENV_MISMATCH = _env("DB_ALLOW_ENV_MISMATCH", "0") == "1"
DB_ALLOW_SQLITE_FALLBACK = _env("DB_ALLOW_SQLITE_FALLBACK", "0") == "1"
# ================== Web 后台 ==================
WEB_HOST = "0.0.0.0"
# 端口必须避开 Windows 动态端口范围(本机被改成 1024-15000,默认 49152-65535)
# 用 18050 处于动态范围之外,不会被出站连接随机占用,避免 WinError 10013
WEB_PORT = 18050
# ================== 数据目录 ==================
# 持久化数据(分组、任务计划)存放目录
DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
# APK 文件存储目录(应用管理功能)
APK_DIR = os.path.join(DATA_DIR, "apks")
# ================== 系统数据备份(导出/导入) ==================
# 导出 zip 与恢复前快照存放;导入暂存目录;待下次启动生效的恢复目录
# (运行时产物,不入 git,见 .gitignore data/backups 等)
BACKUP_DIR = os.path.join(DATA_DIR, "backups")
RESTORE_STAGING_DIR = os.path.join(DATA_DIR, "restore_staging")
RESTORE_PENDING_DIR = os.path.join(DATA_DIR, "restore_pending")
# ================== SSH 到部署机(已废弃,STF 相关操作退役) ==================
# 阶段 3 后代码不再使用 SSH;保留配置仅供手动运维 220(如 docker stop stf)。
STF_SSH_TARGET = _env("STF_SSH_TARGET", "")
STF_SSH_PASSWORD = _env("STF_SSH_PASSWORD", "")
STF_DOCKER_CONTAINER = _env("STF_DOCKER_CONTAINER", "")
STF_ADB_CONTAINER = _env("STF_ADB_CONTAINER", "")
STF_SCRIPT_PATH = _env("STF_SCRIPT_PATH", "")
# ================== USB 设备远程 adb server(阶段 2) ==================
# USB 设备插在 220(STF 主机)上,本机 adb 看不到;220 的 adb 容器是 host 网络
# 模式,5037 已监听所有网卡(含 Tailscale),平台用 adb -H/-P 直接驱动。
# 平台自身 USB(serial 无冒号且在本机 adb 里)不受影响,仍走本地。
USB_ADB_HOST = _env("USB_ADB_HOST", "100.100.10.1") # 220 的 Tailscale IP
USB_ADB_PORT = int(_env("USB_ADB_PORT", "5037"))
# ================== Tailscale 管理(工具页) ==================
# 调用官方 API v2 管理 tailnet 设备。配置从 .env 读取:
# TAILSCALE_API_KEY:后台 → Settings → API Access Tokens 生成(或 OAuth client_id:secret)
# TAILSCALE_TAILNET:tailnet 名称/ID,个人账号一般是登录邮箱前缀
# 安全:密钥用环境变量/.env 注入,不要写死在代码或提交到 git。
TAILSCALE_API_KEY = _env("TAILSCALE_API_KEY", "") # 写入 .env,不要提交到 git
TAILSCALE_TAILNET = _env("TAILSCALE_TAILNET", "")
# ================== 设备自动发现 ==================
# 定时扫描网段中开放 adb 5555 的设备 → 待连接池(用户确认后才加入正式池)。
# 网段默认:局域网 + Tailscale;可在工具页设备池面板修改(存 app_meta)。
DISCOVERY_PORT = int(_env("DISCOVERY_PORT", "5555"))
DISCOVERY_SUBNETS = ["192.168.20.0/24", "100.100.10.0/24"]
DISCOVERY_INTERVAL = int(_env("DISCOVERY_INTERVAL", "60"))