Archived
统一鉴权中心接入(ADR-012):org 重写为 Keycloak 身份 + 角色→端;中间件按路由归属统一判;口令/会话/岗位授权退役;迁移 0032
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015kAzS35kMnZNt4YGgJXtfw
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { extractText } from '../upload-extract.ts';
|
||||
import type { Hono } from 'hono';
|
||||
import type { ApiError, GovernanceFunction, GrantAction } from '@contracts';
|
||||
import type { ApiError } from '@contracts';
|
||||
import {
|
||||
browseLedgerRoute,
|
||||
controlTaskRoute,
|
||||
@@ -71,6 +71,7 @@ import {
|
||||
VersionTakenError,
|
||||
} from '@modules/process';
|
||||
import type { Container } from '../container.ts';
|
||||
import { staffOf } from '../http-env.ts';
|
||||
import type { AppEnv } from '../http-env.ts';
|
||||
import { displayLog } from './log-lines.ts';
|
||||
|
||||
@@ -366,46 +367,9 @@ async function assembleFromMail(
|
||||
}
|
||||
|
||||
export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Container): void {
|
||||
/**
|
||||
* 岗位授权:客体 = 治理功能,动作按路由分级 —— **看是 `read`,动才是 `govern`**。
|
||||
*
|
||||
* 客体三类 × 动作三种的分法就是为此:BD 有 `task:read` 能看在途,却动不了控制事件;
|
||||
* PM 有 `task:govern`,读写皆通(动作蕴含 govern ⊃ write ⊃ read)。
|
||||
* 一律要 govern 会把只读监控也锁死,等于把三种动作压成一种。
|
||||
*/
|
||||
function guard(fn: GovernanceFunction, action: GrantAction) {
|
||||
return async (account: string): Promise<ApiError | undefined> => {
|
||||
const staff = await container.org.findByAccount(account);
|
||||
if (staff === undefined) return { code: 'unauthenticated', message: '会话无效' };
|
||||
const ok = await container.org.authorize(staff.id, { kind: 'governance', function: fn }, action);
|
||||
if (ok) return undefined;
|
||||
const need = action === 'govern' ? '治理权' : '查看权';
|
||||
return { code: 'forbidden', message: `岗位 ${staff.position.code} 无「${fn}」${need}` };
|
||||
};
|
||||
}
|
||||
|
||||
const readTask = guard('task', 'read');
|
||||
const governTask = guard('task', 'govern');
|
||||
const readProcess = guard('process', 'read');
|
||||
const governProcess = guard('process', 'govern');
|
||||
const readTrigger = guard('trigger', 'read');
|
||||
const governTrigger = guard('trigger', 'govern');
|
||||
const readActivity = guard('activity', 'read');
|
||||
const governActivity = guard('activity', 'govern');
|
||||
const readTool = guard('tool', 'read');
|
||||
const governTool = guard('tool', 'govern');
|
||||
const readData = guard('data', 'read');
|
||||
const readMemory = guard('memory', 'read');
|
||||
|
||||
/** 拒绝的 HTTP 语义:无权 403,无会话 401 */
|
||||
const denyStatus = (body: ApiError): 401 | 403 => (body.code === 'forbidden' ? 403 : 401);
|
||||
|
||||
/* ── 任务管理:在途监控 · 控制 · 改派 · 任务日志 ──────────────────── */
|
||||
|
||||
app.get(listGovTasksRoute.path, async (c) => {
|
||||
const denied = await readTask(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
|
||||
const { staff } = await container.org.listStaff();
|
||||
const nameOf = new Map(staff.map((s) => [s.id, s.name]));
|
||||
const rows = await Promise.all(
|
||||
@@ -438,9 +402,7 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
|
||||
/** 控制三语义 —— 叫停留痕为最重档纠正信号 */
|
||||
app.post(controlTaskRoute.path, async (c) => {
|
||||
const denied = await governTask(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const staff = await container.org.findByAccount(c.get('account'));
|
||||
const staff = staffOf(c);
|
||||
const parsed = controlTaskRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success || staff === undefined) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '入参不合法' };
|
||||
@@ -456,18 +418,14 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.post(resumeTaskRoute.path, async (c) => {
|
||||
const denied = await governTask(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const staff = await container.org.findByAccount(c.get('account'));
|
||||
const staff = staffOf(c);
|
||||
if (staff === undefined) return c.json({ code: 'unauthenticated', message: '会话无效' } satisfies ApiError, 401);
|
||||
const task = await container.task.resume(taskIdSchema.parse(c.req.param('taskId')), staff.id);
|
||||
return c.json({ ok: true, runtime: task.runtime });
|
||||
});
|
||||
|
||||
app.post(reassignTaskRoute.path, async (c) => {
|
||||
const denied = await governTask(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const staff = await container.org.findByAccount(c.get('account'));
|
||||
const staff = staffOf(c);
|
||||
const parsed = reassignTaskRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success || staff === undefined) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '入参不合法' };
|
||||
@@ -482,8 +440,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.get(govTaskLogRoute.path, async (c) => {
|
||||
const denied = await readTask(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const lines = await container.memory.taskLog(taskIdSchema.parse(c.req.param('taskId')));
|
||||
return c.json({
|
||||
// 与工作台同一份日志、同一套口径:章是四字动作,内容带主语
|
||||
@@ -502,9 +458,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 它在实现上是卡到审批人工位的验收工单,`taskId` 位坐的是 workflowId(见 wiring/engines.ts)。
|
||||
*/
|
||||
app.get(listApprovalsRoute.path, async (c) => {
|
||||
const denied = await readTask(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
|
||||
const workflows = await container.process.listWorkflows(50);
|
||||
const perWorkflow = await Promise.all(
|
||||
workflows.map(async (w) => {
|
||||
@@ -551,14 +504,12 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* (指派不是代答,人那一环还是真的),测试台只管起单与看穿全程。
|
||||
*/
|
||||
app.post(testRunRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = testRunRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '试跑入参不合协议', detail: parsed.error.issues };
|
||||
return c.json(body, 400);
|
||||
}
|
||||
const me = await container.org.findByAccount(c.get('account'));
|
||||
const me = staffOf(c);
|
||||
const { packageId, mail, assignments } = parsed.data;
|
||||
try {
|
||||
const data = await assembleFromMail(container, packageId, mail);
|
||||
@@ -592,8 +543,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 仿真不是生产:试跑留下的任务不该在别人工作台上过夜,也不该混进在途口径。
|
||||
*/
|
||||
app.post(testStopRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = testStopRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '停止仿真入参不合协议' };
|
||||
@@ -620,8 +569,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
|
||||
/** 当前在跑的仿真 —— 刷新页面也认得回来(测试台一次只跑一条) */
|
||||
app.get(testCurrentRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
/*
|
||||
* **在跑与跑完要分得开**(`live`)。
|
||||
*
|
||||
@@ -668,14 +615,12 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* `origin='testbed'`,好让人随时连根拔掉。
|
||||
*/
|
||||
app.post(testRunActivityRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = testRunActivityRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '试跑入参不合协议', detail: parsed.error.issues };
|
||||
return c.json(body, 400);
|
||||
}
|
||||
const me = await container.org.findByAccount(c.get('account'));
|
||||
const me = staffOf(c);
|
||||
const { activityId, data, message, ownerId } = parsed.data;
|
||||
/*
|
||||
* 派给谁:显式指定优先,否则**派给操作者自己**。
|
||||
@@ -729,8 +674,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 拿 trace 树看不出「第三拍为什么被拒」—— 那句话在任务轨迹里。
|
||||
*/
|
||||
app.get(testActivityStateRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const taskId = taskIdSchema.parse(c.req.param('taskId'));
|
||||
/*
|
||||
* **「这一单没了」是 404,不是 500**(2026-08-23 用户:「推进完成了,但测试台还在
|
||||
@@ -776,16 +719,12 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
/* ── 上传件:附件要能真正进活动 ─────────────────────────────────── */
|
||||
|
||||
/** 上限 8MB —— 超了当场拒,不静默截断(截断过的文件比没有更害人) */
|
||||
const UPLOAD_CAP = 8 * 1024 * 1024;
|
||||
|
||||
app.post(uploadFileRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = uploadFileRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '上传入参不合协议', detail: parsed.error.issues };
|
||||
@@ -806,7 +745,7 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
}
|
||||
// 读文本就在入口这一步做完:活动那一侧读得懂的只有文字,读不出就如实记 note
|
||||
const got = extractText(name, contentType, bytes);
|
||||
const me = await container.org.findByAccount(c.get('account'));
|
||||
const me = staffOf(c);
|
||||
const ref = crypto.randomUUID();
|
||||
await container.db.execute(sql`
|
||||
insert into sys.upload (id, name, content_type, size, bytes, text, note, uploaded_by)
|
||||
@@ -823,8 +762,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.get(downloadFileRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const rows = await container.db.execute<{ name: string; content_type: string; bytes: Buffer }>(
|
||||
sql`select name, content_type, bytes from sys.upload where id = ${c.req.param('id')}`,
|
||||
);
|
||||
@@ -844,8 +781,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
|
||||
/** 列表只给读数与计数 —— 一次试跑的轨迹上百条,列表页不需要正文 */
|
||||
app.get(listTestLogsRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const rows = await container.db.execute<TestLogDbRow>(sql`
|
||||
select id, kind, subject, target, task_id, workflow_id, terminal, reason, used, caps,
|
||||
jsonb_array_length(trail) as trail_count,
|
||||
@@ -857,8 +792,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.get(testLogDetailRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const rows = await container.db.execute<TestLogDbRow & { trail: unknown; drafts: unknown; cards: unknown }>(sql`
|
||||
select id, kind, subject, target, task_id, workflow_id, terminal, reason, used, caps,
|
||||
jsonb_array_length(trail) as trail_count,
|
||||
@@ -875,8 +808,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.delete(deleteTestLogRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const rows = await container.db.execute<{ id: string }>(
|
||||
sql`delete from sys.testbed_run where id = ${c.req.param('id')} returning id`,
|
||||
);
|
||||
@@ -884,25 +815,18 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.delete(clearTestLogsRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const rows = await container.db.execute<{ id: string }>(sql`delete from sys.testbed_run returning id`);
|
||||
return c.json({ deleted: rowsOf<{ id: string }>(rows).length });
|
||||
});
|
||||
|
||||
/** 全链 Trace:一条工作流的 span 树 —— 节点 → 步骤 / 工具 / 模型 / 人卡 */
|
||||
app.get(traceOfWorkflowRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const spans = await container.trace.ofWorkflow(c.req.param('id'));
|
||||
return c.json({ spans });
|
||||
});
|
||||
|
||||
/** 工作流下钻:流程包 × 节点实例 × 在途任务 —— 编排画布的数据面 */
|
||||
app.get(govWorkflowRoute.path, async (c) => {
|
||||
const denied = await readTask(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
|
||||
const workflow = await container.process.getWorkflow(c.req.param('id'));
|
||||
const pkg = await container.process.getPackage(workflow.packageId, workflow.packageVersion);
|
||||
if (pkg === undefined) {
|
||||
@@ -945,8 +869,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
/* ── 工作流管理 ──────────────────────────────────────────────────── */
|
||||
|
||||
app.get(listProcessPackagesRoute.path, async (c) => {
|
||||
const denied = await readProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
return c.json({ packages: await container.process.listPackages() });
|
||||
});
|
||||
|
||||
@@ -955,8 +877,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 编辑即其身:保存写回当前版本;在途钉版不可变,新版本只出自显式新建。
|
||||
*/
|
||||
app.put(saveProcessRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = saveProcessRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '流程包不合 schema', detail: parsed.error.issues };
|
||||
@@ -986,8 +906,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
/** 版本启停:激活即独占,停用即下线 —— 在途按启动版本跑完,零影响 */
|
||||
/** 删一版:生效版先停用(409),在途钉版不许删(409)—— 那些单要按它跑完 */
|
||||
app.delete(deleteProcessVersionRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
try {
|
||||
const packages = await container.process.deleteVersion(c.req.param('id'), Number(c.req.param('version')));
|
||||
return c.json({ packages });
|
||||
@@ -1004,14 +922,10 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
/* ── 审批模板(审批活动)—— 活动库里配、编排里引 ─────────────── */
|
||||
|
||||
app.get(listApprovalTemplatesRoute.path, async (c) => {
|
||||
const denied = await readProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
return c.json({ templates: await container.process.approvalTemplates() });
|
||||
});
|
||||
|
||||
app.put(upsertApprovalTemplateRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = upsertApprovalTemplateRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '审批模板不合协议(标识与名称必填)' };
|
||||
@@ -1021,8 +935,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.delete(deleteApprovalTemplateRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
try {
|
||||
await container.process.deleteApprovalTemplate(c.req.param('id'));
|
||||
return c.json({ ok: true });
|
||||
@@ -1042,8 +954,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 反查放在路由层而不是用例层:process 不该知道 trigger 的存在(L7 不依赖它)。
|
||||
*/
|
||||
app.delete(deleteProcessFlowRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const id = c.req.param('id');
|
||||
const rules = (await container.trigger.list()).filter((t) => t.processPackageId === id);
|
||||
if (rules.length > 0) {
|
||||
@@ -1067,8 +977,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
|
||||
/** 改版本记录:号自己定、名随之 —— 撞号与「跑过单改号」409,别的版一字不动 */
|
||||
app.post(editProcessVersionRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = editProcessVersionRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '版本号要 1–9999 的整数,名字 1–40 字' };
|
||||
@@ -1096,8 +1004,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.post(setProcessStateRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = setProcessStateRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '启停状态不合协议' };
|
||||
@@ -1122,8 +1028,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
|
||||
/** 画图即校验 —— 与前端画布同源 */
|
||||
app.post(validateProcessRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = validateProcessRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '流程包不合 schema', detail: parsed.error.issues };
|
||||
@@ -1135,14 +1039,10 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
/* ── 触发器配置 ──────────────────────────────────────────────────── */
|
||||
|
||||
app.get(listTriggersRoute.path, async (c) => {
|
||||
const denied = await readTrigger(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
return c.json({ triggers: await container.trigger.list() });
|
||||
});
|
||||
|
||||
app.post(toggleTriggerRoute.path, async (c) => {
|
||||
const denied = await governTrigger(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = toggleTriggerRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '入参不合法' };
|
||||
@@ -1158,8 +1058,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 设计动规则不碰这两项,运营动这两项不碰规则。
|
||||
*/
|
||||
app.post(setTriggerConfirmRoute.path, async (c) => {
|
||||
const denied = await governTrigger(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = setTriggerConfirmRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '入参不合法' };
|
||||
@@ -1178,15 +1076,11 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
|
||||
/** 节点负责人(运营配置):显式指派 > 这里 > 轮值 */
|
||||
app.get(listNodeOwnersRoute.path, async (c) => {
|
||||
const denied = await readProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const owners = await container.process.nodeOwners(c.req.param('id'));
|
||||
return c.json({ owners: Object.entries(owners).map(([nodeId, staffId]) => ({ nodeId, staffId })) });
|
||||
});
|
||||
|
||||
app.put(setNodeOwnerRoute.path, async (c) => {
|
||||
const denied = await governProcess(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = setNodeOwnerRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '入参不合法' };
|
||||
@@ -1201,8 +1095,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 启停不走这里(那是运营的事,见 toggle);改定义同样不动任何流程包版本。
|
||||
*/
|
||||
app.put(upsertTriggerRoute.path, async (c) => {
|
||||
const denied = await governTrigger(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = upsertTriggerRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '触发器不合 schema', detail: parsed.error.issues };
|
||||
@@ -1219,8 +1111,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 恒为新增、版本并存:装入不覆盖旧版本,在途任务按启动时版本跑完。
|
||||
*/
|
||||
app.post(installActivityRoute.path, async (c) => {
|
||||
const denied = await governActivity(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = installActivityRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '活动清单不合 schema', detail: parsed.error.issues };
|
||||
@@ -1259,8 +1149,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.get(listActivitiesRoute.path, async (c) => {
|
||||
const denied = await readActivity(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const cards = await container.registry.list();
|
||||
/*
|
||||
* 契约面随卡一起给 —— 开发一个活动要照着的东西全在清单里:
|
||||
@@ -1374,8 +1262,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 入目录不等于能用:还要治理台把它放进某个活动的白名单(授权在白名单与契约,不在活动代码)。
|
||||
*/
|
||||
app.post(registerToolRoute.path, async (c) => {
|
||||
const denied = await governTool(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = registerToolRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '工具契约不合 schema', detail: parsed.error.issues };
|
||||
@@ -1397,8 +1283,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.get(listToolsRoute.path, async (c) => {
|
||||
const denied = await readTool(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const tools = await container.toolbox.list();
|
||||
return c.json({
|
||||
tools: tools.map((t) => ({
|
||||
@@ -1417,8 +1301,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
/* ── 数据管理:域 / 对象目录 + 记录浏览 ─────────────────────────── */
|
||||
|
||||
app.get(listLedgerObjectsRoute.path, async (c) => {
|
||||
const denied = await readData(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
/*
|
||||
* 性质随 `listObjects` **一次查询带回**(0026_object_meta)——
|
||||
* 这里从前是拿 12 个活动的清单现场重建的:`manifestOf` 是 select *,
|
||||
@@ -1438,9 +1320,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* · 草稿行数与时刻 —— `ledger.__draft`(在办中的单写下的,放行才进正式表)。
|
||||
*/
|
||||
app.get(ledgerByActivityRoute.path, async (c) => {
|
||||
const denied = await readData(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
|
||||
const [cards, materialized] = await Promise.all([
|
||||
container.registry.list(),
|
||||
container.ledger.listObjects(),
|
||||
@@ -1557,8 +1436,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.get(browseLedgerRoute.path, async (c) => {
|
||||
const denied = await readData(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const address = { domain: c.req.param('domain'), object: c.req.param('object') };
|
||||
const described = await container.ledger.describe(address);
|
||||
if (described === undefined) {
|
||||
@@ -1595,8 +1472,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
* 仪表读数,同源同口径」),这里只是把它接上,不另起一套口径。
|
||||
*/
|
||||
app.get(govMetricsRoute.path, async (c) => {
|
||||
const denied = await readMemory(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const parsed = govMetricsRoute.query.safeParse(c.req.query());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '统计窗口不合法', detail: parsed.error.issues };
|
||||
@@ -1612,8 +1487,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
/* ── 系统管理 · 通道实例 ────────────────────────────────────────── */
|
||||
|
||||
app.get(listChannelsRoute.path, async (c) => {
|
||||
const denied = await readTrigger(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const channels = await container.channel.list();
|
||||
return c.json({
|
||||
channels: channels.map((ch) => ({
|
||||
@@ -1632,8 +1505,6 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
|
||||
});
|
||||
|
||||
app.get(listActivityMemoryRoute.path, async (c) => {
|
||||
const denied = await readMemory(c.get('account'));
|
||||
if (denied) return c.json(denied, denyStatus(denied));
|
||||
const entries = await container.memory.loadActivityMemory(c.req.param('activityId'), { limit: 100 });
|
||||
return c.json({
|
||||
entries: entries.map((e) => ({
|
||||
|
||||
Reference in New Issue
Block a user