aa4befa0ff
- 下载资源管理与 /download/{slug} 下载链接
- 下载统计(次数/去重人数/明细/CSV 导出)
- 按资源邮箱验证(Halo 通知中心发信,互认评论插件已验证邮箱)
- 本地附件防直链(/upload/** 404)
- 文章引用扫描
- Console 前端:Vue3 + ui-plugin-bundler-kit
4.0 KiB
4.0 KiB
AGENTS.md — sharelink(Halo 资源下载管理插件)
项目概述
Halo 2.x(>=2.22,目标实例 Halo Pro 2.25.4)插件。管理文章中的资源下载链接:按资源的下载统计、邮箱验证、防附件直链、文章引用扫描。邮箱验证体系复制改造自 ~/Documents/Code/Ai/blog-comment(plugin-comment-widget fork 的 emailcode/guard 包)。
结构
src/main/java/run/halo/sharelink/model/自定义 Extension(@GVK groupsharelink.halo.run):DownloadResource(spec.slug 唯一索引)、DownloadRecord(spec.resourceSlug 索引)、VerifiedDownloader(name=SHA-256(归一化邮箱))emailcode/验证码:Guava 内存缓存、限流、NotificationCenter 发信(REASON_TYPEsharelink-download-verification,模板在resources/extensions/notification.yaml)verify/公开端点api.sharelink.halo.run/v1alpha1(email-verify/-/send|/-/check)+ VerifiedEmailService(本插件 VerifiedDownloader ∪ 评论插件 VerifiedCommenter,后者运行时 Unstructured fetch,缺席自动降级)download/RouterFunction Bean:GET /download/{slug}(自包含 HTML 页)、GET /download/{slug}/file(一次性 token 核销 → 记录 → 流式下载);FileStreamer 本地附件直接读盘(halo.work-dir/attachments +storage.halo.run/local-relative-path注解),外部存储回环 HTTP(带X-Sharelink-Internal头)protect/UploadProtectFilterAdditionalWebFilter:GET/HEAD/upload/**命中受保护附件 → 404reference/PostReferenceService扫描已发布文章 releaseSnapshot 内容匹配/download/{slug},5min 缓存console/ConsoleEndpointconsole.api.sharelink.halo.run/v1alpha1:资源 CRUD(body 为扁平 spec)、记录分页/删除/CSV、references 查询/刷新
src/main/resources/extensions/settings.yaml、notification.yaml、role-templates.yaml(匿名放行公开 API;console view/manage 角色,ui-permissions: plugin:sharelink:view/manage)ui/console 前端:Vue 3 + @halo-dev/ui-shared + ui-plugin-bundler-kit(rsbuild),产物输出到src/main/resources/console/(构建前会覆盖该目录,勿手改)。API 层ui/src/api/index.ts的normalizeResource()把后端扁平 ResourceVo 转成 Halo 风格结构
构建 / 部署
pnpm -C ui install && pnpm -C ui build
export JAVA_HOME=~/.gradle/jdks/jdk-21.0.12+8/Contents/Home # 本机无系统 JDK
./gradlew build -x test # build/libs/sharelink-<version>.jar
关键经验(踩过的坑):
- 改代码后发布必须递增
gradle.properties的 version——console 静态资源按?version=缓存,不递增浏览器会用旧包。 - Console UI 升级插件会弹「插件已存在,是否升级?」确认框,不点确定不会真正替换 jar。
- 插件数据存 ExtensionStore(MySQL),卸载/重装插件不丢数据;但 Halo 的插件静态资源目录在重装时才重新解压。
- 计数在「token 核销 + 附件可流式输出」之后才写入,下载失败不计数;免验证资源按 slug+IP 内存窗口去重。
- UI 安装有两条路:①「远程下载」需要 Halo 服务器能反向访问本机 jar 服务(本机换网/IP 变化后会失败);②「本地上传」用 webbridge 时 CDP
setFileInputFiles被浏览器禁用,可用兜底方案:分块 base64 经evaluate推到页面window.__jarB64→ JS 构造File+DataTransfer→ 对.uppy-Dashboard-inner派发合成dragenter/dragover/drop事件触发 Uppy 上传 → 再点升级确认框「确定」。 - 下载页自包含 HTML 里若给元素自定义了
display(如 flex),必须补[hidden] { display: none !important; },否则hidden属性失效导致元素误显示。
测试
端到端验证依赖目标实例(http://192.168.3.2:8090)。公开 API 可用 curl 匿名测试:
POST /apis/api.sharelink.halo.run/v1alpha1/downloads/-/token {"slug":"..."} → GET /download/{slug}/file?token=...。