新增工具、可用性门控与界面集成
Advanced Tools 通过统一注册表声明工具身份、界面文案、懒加载组件和部署条件,再用 isToolAvailable() 决定工具是否出现在卡片与导航中。展示门控不等于访问控制:深链接不受这套门控限制。
目的与范围
本页面向新增或维护高级工具的开发者,覆盖注册字段、可用性判断、独立页面与抽屉的集成约定,以及国际化、界面交互、命令和结果事件的扩展要求。
工具自身的网络测试算法、后端鉴权、部署配置生成、成就计算和报告存储不在本页展开。涉及这些能力时,应分别阅读对应实现与前后端贡献规范,而不是把所有业务逻辑加入注册表。
**证据边界:**本页直接核对了 tools.js 与 tool-availability.js。路由、卡片、抽屉和事件总线的接入说明来自注册表注释及 frontend/AGENTS.md,本次未读取这些消费端的实现,不据此推断其错误界面、加载时序或清理细节。
概述
注册表解决的核心问题是多个入口之间的信息漂移。源码注释明确说明,路由数组与卡片数组曾分别手工维护;现在以一份按 slug 排列的工具清单描述它们共同需要的信息。
当前清单包含 15 个工具,其中:
- 11 个条目没有部署展示条件,包括 Whois、DNS Resolver、IP Calculator 等。
asn需要configs.cloudFlare为真值。invisibilitytest、enhanceddnsleaktest、personacheck需要configs.originalSite为真值。personacheck还带有noStandalone: true,因为它依赖首页测试结果,运行这些测试会导航回首页。
这里的“可用”仅表示应当被列出,并不证明上游服务健康、用户已登录或当前操作具有额度。
来源:tools.js、tool-availability.js。
架构与集成边界
下图展示源码声明的注册表消费关系。路由及界面侧连线来自文件注释与前端规范;TOOL_BY_SLUG 和门控函数则有直接实现可核对。
Sources: tools.js、tools.js、tool-availability.js。
这个边界有三层含义:
- 工具元数据集中声明。 URL 身份、标题、说明与组件加载入口不应在多个列表中各维护一份。
- 部署能力统一解释。 列表消费者通过同一函数读取门控,不各自复制条件判断。
- 展示与执行分离。 隐藏入口不能代替后端权限检查,也不能阻止用户直接使用深链接。
注册字段与配置契约
工具条目
这些字段来自 JavaScript 对象约定,而不是已验证的运行时 schema;已读实现中没有条目校验器。
| 字段 | 类型或实际形态 | 缺省行为 | 作用 |
|---|---|---|---|
slug | string | 未提供默认值 | 稳定标识,供抽屉查询参数和 /tools/:slug 使用 |
emoji | string | 未提供默认值 | 卡片与抽屉标题图标 |
titleKey | string | 未提供默认值 | 工具标题的 i18n 键 |
noteKey | string | 未提供默认值 | 一行工具介绍的 i18n 键 |
component | 返回动态 import() 的函数 | 未提供默认值 | 抽屉及独立页面使用的懒加载组件入口 |
requiresOriginalSite | 可选 boolean | 省略时不检查原站标志 | 要求 configs.originalSite 为真值才列出 |
requiresConfig | 可选 string | 省略时不检查具名配置 | 将字段值作为 configs 的属性名读取 |
noStandalone | 可选 boolean | 已读源码未展示消费端缺省实现 | personacheck 用它表达不应提供独立工具页面 |
来源:tools.js。
运行时可用性配置
| 配置 | 预期含义 | 配置未到达时 | 当前使用者 |
|---|---|---|---|
configs.originalSite | 是否为原站部署 | 对应门控不通过 | invisibilitytest、enhanceddnsleaktest、personacheck |
configs.cloudFlare | Radar 相关能力标志 | 对应门控不通过 | asn |
configs[tool.requiresConfig] | 任意具名能力标志 | 属性缺失时不通过 | 供后续工具扩展 |
源码注释说明,配置异步到达,在此之前为 {}。函数使用 JavaScript 真值判断,不是 === true;因此字符串 "false" 也是真值。新增配置时应保持布尔语义,不能把该函数当作配置类型校验器。后端如何生成这些标志、是否由环境变量控制,本次读取范围内未确认。
可用性判断的真实控制流
API:isToolAvailable(tool, configs)
该函数没有 TypeScript 类型声明。根据实际实现,tool 是工具对象,configs 是运行时配置对象,返回值始终为 boolean(对于正常对象输入)。
| 步骤 | 条件 | 结果 |
|---|---|---|
| 1 | tool 为假值 | 立即返回 false |
| 2 | 声明 requiresOriginalSite,但 configs?.originalSite 为假值 | 返回 false |
| 3 | 声明 requiresConfig,但对应配置值为假值 | 返回 false |
| 4 | 以上条件均未拒绝 | 返回 true |
两个门控同时存在时采用 AND 语义,必须全部满足。原站条件先检查,失败后不会继续读取具名配置。
1export const isToolAvailable = (tool, configs) => {
2 if (!tool) return false;
3 if (tool.requiresOriginalSite && !configs?.originalSite) return false;
4 if (tool.requiresConfig && !configs?.[tool.requiresConfig]) return false;
5 return true;
6};Source: tool-availability.js。
函数没有网络请求、缓存、状态写入、重试或显式 throw。configs 为 null 或 undefined 时,可选链避免属性访问错误;但没有门控的有效条目仍返回 true。
从配置到展示、从 URL 到工具
Sources: tools.js、tools.js、frontend/AGENTS.md。
阅读或排查时应把两条路径分开:列表路径判断展示条件;深链接路径不使用这些条件拦截。noStandalone 则是另一类页面适用性声明,并非 isToolAvailable() 的检查项。
新增工具的实施步骤与真实示例
以下示例均为当前仓库已有代码摘录,不是虚构的新工具脚手架。
1. 明确工具身份和承载方式
先确定稳定的 slug、工具组件、标题与说明键,以及工具是否适合独立页面。slug 同时进入 URL 和索引,是对外标识,不宜作为随文案变化的显示名称。
已有的公共工具条目如下:
{ slug: 'whois', emoji: '📓', titleKey: 'whois.Title', noteKey: 'advancedtools.Whois', component: () => import('@/components/advanced-tools/Whois.vue') },Source: tools.js。
该条目不声明部署条件,因此门控不会等待任何配置标志。component 保存的是函数,不是在注册表求值时立即执行的导入;具体何时调用、如何显示加载与失败状态,由消费者决定,本次未核对其实现。
2. 只声明工具真正依赖的展示条件
ASN 工具的真实条目展示了具名能力门控:
{ slug: 'asn', emoji: '🛂', titleKey: 'asnprofile.Title', noteKey: 'advancedtools.AsnProfile', component: () => import('@/components/advanced-tools/AsnProfile.vue'), requiresConfig: 'cloudFlare' },Source: tools.js。
这里不是要求原站,而是要求 cloudFlare 标志。不要因为一个工具调用后端,就自动为它增加 requiresOriginalSite;应按照实际部署依赖选择条件。也不要在卡片组件里新增一套特殊判断,前端规范要求统一通过 isToolAvailable() 读取门控。
3. 对依赖首页上下文的工具单独处理
{ slug: 'personacheck', emoji: '🎭', titleKey: 'personacheck.Title', noteKey: 'advancedtools.PersonaCheck', component: () => import('@/components/advanced-tools/PersonaCheck.vue'), requiresOriginalSite: true, noStandalone: true },Source: tools.js。
requiresOriginalSite 表达列表展示条件;noStandalone 表达页面承载限制。源码注释给出的原因是 Persona Check 依赖首页测试结果,执行这些测试又会回到首页,独立页面会产生立即离开的体验。两者不能合并为同一种“禁用”状态。
来源:tools.js。
4. 保持索引与清单一致
注册表末尾直接生成 slug 索引:
export const TOOL_BY_SLUG = new Map(ADVANCED_TOOLS.map((tool) => [tool.slug, tool]));Source: tools.js。
不需要另外维护一份 slug 到组件的手写映射。这个表达式也带来两个约束:
- slug 必须唯一。 JavaScript
Map遇到重复键会保留后一个值,但原数组仍保留重复条目;已读实现没有重复检测。 - 索引是构建时快照式派生。 它在模块求值时构建一次,不是响应式计算。后续直接向数组加入新条目,不会自动新增 Map 键;已有条目的对象引用则仍共享。
因此,当前机制更适合静态注册,不应未经额外设计就当作运行时插件注册 API。
界面与跨组件集成约定
本节是仓库明确规定的接入要求,而非对未读取组件实现的逐行分析。
Vue、界面原语与状态
- 新代码使用 JavaScript 与 Vue Composition API,组件采用
<script setup>;不要引入 TypeScript 或 Options API。 - 优先复用 shadcn-vue 界面原语。工具执行按钮采用
variant="action"、运行中的Spinner和禁用状态,避免重复提交的视觉入口。 - 高级工具使用底部 Drawer;侧向面板使用 Sheet。Dialog、Sheet、Drawer 根组件接入覆盖层快捷键暂停机制,基于它们构建的界面继承这套约定。
- 业务状态到颜色的映射通过
use-status-tone.js,避免各工具自定义互不一致的成功、等待、失败颜色。 - 自由输入框遵守仓库列出的六项 AutoFill 防护属性,避免密码管理器及浏览器自动填充干扰工具输入。
来源:frontend/AGENTS.md、frontend/AGENTS.md、frontend/AGENTS.md。
可分享输入与 URL 同步
前端规范以 AsnProfile 为范例:监听 route.query.q,并使用 immediate 选项,使首次挂载和抽屉打开后的查询参数变化都能触发处理;每次执行时通过 router.replace 更新查询参数。同时考虑 /tools/ 和 ?tool= 两种承载形式。
这一约定的重点是不要只在初次挂载读取输入,否则一个已经打开的工具可能无法响应后来变化的分享链接参数。这里未提供复制式代码,因为本次没有读取 AsnProfile 的实际 watcher 实现。
国际化
注册表只保存 titleKey 与 noteKey,不应把显示文案硬编码到条目里。涉及用户可见文案的改动必须在同一次变更中覆盖所有 full locale;beta locale 可以沿既定 fallback chain 回退。仓库规范也要求变更日志同步遵守 full locale 覆盖要求。
命令、完成事件与报告
当新工具需要跨组件协作时,区分两个方向:
| 需求 | 仓库规定的机制 | 集成注意点 |
|---|---|---|
| 通知一次测试已经完成 | emitAppEvent 领域事件 | 工具不直接操作成就;成就规则和守卫由外部机制负责 |
| 请求某个工具执行操作 | dispatchAppCommand | 通过命令总线,不跨组件使用模板 ref 触发业务 |
| 注册命令处理者 | use-app-command.js | setup 阶段注册、与作用域绑定;调用者先等待命令可用 |
| 把结果加入可分享报告 | 完成事件、builder、schema | 新可报告测试需同时更新事件、构建器和 schema;不能只更新渲染界面 |
报告链接公开可访问,规范明确要求在 builder 层排除访客输入的敏感内容,而不是只在 renderer 中隐藏。命令错误约定包含 auth、quota、input,总线补充 unavailable、timeout;这些是集成契约,不是 isToolAvailable() 的异常类型。
边界情况、并发与运维注意事项
展示门控不是权限屏障
requiresOriginalSite 只检查配置标志,不检查登录态。配置门控不会拦截 ?tool= 或 /tools/:slug。如果工具需要私有 API、额度限制或身份校验,必须在相应执行路径落实,不应依赖卡片不可见。
根目录规范把访问控制与超时放在共享后端机制中,并要求上游 HTTP 调用经过 fetchUpstream。本页未读取这些中间件或工具 handler,因此不列举具体接口的鉴权返回码。
来源:tool-availability.js、AGENTS.md。
配置晚到与缺失
配置到达前,受门控工具不可用,无门控工具可用。这是注释明确描述的行为。配置改变后重新调用函数会得到新结果,但函数本身不会发起刷新或订阅变化;消费端是否通过响应式计算重新评估,本次未核对。
配置名拼写错误与真实能力关闭都会表现为对应属性是假值,函数不会区分两种原因。排查“工具卡片消失”时,先核对条目的配置键,再核对实际配置值,而不是先排查组件导入。
性能与并发
门控只有固定数量的属性读取和分支,单次调用为常数级操作,不产生异步竞争。注册表构建 Map 需要遍历一次工具数组;数组与 Map 同时持有条目对象引用。
动态导入函数为延迟加载提供入口,但不代表列表门控会阻止代码被加载,也不意味着存在取消、重试或错误恢复。工具内部网络请求的并发、超时、取消和重复执行策略,需要在各工具实现中单独核对。
这一层没有数据库写入或用户设置持久化,也没有后台作业;不要把工具注册信息与工具运行结果混为一类数据。
验证清单与测试边界
仓库规范要求可脱离真实网络执行的非视觉逻辑随改动附带测试,并以 pnpm check 执行测试与构建;UI 渲染、浏览器 API 和真实网络行为不属于其规定的纯逻辑测试范围,视觉变化需要人工验证。本次没有读取测试文件,因此以下是建议新增或核对的验收项,不是已有测试覆盖声明。
| 验证项 | 应核对的行为 |
|---|---|
| 缺失工具 | isToolAvailable() 返回 false |
| 无门控工具、配置缺失 | 有效条目返回 true |
| 原站门控 | 缺失或假值拒绝,真值放行 |
| 具名配置门控 | 精确读取 requiresConfig 指定的键 |
| 双门控 | 任一条件不满足即拒绝 |
| 配置晚到 | 使用新配置重新评估后,返回值相应变化 |
| 重复 slug | 防止数组展示与 Map 解析不一致 |
| 深链接 | 不把列表门控错误地扩展成路由权限控制 |
| 页面适用性 | noStandalone 工具不被当作普通独立页面处理 |
| 分享与抽屉 | 已打开工具能响应 q 的变化 |
| 国际化与视觉 | full locale 文案齐全,按钮、加载、覆盖层与快捷键行为符合规范 |
来源:AGENTS.md、AGENTS.md、frontend/AGENTS.md。
相关链接
- 前端贡献规范:门控、命令、事件与 UI 接入:继续了解本页涉及的跨组件约定。
- 通用贡献规范:国际化与测试:核对交付要求与后端安全边界。
- 工具注册字段说明:维护新工具条目时的直接契约。
- 可用性判断实现:排查卡片和导航展示条件时的最小入口。