ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Halo 编辑器外部媒体资源识别改进:基于 Attachment 永久链接匹配的完整实现解析

Halo 编辑器外部媒体资源识别改进:基于 Attachment 永久链接匹配的完整实现解析 Halo 编辑器外部媒体资源识别改进基于 Attachment 永久链接匹配的完整实现解析【免费下载链接】haloHalo 是一款强大易用的开源建站工具从个人博客、知识库到企业官网、在线商城Halo 都能助您轻松实现一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/haloHalo 默认编辑器此前把所有非当前站点的http(s)媒体地址一律当作外部资源处理导致用户自定义对象存储如通过 CDN 或桶域名暴露文件下已上传附件被误判并弹出传输确认框。本文基于 Halo 仓库中improve-editor-external-asset-transfer的规格文档spec、设计文档design与任务清单tasks结合后端与前端源码讲解附件永久链接匹配 API与编辑器异步判定机制的完整实现读完可掌握其 API 契约、归一化匹配算法、权限边界与编辑器会话级缓存方案。问题背景对象存储永久链接为何被误判为外部资源Halo 的默认编辑器在检测粘贴的图片、音频、视频节点时采用同步判断相对 URL 与当前源current origin的 URL 被视作本地资源其余http(s)URL 一律被视作外部资源。这一规则对常规场景有效但存在一个明显的误判窗口当附件存储策略通过自定义 CDN 或桶域名bucket domain暴露文件时Attachment.status.permalink实际指向的是第三方存储域名。用户在编辑器里粘贴这样的地址时它满足非当前源、是 http(s)两个条件于是被当作外部媒体触发了本不该出现的传输确认对话框。规格中给出的约束相当明确不引入存储域白名单storage-domain allowlist、不根据 URL 主机名推断归属而是复用 Attachment 自身的事实——因为后端本就把附件访问地址存储在Attachment.status.permalink、对该字段建立了索引并已在缩略图解析等场景中做过精确匹配查询。详情参见 设计文档。需求与实现路径总览规格文档本身以 openspec 的 ADDED Requirements 形式描述了该能力核心包括两个需求块附件永久链接匹配 API提供有上传权限的 API将输入 URL 字符串与已有 Attachment 永久链接做匹配编辑器使用匹配结果做外部资源判定默认编辑器在提示用户传输粘贴的媒体前先调用永久链接匹配命中已有附件的不再提示。配套的 任务清单 显示该功能已经完整落地分为四大板块后端匹配服务与双端路由、OpenAPI 文档与 UI API 客户端重新生成、编辑器匹配与传输 UX 改造、以及后端/前端测试与代码规范校验。后端POST /attachments/-/match-permalinks匹配 API请求与响应契约新增接口放在附件上传相关端点旁路由为POST /attachments/-/match-permalinks请求体与响应体的 DTO 分别定义在AttachmentPermalinkMatchRequest.java请求体是record AttachmentPermalinkMatchRequest(ListString urls)urls必填AttachmentPermalinkMatchList.java响应体是record AttachmentPermalinkMatchList(ListAttachmentPermalinkMatchResult items)保持与输入相同顺序返回。每个匹配结果仅含url原始输入字符串与matched布尔值绝不泄露附件名称、归属者、分组、存储策略、媒体类型或体积等元数据。规格中明确要求每个输入返回一个结果、保持原顺序这是为了配合前端把哪些粘贴 URL 需要传输直接映射回原始候选列表。一个请求与响应示例// 请求 POST /attachments/-/match-permalinks Content-Type: application/json { urls: [ https://bucket.example.com/2024/01/photo.png, https://external.example.com/banner.png, /upload/2024/01/photo.png ] } // 响应顺序与输入一致 { items: [ { url: https://bucket.example.com/2024/01/photo.png, matched: true }, { url: https://external.example.com/banner.png, matched: false }, { url: /upload/2024/01/photo.png, matched: true } ] }共享匹配服务AttachmentPermalinkMatcherConsole 与 UC 两个端点共享同一套服务逻辑核心实现是 AttachmentPermalinkMatcher.java其算法要点如下窄归一化生成候选集合为每个输入 URL 生成原字符串 少量规范等价形式组成的候选永久链接集合去重后批量查询所有候选先经LinkedHashSet去重再用in(status.permalink, uniqueCandidates)构造索引查询Queries.in通过ReactiveExtensionClient.listAll拉取命中的 Attachment按输入顺序回填结果对每个候选检查其归一化集合与命中集合是否有交集最终Candidate.matches(...)落到AttachmentPermalinkMatchResult(url, matched)。归一化的三种形态createCandidate 展示了精确匹配优先、小集合归一化兜底的设计输入形态候选永久链接原始字符串一律加入原始值url.strip()后同源绝对 URL追加path query形式覆盖附件以相对路径存储、站点以绝对方式访问的情况相对 URL用站点 URL来自ExternalUrlSupplier提供的siteUrlresolve后追加解析结果也就是说同站绝对 URL 与存储的相对路径/查询可以互相命中相对 URL 与配置的外部访问地址也能互相命中。这正是规格中canonical permalink variants场景的实现载体。同时归一化被刻意限定在这三种形态内不做模糊路径、文件名校验或存储策略比对——避免把同域名下的无关文件误判为 Halo 附件。输入校验空值与非法协议整体拒绝AttachmentPermalinkMatcher.java 中的校验逻辑与规格逐条对应空/空白输入urls为空或某个 URL 为空白blank时抛出ServerWebInputException前端会收到 400整批请求被拒绝不返回部分匹配结果非法绝对协议isUnsupportedAbsoluteUri只放行http、httpsSUPPORTED_ABSOLUTE_URI_SCHEMESdata:、blob:、file:、ftp:、mailto:等绝对协议一旦出现即抛错可容错的字符串语义输入不要求是合法的java.net.URL绝对地址因此相对路径、带查询串的 path 等均可进入匹配流程无法解析为 URI 的字符串也会按原值参与精确匹配。规格特别强调拒绝非法输入时不返回部分匹配结果拒绝整个请求设计文档说明这是为了避免该端点退化成通用的 URL 分类器。Console 与 UC 双端点与权限隔离匹配端点分别注册在用户中心的 UC 附件 API 与 Console 附件 API 下路由在端点类中定义为POST /attachments/-/match-permalinks子路由统一委托给AttachmentHandler.handleMatchPermalinks并由 AttachmentHandler.buildMatchPermalinksDoc 生成 OpenAPI 元数据AttachmentUcEndpoint.javaUC 路由operationId 为MatchAttachmentPermalinksForUcAttachmentConsoleEndpoint.javaConsole 路由二者在各自的权限体系下暴露。权限方面规格与任务清单的要求是匹配能力与附件上传/管理权限保持一致。任务清单明确指出Console 端在具备system:attachments:manage时提供该行为UC 端在具备uc:attachments:manage时提供该行为见 tasks.md对应的角色模板 role-template-attachment.yaml 与 role-template-uc-attachment.yaml 都涉及该权限点。之所以不把端点对所有人开放尽管永久链接本身是公开可访问的是为了避免向无法执行上传/传输操作的用户展示相关 UI 或开放探测接口。后端测试覆盖AttachmentPermalinkMatcherTest.java 针对匹配服务做了归一化与校验层面的验证AttachmentConsoleEndpointTest.java 与 AttachmentUcEndpointTest.java 覆盖端点行为与授权边界AttachmentRoleTemplateTest.java 校验权限模板可对应任务清单 1.5 中有序返回、无元数据泄露、相对/绝对匹配、规范变体、空输入拒绝、非法协议拒绝、未授权访问等测试条目。编辑器从同步判定到异步候选匹配判定策略的根本变化改动前编辑器对粘贴节点的媒体源执行同步的isExternalAsset()检查遇到对象存储永久链接即产生误报。改动后粘贴处理流程变为收集粘贴富文本切片中的所有媒体候选src值在浏览器端先过滤掉明显本地的值相对路径、当前源等当用户具备附件上传权限时对剩余候选批量调用匹配 API只有未命中的结果才作为外部传输候选进入既有的粘贴时传输确认对话框用户确认后仅传输那些未命中的外部媒体资源。规格中四条编辑器场景全部由此实现命中已有Attachment.status.permalink的不再提示真正的外部媒体保留粘贴时确认对话框且只传输未命中的资源无上传权限的用户既不调用匹配 API 也不显示任何上传/传输提示单节点显式传输动作仅对未命中的外部媒体出现。编辑器会话级匹配缓存为避免同一永久链接被反复查询编辑器在 upload 扩展存储中维护了匹配缓存。upload/index.ts 中ExtensionUploadStorage增加三个成员matchAttachmentPermalinks?: MatchAttachmentPermalinks宿主注入的匹配回调matchCache: Mapstring, booleanURL → 是否命中cacheVersion: Refnumber缓存版本号用于触发 Vue 响应式更新。匹配函数会先对输入去重再剔除缓存中已有的 URL只对真正未查过的值发起请求结果统一写入matchCache并递增cacheVersion当没有注入 matcher例如无上传权限的宿主时直接把缓存标记为false并递增版本从而不发起任何探测请求见 upload/index.ts。粘贴流程与单节点传输组件粘贴确认对话框的门控getUnmatchedExternalNodes读取切片内节点仅返回经过同步过滤仍是候选、且matchCache标记为未命中的节点作为后续确认与传输的目标单节点显式传输use-attachment.ts 中的useExternalAssetsTransfer为图片/视频/音频节点视图提供是否外部资源、是否传输中、执行传输三件套isExternalAsset的计算依赖matchCache.get(src.value) false——命中附件的节点一律不显示传输按钮未命中且具备上传能力的节点才允许显式传输执行handleTransfer前还会先做一次匹配复核若已被识别为附件则直接返回。isExternalAsset用于明显本地资源的浏览器端预判仍保留在 upload.ts 中作为调用匹配 API 之前的快速过滤器其外部分类逻辑与 ImageView.vue、VideoView.vue、AudioView.vue 三个媒体节点视图配合覆盖图片、音频、视频三类粘贴媒体。Console 与 UC 宿主各自注入行为关键设计约束是编辑器包本身不硬编码 UC 上传客户端。Console 的博文/单页编辑器宿主与 UC 的博文编辑器宿主各自注入匹配 URL 传输回调使 Console 行为遵循系统附件权限、UC 行为遵循其附件权限避免两者因共用单一客户端而产生权限错位。生成后的 API 客户端中匹配操作已分别出现在 attachment-v1alpha1-console-api.ts 与 attachment-v1alpha1-uc-api.ts。变更影响、回归与回滚规格与任务清单明确该改动属于纯代码级变更无数据库 schema 变更、无新增第三方依赖、不修改插件/主题公共 API、不改动附件存储策略模型后端新增 UC 端点的 DTO、匹配服务、校验、授权与测试新增端点后需重新生成 OpenAPI 文档与 UI API 客户端任务清单中的命令为./gradlew generateOpenApiDocs与pnpm -C ui api-client:gen并对依赖生成产物的包执行构建编辑器包更新外部媒体检测、粘贴处理与聚焦测试回滚仅涉及代码级回退不需要数据迁移。仓库中以 spec.md、proposal.md、design.md 与 tasks.md 组成的整套 openspec 记录可以按需求场景 → 设计取舍 → 落地任务 → 源码实现的链路完整追溯本特性的每一处细节其中风险与取舍也值得参考例如批量in查询 输入去重 合理请求上限来缓解多 URL 查询压力以及仅会话内缓存、允许后续粘贴刷新过期结果来避免上传后匹配结果过期带来的误判。小结本次改进的价值在于不再依赖域名看起来像外部这种启发式规则而是让编辑器直接向后端确认这个 URL 是不是已经是 Halo 的附件。匹配 API 只回答布尔问题、只暴露给具备附件管理权限的调用方配合编辑器端异步候选匹配与会话级缓存既消灭了对象存储永久链接的误报又保留了针对真正外部媒体资源的粘贴确认与单节点显式传输体验。后端从Attachment.status.permalink出发的窄归一化算法与前端缓存策略共同构成了这条少打扰、高准确率的外部资源识别链路。【免费下载链接】haloHalo 是一款强大易用的开源建站工具从个人博客、知识库到企业官网、在线商城Halo 都能助您轻松实现一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/halo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表