外观
Sumjess Media:把 NAS 做成一个可接入的网站媒体中枢
约 2981 字大约 10 分钟
NASNode.jsMinIOLsky Pro
2026-06-11
公开分享说明:本文是项目复盘版,不包含真实 Token、密钥、内网地址、NAS 真实路径和后台配置。示例域名、Token 和命令里的敏感值都使用占位符。

我一开始想要的其实很简单:有一个稳定的地方放图片、视频、压缩包和网站素材;日常可以网页上传,网站后端也能通过 API 上传;图片最好继续走图床,其他大文件走对象存储;出了问题还能知道是图床、对象存储、备份还是访问安全出了问题。
最后做出来的 Sumjess Media,不再只是一个“上传页面”,而是一个跑在 NAS 上的私有媒体网关:
- 人工上传走 Web 后台。
- 网站、自动化工具、客户端走统一 API。
- 图片自动分流到 Lsky Pro。
- 视频、音频、压缩包、普通文件自动分流到 MinIO。
- Sumjess Media 自己保存项目、Token、媒体索引、审计日志和访问安全记录。
- NAS 定时任务负责备份和 AI 诊断,Web 容器不直接执行主机级操作。
为什么不直接用图床或对象存储
直接把网站接到图床或 MinIO,短期也能跑。但时间一长,会遇到几个很现实的问题:
- 图片、视频、压缩包的存储后端不同,调用方式也不同。
- 不同网站、不同工具共用一个 Token,后续排查和撤销会很痛苦。
- 文件上传成功后,网站还要自己记录链接、类型、大小、项目归属。
- 图床、对象存储、网站数据库三边不一致时,很难判断到底哪里才是事实来源。
- NAS 上的备份、端口、访问安全和诊断如果没有统一入口,维护会变成翻日志和猜状态。
所以我把 Sumjess Media 设计成一个中间层。外部只面对它;它在后端根据文件类型决定具体去哪儿。
整体架构

这套系统的核心组件是:
| 组件 | 作用 |
|---|---|
| Nginx Gateway | 提供公网 HTTPS 入口、转发请求、配合上传限制 |
| Sumjess Media | Web 后台、API、上传接收、文件分流、审计和索引 |
| Lsky Pro | 图片存储、图片外链和相册管理 |
| MinIO | 视频、音频、压缩包、文档和普通对象存储 |
| SQLite | Sumjess Media 自己的项目、Token、媒体索引和访问记录 |
| NAS 定时任务 | 备份、生产检查、AI 诊断快照和分析 |
这里有一个很重要的边界:Sumjess Media、Lsky Pro 和 MinIO 是三套元数据系统。Sumjess Media 记录“我上传过什么、属于哪个项目、最终链接是什么”;Lsky 管图片和相册;MinIO 管对象。它们不应该被强行当成同一个数据库使用。
这个边界让我后面处理删除、404、备份和诊断时都更清醒:如果外部对象已经被删了,Sumjess Media 可以容忍存储侧不存在,然后继续清理自己的索引;如果要做对账,也应该是独立的审计任务,而不是跨库硬改。
上传链路

