修复轮 1:邮件 GET 改归 ops,不撑大两端共用读路由名单

全仓测试 route-access.test.ts 钉着一份有意枚举、随 Keycloak spec 审过的名单:哪些读路由被两端共用。
之前把邮件 7 条 GET 标成 design|ops,名单从八条长到十四条。裁决改路由、不改名单:邮箱那一屏落在
运营中心,设计端没有消费方;入站正文装的是客户来往内容,ADR-012 已把「设计端能读客户主数据」记为
一处有意承担的代价,不再无故加宽。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
This commit is contained in:
xcs
2026-08-26 01:36:12 +08:00
co-authored by Claude Fable 5
parent 43bacffd90
commit c5cb779390
2 changed files with 13 additions and 13 deletions
+9 -8
View File
@@ -1,7 +1,8 @@
/**
* 邮件收发的路由契约(设计稿 §7.2;ADR-013)。
*
* 读标两端(设计中心要看通道对上了哪个信箱,运营中心管配置);写归运营中心。
* 读写都归运营中心(设计稿 §7.2 + 2026-08-26 裁决):邮箱那一屏落在运营中心,设计端没有消费方;
* 而入站正文装的是客户来往内容 —— ADR-012 已把「设计端能读客户主数据」记为一处后果,不再无故加宽。
* `/api/gov/*` 的写路由由 app 中间件自动广播 `{kind:'system', id:'gov'}`。
* 探测 / 试发 / 拉取的**厂商失败不是接口错误**:200 的 body 里带原话(脱敏)。
*/
@@ -30,7 +31,7 @@ const listQuery = z.object({
export const listMailAccountsRoute = {
method: 'GET',
path: '/api/gov/mail/accounts',
access: ['design', 'ops'],
access: 'ops',
output: z.object({ accounts: z.array(mailAccountViewSchema) }),
} as const satisfies RouteContract;
@@ -56,7 +57,7 @@ export const probeMailDraftRoute = {
export const getMailAccountRoute = {
method: 'GET',
path: '/api/gov/mail/accounts/:accountId',
access: ['design', 'ops'],
access: 'ops',
params: accountParams,
output: mailAccountViewSchema,
} as const satisfies RouteContract;
@@ -109,7 +110,7 @@ export const sendTestMailRoute = {
export const listMailInboundRoute = {
method: 'GET',
path: '/api/gov/mail/inbound',
access: ['design', 'ops'],
access: 'ops',
query: listQuery,
output: z.object({ mails: z.array(mailInboundSummarySchema) }),
} as const satisfies RouteContract;
@@ -117,7 +118,7 @@ export const listMailInboundRoute = {
export const getMailInboundRoute = {
method: 'GET',
path: '/api/gov/mail/inbound/:mailId',
access: ['design', 'ops'],
access: 'ops',
params: mailParams,
output: mailInboundDetailSchema,
} as const satisfies RouteContract;
@@ -126,7 +127,7 @@ export const getMailInboundRoute = {
export const mailAttachmentRoute = {
method: 'GET',
path: '/api/gov/mail/inbound/:mailId/attachments/:index',
access: ['design', 'ops'],
access: 'ops',
params: z.object({ mailId: z.string().min(1), index: z.coerce.number().int().min(0) }),
output: z.object({ name: z.string(), contentType: z.string(), dataBase64: z.string() }),
} as const satisfies RouteContract;
@@ -135,7 +136,7 @@ export const mailAttachmentRoute = {
export const mailRawRoute = {
method: 'GET',
path: '/api/gov/mail/inbound/:mailId/raw',
access: ['design', 'ops'],
access: 'ops',
params: mailParams,
output: z.object({ name: z.string(), contentType: z.literal('message/rfc822'), dataBase64: z.string() }),
} as const satisfies RouteContract;
@@ -153,7 +154,7 @@ export const redeliverMailInboundRoute = {
export const listMailOutboundRoute = {
method: 'GET',
path: '/api/gov/mail/outbound',
access: ['design', 'ops'],
access: 'ops',
query: listQuery,
output: z.object({ mails: z.array(mailOutboundViewSchema) }),
} as const satisfies RouteContract;