添加Docker部署方案
- 多阶段Dockerfile:第一阶段构建前端(npm),第二阶段Python运行时(uv+opencv) - docker-compose.yml:单服务,data/logs目录挂载持久化 - deploy.sh一键部署脚本:自动构建+启动+健康检查 - .dockerignore排除不必要文件减小构建上下文 - main.py添加前端静态文件服务(生产环境单端口部署) - README更新部署文档和代理配置说明
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
*/__pycache__
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# 运行时数据(通过 volume 挂载)
|
||||||
|
data/
|
||||||
|
logs/
|
||||||
|
*.db
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# 前端依赖和构建产物(Docker 内重新构建)
|
||||||
|
web/frontend/node_modules/
|
||||||
|
web/frontend/dist/
|
||||||
|
|
||||||
|
# geetest 临时图片
|
||||||
|
bg.jpg
|
||||||
|
fullbg.jpg
|
||||||
|
slice.jpg
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# 文档
|
||||||
|
*.md
|
||||||
|
|
||||||
|
# 测试日志
|
||||||
|
11.log
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
# ===== 阶段1: 构建前端 =====
|
||||||
|
FROM node:20-slim AS frontend-builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
COPY web/frontend/package.json web/frontend/package-lock.json* ./
|
||||||
|
RUN npm ci || npm install
|
||||||
|
COPY web/frontend/ ./
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# ===== 阶段2: Python 运行时 =====
|
||||||
|
FROM python:3.12-slim AS runtime
|
||||||
|
|
||||||
|
# 安装 OpenCV 系统依赖
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libgl1 \
|
||||||
|
libglib2.0-0 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 安装 uv 用于快速安装依赖
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 先复制依赖文件,利用 Docker 缓存层
|
||||||
|
COPY pyproject.toml uv.lock* ./
|
||||||
|
RUN uv venv /app/.venv && uv pip install -e .
|
||||||
|
|
||||||
|
# 复制项目代码
|
||||||
|
COPY core/ ./core/
|
||||||
|
COPY utils/ ./utils/
|
||||||
|
COPY web/backend/ ./web/backend/
|
||||||
|
COPY README.md ./
|
||||||
|
|
||||||
|
# 复制前端构建产物
|
||||||
|
COPY --from=frontend-builder /build/dist ./web/frontend/dist
|
||||||
|
|
||||||
|
# 创建数据和日志目录
|
||||||
|
RUN mkdir -p /app/data /app/logs
|
||||||
|
|
||||||
|
# 环境变量
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PATH="/app/.venv/bin:$PATH"
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
||||||
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')" || exit 1
|
||||||
|
|
||||||
|
# 启动命令
|
||||||
|
CMD ["python", "-m", "uvicorn", "web.backend.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]
|
||||||
@@ -83,3 +83,63 @@ cd web/frontend && npm install && npm run dev
|
|||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
### Docker 部署(推荐,生产环境)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 一键部署(自动构建镜像 + 启动服务)
|
||||||
|
./deploy.sh
|
||||||
|
|
||||||
|
# 或手动操作:
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
部署完成后访问 `http://服务器IP:8000`,默认账号 `admin / admin123`。
|
||||||
|
|
||||||
|
**Docker 常用命令:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f # 查看实时日志
|
||||||
|
docker compose restart # 重启服务
|
||||||
|
docker compose down # 停止服务
|
||||||
|
docker compose up -d --build # 重新构建并启动(代码更新后)
|
||||||
|
```
|
||||||
|
|
||||||
|
**数据持久化:** 数据库和日志通过 volume 挂载到宿主机 `./data` 和 `./logs` 目录,容器重建后数据不丢失。
|
||||||
|
|
||||||
|
### 本地开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 一键启动(自动检查依赖,前后端分离开发模式)
|
||||||
|
./start_web.sh
|
||||||
|
|
||||||
|
# 或手动启动:
|
||||||
|
# 后端
|
||||||
|
uv pip install -e .
|
||||||
|
.venv/bin/python -m uvicorn web.backend.main:app --reload --port 8000
|
||||||
|
|
||||||
|
# 前端
|
||||||
|
cd web/frontend && npm install && npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
访问 `http://localhost:5173`(开发模式),默认账号 `admin / admin123`。
|
||||||
|
|
||||||
|
### 代理配置说明
|
||||||
|
|
||||||
|
在 Web 界面"代理配置"页面设置:
|
||||||
|
|
||||||
|
1. **API 代理**:填入代理 API 地址(支持返回 JSON 格式),系统自动获取并验证代理
|
||||||
|
2. **白名单**:如代理需要白名单,填入 UID 和 UKEY,系统自动同步出口 IP
|
||||||
|
3. **出口 IP 漂移**:移动网络出口 IP 频繁漂移时,系统会保留多个出口 IP 在白名单中
|
||||||
|
|
||||||
|
**代理 API JSON 格式:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
// 正常返回
|
||||||
|
{"code":0,"success":"true","msg":"","data":[{"IP":"1.2.3.4","Port":8080}]}
|
||||||
|
|
||||||
|
// 白名单错误
|
||||||
|
{"code":-1,"msg":"51.请先添加白名单:1.2.3.4","data":""}
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ===== 斗鱼批量登录 Docker 部署脚本 =====
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=============================="
|
||||||
|
echo " 斗鱼批量登录 - Docker 部署"
|
||||||
|
echo "=============================="
|
||||||
|
|
||||||
|
# 检查 Docker
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
echo "❌ 未安装 Docker,请先安装:"
|
||||||
|
echo " curl -fsSL https://get.docker.com | sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 docker compose
|
||||||
|
if docker compose version &> /dev/null; then
|
||||||
|
COMPOSE="docker compose"
|
||||||
|
elif command -v docker-compose &> /dev/null; then
|
||||||
|
COMPOSE="docker-compose"
|
||||||
|
else
|
||||||
|
echo "❌ 未安装 docker compose,请先安装 Docker Compose"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " 使用: $COMPOSE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 创建数据和日志目录
|
||||||
|
mkdir -p data logs
|
||||||
|
|
||||||
|
# 构建并启动
|
||||||
|
echo "正在构建镜像(首次构建约3-5分钟)..."
|
||||||
|
$COMPOSE build
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "正在启动服务..."
|
||||||
|
$COMPOSE up -d
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "等待服务启动..."
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
for i in $(seq 1 10); do
|
||||||
|
if curl -s http://localhost:8000/api/health | grep -q "ok" 2>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
echo "=============================="
|
||||||
|
echo " ✅ 部署成功!"
|
||||||
|
echo "=============================="
|
||||||
|
echo " 访问地址: http://localhost:8000"
|
||||||
|
echo " 默认账号: admin / admin123"
|
||||||
|
echo ""
|
||||||
|
echo " 查看日志: $COMPOSE logs -f"
|
||||||
|
echo " 停止服务: $COMPOSE down"
|
||||||
|
echo " 重启服务: $COMPOSE restart"
|
||||||
|
echo "=============================="
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo " 等待中... ($i/10)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "❌ 服务启动超时,查看日志:"
|
||||||
|
$COMPOSE logs --tail 30
|
||||||
|
exit 1
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
services:
|
||||||
|
douyu-login:
|
||||||
|
build: .
|
||||||
|
container_name: douyu-login
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
volumes:
|
||||||
|
# 持久化数据库和日志
|
||||||
|
- ./data:/app/data
|
||||||
|
- ./logs:/app/logs
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
+30
-1
@@ -1,9 +1,13 @@
|
|||||||
"""FastAPI 入口"""
|
"""FastAPI 入口"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
|
|
||||||
from .database import init_db
|
from .database import init_db
|
||||||
from .routers import auth, users, accounts, login, proxy, cookies, logs
|
from .routers import auth, users, accounts, login, proxy, cookies, logs
|
||||||
@@ -45,6 +49,31 @@ def health():
|
|||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
|
||||||
|
# ---- 生产环境:serve 前端静态文件 ----
|
||||||
|
# Docker 部署时前端构建产物会被复制到 web/frontend/dist
|
||||||
|
_FRONTEND_DIST = Path(__file__).resolve().parents[2] / "web" / "frontend" / "dist"
|
||||||
|
_INDEX_HTML = _FRONTEND_DIST / "index.html"
|
||||||
|
|
||||||
|
if _INDEX_HTML.exists():
|
||||||
|
# 挂载静态资源目录(js/css/图片等)
|
||||||
|
_ASSETS_DIR = _FRONTEND_DIST / "assets"
|
||||||
|
if _ASSETS_DIR.exists():
|
||||||
|
app.mount("/assets", StaticFiles(directory=str(_ASSETS_DIR)), name="assets")
|
||||||
|
|
||||||
|
@app.get("/{full_path:path}")
|
||||||
|
async def serve_spa(full_path: str, request: Request):
|
||||||
|
"""SPA fallback:非 /api 路径返回 index.html"""
|
||||||
|
# 排除 API 路径
|
||||||
|
if full_path.startswith("api"):
|
||||||
|
return {"detail": "Not Found"}
|
||||||
|
# 尝试返回静态文件
|
||||||
|
file_path = _FRONTEND_DIST / full_path
|
||||||
|
if file_path.is_file():
|
||||||
|
return FileResponse(str(file_path))
|
||||||
|
# SPA fallback 到 index.html
|
||||||
|
return FileResponse(str(_INDEX_HTML))
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
uvicorn.run("web.backend.main:app", host="0.0.0.0", port=8000, reload=True)
|
uvicorn.run("web.backend.main:app", host="0.0.0.0", port=8000, reload=True)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user