diff --git a/Dockerfile b/Dockerfile index ba3a6bb..c152104 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,18 @@ # 使用国内镜像源加速 -FROM mcr.microsoft.com/playwright/python:v1.40.0-jammy +FROM python:3.10-slim-bullseye # 设置工作目录 WORKDIR /app # 设置环境变量 ENV PYTHONUNBUFFERED=1 -ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright ENV TZ=Asia/Shanghai +# 安装 wkhtmltopdf(包含 wkhtmltoimage)与中文字体 +RUN apt-get update && \ + apt-get install -y --no-install-recommends wkhtmltopdf fonts-noto-cjk && \ + rm -rf /var/lib/apt/lists/* + # 配置 pip 使用国内镜像源 RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ && pip config set install.trusted-host mirrors.aliyun.com @@ -22,10 +26,8 @@ RUN pip install --no-cache-dir -r requirements.txt COPY app.py . COPY database.py . COPY db_pool.py . -COPY playwright_automation.py . COPY api_browser.py . COPY browser_pool_worker.py . -COPY browser_installer.py . COPY password_utils.py . COPY crypto_utils.py . COPY task_checkpoint.py . diff --git a/README.md b/README.md index e6d0c17..e642ffd 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ ## 项目简介 -本项目是一个 **Docker 容器化应用**,使用 Flask + Playwright + SQLite 构建,提供: +本项目是一个 **Docker 容器化应用**,使用 Flask + Requests + wkhtmltopdf + SQLite 构建,提供: - 多用户注册登录系统 -- 浏览器自动化任务 +- 自动化任务(HTTP 模拟) - 定时任务调度 - 截图管理 - VIP用户管理 @@ -22,7 +22,8 @@ - **后端**: Python 3.8+, Flask - **数据库**: SQLite -- **自动化**: Playwright (Chromium) +- **自动化**: Requests + BeautifulSoup +- **截图**: wkhtmltopdf / wkhtmltoimage - **容器化**: Docker + Docker Compose - **前端**: HTML + JavaScript + Socket.IO @@ -39,10 +40,8 @@ zsglpt/ ├── database.py # 数据库稳定门面(对外 API) ├── db/ # DB 分域实现 + schema/migrations ├── db_pool.py # 数据库连接池 -├── playwright_automation.py # Playwright 自动化 ├── api_browser.py # Requests 自动化(主浏览流程) -├── browser_pool_worker.py # 截图 WorkerPool(浏览器复用) -├── browser_installer.py # 浏览器安装检查 +├── browser_pool_worker.py # 截图 WorkerPool ├── app_config.py # 配置管理 ├── app_logger.py # 日志系统 ├── app_security.py # 安全模块 @@ -122,8 +121,8 @@ cd /www/wwwroot/zsgpt2 ### 步骤4: 创建必要的目录 ```bash -mkdir -p data logs 截图 playwright -chmod 777 data logs 截图 playwright +mkdir -p data logs 截图 +chmod 777 data logs 截图 ``` ### 步骤5: 构建并启动Docker容器 @@ -447,19 +446,19 @@ docker-compose down docker-compose up -d ``` -### 5. 浏览器下载失败 +### 5. 截图工具未安装 -**问题**: Playwright浏览器下载失败 +**问题**: wkhtmltoimage 命令不存在 **解决方案**: ```bash # 进入容器手动安装 docker exec -it knowledge-automation-multiuser bash -playwright install chromium +apt-get update +apt-get install -y wkhtmltopdf -# 或使用国内镜像 -export PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ -playwright install chromium +# 验证安装 +wkhtmltoimage --version ``` --- @@ -631,7 +630,12 @@ docker logs knowledge-automation-multiuser | grep "数据库" |--------|------|--------| | TZ | 时区 | Asia/Shanghai | | PYTHONUNBUFFERED | Python输出缓冲 | 1 | -| PLAYWRIGHT_BROWSERS_PATH | 浏览器路径 | /ms-playwright | +| WKHTMLTOIMAGE_PATH | wkhtmltoimage 可执行文件路径 | 自动探测 | +| WKHTMLTOIMAGE_JS_DELAY_MS | JS 等待时间(毫秒) | 3000 | +| WKHTMLTOIMAGE_WIDTH | 截图宽度 | 1920 | +| WKHTMLTOIMAGE_QUALITY | JPG截图质量 | 95 | +| WKHTMLTOIMAGE_TIMEOUT_SECONDS | 截图超时时间(秒) | 60 | +| WKHTMLTOIMAGE_USER_AGENT | 截图使用的 UA | Chrome 120 | --- @@ -641,13 +645,13 @@ docker logs knowledge-automation-multiuser | grep "数据库" - **项目名称**: 知识管理平台自动化工具 - **版本**: Docker 多用户版 -- **技术栈**: Python + Flask + Playwright + SQLite + Docker +- **技术栈**: Python + Flask + Requests + wkhtmltopdf + SQLite + Docker ### 常用文档链接 - [Docker 官方文档](https://docs.docker.com/) - [Flask 官方文档](https://flask.palletsprojects.com/) -- [Playwright 官方文档](https://playwright.dev/python/) +- [wkhtmltopdf 官方文档](https://wkhtmltopdf.org/) ### 故障排查 @@ -683,8 +687,8 @@ ssh root@your-ip # 3. 进入目录并创建必要目录 cd /www/wwwroot/zsgpt2 -mkdir -p data logs 截图 playwright -chmod 777 data logs 截图 playwright +mkdir -p data logs 截图 +chmod 777 data logs 截图 # 4. 启动容器 docker-compose up -d diff --git a/admin-frontend/src/api/security.js b/admin-frontend/src/api/security.js index 7d117f1..a7aed95 100644 --- a/admin-frontend/src/api/security.js +++ b/admin-frontend/src/api/security.js @@ -46,6 +46,11 @@ export async function getIpRisk(ip) { return data } +export async function clearIpRisk(ip) { + const { data } = await api.post('/admin/security/ip-risk/clear', { ip }) + return data +} + export async function getUserRisk(userId) { const safeUserId = encodeURIComponent(String(userId || '').trim()) const { data } = await api.get(`/admin/security/user-risk/${safeUserId}`) @@ -56,4 +61,3 @@ export async function cleanup() { const { data } = await api.post('/admin/security/cleanup', {}) return data } - diff --git a/admin-frontend/src/pages/ReportPage.vue b/admin-frontend/src/pages/ReportPage.vue index 9090f37..4ed91c6 100644 --- a/admin-frontend/src/pages/ReportPage.vue +++ b/admin-frontend/src/pages/ReportPage.vue @@ -25,6 +25,7 @@ const refreshStats = inject('refreshStats', null) const adminStats = inject('adminStats', null) const loading = ref(false) +const refreshing = ref(false) const lastUpdatedAt = ref('') const taskStats = ref(null) @@ -181,9 +182,13 @@ const runningCountsLabel = computed(() => { return `运行中 ${runningCount} / 排队 ${queuingCount} / 并发上限 ${maxGlobal || maxConcurrentGlobal.value || '-'}` }) -async function refreshAll() { - if (loading.value) return - loading.value = true +async function refreshAll(options = {}) { + const showLoading = options.showLoading ?? true + if (refreshing.value) return + refreshing.value = true + if (showLoading) { + loading.value = true + } try { const [ taskResult, @@ -217,15 +222,22 @@ async function refreshAll() { await refreshStats?.() recordUpdatedAt() } finally { - loading.value = false + refreshing.value = false + if (showLoading) { + loading.value = false + } } } let refreshTimer = null +function manualRefresh() { + return refreshAll({ showLoading: true }) +} + onMounted(() => { - refreshAll() - refreshTimer = setInterval(refreshAll, 1000) + refreshAll({ showLoading: false }) + refreshTimer = setInterval(() => refreshAll({ showLoading: false }), 1000) }) onUnmounted(() => { @@ -252,7 +264,7 @@ onUnmounted(() => {