Files
BPOS-Service-Gateway/src/app/app.ts
T
lovefyfandClaude Fable 5 5ebef45b1d feat: SSE 放行 touch.workflow——参与人+admin 可见性(经任务服务归属端口)
I6 把 touch.workflow. 从可订阅集摘出去,理由是**当时没有规则**:形状一过就直接进订阅集,
而鉴权分支只覆盖 touch.task./stream.task.,任何登录用户订 touch.workflow.<任意 id> 都能收
别人流程的推送。N3 的引擎每次推进都发 touch.workflow.<instanceId>,规则也定了:参与人
(节点 ownerSub)/审批人(approverSub)/admin。规则到位,故放回来。

- workflow-access.ts:与 task-access 同形的 HTTP 口——服务账号 token、3s 超时、非 200
  一律 false 并告警(失败关闭)。打的是任务侧新加的 /workflow/:id/access。
- hub:SUBSCRIBABLE 收回 touch.workflow.,subscribe 补上对应鉴权分支——放形状与补鉴权
  必须同一次改动,只放形状就是把 I6 原样退回去。admin 旁路(全可见,也免掉白跑的往返)。
- 两口分开而不是复用一口:任务问的是单张单的归属,工作流问的是实例**定义里**的节点归属。
- app:两口共用同一个 token provider,缓存只此一份。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 21:07:47 +08:00

37 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Hono } from 'hono';
import { healthRoute, registerHealthCheck } from './health';
import { checkNats } from '../shared/bus/nats';
import { createKeycloakTokenProvider } from '../shared/service-token';
import { proxyRoutes } from '../modules/proxy/routes';
import { uploadRoutes } from '../modules/upload/routes';
import { SseHub } from '../modules/sse/hub';
import { sseRoutes } from '../modules/sse/routes';
import { createHttpTaskAccess } from '../modules/sse/task-access';
import { createHttpWorkflowAccess } from '../modules/sse/workflow-access';
/**
* hub 由外面传:server.ts 要拿同一个实例去 attachNats/shutdown。缺省自建一个,
* 让只关心 HTTP 面的测试照旧 buildApp() 就能用。
*/
export function buildApp(hub: SseHub = new SseHub()): Hono {
registerHealthCheck('nats', checkNats);
const app = new Hono();
// 统一错误形状:任何未捕获异常都收敛成 {error,message} 的 500,不漏 Hono 默认裸文本/栈
app.onError((err, c) => {
console.error(err);
return c.json({ error: 'internal_error', message: '服务内部错误——已记录,稍后重试或联系维护' }, 500);
});
app.route('/health', healthRoute()); // 公开:不过 requireAuth
// 惰性构造:createKeycloakTokenProvider 只在闭包被调用时读 KEYCLOAK_CLIENT_SECRET,
// 建 app 本身不要求环境齐备,否则任何 buildApp() 的测试都会当场炸。
// 两口共用同一个 token provider:换来的是同一个服务账号 token,缓存也只此一份
const serviceToken = createKeycloakTokenProvider();
const access = createHttpTaskAccess(serviceToken);
const workflowAccess = createHttpWorkflowAccess(serviceToken);
// 顺序要紧:/api/stream 与 /api/upload 必须先于 /api 代理挂载,否则被代理的 catch-all 吞掉
app.route('/api/stream', sseRoutes(hub, access, workflowAccess));
app.route('/api/upload', uploadRoutes());
app.route('/api', proxyRoutes());
return app;
}