契约:三个端与路由归属(access 必填)、Principal / 角色→端 / 名册同步 schema、退役路由标 @deprecated(ADR-012)

- packages/contracts/src/api/http.ts:新增 SURFACES / surfaceSchema / Surface / SURFACE_LABEL /
  RouteAccess;RouteContract.access 必填;API_ERROR_CODES 增 'gone'。
- packages/contracts/src/org.ts:staffSchema 补 email? / roles(realm 角色快照);新增
  principalSchema/Principal、roleAccessSchema/RoleAccess、rosterSyncOutcomeSchema/RosterSyncOutcome、
  inFlightWorkSchema/InFlightWork;旧岗位授权(grant* / governanceFunction*)与 sessionSchema/Session
  全部标 @deprecated 留一轮(web 编译不受影响,下个迭代删)。
- workbench.ts / governance.ts / ingest.ts:每条既有路由补 access(按所属端/两端共用/退役 public);
  新增 meRoute、authTicketRoute(access:'any')与 syncStaffRoute、setStaffPositionRoute、
  staffInFlightRoute、listRoleAccessRoute、setRoleAccessRoute、deleteRoleAccessRoute(access:'ops')。
- apps/api 的 org-service.ts 两处 staff.insert 补 roles: [](staffSchema 新必填字段,该文件下个
  迭代会整份替换,这里只是让本次 typecheck 过)。
