合并应用宝 Worker 容器
This commit is contained in:
+2
-2
@@ -9,8 +9,8 @@ logs/
|
||||
*.db
|
||||
*.log
|
||||
|
||||
# 前端依赖和构建产物(Docker 内重新构建)
|
||||
web/frontend/node_modules/
|
||||
# Node 依赖和前端构建产物(Docker 内重新构建)
|
||||
**/node_modules/
|
||||
web/frontend/dist/
|
||||
|
||||
# geetest 临时图片
|
||||
|
||||
+7
-7
@@ -18,12 +18,12 @@ ADMIN_PASSWORD=admin123
|
||||
# Docker 对外端口(容器内仍监听 8800;服务器当前使用 8000)
|
||||
APP_PORT=8000
|
||||
|
||||
# Docker 构建镜像源(服务器保持 true;本地测试可改为 false)
|
||||
USE_CHINA_MIRRORS=true
|
||||
# Docker 构建镜像源(网络可访问官方源时保持 false;国内服务器可改为 true)
|
||||
USE_CHINA_MIRRORS=false
|
||||
|
||||
# MySQL 配置(生产环境必须设置密码;默认只映射到宿主机 127.0.0.1,供本地 dev.sh 使用)
|
||||
# 服务器部署默认使用国内镜像;./dev.sh 默认临时改用 mysql:8.4,避免本地测试走国内镜像。
|
||||
# MYSQL_IMAGE=docker.m.daocloud.io/library/mysql:8.4
|
||||
# 默认使用官方镜像;国内服务器可按需覆盖为可用镜像加速地址。
|
||||
# MYSQL_IMAGE=mysql:8.4
|
||||
MYSQL_DATABASE=douyu_login
|
||||
MYSQL_USER=douyu_login
|
||||
MYSQL_PASSWORD=
|
||||
@@ -60,11 +60,11 @@ COOKIE_SECURE=false
|
||||
# CORS 允许的源(逗号分隔,不设则默认开发环境)
|
||||
# CORS_ORIGINS=https://example.com,https://www.example.com
|
||||
|
||||
# 应用宝充值 Worker(Docker Compose 内网服务;生产环境必须设置为随机长密钥)
|
||||
# 应用宝充值 Worker(与 Web 合并在同一容器;生产环境必须设置为随机长密钥)
|
||||
# 生成方式: python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
YYB_WORKER_KEY=
|
||||
# Docker 部署使用默认值;dev.sh 自动覆盖为 http://127.0.0.1:${YYB_WORKER_PORT:-8810}
|
||||
# YYB_WORKER_URL=http://yyb-worker:8810
|
||||
# Docker 与 dev.sh 均通过本机 Worker 访问,无需设置 YYB_WORKER_URL。
|
||||
# YYB_WORKER_URL=http://127.0.0.1:8810
|
||||
# YYB_WORKER_TIMEOUT=30
|
||||
# 本地调试 Worker(仅 dev.sh 使用,默认只绑定 127.0.0.1)
|
||||
# YYB_WORKER_PORT=8810
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ bg.jpg
|
||||
fullbg.jpg
|
||||
slice.jpg
|
||||
|
||||
# 前端
|
||||
web/frontend/node_modules/
|
||||
# Node 依赖
|
||||
**/node_modules/
|
||||
web/frontend/dist/
|
||||
data/web.db
|
||||
.DS_Store
|
||||
|
||||
+23
-6
@@ -10,7 +10,17 @@ RUN npm config set registry https://registry.npmmirror.com && \
|
||||
COPY web/frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
# ===== 阶段2: Python 运行时 =====
|
||||
# ===== 阶段2: 构建应用宝 Worker 的 Node 依赖 =====
|
||||
FROM node:20-slim AS yyb-node-builder
|
||||
|
||||
WORKDIR /build
|
||||
COPY services/yyb-worker/package.json services/yyb-worker/package-lock.json ./
|
||||
# 优先 npmmirror,失败时回退官方 registry,避免国内构建时依赖下载中断。
|
||||
RUN npm config set registry https://registry.npmmirror.com && \
|
||||
npm ci --omit=dev || \
|
||||
(npm config set registry https://registry.npmjs.org && npm ci --omit=dev)
|
||||
|
||||
# ===== 阶段3: Python 运行时 =====
|
||||
FROM python:3.12-slim AS runtime
|
||||
|
||||
ARG USE_CHINA_MIRRORS=true
|
||||
@@ -61,11 +71,17 @@ COPY utils/ ./utils/
|
||||
COPY web/backend/ ./web/backend/
|
||||
COPY scripts/ ./scripts/
|
||||
|
||||
# 将应用宝 Worker 合并进同一应用镜像;其 Python 依赖已纳入项目 uv.lock。
|
||||
COPY services/yyb-worker/runtime/ ./services/yyb-worker/runtime/
|
||||
COPY --from=yyb-node-builder /build/node_modules ./services/yyb-worker/runtime/node_modules
|
||||
COPY docker-entrypoint.sh ./docker-entrypoint.sh
|
||||
|
||||
# 复制前端构建产物
|
||||
COPY --from=frontend-builder /build/dist ./web/frontend/dist
|
||||
|
||||
# 创建数据和日志目录
|
||||
RUN mkdir -p /app/data /app/logs
|
||||
RUN mkdir -p /app/data /app/logs /app/data/yyb-worker-jobs && \
|
||||
chmod +x /app/docker-entrypoint.sh
|
||||
|
||||
# 环境变量
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
@@ -73,12 +89,13 @@ ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PATH="/app/.venv/bin:$PATH"
|
||||
# 生产镜像默认关闭文件监控重载,避免 data/logs 写入触发重启循环。
|
||||
ENV UVICORN_RELOAD=false
|
||||
ENV YYB_WORKER_URL=http://127.0.0.1:8810
|
||||
|
||||
EXPOSE 8800
|
||||
|
||||
# 健康检查
|
||||
# 健康检查同时覆盖 Web API 与同容器 Worker。
|
||||
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8800/api/health')" || exit 1
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8800/api/health'); urllib.request.urlopen('http://127.0.0.1:8810/health')" || exit 1
|
||||
|
||||
# 启动命令
|
||||
CMD ["python", "-m", "uvicorn", "web.backend.main:app", "--host", "0.0.0.0", "--port", "8800", "--workers", "1"]
|
||||
# 同一容器内启动 Web 与本机 Worker;入口脚本负责联动退出和信号转发。
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
|
||||
@@ -50,7 +50,7 @@ douyu_login_py/
|
||||
|
||||
### 应用宝充值 Worker
|
||||
|
||||
应用宝充值使用同仓库内独立的 `yyb-worker` Docker 服务,后台不直接运行逆向脚本。两个服务使用 Compose 默认内网通信,不共享目录、Cookie 或数据库。
|
||||
应用宝充值 Worker 已合并到 Web 应用容器中,内部仅监听 `127.0.0.1:8810`,不发布额外端口。任务数据与应用数据一起保存在 `data/yyb-worker-jobs/`。
|
||||
|
||||
```bash
|
||||
cd /opt/douyu_login_py
|
||||
@@ -60,13 +60,12 @@ python3 -c 'import secrets; print(secrets.token_urlsafe(32))'
|
||||
将生成的值写入项目 `.env`:
|
||||
|
||||
```dotenv
|
||||
YYB_WORKER_URL=http://yyb-worker:8810
|
||||
YYB_WORKER_KEY=随机密钥
|
||||
```
|
||||
|
||||
随后执行 `./deploy.sh`,它会同时构建并启动后台、MySQL 和 Worker。Worker 不发布端口;扫码图片和支付二维码只经后台授权接口返回。Worker 的任务文件保存在独立 Docker volume `yyb-worker-data`。服务器只需要此仓库,不需要部署 `js_reverse`。
|
||||
随后执行 `./deploy.sh`,它会构建并启动应用与 MySQL。Worker 不发布端口;扫码图片和支付二维码只经后台授权接口返回。服务器只需要此仓库,不需要部署 `js_reverse`。
|
||||
|
||||
本机热更新调试使用 `./dev.sh`(或 `./deploy.sh dev`)。它会构建并启动一个仅绑定 `127.0.0.1:8810` 的开发 Worker,后端自动使用该地址;Worker 动态数据写入 `data/yyb-worker-dev/`,按 `Ctrl+C` 会停止本次启动的容器。
|
||||
本机热更新调试使用 `./dev.sh`(或 `./deploy.sh dev`)。它会直接启动一个仅绑定 `127.0.0.1:8810` 的开发 Worker,后端自动使用该地址;Worker 动态数据写入 `data/yyb-worker-dev/`,按 `Ctrl+C` 会一并停止。
|
||||
|
||||
**常用命令:**
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ cmd_reset() {
|
||||
echo ""
|
||||
echo " 将删除以下内容:"
|
||||
echo " • MySQL 数据卷"
|
||||
echo " • 应用宝 Worker 任务数据卷(扫码会话、二维码和订单记录)"
|
||||
echo " • 应用宝 Worker 任务数据(扫码会话、二维码和订单记录)"
|
||||
echo " • 历史Cookie (data/cookies/)"
|
||||
echo " • 应用日志 (logs/)"
|
||||
echo " • Docker容器和镜像"
|
||||
@@ -138,6 +138,9 @@ cmd_reset() {
|
||||
echo " 删除Cookie..."
|
||||
rm -rf data/cookies/
|
||||
|
||||
echo " 删除应用宝 Worker 任务..."
|
||||
rm -rf data/yyb-worker-jobs/ data/yyb-worker-dev/
|
||||
|
||||
echo " 删除日志..."
|
||||
rm -rf logs/*.log
|
||||
|
||||
|
||||
@@ -36,10 +36,9 @@ DB_MAX_OVERFLOW="${DB_MAX_OVERFLOW:-20}"
|
||||
DB_POOL_TIMEOUT="${DB_POOL_TIMEOUT:-30}"
|
||||
DB_POOL_RECYCLE="${DB_POOL_RECYCLE:-1800}"
|
||||
YYB_WORKER_PORT="${YYB_WORKER_PORT:-8810}"
|
||||
YYB_WORKER_IMAGE="${YYB_WORKER_IMAGE:-douyu-yyb-worker:local}"
|
||||
DEV_YYB_WORKER_URL="${DEV_YYB_WORKER_URL:-http://127.0.0.1:${YYB_WORKER_PORT}}"
|
||||
YYB_WORKER_CONTAINER="${YYB_WORKER_CONTAINER:-douyu-yyb-worker-dev}"
|
||||
YYB_WORKER_DATA_DIR="${YYB_WORKER_DATA_DIR:-$ROOT_DIR/data/yyb-worker-dev}"
|
||||
YYB_WORKER_NODE_MODULES="$ROOT_DIR/services/yyb-worker/node_modules"
|
||||
LOG_DIR="$ROOT_DIR/logs"
|
||||
LOG_DATE="$(date +%Y-%m-%d)"
|
||||
BACKEND_LOG="$LOG_DIR/backend-$LOG_DATE.log"
|
||||
@@ -48,8 +47,7 @@ WORKER_LOG="$LOG_DIR/yyb-worker-$LOG_DATE.log"
|
||||
|
||||
BACKEND_PID=""
|
||||
FRONTEND_PID=""
|
||||
WORKER_LOG_PID=""
|
||||
WORKER_STARTED_BY_DEV=false
|
||||
WORKER_PID=""
|
||||
|
||||
stop_tree() {
|
||||
local pid="$1"
|
||||
@@ -68,10 +66,7 @@ cleanup() {
|
||||
trap - EXIT INT TERM
|
||||
stop_tree "$FRONTEND_PID"
|
||||
stop_tree "$BACKEND_PID"
|
||||
stop_tree "$WORKER_LOG_PID"
|
||||
if [ "$WORKER_STARTED_BY_DEV" = true ]; then
|
||||
docker stop "$YYB_WORKER_CONTAINER" >/dev/null 2>&1 || true
|
||||
fi
|
||||
stop_tree "$WORKER_PID"
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
@@ -143,26 +138,21 @@ mkdir -p "$YYB_WORKER_DATA_DIR"
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
start_yyb_worker() {
|
||||
if docker ps --format '{{.Names}}' | grep -qx "$YYB_WORKER_CONTAINER"; then
|
||||
echo "本地 YYB Worker 已运行: ${DEV_YYB_WORKER_URL}"
|
||||
return
|
||||
fi
|
||||
if docker ps -a --format '{{.Names}}' | grep -qx "$YYB_WORKER_CONTAINER"; then
|
||||
echo "发现已停止的本地 YYB Worker,正在清理旧容器..."
|
||||
docker rm "$YYB_WORKER_CONTAINER" >/dev/null
|
||||
if [ ! -d "$YYB_WORKER_NODE_MODULES/jsdom" ]; then
|
||||
echo "正在安装本地 YYB Worker Node 依赖..."
|
||||
(cd "$ROOT_DIR/services/yyb-worker" && npm ci)
|
||||
fi
|
||||
|
||||
echo "正在构建本地 YYB Worker..."
|
||||
YYB_WORKER_KEY="$YYB_WORKER_KEY" $COMPOSE build yyb-worker
|
||||
echo "正在启动本地 YYB Worker..."
|
||||
docker run -d --rm \
|
||||
--name "$YYB_WORKER_CONTAINER" \
|
||||
-p "127.0.0.1:${YYB_WORKER_PORT}:8810" \
|
||||
-e TZ="${TZ:-Asia/Shanghai}" \
|
||||
-e YYB_WORKER_KEY="$YYB_WORKER_KEY" \
|
||||
-v "$YYB_WORKER_DATA_DIR:/app/config/worker-jobs" \
|
||||
"$YYB_WORKER_IMAGE" >/dev/null
|
||||
WORKER_STARTED_BY_DEV=true
|
||||
(
|
||||
cd "$ROOT_DIR/services/yyb-worker/runtime"
|
||||
YYB_WORKER_KEY="$YYB_WORKER_KEY" \
|
||||
uv run --project "$ROOT_DIR" python scripts/yyb-worker.py \
|
||||
--host 127.0.0.1 \
|
||||
--port "$YYB_WORKER_PORT" \
|
||||
--data-dir "$YYB_WORKER_DATA_DIR" 2>&1 | tee -a "$WORKER_LOG"
|
||||
) &
|
||||
WORKER_PID=$!
|
||||
|
||||
echo "等待本地 YYB Worker 就绪..."
|
||||
for i in $(seq 1 15); do
|
||||
@@ -172,21 +162,14 @@ start_yyb_worker() {
|
||||
fi
|
||||
if [ "$i" = "15" ]; then
|
||||
echo "YYB Worker 启动超时,查看日志:"
|
||||
docker logs "$YYB_WORKER_CONTAINER" || true
|
||||
tail -n 100 "$WORKER_LOG" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
stream_yyb_worker_logs() {
|
||||
# docker logs 默认只输出到终端;持续转存后保留 Worker 的异常和协议脚本输出。
|
||||
(docker logs --tail 100 --follow "$YYB_WORKER_CONTAINER" 2>&1 | tee -a "$WORKER_LOG") &
|
||||
WORKER_LOG_PID=$!
|
||||
}
|
||||
|
||||
start_yyb_worker
|
||||
stream_yyb_worker_logs
|
||||
|
||||
echo "正在启动本地 MySQL..."
|
||||
MYSQL_IMAGE="$MYSQL_IMAGE" \
|
||||
|
||||
+4
-26
@@ -37,8 +37,8 @@ services:
|
||||
- COOKIE_SECURE=${COOKIE_SECURE:-false}
|
||||
# CORS 允许的源(逗号分隔)
|
||||
- CORS_ORIGINS=${CORS_ORIGINS:-}
|
||||
# 独立 yyb-worker 容器,只通过 Compose 默认内网访问。
|
||||
- YYB_WORKER_URL=${YYB_WORKER_URL:-http://yyb-worker:8810}
|
||||
# Worker 已合并到本容器,仅通过回环地址访问。
|
||||
- YYB_WORKER_URL=http://127.0.0.1:8810
|
||||
- YYB_WORKER_KEY=${YYB_WORKER_KEY:-}
|
||||
# Roundcube 邮件验证码读取服务地址(不填则使用代码默认值)
|
||||
- MAIL_ROUNDCUBE_URL=${MAIL_ROUNDCUBE_URL:-}
|
||||
@@ -50,21 +50,19 @@ services:
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
yyb-worker:
|
||||
condition: service_healthy
|
||||
# 增加文件描述符限制,避免 "Too many open files" 错误
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8800/api/health')"]
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8800/api/health'); urllib.request.urlopen('http://127.0.0.1:8810/health')"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
mysql:
|
||||
image: ${MYSQL_IMAGE:-docker.m.daocloud.io/library/mysql:8.4}
|
||||
image: ${MYSQL_IMAGE:-mysql:8.4}
|
||||
container_name: douyu-login-db
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
@@ -88,28 +86,8 @@ services:
|
||||
retries: 12
|
||||
start_period: 20s
|
||||
|
||||
yyb-worker:
|
||||
build:
|
||||
context: ./services/yyb-worker
|
||||
image: ${YYB_WORKER_IMAGE:-douyu-yyb-worker:local}
|
||||
container_name: douyu-yyb-worker
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- YYB_WORKER_KEY=${YYB_WORKER_KEY:?请在 .env 中设置 YYB_WORKER_KEY}
|
||||
volumes:
|
||||
- yyb-worker-data:/app/config/worker-jobs
|
||||
networks:
|
||||
- default
|
||||
healthcheck:
|
||||
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8810/health')"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
yyb-worker-data:
|
||||
|
||||
networks:
|
||||
order-site-net:
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
# docker compose run 传入命令时直接执行,例如 migrate-mysql 的迁移脚本。
|
||||
if [ "$#" -gt 0 ]; then
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Worker 仅监听容器回环地址,Web 后端通过 127.0.0.1 调用。
|
||||
python /app/services/yyb-worker/runtime/scripts/yyb-worker.py \
|
||||
--host 127.0.0.1 \
|
||||
--port 8810 \
|
||||
--data-dir /app/data/yyb-worker-jobs &
|
||||
worker_pid=$!
|
||||
web_pid=""
|
||||
|
||||
stop_processes() {
|
||||
kill -TERM "$worker_pid" 2>/dev/null || true
|
||||
if [ -n "$web_pid" ]; then
|
||||
kill -TERM "$web_pid" 2>/dev/null || true
|
||||
fi
|
||||
wait "$worker_pid" 2>/dev/null || true
|
||||
if [ -n "$web_pid" ]; then
|
||||
wait "$web_pid" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
trap stop_processes INT TERM
|
||||
|
||||
python -m uvicorn web.backend.main:app --host 0.0.0.0 --port 8800 --workers 1 &
|
||||
web_pid=$!
|
||||
|
||||
while :; do
|
||||
if ! kill -0 "$worker_pid" 2>/dev/null; then
|
||||
if wait "$worker_pid"; then exit_code=0; else exit_code=$?; fi
|
||||
kill -TERM "$web_pid" 2>/dev/null || true
|
||||
wait "$web_pid" 2>/dev/null || true
|
||||
exit "$exit_code"
|
||||
fi
|
||||
if ! kill -0 "$web_pid" 2>/dev/null; then
|
||||
if wait "$web_pid"; then exit_code=0; else exit_code=$?; fi
|
||||
kill -TERM "$worker_pid" 2>/dev/null || true
|
||||
wait "$worker_pid" 2>/dev/null || true
|
||||
exit "$exit_code"
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
@@ -24,6 +24,8 @@ dependencies = [
|
||||
"scipy>=1.13.0",
|
||||
"pyexecjs>=1.5.1",
|
||||
"pymysql>=1.1,<2",
|
||||
"curl-cffi>=0.13",
|
||||
"segno>=1.6",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
FROM node:20-bookworm-slim
|
||||
|
||||
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 NODE_ENV=production
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends python3 python3-pip ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& python3 -m pip install --no-cache-dir --break-system-packages --upgrade pip
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --omit=dev
|
||||
COPY requirements-worker.txt ./
|
||||
RUN python3 -m pip install --no-cache-dir --break-system-packages -r requirements-worker.txt
|
||||
COPY runtime/ ./
|
||||
RUN mkdir -p /app/config/worker-jobs
|
||||
EXPOSE 8810
|
||||
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD python3 -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8810/health')"
|
||||
CMD ["python3", "scripts/yyb-worker.py", "--host", "0.0.0.0", "--port", "8810"]
|
||||
@@ -1,15 +1,14 @@
|
||||
# YYB Worker Runtime
|
||||
|
||||
此目录是部署到 `douyu_login_py` 的最小 YYB 运行时。构建上下文只包含 Worker 启动所需的 Python、Node/jsdom 依赖、协议脚本和静态重放数据。
|
||||
此目录是随 `douyu-login` 主镜像部署的 YYB 运行时代码。构建会将 Worker 所需的 Python、Node/jsdom 依赖、协议脚本和静态重放数据纳入同一镜像。
|
||||
|
||||
- 源码分析和协议验证工作区仍在 `js_reverse/sites/yyb`;不要将该分析仓库上传到服务器。
|
||||
- 不要向 `runtime/config/` 放入 Cookie、订单响应、二维码或付款链接。每个任务的动态数据仅写入 Docker volume 挂载的 `/app/config/worker-jobs/<job-id>/`。
|
||||
- 不要向 `runtime/config/` 放入 Cookie、订单响应、二维码或付款链接。每个任务的动态数据仅写入挂载目录 `/app/data/yyb-worker-jobs/<job-id>/`。
|
||||
- `mall-order-template.json` 仅保留请求结构与非用户常量。登录态、商品、区服、角色、支付平台、动态令牌和加密字段均由当前任务运行时填充。
|
||||
- 本服务没有 Compose 端口映射,只允许 `douyu-login` 通过默认 Compose 内网的 `http://yyb-worker:8810` 访问。`YYB_WORKER_KEY` 必须与后台服务配置一致。
|
||||
- Worker 在应用容器内仅监听 `127.0.0.1:8810`,Web 后端通过本机地址访问;`YYB_WORKER_KEY` 必须与后台服务配置一致。
|
||||
|
||||
本地独立验证:
|
||||
|
||||
```bash
|
||||
docker build -t douyu-yyb-worker:local services/yyb-worker
|
||||
docker run --rm -e YYB_WORKER_KEY=test-key douyu-yyb-worker:local
|
||||
YYB_WORKER_KEY=test-key uv run python runtime/scripts/yyb-worker.py --host 127.0.0.1
|
||||
```
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
curl_cffi>=0.13
|
||||
pycryptodome>=3.20
|
||||
segno>=1.6
|
||||
@@ -14,6 +14,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
@@ -96,7 +97,7 @@ def _start_login(job_id: str, provider: str, timeout: int) -> None:
|
||||
directory = _job_dir(job_id)
|
||||
session = directory / "mall-session.json"
|
||||
qr = directory / ("qq-login.jpg" if provider == "qq" else "wechat-login.jpg")
|
||||
command = ["python3", f"scripts/login-{provider}.py", "--session", str(session),
|
||||
command = [sys.executable, f"scripts/login-{provider}.py", "--session", str(session),
|
||||
"--qr", str(qr), "--timeout", str(timeout)]
|
||||
with _lock:
|
||||
job["provider"] = provider
|
||||
@@ -135,7 +136,7 @@ def _start_payment(job_id: str, selection: dict) -> None:
|
||||
session = directory / "mall-session.json"
|
||||
response = directory / "mall-order-response.json"
|
||||
output = directory / "jsdom-order"
|
||||
command = ["python3", "main.py", "mall", "auto", "--session", str(session),
|
||||
command = [sys.executable, "main.py", "mall", "auto", "--session", str(session),
|
||||
"--order-template", str(ROOT / "config" / "mall-order-template.json"),
|
||||
"--quantity", "1", "--product-id", str(selection["product_id"]),
|
||||
"--offer-id", str(selection["offer_id"]),
|
||||
@@ -149,7 +150,7 @@ def _start_payment(job_id: str, selection: dict) -> None:
|
||||
job = _jobs[job_id]
|
||||
if job.get("status") != "success" or not response.exists():
|
||||
return
|
||||
pay_command = ["python3", "scripts/jsdom-pay.py", "--session", str(session),
|
||||
pay_command = [sys.executable, "scripts/jsdom-pay.py", "--session", str(session),
|
||||
"--mall-response", str(response), "--out-dir", str(output),
|
||||
"--zone-id", str(selection["zone_id"]),
|
||||
"--pf", str(selection.get("order_pf", "")),
|
||||
|
||||
@@ -200,6 +200,30 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl-cffi"
|
||||
version = "0.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "cffi" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b4/23/d32e113b16dbfb458bea408871ed98dd12f306a366a04215e84537e0af7e/curl_cffi-0.16.0.tar.gz", hash = "sha256:b00b423da8028eb6221e3b63bcd63d681150c07cee8b16000d1f7ea292731895", size = 238344, upload-time = "2026-08-01T13:45:12.372Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/fe/0c330de78421af13e6384ab948e3adbcd4c638b06b53b7fff108bf1db121/curl_cffi-0.16.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6128021320f74999ec1216c1817b2c3adcb0f334d204add1ccf18e248bf7efcb", size = 3023503, upload-time = "2026-08-01T13:44:33.452Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/49/3b502d0d09e427b1bdec4f7339bb115c971c9b3fdaf355d02ca06e97ad61/curl_cffi-0.16.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:edd5f6e8f122157f4d2351b0b5e48e6a1c0677a2064da71451bb30ef57af19ba", size = 2780341, upload-time = "2026-08-01T13:44:35.131Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/b5/b341f96f9fa12b28d1150913a1f9007a09a36757c81b4100373c5bdbf78b/curl_cffi-0.16.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:93615d44f23e56c1256700c2e78de4b879310f39a5621828ea7e2a5ecc04bdda", size = 12824596, upload-time = "2026-08-01T13:44:36.556Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/3b/d600b20bff0c55b80b156dc9be0de7c3b3ee2d29977d0a839ea703fab978/curl_cffi-0.16.0-cp310-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3c31e71bf68a9c02a279a184ec9c0ea7c80ce1ef4f1d35073fae3124eb3a7868", size = 12647842, upload-time = "2026-08-01T13:44:38.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/45/9208864ec429558efac168088e50f8a91b947f742ee128cff55b88c7b635/curl_cffi-0.16.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:182416f07d71a342240554fa62c22e591999b78c225b21d3fe27d9f807420dd6", size = 13472637, upload-time = "2026-08-01T13:44:40.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/c8/1639a1d9c8b64d0323330b219b7207a54b14fc54982c30cb08c8cc95aa16/curl_cffi-0.16.0-cp310-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d95c0deccc2184eeee7c2aa18d07de261184b418210995aabd0d29f98717a05c", size = 12828918, upload-time = "2026-08-01T13:44:43.049Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/02/bcdf03ea583a445280568c9b163c10668037ee09ece62e78c15846a62df0/curl_cffi-0.16.0-cp310-abi3-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ce1f823bc5ce675291a7cf14781496775dfae9298638c25059f2565f6a58a704", size = 12604731, upload-time = "2026-08-01T13:44:45.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/8b/4ddae52044c537ace13ad7e46dded8e9340a64e0704e320455c5151108a8/curl_cffi-0.16.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e52586a9dc4ed5e75faa39be0f30353b10cdc7410bad276beb013085e974bb44", size = 12576433, upload-time = "2026-08-01T13:44:47.626Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/f5/38ee2f039db7832f07ce91f6a3d22d87ebfcfaa541130371ac1faa5caea9/curl_cffi-0.16.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce87f301b31147711c3aebc86fb7e16d8dc48f7e5df272c1bea760c687e28eef", size = 13240699, upload-time = "2026-08-01T13:44:49.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/03/b9df2973f1119f9d11d8fb3bf2682e5ffe5c52ef3ab89f720473c60fe97e/curl_cffi-0.16.0-cp310-abi3-win_amd64.whl", hash = "sha256:e22a8212d830108e977ff394237f637238e265f5f65037d6c1ee71ea8cc03bcb", size = 1976497, upload-time = "2026-08-01T13:44:51.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/8b/092beeb5fbe3b7370666708eb5618a9e593ffc80ecb2c97c3395158d270b/curl_cffi-0.16.0-cp310-abi3-win_arm64.whl", hash = "sha256:095fc36e4988736f31521d6fe0aa1f243dba22656b4818fc2fb3b7a547e7a9ba", size = 1711122, upload-time = "2026-08-01T13:44:53.242Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/ea/81cf3858b256494b31a554cf76bbd345def3ea7e7a1a592cc515633b4e28/curl_cffi-0.16.0-cp313-abi3-android_24_arm64_v8a.whl", hash = "sha256:06b1c7e07af8ff7c4c5ce4086ea89cc582ebff9adff4a37cfffa5f5de5d5b943", size = 8603463, upload-time = "2026-08-01T13:44:55.003Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "douyu-login-py"
|
||||
version = "0.2.0"
|
||||
@@ -207,6 +231,7 @@ source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "alembic" },
|
||||
{ name = "bcrypt" },
|
||||
{ name = "curl-cffi" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "loguru" },
|
||||
{ name = "numpy" },
|
||||
@@ -221,6 +246,7 @@ dependencies = [
|
||||
{ name = "python-multipart" },
|
||||
{ name = "requests", extra = ["socks"] },
|
||||
{ name = "scipy" },
|
||||
{ name = "segno" },
|
||||
{ name = "sqlalchemy" },
|
||||
{ name = "uvicorn", extra = ["standard"] },
|
||||
{ name = "websockets" },
|
||||
@@ -230,6 +256,7 @@ dependencies = [
|
||||
requires-dist = [
|
||||
{ name = "alembic", specifier = ">=1.18.4" },
|
||||
{ name = "bcrypt", specifier = ">=4.0.0" },
|
||||
{ name = "curl-cffi", specifier = ">=0.13" },
|
||||
{ name = "fastapi", specifier = ">=0.110.0" },
|
||||
{ name = "loguru", specifier = ">=0.7.0" },
|
||||
{ name = "numpy", specifier = ">=1.24.0" },
|
||||
@@ -244,6 +271,7 @@ requires-dist = [
|
||||
{ name = "python-multipart", specifier = ">=0.0.9" },
|
||||
{ name = "requests", extras = ["socks"], specifier = ">=2.31.0" },
|
||||
{ name = "scipy", specifier = ">=1.13.0" },
|
||||
{ name = "segno", specifier = ">=1.6" },
|
||||
{ name = "sqlalchemy", specifier = ">=2.0.0" },
|
||||
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.27.0" },
|
||||
{ name = "websockets", specifier = ">=16.0" },
|
||||
@@ -693,6 +721,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/99/0e/33f32a2a58987e26aec0f7df252cbbad1e90ae77bdbc76f40dd4ed0cf0ea/scipy-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:d88363fd9d8fbd3511bd273f1a49efb2a540773ddf92a91d57498ce7dd7f3e76", size = 24351481, upload-time = "2026-06-19T15:00:11.103Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "segno"
|
||||
version = "1.6.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/2e/b396f750c53f570055bf5a9fc1ace09bed2dff013c73b7afec5702a581ba/segno-1.6.6.tar.gz", hash = "sha256:e60933afc4b52137d323a4434c8340e0ce1e58cec71439e46680d4db188f11b3", size = 1628586, upload-time = "2025-03-12T22:12:53.324Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/02/12c73fd423eb9577b97fc1924966b929eff7074ae6b2e15dd3d30cb9e4ae/segno-1.6.6-py3-none-any.whl", hash = "sha256:28c7d081ed0cf935e0411293a465efd4d500704072cdb039778a2ab8736190c7", size = 76503, upload-time = "2025-03-12T22:12:48.106Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
|
||||
Reference in New Issue
Block a user