空白名片

API 图库插件开发日志

API 图库插件开发日志 项目:halo-plugin-api-gallery(API 图库) 状态:v1.4.2 已发布,开源地址 https://github.com/lu7875/halo-plugin-api-gallery 插件包: https://github.com/lu7875/halo-plugin-...

评论
API 图库插件开发日志

API 图库插件开发日志

项目:halo-plugin-api-gallery(API 图库) 状态:v1.4.2 已发布,开源地址 https://github.com/lu7875/halo-plugin-api-gallery 插件包:https://github.com/lu7875/halo-plugin-api-gallery/releases/download/v1.4.2/api-gallery-1.4.2.jar

一、为什么做这个插件

站点想有一个图库页,但直接自己存图有几个绕不开的问题:服务器存储有限、图片版权归属复杂、第三方图床又有防盗链和失效风险。

最后定的思路是做一个聚合器而不是存储器:后台配置任意图片 API 地址,插件定时拉取素材,但只缓存地址等元数据,文件始终由第三方图源提供——不下载、不写入附件库、不占本地存储。图挂了最多是前台加载不出,站点本体不受影响。

API 图库插件前台展示页——瀑布流 + 懒加载 + 灯箱 + 按源筛选

二、架构设计

整个插件围绕一条数据流展开:

后台设置表单 → ConfigMap(api-gallery-config)
                    │  watch(保存即触发)
                    ▼
              GalleryService.sync()
                    │  ApiUrlSafety 校验 + 3xx 逐跳复检
                    │  HTTP GET 各数据源(Redirect.NEVER)
                    ▼
        内存缓存(图片元数据,进程内)
                    │
        ┌───────────┴───────────┐
        ▼                       ▼
公开只读组 /items /random   PhotoBridgeService → Photo 记录(只写地址)

几个关键设计决策:

  • 结构自适应解析:图片 API 的响应结构五花八门,插件自动识别顶层数组、{items:[...]} 包裹、单对象(随机图)三种结构,字段名(url/id/title/tags)可单独指定,留空自动推断。
  • 对接官方图库管理插件:把图片地址写成 Photo 记录,后台 /console/photos 和主题 /photos 页面直接展示,依然不占存储。
  • 访客自动放行:插件启动时自动创建 api-gallery-viewer 角色并打聚合标签合并进匿名角色,前台页面无需登录。

站点前台 /photos 页面——对接「图库管理」插件后的展示效果

三、开发过程中的关键问题

1. 随机图源没有列表接口怎么办(v1.1.0)

很多免费图源每次请求直接 302 跳转到一张随机图,没有列表接口。解决办法是重复采样 + 去重:按配置的采样次数串行请求(120ms 间隔,不打挂图源),收集每次的跳转地址去重后攒成图集。实测采样 12 次约 3.4 秒能拿到 11–12 张不重复的图。

2. 攒满后图库永久静止(v1.3.0 修)

这是最隐蔽的一个 bug:旧版本同步时把旧缓存排在前面、新采样的图排在后面,达到「最多累积」上限后被截断丢弃的永远是新图。结果就是图池一旦攒满,定时同步照常跑,内容却永久静止。

修复方案是滚动更新:新图插最前、最老的被淘汰(并同步从图库删除对应记录),图池大小稳定但内容每轮都在换。同时保证拉图失败时保住已有数据,不会清空。

3. 安全整改(v1.4.0)

复查时发现两个必须堵上的口子:

  • 匿名可读管理接口:1.3.1 及以前所有接口共用一个 API group,匿名角色拿到该组的 get/list,意味着访客可以匿名读取图源地址明细、甚至通过 /diagnose 让服务端对外发请求。1.4.0 拆成两个 group,管理诊断组永不对匿名放行。
  • SSRF 风险:插件会根据后台配置的地址发请求,如果不加校验,内网地址、云厂商元数据端点(169.254.169.254)都可能被指向。现在每次请求前统一校验协议与网段,DNS 解析后的每一条 A/AAAA 记录都过检查,防 DNS rebinding。

4. 应用市场审核整改(v1.4.1 / v1.4.2)

提交 Halo 应用市场后收到三条审核意见:3xx 跳转可绕过 SSRF 防护、上架链接 404/500、需补充人工测试声明。

  • v1.4.1:所有对外请求改为 Redirect.NEVER,新增 sendFollowingRedirects() 手动逐跳跟随,每一跳跳转前重新校验协议、主机与 DNS 解析 IP,最大 5 跳防重定向循环;链接统一到 GitHub 仓库;补齐 AI 辅助编码与人工复查声明。
  • v1.4.2:复查时发现 302 随机图源把 Location 直接当作图片地址写入缓存与图库。服务端虽不跟随该跳转(不构成直接 SSRF),但内网、回环、云元数据或非 http 协议的地址仍会污染业务数据并展示到前台。新增 safeImageUrl(),对 Location、200 兜底文本、随机单张 JSON 的 url 统一归一为绝对 URL 后再执行完整校验,不合法直接丢弃;同时让 /diagnose 的脱敏实现与上架材料声明完全一致(去掉 firstRow 字段值与异常 message,补 contentType),并补建 GitHub Release 提供可下载插件包。

四、质量保障

53 个单元测试覆盖了响应解析、端到端拉取(起本地 HTTP 服务模拟图源)、SSRF 校验边界(含 172.32 放行反例)、302/307 跳转逐跳复检与循环保护、响应中图片地址的二次校验、图库写入与撤回隔离。其中端到端测试全程只产生内存对象,不落任何本地文件。

五、后续计划

  • 完成 Halo 应用市场上架审核,插件包与更新日志持续同步到 GitHub Releases
  • 支持更多响应结构(如分页自动抓取)
  • 图源健康度统计与自动降级
  • 展示页主题跟随站点暗色模式

评论

推荐阅读

订阅邮件

订阅更新,及时收到最新文章。

作者