Archived
计划裁决:邮件路由的读也归运营中心,不去撑大「两端共用读路由」那份名单
全仓测试 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:
@@ -505,7 +505,8 @@ git commit -m "$(printf '邮件契约:账号入参按 kind 分两形、视图只
|
||||
// apps/api/src/app/__tests__/mail-routes-registry.test.ts
|
||||
/**
|
||||
* 邮件路由的归属:15 条全部进契约,注册表建得起来(字面 / 参数模版互不撞),
|
||||
* 读标两端、写归运营中心(设计稿 §7.2)。
|
||||
* 读写都归运营中心(设计稿 §7.2 + 2026-08-26 裁决):邮箱那一屏落在运营中心,设计端没有消费方;
|
||||
* 而入站正文装的是客户来往内容 —— ADR-012 已把「设计端能读客户主数据」记为一处后果,不再无故加宽。
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import * as contracts from '@contracts';
|
||||
@@ -522,10 +523,9 @@ describe('邮件路由契约', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('读标两端,写归 ops', () => {
|
||||
it('读写都归 ops', () => {
|
||||
for (const route of MAIL_ROUTES) {
|
||||
if (route.method === 'GET') expect(route.access).toEqual(['design', 'ops']);
|
||||
else expect(route.access).toBe('ops');
|
||||
expect(route.access).toBe('ops');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -533,7 +533,7 @@ describe('邮件路由契约', () => {
|
||||
const registry = buildRouteRegistry(routesOf(Object.values(contracts)));
|
||||
expect(registry.lookup('POST', '/api/gov/mail/accounts/probe')).toBe('ops');
|
||||
expect(registry.lookup('POST', '/api/gov/mail/accounts/acc-1/probe')).toBe('ops');
|
||||
expect(registry.lookup('GET', '/api/gov/mail/inbound/m-1/attachments/0')).toEqual(['design', 'ops']);
|
||||
expect(registry.lookup('GET', '/api/gov/mail/inbound/m-1/attachments/0')).toBe('ops');
|
||||
});
|
||||
});
|
||||
```
|
||||
@@ -550,7 +550,7 @@ Expected: FAIL(`MAIL_ROUTES` 不存在)。
|
||||
/**
|
||||
* 邮件收发的路由契约(设计稿 §7.2;ADR-013)。
|
||||
*
|
||||
* 读标两端(设计中心要看通道对上了哪个信箱,运营中心管配置);写归运营中心。
|
||||
* 读写都归运营中心 —— 邮箱那一屏落在运营中心,设计端没有消费方;入站正文装的是客户来往内容,不无故加宽可读面。
|
||||
* `/api/gov/*` 的写路由由 app 中间件自动广播 `{kind:'system', id:'gov'}`。
|
||||
* 探测 / 试发 / 拉取的**厂商失败不是接口错误**:200 的 body 里带原话(脱敏)。
|
||||
*/
|
||||
@@ -579,7 +579,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;
|
||||
|
||||
@@ -605,7 +605,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;
|
||||
@@ -658,7 +658,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;
|
||||
@@ -666,7 +666,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;
|
||||
@@ -675,7 +675,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;
|
||||
@@ -684,7 +684,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;
|
||||
@@ -702,7 +702,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;
|
||||
@@ -6518,7 +6518,7 @@ git commit -m "$(printf 'mail.send 工具契约 2.0.0(to 数组 / html / 附件
|
||||
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| 前缀 | `/api/gov/mail/*`;读标两端 `design\|ops`,写归 `ops` |
|
||||
| 前缀 | `/api/gov/mail/*`;**读写都归运营中心 `ops`** —— 邮箱那一屏落在运营中心,设计端没有消费方;而入站正文装的是客户来往内容,不无故加宽可读面 |
|
||||
| 广播 | 写路由成功后自动广播 `{kind:'system', id:'gov'}`;轮询器的新来信 / 拉取出错 / 发信结果广播 `{kind:'system', id:'mail', reason}` —— 前端按 `kind:'system'` 失效 `['gov','mail',*]` |
|
||||
| 凭证 | 视图只回 `secretBound: boolean`;`secret` 只写不读,PUT 时缺省 = 不改 |
|
||||
| 厂商失败 | 探测 / 试发 / 立即拉取的**厂商失败不是接口错误**:200 的 body 里 `ok:false` / `state:'failed'` / `error` 带原话(已脱敏) |
|
||||
@@ -6912,16 +6912,16 @@ curl -s "${H[@]}" "localhost:8787/api/gov/mail/outbound?limit=20" | jq '.mails[]
|
||||
| `ops` | POST | `/api/gov/mail/inbound/:mailId/redeliver` |
|
||||
```
|
||||
|
||||
在 `| \`design\|ops\` | GET | \`/api/gov/workflows/:id\` |` 之后加 7 行:
|
||||
在 `| \`ops\` | DELETE | \`/api/gov/access/roles/:role\` |` 那一组的末尾再加 7 行(与上面的写路由并成一组,邮件读写同归 ops):
|
||||
|
||||
```markdown
|
||||
| `design\|ops` | GET | `/api/gov/mail/accounts` |
|
||||
| `design\|ops` | GET | `/api/gov/mail/accounts/:accountId` |
|
||||
| `design\|ops` | GET | `/api/gov/mail/inbound` |
|
||||
| `design\|ops` | GET | `/api/gov/mail/inbound/:mailId` |
|
||||
| `design\|ops` | GET | `/api/gov/mail/inbound/:mailId/attachments/:index`(可用票据) |
|
||||
| `design\|ops` | GET | `/api/gov/mail/inbound/:mailId/raw`(可用票据) |
|
||||
| `design\|ops` | GET | `/api/gov/mail/outbound` |
|
||||
| `ops` | GET | `/api/gov/mail/accounts` |
|
||||
| `ops` | GET | `/api/gov/mail/accounts/:accountId` |
|
||||
| `ops` | GET | `/api/gov/mail/inbound` |
|
||||
| `ops` | GET | `/api/gov/mail/inbound/:mailId` |
|
||||
| `ops` | GET | `/api/gov/mail/inbound/:mailId/attachments/:index`(可用票据) |
|
||||
| `ops` | GET | `/api/gov/mail/inbound/:mailId/raw`(可用票据) |
|
||||
| `ops` | GET | `/api/gov/mail/outbound` |
|
||||
```
|
||||
|
||||
计数那一行改为:`计数:public 2 + 退役 8 + any 2(+ SSE 1 条字面路由)+ workbench 23 + design 33 + ops 27 + 两端 15 = 契约 110 条(邮件收发 15 条见 docs/API文档-邮件收发.md)。`
|
||||
|
||||
Reference in New Issue
Block a user