Skip to content

plugin-hono-server 的 registerStandardEndpoints 把「重复供给」和「独家供给」绑在同一个开关上 —— 拆开是退役的前置条件 #4073

Description

@os-zhuang

⚠️ 本 issue 已订正(见下方评论)。 初版标题与建议是"零非测试消费者,默认翻 false" —— 错的:我当时查的是"谁传这个选项",没查"谁依赖这个默认值"。os serve 正是靠默认值拿到这个面,且其中三个端点没有第二个提供者。照初版执行会打断 console。下文已按证据重写。

按 Prime Directive #10 记录,#4018(静态 discovery 按注册计算,#4063 已合并)实施中发现。#4018 只收口了 discovery 那一块。

真实现象

HonoPluginOptions.registerStandardEndpoints(默认 true)用一个开关同时控制两类性质完全不同的路由:

路由 性质 谁还提供
POST/GET /api/v1/data/:objectGET /data/:object/:id(只有 C+R) 重复供给 @objectstack/rest/data(完整 CRUD + 全部闸门),且因先注册先赢而实际生效
GET /api/v1/discovery/.well-known/objectstack 重复供给 dispatcher / REST(#4018 后已显式 cede)
GET /api/v1/auth/me/permissions 独家供给
GET /api/v1/auth/me/localization 独家供给
GET /api/v1/me/apps 独家供给

三条独家供给的证据:

  • os serve 靠默认值拿到它们 —— packages/cli/src/commands/serve.ts:1057new HonoServerPlugin({ port }),不传这个选项。
  • console 硬依赖其中两条 —— objectui packages/permissions/src/MePermissionsProvider.tsx:72DEFAULT_ENDPOINT = '/api/v1/auth/me/permissions'(整套 PermissionProvider / ObjectForm / RecordDetailView / 相关列表的 apiOperations 都建在这份 payload 上);apps/console/src/AppContent.tsx:121/api/v1/auth/me/localization
  • core 把它们当一等公民 —— packages/core/src/security/auth-gate.ts:30ALLOW_SUFFIXES/me/apps/me/localization,注释写明是"被门禁拦住的用户必须仍能访问,用以补救或引导补救 UI"。
  • grep 确认 packages/restpackages/runtime 都不挂任何 /me/*

所以默认值不能直接翻 false —— 那会让 os serve 上的 console 权限层与本地化整个断掉。

仍然成立的部分

「重复供给」那一半的论据不变,而且是这个面反复付税的根源 —— 每条平台级不变量都要在这里再实现一遍,且每次都是事后补的:

修法:先拆,才谈退役

前置步骤(本 issue 的核心):把三条 /me/*registerStandardEndpoints 门里挪出去,让这个开关名副其实地只覆盖重复供给。两条路线:

  • A. 就地拆分 —— 在 plugin-hono-server 内部改为无条件注册(需把 registerDiscoveryAndCrudEndpoints 里的 resolveCtx / getObjectQL / denyAnonymous 提成共享 helper)。小、安全、对 os serve 行为不变。
  • B. 归还真正的 owner —— /auth/me/* 归 plugin-auth(它已经拥有 /api/v1/auth/*),/me/apps 归 REST 或 dispatcher。契合 ADR-0076 D11「单一 owner」,但跨插件,且 /me/permissions 带着约 195 行权限合并逻辑(含 foldWildcardSuperUser)与 objectql/spec 依赖。

拆完之后才是原计划:默认值翻 false → 观察一个 release → 删除 registerDiscoveryAndCrudEndpoints 余下部分。

顺带发现:/auth/me/* 目前是靠加载顺序活着的

plugin-auth 在 kernel:ready 里注册 rawApp.all('${basePath}/*')auth-plugin.ts:1817),终结式转发给 better-auth、不会 next();hono 的 /auth/me/permissions 也在 kernel:ready 注册。两者谁先,取决于 kernel.use() 顺序(今天是 HonoServerPlugin 先于 AuthPlugin,所以 hono 的具体路由先注册、赢下匹配)。一旦顺序调换,/auth/me/permissions 会落进 better-auth 的 catch-all 拿到 404。

这与 #2567#4018 是同一类「靠顺序侥幸成立」的问题 —— 也是选 B 的最强论据:让 auth 拥有 /auth/* 下的全部路由,这个碰撞根本不存在。

顺带:三个死常量

hono-plugin.tsDEFAULT_ENDPOINT_PRIORITY = 100 / CORE_ENDPOINT_PRIORITY = 950 / DISCOVERY_ENDPOINT_PRIORITY = 900 声明后全仓零引用DISCOVERY_ENDPOINT_PRIORITY 尤其误导:它暗示存在一套 discovery 优先级机制,而真实机制是 Hono 的先注册先赢。

相邻的、可独立处理的缝

该面的 discovery payload 是 { version, apiName, routes, capabilities }不满足 DiscoverySchema(缺 name / environment / locale / services)。services 恰是 D12 定的 single source of truth(handlerReady),standalone 客户端因此读不到 D12 的那一半信息。走完退役路线后这条自动消失。

关联:#4018#4063 已合并)、#2567#3298、ADR-0076 D11/D12、OQ#9。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions