From 73ec12bcbcb71b266d52f497a56e20b6a2277b37 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 25 Jul 2026 11:01:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=BC=80=E5=8F=91=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E4=BD=BF=E7=94=A8=20MySQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 17 ++++++- dev.sh | 99 ++++++++++++++++++++++++++++++++++++++--- docker-compose.yml | 5 ++- web/backend/database.py | 7 +-- 4 files changed, 117 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 721dd55..eab579f 100644 --- a/.env.example +++ b/.env.example @@ -21,11 +21,26 @@ APP_PORT=8000 # Docker 构建镜像源(服务器保持 true;本地测试可改为 false) USE_CHINA_MIRRORS=true -# MySQL 配置(生产环境必须设置密码;MySQL 不映射宿主端口) +# MySQL 配置(生产环境必须设置密码;默认只映射到宿主机 127.0.0.1,供本地 dev.sh 使用) +# 服务器部署默认使用国内镜像;./dev.sh 默认临时改用 mysql:8.4,避免本地测试走国内镜像。 +# MYSQL_IMAGE=docker.m.daocloud.io/library/mysql:8.4 MYSQL_DATABASE=douyu_login MYSQL_USER=douyu_login MYSQL_PASSWORD= MYSQL_ROOT_PASSWORD= +MYSQL_BIND_HOST=127.0.0.1 +MYSQL_HOST_PORT=3307 + +# 本地调试后端连接 MySQL(./dev.sh 使用;默认连接上面的宿主机端口) +DEV_DB_HOST=127.0.0.1 +DEV_DB_PORT=3307 + +# 直接运行后端时使用;./dev.sh 会自动按 DEV_DB_* 和 MYSQL_* 生成这些变量 +# DB_HOST=127.0.0.1 +# DB_PORT=3307 +# DB_NAME=douyu_login +# DB_USER=douyu_login +# DB_PASSWORD= # 数据库连接池配置(仅 MySQL 生效) DB_POOL_SIZE=20 diff --git a/dev.sh b/dev.sh index e8f53ec..0e1bc5a 100755 --- a/dev.sh +++ b/dev.sh @@ -19,18 +19,43 @@ BACKEND_PORT="${BACKEND_PORT:-8800}" FRONTEND_HOST="${FRONTEND_HOST:-0.0.0.0}" FRONTEND_PORT="${FRONTEND_PORT:-5174}" LOG_LEVEL="${LOG_LEVEL:-DEBUG}" +MYSQL_IMAGE="${MYSQL_IMAGE:-mysql:8.4}" +MYSQL_BIND_HOST="${MYSQL_BIND_HOST:-127.0.0.1}" +MYSQL_HOST_PORT="${MYSQL_HOST_PORT:-3307}" +MYSQL_DATABASE="${MYSQL_DATABASE:-douyu_login}" +MYSQL_USER="${MYSQL_USER:-douyu_login}" +DEV_DB_HOST="${DEV_DB_HOST:-127.0.0.1}" +DEV_DB_PORT="${DEV_DB_PORT:-$MYSQL_HOST_PORT}" +DB_HOST="$DEV_DB_HOST" +DB_PORT="$DEV_DB_PORT" +DB_NAME="${DB_NAME:-$MYSQL_DATABASE}" +DB_USER="${DB_USER:-$MYSQL_USER}" +DB_PASSWORD="${DB_PASSWORD:-${MYSQL_PASSWORD:-}}" +DB_POOL_SIZE="${DB_POOL_SIZE:-20}" +DB_MAX_OVERFLOW="${DB_MAX_OVERFLOW:-20}" +DB_POOL_TIMEOUT="${DB_POOL_TIMEOUT:-30}" +DB_POOL_RECYCLE="${DB_POOL_RECYCLE:-1800}" BACKEND_PID="" FRONTEND_PID="" +stop_tree() { + local pid="$1" + local children + if [ -z "$pid" ] || ! kill -0 "$pid" 2>/dev/null; then + return + fi + children="$(pgrep -P "$pid" 2>/dev/null || true)" + for child in $children; do + stop_tree "$child" + done + kill "$pid" 2>/dev/null || true +} + cleanup() { trap - EXIT INT TERM - if [ -n "$FRONTEND_PID" ] && kill -0 "$FRONTEND_PID" 2>/dev/null; then - kill "$FRONTEND_PID" 2>/dev/null || true - fi - if [ -n "$BACKEND_PID" ] && kill -0 "$BACKEND_PID" 2>/dev/null; then - kill "$BACKEND_PID" 2>/dev/null || true - fi + stop_tree "$FRONTEND_PID" + stop_tree "$BACKEND_PID" } require_cmd() { @@ -42,9 +67,61 @@ require_cmd() { require_cmd uv require_cmd npm +require_cmd docker + +detect_compose() { + if docker compose version >/dev/null 2>&1; then + echo "docker compose" + elif command -v docker-compose >/dev/null 2>&1; then + echo "docker-compose" + else + echo "" + fi +} + +if [ -n "${DATABASE_URL:-}" ] && [[ "$DATABASE_URL" != mysql+* ]]; then + echo "dev.sh 只支持 MySQL,请移除 SQLite DATABASE_URL: $DATABASE_URL" + exit 1 +fi + +if [ -z "${MYSQL_PASSWORD:-}" ] || [ -z "${MYSQL_ROOT_PASSWORD:-}" ]; then + echo "请先在 .env 中设置 MYSQL_PASSWORD 和 MYSQL_ROOT_PASSWORD" + exit 1 +fi + +if [ -z "$DB_PASSWORD" ]; then + echo "请先在 .env 中设置 DB_PASSWORD 或 MYSQL_PASSWORD" + exit 1 +fi + +COMPOSE=$(detect_compose) +if [ -z "$COMPOSE" ]; then + echo "缺少 docker compose" + exit 1 +fi mkdir -p data logs +echo "正在启动本地 MySQL..." +MYSQL_IMAGE="$MYSQL_IMAGE" \ +MYSQL_BIND_HOST="$MYSQL_BIND_HOST" \ +MYSQL_HOST_PORT="$MYSQL_HOST_PORT" \ +$COMPOSE up -d mysql + +echo "等待 MySQL 就绪..." +for i in $(seq 1 30); do + if $COMPOSE exec -T mysql sh -c 'mysqladmin ping -h localhost -uroot -p"$MYSQL_ROOT_PASSWORD" --silent' >/dev/null 2>&1; then + echo "MySQL 已就绪" + break + fi + if [ "$i" = "30" ]; then + echo "MySQL 启动超时,查看日志:" + $COMPOSE logs --tail 50 mysql + exit 1 + fi + sleep 2 +done + echo "" echo "==============================" echo " 本地调试模式" @@ -52,12 +129,22 @@ echo "==============================" echo " 后端: http://${BACKEND_HOST}:${BACKEND_PORT}" echo " 前端: http://localhost:${FRONTEND_PORT}" echo " API 代理: http://127.0.0.1:${BACKEND_PORT}" +echo " MySQL: ${DB_HOST}:${DB_PORT}/${DB_NAME}" echo " 退出: Ctrl+C" echo "==============================" echo "" trap cleanup EXIT INT TERM +DB_HOST="$DB_HOST" \ +DB_PORT="$DB_PORT" \ +DB_NAME="$DB_NAME" \ +DB_USER="$DB_USER" \ +DB_PASSWORD="$DB_PASSWORD" \ +DB_POOL_SIZE="$DB_POOL_SIZE" \ +DB_MAX_OVERFLOW="$DB_MAX_OVERFLOW" \ +DB_POOL_TIMEOUT="$DB_POOL_TIMEOUT" \ +DB_POOL_RECYCLE="$DB_POOL_RECYCLE" \ LOG_LEVEL="$LOG_LEVEL" \ uv run uvicorn web.backend.main:app \ --host "$BACKEND_HOST" \ diff --git a/docker-compose.yml b/docker-compose.yml index cb67517..0c88023 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,9 +56,12 @@ services: retries: 3 mysql: - image: docker.m.daocloud.io/library/mysql:8.4 + image: ${MYSQL_IMAGE:-docker.m.daocloud.io/library/mysql:8.4} container_name: douyu-login-db restart: unless-stopped + ports: + # 仅绑定宿主机本地地址,供 ./dev.sh 的本地后端连接,不对外网开放。 + - "${MYSQL_BIND_HOST:-127.0.0.1}:${MYSQL_HOST_PORT:-3307}:3306" environment: - TZ=Asia/Shanghai - MYSQL_DATABASE=${MYSQL_DATABASE:-douyu_login} diff --git a/web/backend/database.py b/web/backend/database.py index 575b30b..8fe8e6d 100644 --- a/web/backend/database.py +++ b/web/backend/database.py @@ -8,8 +8,6 @@ from sqlalchemy import create_engine, event from sqlalchemy.orm import sessionmaker, declarative_base PROJECT_ROOT = Path(__file__).resolve().parents[2] -DB_PATH = PROJECT_ROOT / "data" / "web.db" -DB_PATH.parent.mkdir(parents=True, exist_ok=True) def _env_int(name: str, default: int) -> int: @@ -40,7 +38,10 @@ def _get_database_url() -> str: f"@{db_host}:{db_port}/{db_name}?charset=utf8mb4" ) - return f"sqlite:///{DB_PATH}" + raise RuntimeError( + "未配置数据库连接。请设置 DATABASE_URL=mysql+pymysql://...," + "或设置 DB_HOST、DB_NAME、DB_USER、DB_PASSWORD" + ) DATABASE_URL = _get_database_url()