添加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
|
||||
|
||||
## 部署
|
||||
|
||||
### 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 入口"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import uvicorn
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from .database import init_db
|
||||
from .routers import auth, users, accounts, login, proxy, cookies, logs
|
||||
@@ -45,6 +49,31 @@ def health():
|
||||
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():
|
||||
uvicorn.run("web.backend.main:app", host="0.0.0.0", port=8000, reload=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user