- 新增 packages/contracts/src/__tests__/route-access.test.ts:95 条路由的 access 归属断言。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015kAzS35kMnZNt4YGgJXtfw
This commit is contained in:
xcs
2026-08-25 15:54:20 +08:00
co-authored by Claude Opus 5
parent 61f96d5ab2
commit a5c8d4786d
13 changed files with 771 additions and 65 deletions
@@ -0,0 +1 @@
export {};
+190
View File
@@ -6,16 +6,19 @@ import { z } from 'zod';
export declare const listStaffRoute: { export declare const listStaffRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/staff"; readonly path: "/api/gov/staff";
readonly access: readonly ["design", "ops"];
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
staff: z.ZodArray<z.ZodObject<{ staff: z.ZodArray<z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString; account: z.ZodString;
name: z.ZodString; name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{ state: z.ZodDefault<z.ZodEnum<{
active: "active"; active: "active";
disabled: "disabled"; disabled: "disabled";
}>>; }>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
position: z.ZodObject<{ position: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
code: z.ZodString; code: z.ZodString;
@@ -31,9 +34,11 @@ export declare const listStaffRoute: {
}, z.core.$strip>>; }, z.core.$strip>>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** @deprecated 随 ADR-012 退役,后端回 410;人来自 Keycloak,改用 syncStaffRoute */
export declare const createStaffRoute: { export declare const createStaffRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/staff"; readonly path: "/api/gov/staff";
readonly access: "public";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
account: z.ZodString; account: z.ZodString;
name: z.ZodString; name: z.ZodString;
@@ -43,16 +48,20 @@ export declare const createStaffRoute: {
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString; account: z.ZodString;
name: z.ZodString; name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{ state: z.ZodDefault<z.ZodEnum<{
active: "active"; active: "active";
disabled: "disabled"; disabled: "disabled";
}>>; }>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** @deprecated 随 ADR-012 退役,后端回 410;改用 staffInFlightRoute */
export declare const disableStaffCheckRoute: { export declare const disableStaffCheckRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/staff/:staffId/disable-check"; readonly path: "/api/gov/staff/:staffId/disable-check";
readonly access: "public";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -73,9 +82,11 @@ export declare const disableStaffCheckRoute: {
}, z.core.$strip>>>; }, z.core.$strip>>>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** @deprecated 随 ADR-012 退役,后端回 410;停用改在 Keycloak 做,名册同步据此落 disabled */
export declare const disableStaffRoute: { export declare const disableStaffRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/staff/:staffId/disable"; readonly path: "/api/gov/staff/:staffId/disable";
readonly access: "public";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -99,9 +110,11 @@ export declare const disableStaffRoute: {
}, z.core.$strip>; }, z.core.$strip>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** @deprecated 随 ADR-012 退役,后端回 410;权限只到端,改用 listRoleAccessRoute */
export declare const listGrantsRoute: { export declare const listGrantsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/positions/:positionId/grants"; readonly path: "/api/gov/positions/:positionId/grants";
readonly access: "public";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -138,9 +151,11 @@ export declare const listGrantsRoute: {
}, z.core.$strip>>; }, z.core.$strip>>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** @deprecated 随 ADR-012 退役,后端回 410;权限只到端,改用 setRoleAccessRoute */
export declare const setGrantsRoute: { export declare const setGrantsRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/positions/:positionId/grants"; readonly path: "/api/gov/positions/:positionId/grants";
readonly access: "public";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -209,6 +224,128 @@ export declare const setGrantsRoute: {
}, z.core.$strip>>; }, z.core.$strip>>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** 从 Keycloak Admin API 同步名册;没配 provisioning 服务账号就如实回 configured:false */
export declare const syncStaffRoute: {
readonly method: "POST";
readonly path: "/api/gov/staff/sync";
readonly access: "ops";
readonly output: z.ZodDiscriminatedUnion<[z.ZodObject<{
configured: z.ZodLiteral<false>;
message: z.ZodString;
}, z.core.$strip>, z.ZodObject<{
configured: z.ZodLiteral<true>;
inserted: z.ZodNumber;
updated: z.ZodNumber;
disabled: z.ZodNumber;
total: z.ZodNumber;
}, z.core.$strip>], "configured">;
};
/** 指派岗位;指回哨兵岗 NONE = 取消指派 */
export declare const setStaffPositionRoute: {
readonly method: "PUT";
readonly path: "/api/gov/staff/:staffId/position";
readonly access: "ops";
readonly params: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
}, z.core.$strip>;
readonly body: z.ZodObject<{
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
}, z.core.$strip>;
readonly output: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString;
name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{
active: "active";
disabled: "disabled";
}>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
position: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
code: z.ZodString;
name: z.ZodString;
note: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
}, z.core.$strip>;
};
/** 此人身上还挂着什么(只读)—— 原「停用前置检查」;停用本身在 Keycloak 做 */
export declare const staffInFlightRoute: {
readonly method: "GET";
readonly path: "/api/gov/staff/:staffId/in-flight";
readonly access: "ops";
readonly params: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
}, z.core.$strip>;
readonly output: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
blocked: z.ZodBoolean;
openTasks: z.ZodDefault<z.ZodArray<z.ZodObject<{
taskId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openCards: z.ZodDefault<z.ZodArray<z.ZodObject<{
cardId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openApprovals: z.ZodDefault<z.ZodArray<z.ZodObject<{
approvalId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
}, z.core.$strip>;
};
export declare const listRoleAccessRoute: {
readonly method: "GET";
readonly path: "/api/gov/access/roles";
readonly access: "ops";
readonly output: z.ZodObject<{
roles: z.ZodArray<z.ZodObject<{
role: z.ZodString;
ends: z.ZodArray<z.ZodEnum<{
design: "design";
ops: "ops";
workbench: "workbench";
}>>;
}, z.core.$strip>>;
knownRoles: z.ZodArray<z.ZodString>;
}, z.core.$strip>;
};
/** 锁死不变量不过(改完没人能进运营中心)→ 409 conflict */
export declare const setRoleAccessRoute: {
readonly method: "PUT";
readonly path: "/api/gov/access/roles/:role";
readonly access: "ops";
readonly params: z.ZodObject<{
role: z.ZodString;
}, z.core.$strip>;
readonly body: z.ZodObject<{
ends: z.ZodArray<z.ZodEnum<{
design: "design";
ops: "ops";
workbench: "workbench";
}>>;
}, z.core.$strip>;
readonly output: z.ZodObject<{
role: z.ZodString;
ends: z.ZodArray<z.ZodEnum<{
design: "design";
ops: "ops";
workbench: "workbench";
}>>;
}, z.core.$strip>;
};
export declare const deleteRoleAccessRoute: {
readonly method: "DELETE";
readonly path: "/api/gov/access/roles/:role";
readonly access: "ops";
readonly params: z.ZodObject<{
role: z.ZodString;
}, z.core.$strip>;
readonly output: z.ZodObject<{
ok: z.ZodBoolean;
}, z.core.$strip>;
};
export declare const LLM_PROVIDERS: readonly ["openai-compatible", "openai", "deepseek", "anthropic", "qwen"]; export declare const LLM_PROVIDERS: readonly ["openai-compatible", "openai", "deepseek", "anthropic", "qwen"];
export declare const llmProviderSchema: z.ZodEnum<{ export declare const llmProviderSchema: z.ZodEnum<{
"openai-compatible": "openai-compatible"; "openai-compatible": "openai-compatible";
@@ -243,6 +380,7 @@ export type LlmInstanceView = z.infer<typeof llmInstanceViewSchema>;
export declare const listLlmProvidersRoute: { export declare const listLlmProvidersRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/llm/providers"; readonly path: "/api/gov/llm/providers";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
providers: z.ZodArray<z.ZodObject<{ providers: z.ZodArray<z.ZodObject<{
provider: z.ZodEnum<{ provider: z.ZodEnum<{
@@ -264,6 +402,7 @@ export declare const listLlmProvidersRoute: {
export declare const listLlmInstancesRoute: { export declare const listLlmInstancesRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/llm"; readonly path: "/api/gov/llm";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
instances: z.ZodArray<z.ZodObject<{ instances: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -286,6 +425,7 @@ export declare const listLlmInstancesRoute: {
export declare const upsertLlmInstanceRoute: { export declare const upsertLlmInstanceRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/llm"; readonly path: "/api/gov/llm";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
id: z.ZodOptional<z.ZodString>; id: z.ZodOptional<z.ZodString>;
name: z.ZodString; name: z.ZodString;
@@ -331,6 +471,7 @@ export declare const upsertLlmInstanceRoute: {
export declare const deleteLlmInstanceRoute: { export declare const deleteLlmInstanceRoute: {
readonly method: "DELETE"; readonly method: "DELETE";
readonly path: "/api/gov/llm/:id"; readonly path: "/api/gov/llm/:id";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -341,6 +482,7 @@ export declare const deleteLlmInstanceRoute: {
export declare const probeLlmDraftRoute: { export declare const probeLlmDraftRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/llm/probe"; readonly path: "/api/gov/llm/probe";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
id: z.ZodOptional<z.ZodString>; id: z.ZodOptional<z.ZodString>;
name: z.ZodOptional<z.ZodString>; name: z.ZodOptional<z.ZodString>;
@@ -364,6 +506,7 @@ export declare const probeLlmDraftRoute: {
export declare const probeLlmInstanceRoute: { export declare const probeLlmInstanceRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/llm/:id/probe"; readonly path: "/api/gov/llm/:id/probe";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -402,6 +545,7 @@ export type GovTaskRow = z.infer<typeof govTaskRowSchema>;
export declare const listGovTasksRoute: { export declare const listGovTasksRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/tasks"; readonly path: "/api/gov/tasks";
readonly access: "ops";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
tasks: z.ZodArray<z.ZodObject<{ tasks: z.ZodArray<z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; id: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
@@ -447,6 +591,7 @@ export declare const listGovTasksRoute: {
export declare const controlTaskRoute: { export declare const controlTaskRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/tasks/:taskId/control"; readonly path: "/api/gov/tasks/:taskId/control";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -466,6 +611,7 @@ export declare const controlTaskRoute: {
export declare const resumeTaskRoute: { export declare const resumeTaskRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/tasks/:taskId/resume"; readonly path: "/api/gov/tasks/:taskId/resume";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -485,6 +631,7 @@ export declare const resumeTaskRoute: {
export declare const reassignTaskRoute: { export declare const reassignTaskRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/tasks/:taskId/reassign"; readonly path: "/api/gov/tasks/:taskId/reassign";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -500,6 +647,7 @@ export declare const reassignTaskRoute: {
export declare const govTaskLogRoute: { export declare const govTaskLogRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/tasks/:taskId/log"; readonly path: "/api/gov/tasks/:taskId/log";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -516,6 +664,7 @@ export declare const govTaskLogRoute: {
export declare const listProcessPackagesRoute: { export declare const listProcessPackagesRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/process"; readonly path: "/api/gov/process";
readonly access: readonly ["design", "ops"];
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
packages: z.ZodArray<z.ZodObject<{ packages: z.ZodArray<z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "ProcessPackageId", "out">; id: z.core.$ZodBranded<z.ZodString, "ProcessPackageId", "out">;
@@ -635,6 +784,7 @@ export declare const listProcessPackagesRoute: {
export declare const saveProcessRoute: { export declare const saveProcessRoute: {
readonly method: "PUT"; readonly method: "PUT";
readonly path: "/api/gov/process"; readonly path: "/api/gov/process";
readonly access: "design";
/** /**
* 编辑即其身:pkg.version 已存在 → 就地写回该版(号不变)。 * 编辑即其身:pkg.version 已存在 → 就地写回该版(号不变)。
* `asNew: true`(或版本不存在,如新流程首版)→ 产生新版本号(服务端算,不生效)。 * `asNew: true`(或版本不存在,如新流程首版)→ 产生新版本号(服务端算,不生效)。
@@ -879,6 +1029,7 @@ export declare const saveProcessRoute: {
export declare const setProcessStateRoute: { export declare const setProcessStateRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/process/:id/:version/state"; readonly path: "/api/gov/process/:id/:version/state";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
version: z.ZodString; version: z.ZodString;
@@ -1005,6 +1156,7 @@ export declare const setProcessStateRoute: {
export declare const deleteProcessVersionRoute: { export declare const deleteProcessVersionRoute: {
readonly method: "DELETE"; readonly method: "DELETE";
readonly path: "/api/gov/process/:id/:version"; readonly path: "/api/gov/process/:id/:version";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
version: z.ZodString; version: z.ZodString;
@@ -1121,6 +1273,7 @@ export declare const deleteProcessVersionRoute: {
export declare const listApprovalTemplatesRoute: { export declare const listApprovalTemplatesRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/approval-templates"; readonly path: "/api/gov/approval-templates";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
templates: z.ZodArray<z.ZodObject<{ templates: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -1151,6 +1304,7 @@ export declare const listApprovalTemplatesRoute: {
export declare const upsertApprovalTemplateRoute: { export declare const upsertApprovalTemplateRoute: {
readonly method: "PUT"; readonly method: "PUT";
readonly path: "/api/gov/approval-templates"; readonly path: "/api/gov/approval-templates";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
template: z.ZodObject<{ template: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -1198,6 +1352,7 @@ export declare const upsertApprovalTemplateRoute: {
export declare const deleteApprovalTemplateRoute: { export declare const deleteApprovalTemplateRoute: {
readonly method: "DELETE"; readonly method: "DELETE";
readonly path: "/api/gov/approval-templates/:id"; readonly path: "/api/gov/approval-templates/:id";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -1213,6 +1368,7 @@ export declare const deleteApprovalTemplateRoute: {
export declare const deleteProcessFlowRoute: { export declare const deleteProcessFlowRoute: {
readonly method: "DELETE"; readonly method: "DELETE";
readonly path: "/api/gov/process/:id"; readonly path: "/api/gov/process/:id";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -1335,6 +1491,7 @@ export declare const deleteProcessFlowRoute: {
export declare const editProcessVersionRoute: { export declare const editProcessVersionRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/process/:id/:version/edit"; readonly path: "/api/gov/process/:id/:version/edit";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
version: z.ZodString; version: z.ZodString;
@@ -1455,6 +1612,7 @@ export declare const editProcessVersionRoute: {
export declare const validateProcessRoute: { export declare const validateProcessRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/process/validate"; readonly path: "/api/gov/process/validate";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
pkg: z.ZodObject<{ pkg: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "ProcessPackageId", "out">; id: z.core.$ZodBranded<z.ZodString, "ProcessPackageId", "out">;
@@ -1580,6 +1738,7 @@ export declare const validateProcessRoute: {
export declare const listTriggersRoute: { export declare const listTriggersRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/triggers"; readonly path: "/api/gov/triggers";
readonly access: readonly ["design", "ops"];
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
triggers: z.ZodArray<z.ZodObject<{ triggers: z.ZodArray<z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "TriggerId", "out">; id: z.core.$ZodBranded<z.ZodString, "TriggerId", "out">;
@@ -1639,6 +1798,7 @@ export declare const listTriggersRoute: {
export declare const toggleTriggerRoute: { export declare const toggleTriggerRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/triggers/:id/toggle"; readonly path: "/api/gov/triggers/:id/toggle";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -1659,6 +1819,7 @@ export declare const toggleTriggerRoute: {
export declare const upsertTriggerRoute: { export declare const upsertTriggerRoute: {
readonly method: "PUT"; readonly method: "PUT";
readonly path: "/api/gov/triggers"; readonly path: "/api/gov/triggers";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
trigger: z.ZodObject<{ trigger: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "TriggerId", "out">; id: z.core.$ZodBranded<z.ZodString, "TriggerId", "out">;
@@ -1779,6 +1940,7 @@ export declare const upsertTriggerRoute: {
export declare const listWorkGroupsRoute: { export declare const listWorkGroupsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/work-groups"; readonly path: "/api/gov/work-groups";
readonly access: "ops";
/** `ownerName` 是组长姓名的呈现补充,读表时一并带出,免得前端再对一次 id */ /** `ownerName` 是组长姓名的呈现补充,读表时一并带出,免得前端再对一次 id */
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
groups: z.ZodArray<z.ZodObject<{ groups: z.ZodArray<z.ZodObject<{
@@ -1794,6 +1956,7 @@ export declare const listWorkGroupsRoute: {
export declare const saveWorkGroupRoute: { export declare const saveWorkGroupRoute: {
readonly method: "PUT"; readonly method: "PUT";
readonly path: "/api/gov/work-groups"; readonly path: "/api/gov/work-groups";
readonly access: "ops";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
ownerId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; ownerId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
name: z.ZodString; name: z.ZodString;
@@ -1818,6 +1981,7 @@ export declare const saveWorkGroupRoute: {
export declare const installActivityRoute: { export declare const installActivityRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/activities"; readonly path: "/api/gov/activities";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
manifest: z.ZodObject<{ manifest: z.ZodObject<{
identity: z.ZodObject<{ identity: z.ZodObject<{
@@ -2126,6 +2290,7 @@ export declare const installActivityRoute: {
export declare const registerToolRoute: { export declare const registerToolRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/tools"; readonly path: "/api/gov/tools";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
contract: z.ZodObject<{ contract: z.ZodObject<{
name: z.ZodString; name: z.ZodString;
@@ -2170,6 +2335,7 @@ export declare const registerToolRoute: {
export declare const listActivitiesRoute: { export declare const listActivitiesRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/activities"; readonly path: "/api/gov/activities";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
activities: z.ZodArray<z.ZodObject<{ activities: z.ZodArray<z.ZodObject<{
activityId: z.ZodString; activityId: z.ZodString;
@@ -2228,6 +2394,7 @@ export declare const listActivitiesRoute: {
export declare const listToolsRoute: { export declare const listToolsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/tools"; readonly path: "/api/gov/tools";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
tools: z.ZodArray<z.ZodObject<{ tools: z.ZodArray<z.ZodObject<{
name: z.ZodString; name: z.ZodString;
@@ -2246,6 +2413,7 @@ export declare const listToolsRoute: {
export declare const listLedgerObjectsRoute: { export declare const listLedgerObjectsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/data/objects"; readonly path: "/api/gov/data/objects";
readonly access: readonly ["design", "ops"];
/** `label` 是人读名(落在表注释上);缺省即没登记,呈现退回对象键 */ /** `label` 是人读名(落在表注释上);缺省即没登记,呈现退回对象键 */
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
objects: z.ZodArray<z.ZodObject<{ objects: z.ZodArray<z.ZodObject<{
@@ -2278,6 +2446,7 @@ export declare const listLedgerObjectsRoute: {
export declare const ledgerByActivityRoute: { export declare const ledgerByActivityRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/data/by-activity"; readonly path: "/api/gov/data/by-activity";
readonly access: readonly ["design", "ops"];
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
activities: z.ZodArray<z.ZodObject<{ activities: z.ZodArray<z.ZodObject<{
activityId: z.ZodString; activityId: z.ZodString;
@@ -2313,6 +2482,7 @@ export declare const ledgerByActivityRoute: {
export declare const browseLedgerRoute: { export declare const browseLedgerRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/data/:domain/:object"; readonly path: "/api/gov/data/:domain/:object";
readonly access: readonly ["design", "ops"];
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
domain: z.ZodString; domain: z.ZodString;
object: z.ZodString; object: z.ZodString;
@@ -2332,6 +2502,7 @@ export declare const browseLedgerRoute: {
export declare const listActivityMemoryRoute: { export declare const listActivityMemoryRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/memory/:activityId"; readonly path: "/api/gov/memory/:activityId";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
activityId: z.ZodString; activityId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2350,6 +2521,7 @@ export declare const listActivityMemoryRoute: {
export declare const listChannelsRoute: { export declare const listChannelsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/channels"; readonly path: "/api/gov/channels";
readonly access: readonly ["design", "ops"];
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
channels: z.ZodArray<z.ZodObject<{ channels: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -2382,6 +2554,7 @@ export declare const listChannelsRoute: {
export declare const listApprovalsRoute: { export declare const listApprovalsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/approvals"; readonly path: "/api/gov/approvals";
readonly access: "ops";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
approvals: z.ZodArray<z.ZodObject<{ approvals: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -2409,6 +2582,7 @@ export declare const listApprovalsRoute: {
export declare const setTriggerConfirmRoute: { export declare const setTriggerConfirmRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/triggers/:id/confirm"; readonly path: "/api/gov/triggers/:id/confirm";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2424,6 +2598,7 @@ export declare const setTriggerConfirmRoute: {
export declare const listNodeOwnersRoute: { export declare const listNodeOwnersRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/packages/:id/owners"; readonly path: "/api/gov/packages/:id/owners";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2437,6 +2612,7 @@ export declare const listNodeOwnersRoute: {
export declare const setNodeOwnerRoute: { export declare const setNodeOwnerRoute: {
readonly method: "PUT"; readonly method: "PUT";
readonly path: "/api/gov/packages/:id/owners/:nodeId"; readonly path: "/api/gov/packages/:id/owners/:nodeId";
readonly access: "ops";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
nodeId: z.ZodString; nodeId: z.ZodString;
@@ -2452,6 +2628,7 @@ export declare const setNodeOwnerRoute: {
export declare const testRunRoute: { export declare const testRunRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/testbed/run"; readonly path: "/api/gov/testbed/run";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
packageId: z.ZodString; packageId: z.ZodString;
mail: z.ZodObject<{ mail: z.ZodObject<{
@@ -2491,6 +2668,7 @@ export declare const testRunRoute: {
export declare const testRunActivityRoute: { export declare const testRunActivityRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/testbed/run-activity"; readonly path: "/api/gov/testbed/run-activity";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
activityId: z.ZodString; activityId: z.ZodString;
data: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>; data: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -2510,6 +2688,7 @@ export declare const testRunActivityRoute: {
export declare const testStopRoute: { export declare const testStopRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/testbed/stop"; readonly path: "/api/gov/testbed/stop";
readonly access: "design";
/** 两种试法各有其拔法:流程包给 workflowId,单活动给 taskId;都不给 = 拔掉所有试跑残留 */ /** 两种试法各有其拔法:流程包给 workflowId,单活动给 taskId;都不给 = 拔掉所有试跑残留 */
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
workflowId: z.ZodDefault<z.ZodString>; workflowId: z.ZodDefault<z.ZodString>;
@@ -2532,6 +2711,7 @@ export declare const testStopRoute: {
export declare const testCurrentRoute: { export declare const testCurrentRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/testbed/current"; readonly path: "/api/gov/testbed/current";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
workflowId: z.ZodOptional<z.ZodString>; workflowId: z.ZodOptional<z.ZodString>;
subject: z.ZodOptional<z.ZodString>; subject: z.ZodOptional<z.ZodString>;
@@ -2551,6 +2731,7 @@ export declare const testCurrentRoute: {
export declare const testActivityStateRoute: { export declare const testActivityStateRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/testbed/activity/:taskId"; readonly path: "/api/gov/testbed/activity/:taskId";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.ZodString; taskId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2617,6 +2798,7 @@ export declare const testActivityStateRoute: {
export declare const uploadFileRoute: { export declare const uploadFileRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/gov/upload"; readonly path: "/api/gov/upload";
readonly access: "design";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
name: z.ZodString; name: z.ZodString;
contentType: z.ZodDefault<z.ZodString>; contentType: z.ZodDefault<z.ZodString>;
@@ -2636,6 +2818,7 @@ export type UploadedFile = z.infer<typeof uploadFileRoute.output>;
export declare const downloadFileRoute: { export declare const downloadFileRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/upload/:id"; readonly path: "/api/gov/upload/:id";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2685,6 +2868,7 @@ export type TestLogRow = z.infer<typeof testLogRowSchema>;
export declare const listTestLogsRoute: { export declare const listTestLogsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/testbed/logs"; readonly path: "/api/gov/testbed/logs";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
runs: z.ZodArray<z.ZodObject<{ runs: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -2720,6 +2904,7 @@ export declare const listTestLogsRoute: {
export declare const testLogDetailRoute: { export declare const testLogDetailRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/testbed/logs/:id"; readonly path: "/api/gov/testbed/logs/:id";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2774,6 +2959,7 @@ export declare const testLogDetailRoute: {
export declare const deleteTestLogRoute: { export declare const deleteTestLogRoute: {
readonly method: "DELETE"; readonly method: "DELETE";
readonly path: "/api/gov/testbed/logs/:id"; readonly path: "/api/gov/testbed/logs/:id";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2784,6 +2970,7 @@ export declare const deleteTestLogRoute: {
export declare const clearTestLogsRoute: { export declare const clearTestLogsRoute: {
readonly method: "DELETE"; readonly method: "DELETE";
readonly path: "/api/gov/testbed/logs"; readonly path: "/api/gov/testbed/logs";
readonly access: "design";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
deleted: z.ZodNumber; deleted: z.ZodNumber;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2792,6 +2979,7 @@ export declare const clearTestLogsRoute: {
export declare const traceOfWorkflowRoute: { export declare const traceOfWorkflowRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/workflows/:id/trace"; readonly path: "/api/gov/workflows/:id/trace";
readonly access: "design";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2825,6 +3013,7 @@ export declare const traceOfWorkflowRoute: {
export declare const govWorkflowRoute: { export declare const govWorkflowRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/workflows/:id"; readonly path: "/api/gov/workflows/:id";
readonly access: readonly ["design", "ops"];
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -2986,6 +3175,7 @@ export declare const govWorkflowRoute: {
export declare const govMetricsRoute: { export declare const govMetricsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/gov/metrics"; readonly path: "/api/gov/metrics";
readonly access: "ops";
/** 统计窗口 —— 不传按月;驾驶舱可切日/周/月/年 */ /** 统计窗口 —— 不传按月;驾驶舱可切日/周/月/年 */
readonly query: z.ZodObject<{ readonly query: z.ZodObject<{
period: z.ZodOptional<z.ZodEnum<{ period: z.ZodOptional<z.ZodEnum<{
+23 -4
View File
@@ -4,16 +4,35 @@
* 前端只经 `shared/typed-fetch` + 本目录的路由契约与服务端交互;组件内禁止裸 fetch。 * 前端只经 `shared/typed-fetch` + 本目录的路由契约与服务端交互;组件内禁止裸 fetch。
* **禁止 Hono RPC 客户端** —— 它要求前端 import 后端类型,违反 §5.1。 * **禁止 Hono RPC 客户端** —— 它要求前端 import 后端类型,违反 §5.1。
*/ */
import type { z } from 'zod'; import { z } from 'zod';
export declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"]; export declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"];
export type HttpMethod = (typeof HTTP_METHODS)[number]; export type HttpMethod = (typeof HTTP_METHODS)[number];
/**
* 三个端(ADR-012):设计中心 / 运营中心 / 工作台。
* 权限只到端 —— 一个角色能进哪几个端由运营中心的「角色 → 端」表定,不写死在代码里。
*/
export declare const SURFACES: readonly ["design", "ops", "workbench"];
export declare const surfaceSchema: z.ZodEnum<{
design: "design";
ops: "ops";
workbench: "workbench";
}>;
export type Surface = z.infer<typeof surfaceSchema>;
export declare const SURFACE_LABEL: Readonly<Record<Surface, string>>;
/**
* 一条路由归哪个端(ADR-012):
* `public` 不需要凭据;`any` 登录即可、不看端;端名 / 端名数组 —— 能进其中任一端即可。
* 鉴权中间件按它统一判,路由里不再各写守卫;漏写编译不过(`satisfies RouteContract`)。
*/
export type RouteAccess = 'public' | 'any' | Surface | readonly Surface[];
/** /**
* 一条路由契约。`path` 用 `:name` 表示路径参数,与 `params` 的键一一对应。 * 一条路由契约。`path` 用 `:name` 表示路径参数,与 `params` 的键一一对应。
* body 只在 POST/PATCH 上有意义;GET 用 query。 * body 只在 POST/PUT/PATCH 上有意义;GET 用 query。
*/ */
export type RouteContract<TParams extends z.ZodTypeAny = z.ZodTypeAny, TQuery extends z.ZodTypeAny = z.ZodTypeAny, TBody extends z.ZodTypeAny = z.ZodTypeAny, TOutput extends z.ZodTypeAny = z.ZodTypeAny> = { export type RouteContract<TParams extends z.ZodTypeAny = z.ZodTypeAny, TQuery extends z.ZodTypeAny = z.ZodTypeAny, TBody extends z.ZodTypeAny = z.ZodTypeAny, TOutput extends z.ZodTypeAny = z.ZodTypeAny> = {
readonly method: HttpMethod; readonly method: HttpMethod;
readonly path: string; readonly path: string;
readonly access: RouteAccess;
readonly params?: TParams; readonly params?: TParams;
readonly query?: TQuery; readonly query?: TQuery;
readonly body?: TBody; readonly body?: TBody;
@@ -21,8 +40,8 @@ export type RouteContract<TParams extends z.ZodTypeAny = z.ZodTypeAny, TQuery ex
}; };
export type RouteOutput<R extends RouteContract> = z.infer<R['output']>; export type RouteOutput<R extends RouteContract> = z.infer<R['output']>;
export type RouteBody<R extends RouteContract> = R['body'] extends z.ZodTypeAny ? z.infer<R['body']> : never; export type RouteBody<R extends RouteContract> = R['body'] extends z.ZodTypeAny ? z.infer<R['body']> : never;
/** 结构化错误:前端按 code 分辨,不解析文案 */ /** 结构化错误:前端按 code 分辨,不解析文案。`gone` = 410,路由已随统一鉴权中心退役 */
export declare const API_ERROR_CODES: readonly ["unauthenticated", "forbidden", "not-found", "validation-failed", "conflict", "precondition-failed", "internal"]; export declare const API_ERROR_CODES: readonly ["unauthenticated", "forbidden", "not-found", "validation-failed", "conflict", "precondition-failed", "internal", "gone"];
export type ApiErrorCode = (typeof API_ERROR_CODES)[number]; export type ApiErrorCode = (typeof API_ERROR_CODES)[number];
export type ApiError = { export type ApiError = {
readonly code: ApiErrorCode; readonly code: ApiErrorCode;
+1
View File
@@ -26,6 +26,7 @@ export type InboundMail = z.infer<typeof inboundMailSchema>;
export declare const ingestMailRoute: { export declare const ingestMailRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/ingest/channel/:channelId/mail"; readonly path: "/api/ingest/channel/:channelId/mail";
readonly access: "public";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
channelId: z.core.$ZodBranded<z.ZodString, "ChannelId", "out">; channelId: z.core.$ZodBranded<z.ZodString, "ChannelId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
+79 -6
View File
@@ -3,20 +3,23 @@
* 工作台五要素:任务 / 审批 / 消息 / 纪要 / 汇报 —— 同一份过程事实的五种投影。 * 工作台五要素:任务 / 审批 / 消息 / 纪要 / 汇报 —— 同一份过程事实的五种投影。
*/ */
import { z } from 'zod'; import { z } from 'zod';
/** 我是谁 —— 无会话即 401,前端据此落登录页(ADR-010) */ /** @deprecated 随 ADR-012 退役,后端回 410;改用 meRoute */
export declare const sessionRoute: { export declare const sessionRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/session"; readonly path: "/api/session";
readonly access: "public";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
staff: z.ZodObject<{ staff: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString; account: z.ZodString;
name: z.ZodString; name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{ state: z.ZodDefault<z.ZodEnum<{
active: "active"; active: "active";
disabled: "disabled"; disabled: "disabled";
}>>; }>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
}, z.core.$strip>; }, z.core.$strip>;
position: z.ZodObject<{ position: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
@@ -56,13 +59,11 @@ export declare const sessionRoute: {
}, z.core.$strip>>; }, z.core.$strip>>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** /** @deprecated 随 ADR-012 退役,后端回 410;改用 meRoute */
* 登录 —— 单组织 cookie session(编码规范 §4 / ADR-010)。
* 成功即种 `jcai_session`(HttpOnly);失败只回一句话,不区分账号不存在还是口令错。
*/
export declare const loginRoute: { export declare const loginRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/login"; readonly path: "/api/login";
readonly access: "public";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
account: z.ZodString; account: z.ZodString;
password: z.ZodString; password: z.ZodString;
@@ -72,11 +73,13 @@ export declare const loginRoute: {
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString; account: z.ZodString;
name: z.ZodString; name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{ state: z.ZodDefault<z.ZodEnum<{
active: "active"; active: "active";
disabled: "disabled"; disabled: "disabled";
}>>; }>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
}, z.core.$strip>; }, z.core.$strip>;
position: z.ZodObject<{ position: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
@@ -116,10 +119,11 @@ export declare const loginRoute: {
}, z.core.$strip>>; }, z.core.$strip>>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** 登出 —— 服务端会话立刻作废,不等 cookie 到期 */ /** @deprecated 随 ADR-012 退役,后端回 410;改用 meRoute */
export declare const logoutRoute: { export declare const logoutRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/logout"; readonly path: "/api/logout";
readonly access: "public";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
ok: z.ZodBoolean; ok: z.ZodBoolean;
}, z.core.$strip>; }, z.core.$strip>;
@@ -127,6 +131,7 @@ export declare const logoutRoute: {
export declare const healthRoute: { export declare const healthRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/health"; readonly path: "/api/health";
readonly access: "public";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
ok: z.ZodBoolean; ok: z.ZodBoolean;
version: z.ZodString; version: z.ZodString;
@@ -136,6 +141,51 @@ export declare const healthRoute: {
}>; }>;
}, z.core.$strip>; }, z.core.$strip>;
}; };
/** 我是谁、我能进哪几个端(ADR-012)—— 中间件解好 principal 放进上下文,这里只回投影 */
export declare const meRoute: {
readonly method: "GET";
readonly path: "/api/me";
readonly access: "any";
readonly output: z.ZodObject<{
staff: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString;
name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{
active: "active";
disabled: "disabled";
}>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
}, z.core.$strip>;
position: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
code: z.ZodString;
name: z.ZodString;
note: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
roles: z.ZodArray<z.ZodString>;
ends: z.ZodArray<z.ZodEnum<{
design: "design";
ops: "ops";
workbench: "workbench";
}>>;
}, z.core.$strip>;
};
/**
* 60 秒票据(ADR-012 §9.3)—— 只为两条带不了 Authorization 头的路:
* `EventSource('/api/stream')` 与 `<a href="…&raw=1" download>`。GET 上 `?ticket=` 认它。
*/
export declare const authTicketRoute: {
readonly method: "GET";
readonly path: "/api/auth/ticket";
readonly access: "any";
readonly output: z.ZodObject<{
ticket: z.ZodString;
expiresAt: z.ZodString;
}, z.core.$strip>;
};
/** 任务卡 —— 清单里一条 = 一个活动的一次运行,不是一整条工作流 */ /** 任务卡 —— 清单里一条 = 一个活动的一次运行,不是一整条工作流 */
export declare const taskCardSchema: z.ZodObject<{ export declare const taskCardSchema: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; id: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
@@ -177,6 +227,7 @@ export type TaskCard = z.infer<typeof taskCardSchema>;
export declare const listTasksRoute: { export declare const listTasksRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/desk/tasks"; readonly path: "/api/desk/tasks";
readonly access: "workbench";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
tasks: z.ZodArray<z.ZodObject<{ tasks: z.ZodArray<z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; id: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
@@ -2138,6 +2189,7 @@ export type TaskDetail = z.infer<typeof taskDetailSchema>;
export declare const taskDetailRoute: { export declare const taskDetailRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/desk/tasks/:taskId"; readonly path: "/api/desk/tasks/:taskId";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4043,6 +4095,7 @@ export declare const taskDetailRoute: {
export declare const replyWorkOrderRoute: { export declare const replyWorkOrderRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/work-orders/reply"; readonly path: "/api/desk/work-orders/reply";
readonly access: "workbench";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
reply: z.ZodRecord<z.ZodString, z.ZodUnknown>; reply: z.ZodRecord<z.ZodString, z.ZodUnknown>;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4058,6 +4111,7 @@ export declare const replyWorkOrderRoute: {
export declare const replyChatCardRoute: { export declare const replyChatCardRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/chat-cards/reply"; readonly path: "/api/desk/chat-cards/reply";
readonly access: "workbench";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
reply: z.ZodRecord<z.ZodString, z.ZodUnknown>; reply: z.ZodRecord<z.ZodString, z.ZodUnknown>;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4072,6 +4126,7 @@ export declare const replyChatCardRoute: {
export declare const markCommReadRoute: { export declare const markCommReadRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/comms/:commId/read"; readonly path: "/api/desk/comms/:commId/read";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
commId: z.ZodString; commId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4120,6 +4175,7 @@ export declare const markCommReadRoute: {
export declare const saveTableRoute: { export declare const saveTableRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/tasks/:taskId/table"; readonly path: "/api/desk/tasks/:taskId/table";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4149,6 +4205,7 @@ export declare const saveTableRoute: {
export declare const openAssetFileRoute: { export declare const openAssetFileRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/desk/tasks/:taskId/file"; readonly path: "/api/desk/tasks/:taskId/file";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4199,6 +4256,7 @@ export declare const openAssetFileRoute: {
export declare const sayRoute: { export declare const sayRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/tasks/:taskId/say"; readonly path: "/api/desk/tasks/:taskId/say";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">; taskId: z.core.$ZodBranded<z.ZodString, "TaskId", "out">;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4393,6 +4451,7 @@ export type SystemMessageView = z.infer<typeof systemMessageSchema>;
export declare const listMessagesRoute: { export declare const listMessagesRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/desk/messages"; readonly path: "/api/desk/messages";
readonly access: "workbench";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
messages: z.ZodArray<z.ZodObject<{ messages: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -4448,6 +4507,7 @@ export declare const listMessagesRoute: {
export declare const markMessageReadRoute: { export declare const markMessageReadRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/messages/:messageId/read"; readonly path: "/api/desk/messages/:messageId/read";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
messageId: z.ZodString; messageId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4459,6 +4519,7 @@ export declare const markMessageReadRoute: {
export declare const answerMessageRoute: { export declare const answerMessageRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/messages/:messageId/answer"; readonly path: "/api/desk/messages/:messageId/answer";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
messageId: z.ZodString; messageId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4473,6 +4534,7 @@ export declare const answerMessageRoute: {
export declare const confirmTriggerPendingRoute: { export declare const confirmTriggerPendingRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/desk/trigger-pending/:id/confirm"; readonly path: "/api/desk/trigger-pending/:id/confirm";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
id: z.ZodString; id: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4521,6 +4583,7 @@ export type MinuteView = z.infer<typeof minuteSchema>;
export declare const listMinutesRoute: { export declare const listMinutesRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/minutes"; readonly path: "/api/minutes";
readonly access: "workbench";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
minutes: z.ZodArray<z.ZodObject<{ minutes: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -4550,6 +4613,7 @@ export declare const listMinutesRoute: {
export declare const createMinuteRoute: { export declare const createMinuteRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/minutes"; readonly path: "/api/minutes";
readonly access: "workbench";
readonly body: z.ZodObject<{ readonly body: z.ZodObject<{
title: z.ZodString; title: z.ZodString;
body: z.ZodString; body: z.ZodString;
@@ -4587,6 +4651,7 @@ export declare const createMinuteRoute: {
export declare const appendResolutionRoute: { export declare const appendResolutionRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/minutes/:minuteId/resolutions"; readonly path: "/api/minutes/:minuteId/resolutions";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
minuteId: z.ZodString; minuteId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4622,6 +4687,7 @@ export declare const appendResolutionRoute: {
export declare const registerResolutionRoute: { export declare const registerResolutionRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/minutes/resolutions/:resolutionId/register"; readonly path: "/api/minutes/resolutions/:resolutionId/register";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
resolutionId: z.ZodString; resolutionId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4704,6 +4770,7 @@ export type ReportView = z.infer<typeof reportSchema>;
export declare const draftReportRoute: { export declare const draftReportRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/reports/:period"; readonly path: "/api/reports/:period";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
period: z.ZodEnum<{ period: z.ZodEnum<{
day: "day"; day: "day";
@@ -4759,6 +4826,7 @@ export declare const draftReportRoute: {
export declare const listReportsRoute: { export declare const listReportsRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/reports"; readonly path: "/api/reports";
readonly access: "workbench";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
reports: z.ZodArray<z.ZodObject<{ reports: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -4809,6 +4877,7 @@ export declare const listReportsRoute: {
export declare const submitReportRoute: { export declare const submitReportRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/reports/:reportId/submit"; readonly path: "/api/reports/:reportId/submit";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
reportId: z.ZodString; reportId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
@@ -4884,6 +4953,7 @@ export type ApprovalItem = z.infer<typeof approvalItemSchema>;
export declare const listApprovalsForMeRoute: { export declare const listApprovalsForMeRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/approvals"; readonly path: "/api/approvals";
readonly access: "workbench";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
approvals: z.ZodArray<z.ZodObject<{ approvals: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -4910,6 +4980,7 @@ export declare const listApprovalsForMeRoute: {
export declare const decideApprovalRoute: { export declare const decideApprovalRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/approvals/:workflowId/:nodeId/decide"; readonly path: "/api/approvals/:workflowId/:nodeId/decide";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
workflowId: z.ZodString; workflowId: z.ZodString;
nodeId: z.ZodString; nodeId: z.ZodString;
@@ -4940,6 +5011,7 @@ export type ListApprovalsForMeOutput = z.infer<typeof listApprovalsForMeRoute.ou
export declare const listRegistrationTriggersRoute: { export declare const listRegistrationTriggersRoute: {
readonly method: "GET"; readonly method: "GET";
readonly path: "/api/triggers/registration"; readonly path: "/api/triggers/registration";
readonly access: "workbench";
readonly output: z.ZodObject<{ readonly output: z.ZodObject<{
triggers: z.ZodArray<z.ZodObject<{ triggers: z.ZodArray<z.ZodObject<{
id: z.ZodString; id: z.ZodString;
@@ -4953,6 +5025,7 @@ export declare const listRegistrationTriggersRoute: {
export declare const fireRegistrationRoute: { export declare const fireRegistrationRoute: {
readonly method: "POST"; readonly method: "POST";
readonly path: "/api/triggers/:triggerId/register"; readonly path: "/api/triggers/:triggerId/register";
readonly access: "workbench";
readonly params: z.ZodObject<{ readonly params: z.ZodObject<{
triggerId: z.ZodString; triggerId: z.ZodString;
}, z.core.$strip>; }, z.core.$strip>;
+107 -23
View File
@@ -1,9 +1,9 @@
/** /**
* 员工、岗位与权限(编码规范 §4 · `org`;语义出处 OS 02 员工管理)。 * 员工、岗位与端(编码规范 §4 · `org`;语义出处 OS 02 员工管理;鉴权归统一鉴权中心,ADR-012)。
* *
* 权限按岗位授予:客体三类(数据 域.对象[.字段] / 治理功能 / 任务)× 动作三种(读 / 写 / 治理)。 * 身份来自 Keycloak:`Staff.id` 就是 token 的 `sub`。权限只到「端」(design / ops / workbench),
* 任务负责人与被征答人对所涉内容**天然可见**,不另行授权。 * 角色 → 端由运营中心的表定;岗位仍是本系统的表(流程引擎按它解析负责人)。
* 单组织、≤50 人:不做多租户,不引 OAuth/OIDC(编码规范 §4 鉴权定调)。 * 旧的岗位授权(客体三类 × 动作三种)与口令会话已退役 —— 类型留一轮(@deprecated,下个迭代删)。
*/ */
import { z } from 'zod'; import { z } from 'zod';
export declare const positionSchema: z.ZodObject<{ export declare const positionSchema: z.ZodObject<{
@@ -23,23 +23,115 @@ export declare const staffSchema: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString; account: z.ZodString;
name: z.ZodString; name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{ state: z.ZodDefault<z.ZodEnum<{
active: "active"; active: "active";
disabled: "disabled"; disabled: "disabled";
}>>; }>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
}, z.core.$strip>; }, z.core.$strip>;
export type Staff = z.infer<typeof staffSchema>; export type Staff = z.infer<typeof staffSchema>;
export declare const principalSchema: z.ZodObject<{
staff: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString;
name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{
active: "active";
disabled: "disabled";
}>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
}, z.core.$strip>;
position: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
code: z.ZodString;
name: z.ZodString;
note: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
roles: z.ZodArray<z.ZodString>;
ends: z.ZodArray<z.ZodEnum<{
design: "design";
ops: "ops";
workbench: "workbench";
}>>;
}, z.core.$strip>;
export type Principal = z.infer<typeof principalSchema>;
/** 角色 → 端:一行一个 realm 角色 */
export declare const roleAccessSchema: z.ZodObject<{
role: z.ZodString;
ends: z.ZodArray<z.ZodEnum<{
design: "design";
ops: "ops";
workbench: "workbench";
}>>;
}, z.core.$strip>;
export type RoleAccess = z.infer<typeof roleAccessSchema>;
/** 名册同步的结果 —— 没配 provisioning 就如实说没配,不摆零 */
export declare const rosterSyncOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
configured: z.ZodLiteral<false>;
message: z.ZodString;
}, z.core.$strip>, z.ZodObject<{
configured: z.ZodLiteral<true>;
inserted: z.ZodNumber;
updated: z.ZodNumber;
disabled: z.ZodNumber;
total: z.ZodNumber;
}, z.core.$strip>], "configured">;
export type RosterSyncOutcome = z.infer<typeof rosterSyncOutcomeSchema>;
export declare const inFlightWorkSchema: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
blocked: z.ZodBoolean;
openTasks: z.ZodDefault<z.ZodArray<z.ZodObject<{
taskId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openCards: z.ZodDefault<z.ZodArray<z.ZodObject<{
cardId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openApprovals: z.ZodDefault<z.ZodArray<z.ZodObject<{
approvalId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
}, z.core.$strip>;
export type InFlightWork = z.infer<typeof inFlightWorkSchema>;
/** @deprecated 随 ADR-012 改名 `inFlightWorkSchema`;下个迭代删 */
export declare const disableCheckSchema: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
blocked: z.ZodBoolean;
openTasks: z.ZodDefault<z.ZodArray<z.ZodObject<{
taskId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openCards: z.ZodDefault<z.ZodArray<z.ZodObject<{
cardId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openApprovals: z.ZodDefault<z.ZodArray<z.ZodObject<{
approvalId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
}, z.core.$strip>;
/** @deprecated 改用 `InFlightWork` */
export type DisableCheck = InFlightWork;
/** @deprecated 权限只到端,动作三分已退役 */
export declare const GRANT_ACTIONS: readonly ["read", "write", "govern"]; export declare const GRANT_ACTIONS: readonly ["read", "write", "govern"];
/** @deprecated */
export declare const grantActionSchema: z.ZodEnum<{ export declare const grantActionSchema: z.ZodEnum<{
read: "read"; read: "read";
write: "write"; write: "write";
govern: "govern"; govern: "govern";
}>; }>;
/** @deprecated */
export type GrantAction = z.infer<typeof grantActionSchema>; export type GrantAction = z.infer<typeof grantActionSchema>;
/** @deprecated */
export declare const GRANT_ACTION_LABEL: Readonly<Record<GrantAction, string>>; export declare const GRANT_ACTION_LABEL: Readonly<Record<GrantAction, string>>;
/** 治理台九功能 —— 授权客体之一 */ /** @deprecated 治理台九功能不再是授权客体 */
export declare const GOVERNANCE_FUNCTIONS: readonly ["task", "staff", "process", "trigger", "activity", "tool", "data", "memory", "system"]; export declare const GOVERNANCE_FUNCTIONS: readonly ["task", "staff", "process", "trigger", "activity", "tool", "data", "memory", "system"];
/** @deprecated */
export declare const governanceFunctionSchema: z.ZodEnum<{ export declare const governanceFunctionSchema: z.ZodEnum<{
trigger: "trigger"; trigger: "trigger";
data: "data"; data: "data";
@@ -51,8 +143,11 @@ export declare const governanceFunctionSchema: z.ZodEnum<{
process: "process"; process: "process";
memory: "memory"; memory: "memory";
}>; }>;
/** @deprecated */
export type GovernanceFunction = z.infer<typeof governanceFunctionSchema>; export type GovernanceFunction = z.infer<typeof governanceFunctionSchema>;
/** @deprecated */
export declare const GOVERNANCE_FUNCTION_LABEL: Readonly<Record<GovernanceFunction, string>>; export declare const GOVERNANCE_FUNCTION_LABEL: Readonly<Record<GovernanceFunction, string>>;
/** @deprecated */
export declare const grantObjectSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ export declare const grantObjectSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"data">; kind: z.ZodLiteral<"data">;
domain: z.ZodString; domain: z.ZodString;
@@ -75,7 +170,9 @@ export declare const grantObjectSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"task">; kind: z.ZodLiteral<"task">;
activityId: z.ZodOptional<z.ZodString>; activityId: z.ZodOptional<z.ZodString>;
}, z.core.$strip>], "kind">; }, z.core.$strip>], "kind">;
/** @deprecated */
export type GrantObject = z.infer<typeof grantObjectSchema>; export type GrantObject = z.infer<typeof grantObjectSchema>;
/** @deprecated */
export declare const grantSchema: z.ZodObject<{ export declare const grantSchema: z.ZodObject<{
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
object: z.ZodDiscriminatedUnion<[z.ZodObject<{ object: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -106,35 +203,21 @@ export declare const grantSchema: z.ZodObject<{
govern: "govern"; govern: "govern";
}>; }>;
}, z.core.$strip>; }, z.core.$strip>;
/** @deprecated */
export type Grant = z.infer<typeof grantSchema>; export type Grant = z.infer<typeof grantSchema>;
/** 员工停用前,其在途任务与待答卡须先改派完毕 */ /** @deprecated 改用 `principalSchema` / `GET /api/me` */
export declare const disableCheckSchema: z.ZodObject<{
staffId: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
blocked: z.ZodBoolean;
openTasks: z.ZodDefault<z.ZodArray<z.ZodObject<{
taskId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openCards: z.ZodDefault<z.ZodArray<z.ZodObject<{
cardId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
openApprovals: z.ZodDefault<z.ZodArray<z.ZodObject<{
approvalId: z.ZodString;
title: z.ZodString;
}, z.core.$strip>>>;
}, z.core.$strip>;
export type DisableCheck = z.infer<typeof disableCheckSchema>;
export declare const sessionSchema: z.ZodObject<{ export declare const sessionSchema: z.ZodObject<{
staff: z.ZodObject<{ staff: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">; id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
account: z.ZodString; account: z.ZodString;
name: z.ZodString; name: z.ZodString;
email: z.ZodOptional<z.ZodString>;
positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; positionId: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
state: z.ZodDefault<z.ZodEnum<{ state: z.ZodDefault<z.ZodEnum<{
active: "active"; active: "active";
disabled: "disabled"; disabled: "disabled";
}>>; }>>;
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
}, z.core.$strip>; }, z.core.$strip>;
position: z.ZodObject<{ position: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">; id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
@@ -173,6 +256,7 @@ export declare const sessionSchema: z.ZodObject<{
}>; }>;
}, z.core.$strip>>; }, z.core.$strip>>;
}, z.core.$strip>; }, z.core.$strip>;
/** @deprecated 改用 `Principal` */
export type Session = z.infer<typeof sessionSchema>; export type Session = z.infer<typeof sessionSchema>;
/** /**
* 工作组 —— 一组「岗位 → 员工」的绑定,起单时装载、随工作流走。 * 工作组 —— 一组「岗位 → 员工」的绑定,起单时装载、随工作流走。
@@ -98,6 +98,7 @@ export function createOrgService(deps: OrgServiceDeps): OrgService {
name: input.name, name: input.name,
positionId, positionId,
state: 'active', state: 'active',
roles: [],
}); });
// 新员工带初始口令建档 —— 不留「有账号但登不进」的半成品 // 新员工带初始口令建档 —— 不留「有账号但登不进」的半成品
await deps.staff.setPasswordHash(created.id, await deps.passwords.hash(deps.seedPassword)); await deps.staff.setPasswordHash(created.id, await deps.passwords.hash(deps.seedPassword));
@@ -254,6 +255,7 @@ export function createOrgService(deps: OrgServiceDeps): OrgService {
name: seed.name, name: seed.name,
positionId, positionId,
state: 'active', state: 'active',
roles: [],
}); });
// 新员工一律带初始口令入库 —— 没有「无口令即可登录」的窗口 // 新员工一律带初始口令入库 —— 没有「无口令即可登录」的窗口
await deps.staff.setPasswordHash(created.id, await deps.passwords.hash(deps.seedPassword)); await deps.staff.setPasswordHash(created.id, await deps.passwords.hash(deps.seedPassword));
@@ -0,0 +1,81 @@
/** 路由契约:每条都得说清归哪个端(ADR-012)—— 中间件靠它,漏一条就是一个没人管的口子 */
import { describe, expect, it } from 'vitest';
import * as contracts from '../index.ts';
import type { RouteContract } from '../index.ts';
import { SURFACES } from '../index.ts';
function isRoute(value: unknown): value is RouteContract {
if (typeof value !== 'object' || value === null) return false;
const v = value as { method?: unknown; path?: unknown; output?: unknown };
return typeof v.method === 'string' && typeof v.path === 'string' && v.output !== undefined;
}
/**
* `contracts` 是异质的命名空间(schema / 常量 / 路由混杂),`Object.entries` 推出的元素类型
* 因而是一个几百项的大联合;`entry is [string, RouteContract]` 直接对着那个联合做型别谓词,
* TS 判不出谓词类型与参数类型互相兼容(TS2677)。先收窄成 `[string, unknown][]` 再筛 ——
* 语义不变(照旧靠 `isRoute` 的运行时判断筛路由),只是把关系判断让给 `unknown`。
*/
const routes = (Object.entries(contracts) as [string, unknown][]).filter(
(entry): entry is [string, RouteContract] => isRoute(entry[1]),
);
const surfaces: readonly string[] = SURFACES;
describe('路由契约的归属(access)', () => {
it('收得到全部路由(至少 90 条)', () => {
expect(routes.length).toBeGreaterThanOrEqual(90);
});
it('access 只取 public / any / 端名 / 非空端名数组', () => {
for (const [name, route] of routes) {
const a = route.access;
const ok =
a === 'public' ||
a === 'any' ||
(typeof a === 'string' ? surfaces.includes(a) : a.length > 0 && a.every((s) => surfaces.includes(s)));
expect(ok, `${name}.access = ${JSON.stringify(a)}`).toBe(true);
}
});
it('公开路由只有健康检查、入站邮件与退役八条', () => {
const publicNames = routes.filter(([, r]) => r.access === 'public').map(([n]) => n).sort();
expect(publicNames).toEqual(
[
'healthRoute',
'ingestMailRoute',
'loginRoute',
'logoutRoute',
'sessionRoute',
'createStaffRoute',
'disableStaffCheckRoute',
'disableStaffRoute',
'listGrantsRoute',
'setGrantsRoute',
].sort(),
);
});
it('只要登录不看端的只有 me / 票据', () => {
const anyNames = routes.filter(([, r]) => r.access === 'any').map(([n]) => n).sort();
expect(anyNames).toEqual(['authTicketRoute', 'meRoute']);
});
it('两端共用的读路由就是 spec §4.4 点名的那八条', () => {
const both = routes
.filter(([, r]) => Array.isArray(r.access))
.map(([n]) => n)
.sort();
expect(both).toEqual(
[
'listStaffRoute',
'listProcessPackagesRoute',
'listTriggersRoute',
'listChannelsRoute',
'listLedgerObjectsRoute',
'ledgerByActivityRoute',
'browseLedgerRoute',
'govWorkflowRoute',
].sort(),
);
});
});
+135 -1
View File
@@ -4,7 +4,17 @@
*/ */
import { z } from 'zod'; import { z } from 'zod';
import type { RouteContract } from './http.ts'; import type { RouteContract } from './http.ts';
import { disableCheckSchema, grantSchema, positionSchema, staffSchema, workGroupSchema } from '../org.ts'; import {
disableCheckSchema,
grantSchema,
inFlightWorkSchema,
positionSchema,
roleAccessSchema,
rosterSyncOutcomeSchema,
staffSchema,
workGroupSchema,
} from '../org.ts';
import { surfaceSchema } from './http.ts';
import { positionIdSchema, staffIdSchema, taskIdSchema } from '../id.ts'; import { positionIdSchema, staffIdSchema, taskIdSchema } from '../id.ts';
import { ledgerObjectKindSchema } from '../ledger.ts'; import { ledgerObjectKindSchema } from '../ledger.ts';
import { approvalTemplateSchema, closureReportSchema, processPackageSchema, triggerConfigSchema } from '../process-package.ts'; import { approvalTemplateSchema, closureReportSchema, processPackageSchema, triggerConfigSchema } from '../process-package.ts';
@@ -17,15 +27,18 @@ import { reportMetricsSchema, reportPeriodSchema } from './workbench.ts';
export const listStaffRoute = { export const listStaffRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/staff', path: '/api/gov/staff',
access: ['design', 'ops'],
output: z.object({ output: z.object({
staff: z.array(staffSchema.extend({ position: positionSchema })), staff: z.array(staffSchema.extend({ position: positionSchema })),
positions: z.array(positionSchema), positions: z.array(positionSchema),
}), }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** @deprecated 随 ADR-012 退役,后端回 410;人来自 Keycloak,改用 syncStaffRoute */
export const createStaffRoute = { export const createStaffRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/staff', path: '/api/gov/staff',
access: 'public',
body: z.object({ body: z.object({
account: z.string().min(1), account: z.string().min(1),
name: z.string().min(1), name: z.string().min(1),
@@ -34,35 +47,103 @@ export const createStaffRoute = {
output: staffSchema, output: staffSchema,
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** @deprecated 随 ADR-012 退役,后端回 410;改用 staffInFlightRoute */
export const disableStaffCheckRoute = { export const disableStaffCheckRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/staff/:staffId/disable-check', path: '/api/gov/staff/:staffId/disable-check',
access: 'public',
params: z.object({ staffId: staffIdSchema }), params: z.object({ staffId: staffIdSchema }),
output: disableCheckSchema, output: disableCheckSchema,
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** @deprecated 随 ADR-012 退役,后端回 410;停用改在 Keycloak 做,名册同步据此落 disabled */
export const disableStaffRoute = { export const disableStaffRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/staff/:staffId/disable', path: '/api/gov/staff/:staffId/disable',
access: 'public',
params: z.object({ staffId: staffIdSchema }), params: z.object({ staffId: staffIdSchema }),
output: z.object({ ok: z.boolean(), check: disableCheckSchema }), output: z.object({ ok: z.boolean(), check: disableCheckSchema }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** @deprecated 随 ADR-012 退役,后端回 410;权限只到端,改用 listRoleAccessRoute */
export const listGrantsRoute = { export const listGrantsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/positions/:positionId/grants', path: '/api/gov/positions/:positionId/grants',
access: 'public',
params: z.object({ positionId: positionIdSchema }), params: z.object({ positionId: positionIdSchema }),
output: z.object({ grants: z.array(grantSchema) }), output: z.object({ grants: z.array(grantSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** @deprecated 随 ADR-012 退役,后端回 410;权限只到端,改用 setRoleAccessRoute */
export const setGrantsRoute = { export const setGrantsRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/positions/:positionId/grants', path: '/api/gov/positions/:positionId/grants',
access: 'public',
params: z.object({ positionId: positionIdSchema }), params: z.object({ positionId: positionIdSchema }),
body: z.object({ grants: z.array(grantSchema) }), body: z.object({ grants: z.array(grantSchema) }),
output: z.object({ grants: z.array(grantSchema) }), output: z.object({ grants: z.array(grantSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
/* ── 员工管理 · 名册与岗位(ADR-012:人来自鉴权中心,岗位本地指派) ────── */
/** 从 Keycloak Admin API 同步名册;没配 provisioning 服务账号就如实回 configured:false */
export const syncStaffRoute = {
method: 'POST',
path: '/api/gov/staff/sync',
access: 'ops',
output: rosterSyncOutcomeSchema,
} as const satisfies RouteContract;
/** 指派岗位;指回哨兵岗 NONE = 取消指派 */
export const setStaffPositionRoute = {
method: 'PUT',
path: '/api/gov/staff/:staffId/position',
access: 'ops',
params: z.object({ staffId: staffIdSchema }),
body: z.object({ positionId: positionIdSchema }),
output: staffSchema.extend({ position: positionSchema }),
} as const satisfies RouteContract;
/** 此人身上还挂着什么(只读)—— 原「停用前置检查」;停用本身在 Keycloak 做 */
export const staffInFlightRoute = {
method: 'GET',
path: '/api/gov/staff/:staffId/in-flight',
access: 'ops',
params: z.object({ staffId: staffIdSchema }),
output: inFlightWorkSchema,
} as const satisfies RouteContract;
/* ── 角色 → 端(ADR-012:权限只到端,这张表运营中心可改) ─────────────── */
export const listRoleAccessRoute = {
method: 'GET',
path: '/api/gov/access/roles',
access: 'ops',
output: z.object({
roles: z.array(roleAccessSchema),
/** 名册上见过的角色 ∪ 已配置的角色 —— 配置前它等于「无端可进」 */
knownRoles: z.array(z.string()),
}),
} as const satisfies RouteContract;
/** 锁死不变量不过(改完没人能进运营中心)→ 409 conflict */
export const setRoleAccessRoute = {
method: 'PUT',
path: '/api/gov/access/roles/:role',
access: 'ops',
params: z.object({ role: z.string().min(1) }),
body: z.object({ ends: z.array(surfaceSchema) }),
output: roleAccessSchema,
} as const satisfies RouteContract;
export const deleteRoleAccessRoute = {
method: 'DELETE',
path: '/api/gov/access/roles/:role',
access: 'ops',
params: z.object({ role: z.string().min(1) }),
output: z.object({ ok: z.boolean() }),
} as const satisfies RouteContract;
/* ── 系统管理 · LLM 实例(四元绑定) ────────────────────────────────── */ /* ── 系统管理 · LLM 实例(四元绑定) ────────────────────────────────── */
export const LLM_PROVIDERS = ['openai-compatible', 'openai', 'deepseek', 'anthropic', 'qwen'] as const; export const LLM_PROVIDERS = ['openai-compatible', 'openai', 'deepseek', 'anthropic', 'qwen'] as const;
@@ -92,6 +173,7 @@ export type LlmInstanceView = z.infer<typeof llmInstanceViewSchema>;
export const listLlmProvidersRoute = { export const listLlmProvidersRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/llm/providers', path: '/api/gov/llm/providers',
access: 'design',
output: z.object({ output: z.object({
providers: z.array( providers: z.array(
z.object({ z.object({
@@ -109,12 +191,14 @@ export const listLlmProvidersRoute = {
export const listLlmInstancesRoute = { export const listLlmInstancesRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/llm', path: '/api/gov/llm',
access: 'design',
output: z.object({ instances: z.array(llmInstanceViewSchema) }), output: z.object({ instances: z.array(llmInstanceViewSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
export const upsertLlmInstanceRoute = { export const upsertLlmInstanceRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/llm', path: '/api/gov/llm',
access: 'design',
body: z.object({ body: z.object({
id: z.string().optional(), id: z.string().optional(),
name: z.string().min(1), name: z.string().min(1),
@@ -142,6 +226,7 @@ export const upsertLlmInstanceRoute = {
export const deleteLlmInstanceRoute = { export const deleteLlmInstanceRoute = {
method: 'DELETE', method: 'DELETE',
path: '/api/gov/llm/:id', path: '/api/gov/llm/:id',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -149,6 +234,7 @@ export const deleteLlmInstanceRoute = {
export const probeLlmDraftRoute = { export const probeLlmDraftRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/llm/probe', path: '/api/gov/llm/probe',
access: 'design',
body: z.object({ body: z.object({
id: z.string().optional(), id: z.string().optional(),
name: z.string().optional(), name: z.string().optional(),
@@ -163,6 +249,7 @@ export const probeLlmDraftRoute = {
export const probeLlmInstanceRoute = { export const probeLlmInstanceRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/llm/:id/probe', path: '/api/gov/llm/:id/probe',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ ok: z.boolean(), latencyMs: z.number().int().optional(), error: z.string().optional() }), output: z.object({ ok: z.boolean(), latencyMs: z.number().int().optional(), error: z.string().optional() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -186,6 +273,7 @@ export type GovTaskRow = z.infer<typeof govTaskRowSchema>;
export const listGovTasksRoute = { export const listGovTasksRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/tasks', path: '/api/gov/tasks',
access: 'ops',
output: z.object({ output: z.object({
tasks: z.array(govTaskRowSchema), tasks: z.array(govTaskRowSchema),
workflows: z.array( workflows: z.array(
@@ -204,6 +292,7 @@ export const listGovTasksRoute = {
export const controlTaskRoute = { export const controlTaskRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/tasks/:taskId/control', path: '/api/gov/tasks/:taskId/control',
access: 'ops',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
body: z.object({ semantic: controlSemanticSchema, reason: z.string().optional() }), body: z.object({ semantic: controlSemanticSchema, reason: z.string().optional() }),
output: z.object({ ok: z.boolean(), resumable: z.boolean() }), output: z.object({ ok: z.boolean(), resumable: z.boolean() }),
@@ -212,6 +301,7 @@ export const controlTaskRoute = {
export const resumeTaskRoute = { export const resumeTaskRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/tasks/:taskId/resume', path: '/api/gov/tasks/:taskId/resume',
access: 'ops',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
output: z.object({ ok: z.boolean(), runtime: taskRuntimeStateSchema }), output: z.object({ ok: z.boolean(), runtime: taskRuntimeStateSchema }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -220,6 +310,7 @@ export const resumeTaskRoute = {
export const reassignTaskRoute = { export const reassignTaskRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/tasks/:taskId/reassign', path: '/api/gov/tasks/:taskId/reassign',
access: 'ops',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
body: z.object({ to: staffIdSchema }), body: z.object({ to: staffIdSchema }),
output: z.object({ ok: z.boolean(), ownerId: staffIdSchema }), output: z.object({ ok: z.boolean(), ownerId: staffIdSchema }),
@@ -229,6 +320,7 @@ export const reassignTaskRoute = {
export const govTaskLogRoute = { export const govTaskLogRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/tasks/:taskId/log', path: '/api/gov/tasks/:taskId/log',
access: 'ops',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
output: z.object({ output: z.object({
// 与工作台同一份日志:章是四字动作,内容带主语(见 workbench 的 log 项) // 与工作台同一份日志:章是四字动作,内容带主语(见 workbench 的 log 项)
@@ -243,6 +335,7 @@ export const govTaskLogRoute = {
export const listProcessPackagesRoute = { export const listProcessPackagesRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/process', path: '/api/gov/process',
access: ['design', 'ops'],
output: z.object({ packages: z.array(processPackageSchema) }), output: z.object({ packages: z.array(processPackageSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -257,6 +350,7 @@ export const listProcessPackagesRoute = {
export const saveProcessRoute = { export const saveProcessRoute = {
method: 'PUT', method: 'PUT',
path: '/api/gov/process', path: '/api/gov/process',
access: 'design',
/** /**
* 编辑即其身:pkg.version 已存在 → 就地写回该版(号不变)。 * 编辑即其身:pkg.version 已存在 → 就地写回该版(号不变)。
* `asNew: true`(或版本不存在,如新流程首版)→ 产生新版本号(服务端算,不生效)。 * `asNew: true`(或版本不存在,如新流程首版)→ 产生新版本号(服务端算,不生效)。
@@ -275,6 +369,7 @@ export const saveProcessRoute = {
export const setProcessStateRoute = { export const setProcessStateRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/process/:id/:version/state', path: '/api/gov/process/:id/:version/state',
access: 'design',
params: z.object({ id: z.string(), version: z.string() }), params: z.object({ id: z.string(), version: z.string() }),
body: z.object({ state: z.enum(['active', 'retired']) }), body: z.object({ state: z.enum(['active', 'retired']) }),
output: z.object({ packages: z.array(processPackageSchema) }), output: z.object({ packages: z.array(processPackageSchema) }),
@@ -288,6 +383,7 @@ export const setProcessStateRoute = {
export const deleteProcessVersionRoute = { export const deleteProcessVersionRoute = {
method: 'DELETE', method: 'DELETE',
path: '/api/gov/process/:id/:version', path: '/api/gov/process/:id/:version',
access: 'design',
params: z.object({ id: z.string(), version: z.string() }), params: z.object({ id: z.string(), version: z.string() }),
output: z.object({ packages: z.array(processPackageSchema) }), output: z.object({ packages: z.array(processPackageSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -298,6 +394,7 @@ export const deleteProcessVersionRoute = {
export const listApprovalTemplatesRoute = { export const listApprovalTemplatesRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/approval-templates', path: '/api/gov/approval-templates',
access: 'design',
output: z.object({ output: z.object({
templates: z.array( templates: z.array(
approvalTemplateSchema.extend({ approvalTemplateSchema.extend({
@@ -312,6 +409,7 @@ export const listApprovalTemplatesRoute = {
export const upsertApprovalTemplateRoute = { export const upsertApprovalTemplateRoute = {
method: 'PUT', method: 'PUT',
path: '/api/gov/approval-templates', path: '/api/gov/approval-templates',
access: 'design',
body: z.object({ template: approvalTemplateSchema }), body: z.object({ template: approvalTemplateSchema }),
output: z.object({ template: approvalTemplateSchema }), output: z.object({ template: approvalTemplateSchema }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -320,6 +418,7 @@ export const upsertApprovalTemplateRoute = {
export const deleteApprovalTemplateRoute = { export const deleteApprovalTemplateRoute = {
method: 'DELETE', method: 'DELETE',
path: '/api/gov/approval-templates/:id', path: '/api/gov/approval-templates/:id',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -332,6 +431,7 @@ export const deleteApprovalTemplateRoute = {
export const deleteProcessFlowRoute = { export const deleteProcessFlowRoute = {
method: 'DELETE', method: 'DELETE',
path: '/api/gov/process/:id', path: '/api/gov/process/:id',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ packages: z.array(processPackageSchema) }), output: z.object({ packages: z.array(processPackageSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -347,6 +447,7 @@ export const deleteProcessFlowRoute = {
export const editProcessVersionRoute = { export const editProcessVersionRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/process/:id/:version/edit', path: '/api/gov/process/:id/:version/edit',
access: 'design',
params: z.object({ id: z.string(), version: z.string() }), params: z.object({ id: z.string(), version: z.string() }),
body: z.object({ body: z.object({
version: z.number().int().min(1).max(9999).optional(), version: z.number().int().min(1).max(9999).optional(),
@@ -360,6 +461,7 @@ export const editProcessVersionRoute = {
export const validateProcessRoute = { export const validateProcessRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/process/validate', path: '/api/gov/process/validate',
access: 'design',
body: z.object({ pkg: processPackageSchema }), body: z.object({ pkg: processPackageSchema }),
output: closureReportSchema, output: closureReportSchema,
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -369,12 +471,14 @@ export const validateProcessRoute = {
export const listTriggersRoute = { export const listTriggersRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/triggers', path: '/api/gov/triggers',
access: ['design', 'ops'],
output: z.object({ triggers: z.array(triggerConfigSchema) }), output: z.object({ triggers: z.array(triggerConfigSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
export const toggleTriggerRoute = { export const toggleTriggerRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/triggers/:id/toggle', path: '/api/gov/triggers/:id/toggle',
access: 'ops',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
body: z.object({ enabled: z.boolean() }), body: z.object({ enabled: z.boolean() }),
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
@@ -390,6 +494,7 @@ export const toggleTriggerRoute = {
export const upsertTriggerRoute = { export const upsertTriggerRoute = {
method: 'PUT', method: 'PUT',
path: '/api/gov/triggers', path: '/api/gov/triggers',
access: 'design',
body: z.object({ trigger: triggerConfigSchema }), body: z.object({ trigger: triggerConfigSchema }),
output: z.object({ trigger: triggerConfigSchema }), output: z.object({ trigger: triggerConfigSchema }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -405,6 +510,7 @@ export const upsertTriggerRoute = {
export const listWorkGroupsRoute = { export const listWorkGroupsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/work-groups', path: '/api/gov/work-groups',
access: 'ops',
/** `ownerName` 是组长姓名的呈现补充,读表时一并带出,免得前端再对一次 id */ /** `ownerName` 是组长姓名的呈现补充,读表时一并带出,免得前端再对一次 id */
output: z.object({ groups: z.array(workGroupSchema.extend({ ownerName: z.string() })) }), output: z.object({ groups: z.array(workGroupSchema.extend({ ownerName: z.string() })) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -413,6 +519,7 @@ export const listWorkGroupsRoute = {
export const saveWorkGroupRoute = { export const saveWorkGroupRoute = {
method: 'PUT', method: 'PUT',
path: '/api/gov/work-groups', path: '/api/gov/work-groups',
access: 'ops',
body: z.object({ body: z.object({
ownerId: staffIdSchema, ownerId: staffIdSchema,
name: z.string().min(1), name: z.string().min(1),
@@ -434,6 +541,7 @@ export const saveWorkGroupRoute = {
export const installActivityRoute = { export const installActivityRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/activities', path: '/api/gov/activities',
access: 'design',
body: z.object({ body: z.object({
/** 清单六区:身份 / 七件契约 / 声明 / 资产初始版 / 样例集 / 代码入口 */ /** 清单六区:身份 / 七件契约 / 声明 / 资产初始版 / 样例集 / 代码入口 */
manifest: activityManifestSchema, manifest: activityManifestSchema,
@@ -465,6 +573,7 @@ export const installActivityRoute = {
export const registerToolRoute = { export const registerToolRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/tools', path: '/api/gov/tools',
access: 'design',
body: z.object({ body: z.object({
contract: z.object({ contract: z.object({
name: z.string().min(1), name: z.string().min(1),
@@ -500,6 +609,7 @@ export const registerToolRoute = {
export const listActivitiesRoute = { export const listActivitiesRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/activities', path: '/api/gov/activities',
access: 'design',
output: z.object({ output: z.object({
activities: z.array( activities: z.array(
z.object({ z.object({
@@ -562,6 +672,7 @@ export const listActivitiesRoute = {
export const listToolsRoute = { export const listToolsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/tools', path: '/api/gov/tools',
access: 'design',
output: z.object({ output: z.object({
tools: z.array( tools: z.array(
z.object({ z.object({
@@ -583,6 +694,7 @@ export const listToolsRoute = {
export const listLedgerObjectsRoute = { export const listLedgerObjectsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/data/objects', path: '/api/gov/data/objects',
access: ['design', 'ops'],
/** `label` 是人读名(落在表注释上);缺省即没登记,呈现退回对象键 */ /** `label` 是人读名(落在表注释上);缺省即没登记,呈现退回对象键 */
output: z.object({ output: z.object({
objects: z.array( objects: z.array(
@@ -623,6 +735,7 @@ export const listLedgerObjectsRoute = {
export const ledgerByActivityRoute = { export const ledgerByActivityRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/data/by-activity', path: '/api/gov/data/by-activity',
access: ['design', 'ops'],
output: z.object({ output: z.object({
activities: z.array( activities: z.array(
z.object({ z.object({
@@ -667,6 +780,7 @@ export const ledgerByActivityRoute = {
export const browseLedgerRoute = { export const browseLedgerRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/data/:domain/:object', path: '/api/gov/data/:domain/:object',
access: ['design', 'ops'],
params: z.object({ domain: z.string(), object: z.string() }), params: z.object({ domain: z.string(), object: z.string() }),
output: z.object({ output: z.object({
label: z.string().optional(), label: z.string().optional(),
@@ -682,6 +796,7 @@ export const browseLedgerRoute = {
export const listActivityMemoryRoute = { export const listActivityMemoryRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/memory/:activityId', path: '/api/gov/memory/:activityId',
access: 'design',
params: z.object({ activityId: z.string() }), params: z.object({ activityId: z.string() }),
output: z.object({ output: z.object({
entries: z.array( entries: z.array(
@@ -703,6 +818,7 @@ export const listActivityMemoryRoute = {
export const listChannelsRoute = { export const listChannelsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/channels', path: '/api/gov/channels',
access: ['design', 'ops'],
output: z.object({ output: z.object({
channels: z.array( channels: z.array(
z.object({ z.object({
@@ -733,6 +849,7 @@ export const listChannelsRoute = {
export const listApprovalsRoute = { export const listApprovalsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/approvals', path: '/api/gov/approvals',
access: 'ops',
output: z.object({ output: z.object({
approvals: z.array( approvals: z.array(
z.object({ z.object({
@@ -763,6 +880,7 @@ export const listApprovalsRoute = {
export const setTriggerConfirmRoute = { export const setTriggerConfirmRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/triggers/:id/confirm', path: '/api/gov/triggers/:id/confirm',
access: 'ops',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
body: z.object({ required: z.boolean(), staffId: z.string().optional() }), body: z.object({ required: z.boolean(), staffId: z.string().optional() }),
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
@@ -772,6 +890,7 @@ export const setTriggerConfirmRoute = {
export const listNodeOwnersRoute = { export const listNodeOwnersRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/packages/:id/owners', path: '/api/gov/packages/:id/owners',
access: 'ops',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ owners: z.array(z.object({ nodeId: z.string(), staffId: z.string() })) }), output: z.object({ owners: z.array(z.object({ nodeId: z.string(), staffId: z.string() })) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -779,6 +898,7 @@ export const listNodeOwnersRoute = {
export const setNodeOwnerRoute = { export const setNodeOwnerRoute = {
method: 'PUT', method: 'PUT',
path: '/api/gov/packages/:id/owners/:nodeId', path: '/api/gov/packages/:id/owners/:nodeId',
access: 'ops',
params: z.object({ id: z.string(), nodeId: z.string() }), params: z.object({ id: z.string(), nodeId: z.string() }),
/** null = 撤掉运营指定,回到轮值 */ /** null = 撤掉运营指定,回到轮值 */
body: z.object({ staffId: z.string().nullable() }), body: z.object({ staffId: z.string().nullable() }),
@@ -788,6 +908,7 @@ export const setNodeOwnerRoute = {
export const testRunRoute = { export const testRunRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/testbed/run', path: '/api/gov/testbed/run',
access: 'design',
body: z.object({ body: z.object({
packageId: z.string(), packageId: z.string(),
/** /**
@@ -827,6 +948,7 @@ export const testRunRoute = {
export const testRunActivityRoute = { export const testRunActivityRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/testbed/run-activity', path: '/api/gov/testbed/run-activity',
access: 'design',
body: z.object({ body: z.object({
activityId: z.string().min(1), activityId: z.string().min(1),
/** 起单入参 —— 按该活动契约的 `input.data` 填;可用包自带样例预填 */ /** 起单入参 —— 按该活动契约的 `input.data` 填;可用包自带样例预填 */
@@ -860,6 +982,7 @@ export const testRunActivityRoute = {
export const testStopRoute = { export const testStopRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/testbed/stop', path: '/api/gov/testbed/stop',
access: 'design',
/** 两种试法各有其拔法:流程包给 workflowId,单活动给 taskId;都不给 = 拔掉所有试跑残留 */ /** 两种试法各有其拔法:流程包给 workflowId,单活动给 taskId;都不给 = 拔掉所有试跑残留 */
body: z.object({ workflowId: z.string().default(''), taskId: z.string().default('') }), body: z.object({ workflowId: z.string().default(''), taskId: z.string().default('') }),
output: z.object({ destroyed: z.boolean(), tasks: z.number().int() }), output: z.object({ destroyed: z.boolean(), tasks: z.number().int() }),
@@ -877,6 +1000,7 @@ export const testStopRoute = {
export const testCurrentRoute = { export const testCurrentRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/testbed/current', path: '/api/gov/testbed/current',
access: 'design',
output: z.object({ output: z.object({
workflowId: z.string().optional(), workflowId: z.string().optional(),
subject: z.string().optional(), subject: z.string().optional(),
@@ -900,6 +1024,7 @@ export const testCurrentRoute = {
export const testActivityStateRoute = { export const testActivityStateRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/testbed/activity/:taskId', path: '/api/gov/testbed/activity/:taskId',
access: 'design',
params: z.object({ taskId: z.string() }), params: z.object({ taskId: z.string() }),
output: z.object({ output: z.object({
taskId: z.string(), taskId: z.string(),
@@ -954,6 +1079,7 @@ export const testActivityStateRoute = {
export const uploadFileRoute = { export const uploadFileRoute = {
method: 'POST', method: 'POST',
path: '/api/gov/upload', path: '/api/gov/upload',
access: 'design',
body: z.object({ body: z.object({
name: z.string().min(1), name: z.string().min(1),
contentType: z.string().default(''), contentType: z.string().default(''),
@@ -976,6 +1102,7 @@ export type UploadedFile = z.infer<typeof uploadFileRoute.output>;
export const downloadFileRoute = { export const downloadFileRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/upload/:id', path: '/api/gov/upload/:id',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ name: z.string(), contentType: z.string(), dataBase64: z.string() }), output: z.object({ name: z.string(), contentType: z.string(), dataBase64: z.string() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -1013,6 +1140,7 @@ export type TestLogRow = z.infer<typeof testLogRowSchema>;
export const listTestLogsRoute = { export const listTestLogsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/testbed/logs', path: '/api/gov/testbed/logs',
access: 'design',
output: z.object({ runs: z.array(testLogRowSchema) }), output: z.object({ runs: z.array(testLogRowSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -1020,6 +1148,7 @@ export const listTestLogsRoute = {
export const testLogDetailRoute = { export const testLogDetailRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/testbed/logs/:id', path: '/api/gov/testbed/logs/:id',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: testLogRowSchema.extend({ output: testLogRowSchema.extend({
trail: z.array( trail: z.array(
@@ -1053,6 +1182,7 @@ export const testLogDetailRoute = {
export const deleteTestLogRoute = { export const deleteTestLogRoute = {
method: 'DELETE', method: 'DELETE',
path: '/api/gov/testbed/logs/:id', path: '/api/gov/testbed/logs/:id',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ deleted: z.number().int() }), output: z.object({ deleted: z.number().int() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -1060,6 +1190,7 @@ export const deleteTestLogRoute = {
export const clearTestLogsRoute = { export const clearTestLogsRoute = {
method: 'DELETE', method: 'DELETE',
path: '/api/gov/testbed/logs', path: '/api/gov/testbed/logs',
access: 'design',
output: z.object({ deleted: z.number().int() }), output: z.object({ deleted: z.number().int() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -1067,6 +1198,7 @@ export const clearTestLogsRoute = {
export const traceOfWorkflowRoute = { export const traceOfWorkflowRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/workflows/:id/trace', path: '/api/gov/workflows/:id/trace',
access: 'design',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ output: z.object({
spans: z.array( spans: z.array(
@@ -1092,6 +1224,7 @@ export const traceOfWorkflowRoute = {
export const govWorkflowRoute = { export const govWorkflowRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/workflows/:id', path: '/api/gov/workflows/:id',
access: ['design', 'ops'],
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ output: z.object({
workflow: z.object({ workflow: z.object({
@@ -1131,6 +1264,7 @@ export const govWorkflowRoute = {
export const govMetricsRoute = { export const govMetricsRoute = {
method: 'GET', method: 'GET',
path: '/api/gov/metrics', path: '/api/gov/metrics',
access: 'ops',
/** 统计窗口 —— 不传按月;驾驶舱可切日/周/月/年 */ /** 统计窗口 —— 不传按月;驾驶舱可切日/周/月/年 */
query: z.object({ period: reportPeriodSchema.optional() }), query: z.object({ period: reportPeriodSchema.optional() }),
/** `period` 原样回声:客户端不传时,由它知道服务端用的是哪个窗口 */ /** `period` 原样回声:客户端不传时,由它知道服务端用的是哪个窗口 */
+26 -3
View File
@@ -4,14 +4,35 @@
* 前端只经 `shared/typed-fetch` + 本目录的路由契约与服务端交互;组件内禁止裸 fetch。 * 前端只经 `shared/typed-fetch` + 本目录的路由契约与服务端交互;组件内禁止裸 fetch。
* **禁止 Hono RPC 客户端** —— 它要求前端 import 后端类型,违反 §5.1。 * **禁止 Hono RPC 客户端** —— 它要求前端 import 后端类型,违反 §5.1。
*/ */
import type { z } from 'zod'; import { z } from 'zod';
export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const; export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const;
export type HttpMethod = (typeof HTTP_METHODS)[number]; export type HttpMethod = (typeof HTTP_METHODS)[number];
/**
* 三个端(ADR-012):设计中心 / 运营中心 / 工作台。
* 权限只到端 —— 一个角色能进哪几个端由运营中心的「角色 → 端」表定,不写死在代码里。
*/
export const SURFACES = ['design', 'ops', 'workbench'] as const;
export const surfaceSchema = z.enum(SURFACES);
export type Surface = z.infer<typeof surfaceSchema>;
export const SURFACE_LABEL: Readonly<Record<Surface, string>> = {
design: '设计中心',
ops: '运营中心',
workbench: '工作台',
};
/**
* 一条路由归哪个端(ADR-012):
* `public` 不需要凭据;`any` 登录即可、不看端;端名 / 端名数组 —— 能进其中任一端即可。
* 鉴权中间件按它统一判,路由里不再各写守卫;漏写编译不过(`satisfies RouteContract`)。
*/
export type RouteAccess = 'public' | 'any' | Surface | readonly Surface[];
/** /**
* 一条路由契约。`path` 用 `:name` 表示路径参数,与 `params` 的键一一对应。 * 一条路由契约。`path` 用 `:name` 表示路径参数,与 `params` 的键一一对应。
* body 只在 POST/PATCH 上有意义;GET 用 query。 * body 只在 POST/PUT/PATCH 上有意义;GET 用 query。
*/ */
export type RouteContract< export type RouteContract<
TParams extends z.ZodTypeAny = z.ZodTypeAny, TParams extends z.ZodTypeAny = z.ZodTypeAny,
@@ -21,6 +42,7 @@ export type RouteContract<
> = { > = {
readonly method: HttpMethod; readonly method: HttpMethod;
readonly path: string; readonly path: string;
readonly access: RouteAccess;
readonly params?: TParams; readonly params?: TParams;
readonly query?: TQuery; readonly query?: TQuery;
readonly body?: TBody; readonly body?: TBody;
@@ -32,7 +54,7 @@ export type RouteBody<R extends RouteContract> = R['body'] extends z.ZodTypeAny
? z.infer<R['body']> ? z.infer<R['body']>
: never; : never;
/** 结构化错误:前端按 code 分辨,不解析文案 */ /** 结构化错误:前端按 code 分辨,不解析文案。`gone` = 410,路由已随统一鉴权中心退役 */
export const API_ERROR_CODES = [ export const API_ERROR_CODES = [
'unauthenticated', 'unauthenticated',
'forbidden', 'forbidden',
@@ -41,6 +63,7 @@ export const API_ERROR_CODES = [
'conflict', 'conflict',
'precondition-failed', 'precondition-failed',
'internal', 'internal',
'gone',
] as const; ] as const;
export type ApiErrorCode = (typeof API_ERROR_CODES)[number]; export type ApiErrorCode = (typeof API_ERROR_CODES)[number];
+1
View File
@@ -28,6 +28,7 @@ export type InboundMail = z.infer<typeof inboundMailSchema>;
export const ingestMailRoute = { export const ingestMailRoute = {
method: 'POST', method: 'POST',
path: '/api/ingest/channel/:channelId/mail', path: '/api/ingest/channel/:channelId/mail',
access: 'public',
params: z.object({ channelId: channelIdSchema }), params: z.object({ channelId: channelIdSchema }),
body: inboundMailSchema, body: inboundMailSchema,
output: z.object({ output: z.object({
+50 -7
View File
@@ -4,39 +4,40 @@
*/ */
import { z } from 'zod'; import { z } from 'zod';
import type { RouteContract } from './http.ts'; import type { RouteContract } from './http.ts';
import { sessionSchema } from '../org.ts'; import { principalSchema, sessionSchema } from '../org.ts';
import { chatMessageSchema, deliverableSchema, fileDocSchema, formFieldSchema, stageSchema, viewBlockSchema, workOrderSchema } from '../card.ts'; import { chatMessageSchema, deliverableSchema, fileDocSchema, formFieldSchema, stageSchema, viewBlockSchema, workOrderSchema } from '../card.ts';
import { attributionSideSchema, taskRuntimeStateSchema, terminalStateSchema, workflowStateSchema } from '../terminal.ts'; import { attributionSideSchema, taskRuntimeStateSchema, terminalStateSchema, workflowStateSchema } from '../terminal.ts';
import { staffIdSchema, taskIdSchema } from '../id.ts'; import { staffIdSchema, taskIdSchema } from '../id.ts';
/** 我是谁 —— 无会话即 401,前端据此落登录页(ADR-010) */ /** @deprecated 随 ADR-012 退役,后端回 410;改用 meRoute */
export const sessionRoute = { export const sessionRoute = {
method: 'GET', method: 'GET',
path: '/api/session', path: '/api/session',
access: 'public',
output: sessionSchema, output: sessionSchema,
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** /** @deprecated 随 ADR-012 退役,后端回 410;改用 meRoute */
* 登录 —— 单组织 cookie session(编码规范 §4 / ADR-010)。
* 成功即种 `jcai_session`(HttpOnly);失败只回一句话,不区分账号不存在还是口令错。
*/
export const loginRoute = { export const loginRoute = {
method: 'POST', method: 'POST',
path: '/api/login', path: '/api/login',
access: 'public',
body: z.object({ account: z.string().min(1), password: z.string().min(1) }), body: z.object({ account: z.string().min(1), password: z.string().min(1) }),
output: sessionSchema, output: sessionSchema,
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** 登出 —— 服务端会话立刻作废,不等 cookie 到期 */ /** @deprecated 随 ADR-012 退役,后端回 410;改用 meRoute */
export const logoutRoute = { export const logoutRoute = {
method: 'POST', method: 'POST',
path: '/api/logout', path: '/api/logout',
access: 'public',
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
export const healthRoute = { export const healthRoute = {
method: 'GET', method: 'GET',
path: '/api/health', path: '/api/health',
access: 'public',
output: z.object({ output: z.object({
ok: z.boolean(), ok: z.boolean(),
version: z.string(), version: z.string(),
@@ -44,6 +45,25 @@ export const healthRoute = {
}), }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
/** 我是谁、我能进哪几个端(ADR-012)—— 中间件解好 principal 放进上下文,这里只回投影 */
export const meRoute = {
method: 'GET',
path: '/api/me',
access: 'any',
output: principalSchema,
} as const satisfies RouteContract;
/**
* 60 秒票据(ADR-012 §9.3)—— 只为两条带不了 Authorization 头的路:
* `EventSource('/api/stream')` 与 `<a href="…&raw=1" download>`。GET 上 `?ticket=` 认它。
*/
export const authTicketRoute = {
method: 'GET',
path: '/api/auth/ticket',
access: 'any',
output: z.object({ ticket: z.string().min(1), expiresAt: z.string() }),
} as const satisfies RouteContract;
/* ── 任务要素:四区(任务列表 / 标题区 / 工作区 / 对话区) ─────────────── */ /* ── 任务要素:四区(任务列表 / 标题区 / 工作区 / 对话区) ─────────────── */
/** 任务卡 —— 清单里一条 = 一个活动的一次运行,不是一整条工作流 */ /** 任务卡 —— 清单里一条 = 一个活动的一次运行,不是一整条工作流 */
@@ -77,6 +97,7 @@ export type TaskCard = z.infer<typeof taskCardSchema>;
export const listTasksRoute = { export const listTasksRoute = {
method: 'GET', method: 'GET',
path: '/api/desk/tasks', path: '/api/desk/tasks',
access: 'workbench',
output: z.object({ tasks: z.array(taskCardSchema) }), output: z.object({ tasks: z.array(taskCardSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -229,6 +250,7 @@ export type TaskDetail = z.infer<typeof taskDetailSchema>;
export const taskDetailRoute = { export const taskDetailRoute = {
method: 'GET', method: 'GET',
path: '/api/desk/tasks/:taskId', path: '/api/desk/tasks/:taskId',
access: 'workbench',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
output: taskDetailSchema, output: taskDetailSchema,
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -237,6 +259,7 @@ export const taskDetailRoute = {
export const replyWorkOrderRoute = { export const replyWorkOrderRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/work-orders/reply', path: '/api/desk/work-orders/reply',
access: 'workbench',
body: z.object({ reply: z.record(z.string(), z.unknown()) }), body: z.object({ reply: z.record(z.string(), z.unknown()) }),
output: z.object({ ok: z.boolean(), producesCompletion: z.boolean() }), output: z.object({ ok: z.boolean(), producesCompletion: z.boolean() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -248,6 +271,7 @@ export const replyWorkOrderRoute = {
export const replyChatCardRoute = { export const replyChatCardRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/chat-cards/reply', path: '/api/desk/chat-cards/reply',
access: 'workbench',
body: z.object({ reply: z.record(z.string(), z.unknown()) }), body: z.object({ reply: z.record(z.string(), z.unknown()) }),
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -259,6 +283,7 @@ export const replyChatCardRoute = {
export const markCommReadRoute = { export const markCommReadRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/comms/:commId/read', path: '/api/desk/comms/:commId/read',
access: 'workbench',
params: z.object({ commId: z.string() }), params: z.object({ commId: z.string() }),
body: z.object({ read: z.boolean() }), body: z.object({ read: z.boolean() }),
output: z.object({ ok: z.boolean(), unread: z.boolean() }), output: z.object({ ok: z.boolean(), unread: z.boolean() }),
@@ -301,6 +326,7 @@ export const markCommReadRoute = {
export const saveTableRoute = { export const saveTableRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/tasks/:taskId/table', path: '/api/desk/tasks/:taskId/table',
access: 'workbench',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
body: z.object({ body: z.object({
/** `{ '域.对象': [ { 台账列名: 值 } ] }` —— 主键列用来认行,不参与改行;台账里没有的行就是**新增** */ /** `{ '域.对象': [ { 台账列名: 值 } ] }` —— 主键列用来认行,不参与改行;台账里没有的行就是**新增** */
@@ -347,6 +373,7 @@ export const saveTableRoute = {
export const openAssetFileRoute = { export const openAssetFileRoute = {
method: 'GET', method: 'GET',
path: '/api/desk/tasks/:taskId/file', path: '/api/desk/tasks/:taskId/file',
access: 'workbench',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
query: z.object({ query: z.object({
/** `域.对象` —— 必须是活动清单里声明过 `file` 段的那张表(授权面) */ /** `域.对象` —— 必须是活动清单里声明过 `file` 段的那张表(授权面) */
@@ -362,6 +389,7 @@ export const openAssetFileRoute = {
export const sayRoute = { export const sayRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/tasks/:taskId/say', path: '/api/desk/tasks/:taskId/say',
access: 'workbench',
params: z.object({ taskId: taskIdSchema }), params: z.object({ taskId: taskIdSchema }),
body: z.object({ text: z.string().min(1) }), body: z.object({ text: z.string().min(1) }),
output: chatMessageSchema, output: chatMessageSchema,
@@ -402,12 +430,14 @@ export type SystemMessageView = z.infer<typeof systemMessageSchema>;
export const listMessagesRoute = { export const listMessagesRoute = {
method: 'GET', method: 'GET',
path: '/api/desk/messages', path: '/api/desk/messages',
access: 'workbench',
output: z.object({ messages: z.array(systemMessageSchema) }), output: z.object({ messages: z.array(systemMessageSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
export const markMessageReadRoute = { export const markMessageReadRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/messages/:messageId/read', path: '/api/desk/messages/:messageId/read',
access: 'workbench',
params: z.object({ messageId: z.string() }), params: z.object({ messageId: z.string() }),
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -416,6 +446,7 @@ export const markMessageReadRoute = {
export const answerMessageRoute = { export const answerMessageRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/messages/:messageId/answer', path: '/api/desk/messages/:messageId/answer',
access: 'workbench',
params: z.object({ messageId: z.string() }), params: z.object({ messageId: z.string() }),
body: z.object({ answer: z.record(z.string(), z.unknown()) }), body: z.object({ answer: z.record(z.string(), z.unknown()) }),
output: z.object({ ok: z.boolean() }), output: z.object({ ok: z.boolean() }),
@@ -425,6 +456,7 @@ export const answerMessageRoute = {
export const confirmTriggerPendingRoute = { export const confirmTriggerPendingRoute = {
method: 'POST', method: 'POST',
path: '/api/desk/trigger-pending/:id/confirm', path: '/api/desk/trigger-pending/:id/confirm',
access: 'workbench',
params: z.object({ id: z.string() }), params: z.object({ id: z.string() }),
output: z.object({ workflowId: z.string(), alreadyStarted: z.boolean() }), output: z.object({ workflowId: z.string(), alreadyStarted: z.boolean() }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -460,12 +492,14 @@ export type MinuteView = z.infer<typeof minuteSchema>;
export const listMinutesRoute = { export const listMinutesRoute = {
method: 'GET', method: 'GET',
path: '/api/minutes', path: '/api/minutes',
access: 'workbench',
output: z.object({ minutes: z.array(minuteSchema) }), output: z.object({ minutes: z.array(minuteSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
export const createMinuteRoute = { export const createMinuteRoute = {
method: 'POST', method: 'POST',
path: '/api/minutes', path: '/api/minutes',
access: 'workbench',
body: z.object({ body: z.object({
title: z.string().min(1), title: z.string().min(1),
body: z.string(), body: z.string(),
@@ -479,6 +513,7 @@ export const createMinuteRoute = {
export const appendResolutionRoute = { export const appendResolutionRoute = {
method: 'POST', method: 'POST',
path: '/api/minutes/:minuteId/resolutions', path: '/api/minutes/:minuteId/resolutions',
access: 'workbench',
params: z.object({ minuteId: z.string() }), params: z.object({ minuteId: z.string() }),
body: z.object({ text: z.string().min(1), ownerId: staffIdSchema.optional() }), body: z.object({ text: z.string().min(1), ownerId: staffIdSchema.optional() }),
output: minuteSchema, output: minuteSchema,
@@ -488,6 +523,7 @@ export const appendResolutionRoute = {
export const registerResolutionRoute = { export const registerResolutionRoute = {
method: 'POST', method: 'POST',
path: '/api/minutes/resolutions/:resolutionId/register', path: '/api/minutes/resolutions/:resolutionId/register',
access: 'workbench',
params: z.object({ resolutionId: z.string() }), params: z.object({ resolutionId: z.string() }),
body: z.object({ processPackageId: z.string().min(1), data: z.record(z.string(), z.unknown()) }), body: z.object({ processPackageId: z.string().min(1), data: z.record(z.string(), z.unknown()) }),
output: z.object({ workflowId: z.string(), resolutionId: z.string() }), output: z.object({ workflowId: z.string(), resolutionId: z.string() }),
@@ -542,6 +578,7 @@ export type ReportView = z.infer<typeof reportSchema>;
export const draftReportRoute = { export const draftReportRoute = {
method: 'GET', method: 'GET',
path: '/api/reports/:period', path: '/api/reports/:period',
access: 'workbench',
params: z.object({ period: reportPeriodSchema }), params: z.object({ period: reportPeriodSchema }),
output: reportSchema, output: reportSchema,
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -549,6 +586,7 @@ export const draftReportRoute = {
export const listReportsRoute = { export const listReportsRoute = {
method: 'GET', method: 'GET',
path: '/api/reports', path: '/api/reports',
access: 'workbench',
output: z.object({ reports: z.array(reportSchema) }), output: z.object({ reports: z.array(reportSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -556,6 +594,7 @@ export const listReportsRoute = {
export const submitReportRoute = { export const submitReportRoute = {
method: 'POST', method: 'POST',
path: '/api/reports/:reportId/submit', path: '/api/reports/:reportId/submit',
access: 'workbench',
params: z.object({ reportId: z.string() }), params: z.object({ reportId: z.string() }),
body: z.object({ note: z.string().optional() }), body: z.object({ note: z.string().optional() }),
output: reportSchema, output: reportSchema,
@@ -585,6 +624,7 @@ export type ApprovalItem = z.infer<typeof approvalItemSchema>;
export const listApprovalsForMeRoute = { export const listApprovalsForMeRoute = {
method: 'GET', method: 'GET',
path: '/api/approvals', path: '/api/approvals',
access: 'workbench',
output: z.object({ approvals: z.array(approvalItemSchema) }), output: z.object({ approvals: z.array(approvalItemSchema) }),
} as const satisfies RouteContract; } as const satisfies RouteContract;
@@ -592,6 +632,7 @@ export const listApprovalsForMeRoute = {
export const decideApprovalRoute = { export const decideApprovalRoute = {
method: 'POST', method: 'POST',
path: '/api/approvals/:workflowId/:nodeId/decide', path: '/api/approvals/:workflowId/:nodeId/decide',
access: 'workbench',
params: z.object({ workflowId: z.string(), nodeId: z.string() }), params: z.object({ workflowId: z.string(), nodeId: z.string() }),
body: z.object({ verdict: z.enum(['pass', 'reject']), comment: z.string().optional() }), body: z.object({ verdict: z.enum(['pass', 'reject']), comment: z.string().optional() }),
output: z.object({ ok: z.boolean(), workflowState: workflowStateSchema }), output: z.object({ ok: z.boolean(), workflowState: workflowStateSchema }),
@@ -608,6 +649,7 @@ export type ListApprovalsForMeOutput = z.infer<typeof listApprovalsForMeRoute.ou
export const listRegistrationTriggersRoute = { export const listRegistrationTriggersRoute = {
method: 'GET', method: 'GET',
path: '/api/triggers/registration', path: '/api/triggers/registration',
access: 'workbench',
output: z.object({ output: z.object({
triggers: z.array( triggers: z.array(
z.object({ z.object({
@@ -625,6 +667,7 @@ export const listRegistrationTriggersRoute = {
export const fireRegistrationRoute = { export const fireRegistrationRoute = {
method: 'POST', method: 'POST',
path: '/api/triggers/:triggerId/register', path: '/api/triggers/:triggerId/register',
access: 'workbench',
params: z.object({ triggerId: z.string() }), params: z.object({ triggerId: z.string() }),
body: z.object({ data: z.record(z.string(), z.unknown()) }), body: z.object({ data: z.record(z.string(), z.unknown()) }),
output: z.object({ output: z.object({
+75 -21
View File
@@ -1,12 +1,13 @@
/** /**
* 员工、岗位与权限(编码规范 §4 · `org`;语义出处 OS 02 员工管理)。 * 员工、岗位与端(编码规范 §4 · `org`;语义出处 OS 02 员工管理;鉴权归统一鉴权中心,ADR-012)。
* *
* 权限按岗位授予:客体三类(数据 域.对象[.字段] / 治理功能 / 任务)× 动作三种(读 / 写 / 治理)。 * 身份来自 Keycloak:`Staff.id` 就是 token 的 `sub`。权限只到「端」(design / ops / workbench),
* 任务负责人与被征答人对所涉内容**天然可见**,不另行授权。 * 角色 → 端由运营中心的表定;岗位仍是本系统的表(流程引擎按它解析负责人)。
* 单组织、≤50 人:不做多租户,不引 OAuth/OIDC(编码规范 §4 鉴权定调)。 * 旧的岗位授权(客体三类 × 动作三种)与口令会话已退役 —— 类型留一轮(@deprecated,下个迭代删)。
*/ */
import { z } from 'zod'; import { z } from 'zod';
import { positionIdSchema, staffIdSchema } from './id.ts'; import { positionIdSchema, staffIdSchema } from './id.ts';
import { surfaceSchema } from './api/http.ts';
export const positionSchema = z.object({ export const positionSchema = z.object({
id: positionIdSchema, id: positionIdSchema,
@@ -21,27 +22,84 @@ export const staffStateSchema = z.enum(STAFF_STATES);
export type StaffState = z.infer<typeof staffStateSchema>; export type StaffState = z.infer<typeof staffStateSchema>;
export const staffSchema = z.object({ export const staffSchema = z.object({
/** = Keycloak `sub`(手册硬约束 8:业务外键用 sub,不用用户名) */
id: staffIdSchema, id: staffIdSchema,
/** = `preferred_username`,只作显示 —— 用户名允许改,别拿它当键 */
account: z.string().min(1), account: z.string().min(1),
/** = `name` claim,缺则回退 account */
name: z.string().min(1), name: z.string().min(1),
email: z.string().optional(),
/** 未指派 = 哨兵岗 `NONE / 未分配` 的 id */
positionId: positionIdSchema, positionId: positionIdSchema,
/** 只由名册同步置 `disabled`(Keycloak 停用或删除);有效 token 不改它 */
state: staffStateSchema.default('active'), state: staffStateSchema.default('active'),
/** realm 角色快照(token 签发时;噪声角色已滤) */
roles: z.array(z.string()).default([]),
}); });
export type Staff = z.infer<typeof staffSchema>; export type Staff = z.infer<typeof staffSchema>;
/* ── 岗位授权:客体三类 × 动作三种 ─────────────────────────────────── */ /* ── 我是谁、能进哪几个端(ADR-012) ───────────────────────────────── */
export const principalSchema = z.object({
staff: staffSchema,
position: positionSchema,
roles: z.array(z.string()),
ends: z.array(surfaceSchema),
});
export type Principal = z.infer<typeof principalSchema>;
/** 角色 → 端:一行一个 realm 角色 */
export const roleAccessSchema = z.object({
role: z.string().min(1),
ends: z.array(surfaceSchema),
});
export type RoleAccess = z.infer<typeof roleAccessSchema>;
/** 名册同步的结果 —— 没配 provisioning 就如实说没配,不摆零 */
export const rosterSyncOutcomeSchema = z.discriminatedUnion('configured', [
z.object({ configured: z.literal(false), message: z.string() }),
z.object({
configured: z.literal(true),
inserted: z.number().int().min(0),
updated: z.number().int().min(0),
disabled: z.number().int().min(0),
total: z.number().int().min(0),
}),
]);
export type RosterSyncOutcome = z.infer<typeof rosterSyncOutcomeSchema>;
/* ── 在途工作(只读;OS 02:改派先于停用,停用本身已归鉴权中心) ───── */
export const inFlightWorkSchema = z.object({
staffId: staffIdSchema,
blocked: z.boolean(),
openTasks: z.array(z.object({ taskId: z.string(), title: z.string() })).default([]),
openCards: z.array(z.object({ cardId: z.string(), title: z.string() })).default([]),
openApprovals: z.array(z.object({ approvalId: z.string(), title: z.string() })).default([]),
});
export type InFlightWork = z.infer<typeof inFlightWorkSchema>;
/** @deprecated 随 ADR-012 改名 `inFlightWorkSchema`;下个迭代删 */
export const disableCheckSchema = inFlightWorkSchema;
/** @deprecated 改用 `InFlightWork` */
export type DisableCheck = InFlightWork;
/* ── 岗位授权(已退役,ADR-012;类型留一轮给 web 编译,下个迭代删) ──── */
/** @deprecated 权限只到端,动作三分已退役 */
export const GRANT_ACTIONS = ['read', 'write', 'govern'] as const; export const GRANT_ACTIONS = ['read', 'write', 'govern'] as const;
/** @deprecated */
export const grantActionSchema = z.enum(GRANT_ACTIONS); export const grantActionSchema = z.enum(GRANT_ACTIONS);
/** @deprecated */
export type GrantAction = z.infer<typeof grantActionSchema>; export type GrantAction = z.infer<typeof grantActionSchema>;
/** @deprecated */
export const GRANT_ACTION_LABEL: Readonly<Record<GrantAction, string>> = { export const GRANT_ACTION_LABEL: Readonly<Record<GrantAction, string>> = {
read: '读', read: '读',
write: '写', write: '写',
govern: '治理', govern: '治理',
}; };
/** 治理台九功能 —— 授权客体之一 */ /** @deprecated 治理台九功能不再是授权客体 */
export const GOVERNANCE_FUNCTIONS = [ export const GOVERNANCE_FUNCTIONS = [
'task', 'task',
'staff', 'staff',
@@ -53,9 +111,11 @@ export const GOVERNANCE_FUNCTIONS = [
'memory', 'memory',
'system', 'system',
] as const; ] as const;
/** @deprecated */
export const governanceFunctionSchema = z.enum(GOVERNANCE_FUNCTIONS); export const governanceFunctionSchema = z.enum(GOVERNANCE_FUNCTIONS);
/** @deprecated */
export type GovernanceFunction = z.infer<typeof governanceFunctionSchema>; export type GovernanceFunction = z.infer<typeof governanceFunctionSchema>;
/** @deprecated */
export const GOVERNANCE_FUNCTION_LABEL: Readonly<Record<GovernanceFunction, string>> = { export const GOVERNANCE_FUNCTION_LABEL: Readonly<Record<GovernanceFunction, string>> = {
task: '任务管理', task: '任务管理',
staff: '员工管理', staff: '员工管理',
@@ -68,6 +128,7 @@ export const GOVERNANCE_FUNCTION_LABEL: Readonly<Record<GovernanceFunction, stri
system: '系统管理', system: '系统管理',
}; };
/** @deprecated */
export const grantObjectSchema = z.discriminatedUnion('kind', [ export const grantObjectSchema = z.discriminatedUnion('kind', [
z.object({ z.object({
kind: z.literal('data'), kind: z.literal('data'),
@@ -78,34 +139,27 @@ export const grantObjectSchema = z.discriminatedUnion('kind', [
z.object({ kind: z.literal('governance'), function: governanceFunctionSchema }), z.object({ kind: z.literal('governance'), function: governanceFunctionSchema }),
z.object({ kind: z.literal('task'), activityId: z.string().optional() }), z.object({ kind: z.literal('task'), activityId: z.string().optional() }),
]); ]);
/** @deprecated */
export type GrantObject = z.infer<typeof grantObjectSchema>; export type GrantObject = z.infer<typeof grantObjectSchema>;
/** @deprecated */
export const grantSchema = z.object({ export const grantSchema = z.object({
positionId: positionIdSchema, positionId: positionIdSchema,
object: grantObjectSchema, object: grantObjectSchema,
action: grantActionSchema, action: grantActionSchema,
}); });
/** @deprecated */
export type Grant = z.infer<typeof grantSchema>; export type Grant = z.infer<typeof grantSchema>;
/* ── 停用前置检查(OS 02) ─────────────────────────────────────────── */ /* ── 会话(已退役:身份在 token 里,ADR-012) ──────────────────────── */
/** 员工停用前,其在途任务与待答卡须先改派完毕 */
export const disableCheckSchema = z.object({
staffId: staffIdSchema,
blocked: z.boolean(),
openTasks: z.array(z.object({ taskId: z.string(), title: z.string() })).default([]),
openCards: z.array(z.object({ cardId: z.string(), title: z.string() })).default([]),
openApprovals: z.array(z.object({ approvalId: z.string(), title: z.string() })).default([]),
});
export type DisableCheck = z.infer<typeof disableCheckSchema>;
/* ── 会话 ─────────────────────────────────────────────────────────────── */
/** @deprecated 改用 `principalSchema` / `GET /api/me` */
export const sessionSchema = z.object({ export const sessionSchema = z.object({
staff: staffSchema, staff: staffSchema,
position: positionSchema, position: positionSchema,
grants: z.array(grantSchema), grants: z.array(grantSchema),
}); });
/** @deprecated 改用 `Principal` */
export type Session = z.infer<typeof sessionSchema>; export type Session = z.infer<typeof sessionSchema>;
/* ── 工作组(ADR-011) ────────────────────────────────────────────────── */ /* ── 工作组(ADR-011) ────────────────────────────────────────────────── */