上传接口统一使用 files 字段,支持单文件和多文件。网站后端上传前可以先查询状态接口,拿到当前上传限制和后端可用状态:
curl https://media.example.com/api/v1/status \
-H "Authorization: Bearer <PROJECT_TOKEN>"真正上传时:
curl -X POST https://media.example.com/api/v1/upload \
-H "Authorization: Bearer <PROJECT_TOKEN>" \
-F "files=@./demo.png" \
-F "files=@./demo.zip"后端收到文件后会做几件事:
- 用项目 Token 判断请求属于哪个项目。
- 检查文件数量、大小和权限。
- 按 MIME 类型判断路由。
- 图片上传到 Lsky Pro。
- 视频、音频、压缩包、文档等上传到 MinIO。
- 把成功或失败结果写入 Sumjess Media 的媒体索引和审计日志。
- 给调用方返回每个文件的处理结果。
我特意让 API 返回两个数组:
{
"ok": false,
"partial": true,
"summary": {
"total": 3,
"success": 2,
"failed": 1
},
"files": [],
"results": []
}files[] 只放成功文件,兼容老接入方式;results[] 放每个文件的成功或失败。这样批量上传时,即使其中一个文件失败,其他成功文件也不会丢。
还有一个容易被忽略的体验点:浏览器上传进度到 100%,只代表文件已经传到 Sumjess Media 后台,不代表图床或对象存储已经处理完成。所以后台 UI 把过程拆成“正在传输”和“后台处理中”,避免用户误以为卡住了。
项目与 Token:把权限拆细
我没有让所有网站共用一个上传 Token,而是引入“项目”这个概念。
一个项目可以包含:
- 项目 ID 和名称。
- MinIO 对象前缀。
- Lsky 相册 ID。
- 一个或多个项目 Token。
- Token 权限范围,例如上传、查询、删除。
这样做有几个好处:
- 博客、工具站、自动化客户端可以彼此隔离。
- 某个 Token 泄露时,只撤销对应项目或对应用途。
- 访问安全页能看到某个项目 Token 从哪些 IP 调用过。
- 删除接口只能删除当前 Token 所属项目下的素材。
项目 Token 明文只在生成时显示一次,持久化只保存哈希。这个规则有点麻烦,但值得:一旦文档、数据库、后台页面或日志泄漏,攻击面会小很多。
安全与运维闭环
这套系统不是面向大众开放的公共图床,所以安全策略不是“尽量让所有人都能访问”,而是“保证可信来源能稳定访问,异常来源能被观察和收口”。
目前我把它分成几层:
| 层次 | 做法 |
|---|---|
| Token 隔离 | 一个项目一个用途,Token 不进前端、不进 URL、不进公开日志 |
| IP 访问规则 | 支持黑名单、白名单、仅限白名单访问、仅限中国 IP 访问 |
| Token 来源记录 | 记录项目 Token 的来源 IP、首次使用、最后使用和累计次数 |
| 可信代理边界 | 只有可信反代来源才信任 X-Forwarded-For / X-Real-IP |
| 日志降噪 | 静态资源、健康检查不默认刷屏,访问日志保留有上限 |
| 操作审计 | 上传、删除、项目、Token、设置类动作都进入审计记录 |
我比较坚持的一点是:不做自动拉黑。
自动拉黑听起来很帅,但私有服务的访问来源可能很复杂:家庭网络出口会变,移动网络会变,网站后端和监控节点也可能迁移。比起自动封人,我更愿意先记录、展示、人工确认,再决定拉黑、白名单或轮换 Token。
备份和 AI 诊断
Sumjess Media 的 Web 容器不执行 NAS 主机级备份命令。备份由 NAS 定时任务执行,Web 后台只读取脱敏后的备份状态。
这个边界很重要:Web 应用哪怕被打穿,也不应该天然拥有主机级清理、备份、改配置的能力。
备份关注几类东西:
- Sumjess Media 配置和 SQLite。
- Lsky Pro 的配置、数据库和必要数据。
- MinIO 的配置和对象数据状态。
- 网关配置和证书相关状态。
AI 诊断也是类似思路:不是让 AI 登录 NAS 自由执行命令,而是由固定脚本生成一份脱敏快照,再把快照交给模型分析。对外只开放最新诊断结论,不开放媒体库、文件链接、原始快照、Token 或密钥。
这个设计让 AI 更像一个“值班分析员”,而不是一个拥有生产权限的机器人。
Lsky 相册这个坑
图片分流到 Lsky Pro 后,我希望不同项目进入不同相册。实际接入时发现,API 上传并不一定天然按传入的相册 ID 归档,可能会回到用户默认相册。
最后的处理方式是保留一个本地补丁:当 API 请求传入相册 ID,并且该相册属于当前 Token 用户时,才允许覆盖默认相册。这个规则既满足项目归档,也避免把图片写进不属于自己的相册。
这件事给我的提醒是:集成第三方系统时,不要只看 API 参数存在不存在,还要确认生产环境真的按这个参数执行。
为什么现在仍然是单文件主应用
Sumjess Media 当前主应用仍然以一个主文件承载核心逻辑。站在“工程洁癖”的角度,它当然可以拆成 config、db、routes、services、views。
但这个项目当时的目标是上线稳定,而不是追求目录结构漂亮。上传、分流、审计、备份状态、AI 诊断、访问安全都已经在生产链路里,贸然大拆很容易引入行为差异。
所以当前策略是:
- 上线前只做小范围修改。
- 行为稳定后再考虑模块化。
- 每次改动后跑本地语法检查、客户端脚本检查和生产依赖审计。
- 发布前先备份,再部署,再验证。
这个取舍不华丽,但很适合个人 NAS 项目:先稳,再美。
这套系统带来的变化
现在我上传素材时,不再需要想“这个文件应该丢给哪个后端”。我只需要选择项目,上传文件,系统会自动分流并返回链接。
网站接入时,也不需要知道 Lsky 和 MinIO 的细节。网站后端只需要拿自己的项目 Token 调 Sumjess Media:
网站后端 -> Sumjess Media API -> Lsky / MinIO如果以后某个网站不用了,撤销它的 Token 即可;如果某个来源 IP 异常,可以从访问安全页看出来;如果上传失败,可以从状态、日志、备份和 AI 诊断一路排查。
这比“我有一个图床”更接近我真正需要的东西:一个长期可维护的私有媒体入口。
后续想做的事
这个项目还有不少可以继续打磨的方向:
- 把主应用逐步拆成更清晰的模块。
- 增加 Lsky、MinIO、Sumjess Media 三方对账任务。
- 把上传改成任务队列,提供更准确的服务端处理进度。
- 增加更完整的 API smoke test。
- 做一个只读的 MinIO 对象检查页。
- 把 Lsky 相册补丁变成更可维护的插件或上游方案。
不过这些都不是第一优先级。对我来说,当前最重要的是:它能稳定接住素材,能清楚记录发生过什么,能在出问题时给出足够线索,并且不会把密钥和生产权限暴露到不该出现的地方。
小结
Sumjess Media 的价值,不在于用了多复杂的技术栈,而在于把几个原本分散的东西收成了一个稳定入口:
- 上传入口统一。
- 存储后端分工明确。
- 项目和 Token 可以隔离。
- 访问安全可观察。
- 备份与诊断不越权。
- 文档和发布流程能交接。
个人 NAS 项目最怕的是“能跑,但没人敢动”。这套媒体库现在还不算完美,但它已经从一个能上传文件的小工具,长成了一个可以维护、可以接入、可以排障的私有媒体系统。
