This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-TMP-REPO/apps/api/src/app/server.ts
T

85 lines
3.3 KiB
TypeScript

/**
* Hono 应用 —— 自建应用,Mastra 以库形态嵌入(ADR-001)。
* 鉴权、岗位权限、SSE 全归 app 层;不暴露 Mastra 默认端点。
*/
import { Hono } from 'hono';
import type { ApiError } from '@contracts';
import { healthRoute, ingestMailRoute, loginRoute } from '@contracts';
import type { Container } from './container.ts';
import type { AppEnv } from './http-env.ts';
import { readSessionToken } from './session-cookie.ts';
import { registerDeskRoutes } from './routes/desk.ts';
import { registerDeskElementRoutes } from './routes/desk-elements.ts';
import { registerDeskApprovalRoutes } from './routes/desk-approval.ts';
import { registerRegistrationRoutes } from './routes/registration.ts';
import { registerGovernanceOpsRoutes } from './routes/governance-ops.ts';
import { registerGovernanceRoutes } from './routes/governance.ts';
import { registerIngestRoutes } from './routes/ingest.ts';
import { registerStreamRoute } from './routes/stream.ts';
import { registerWorkbenchRoutes } from './routes/workbench.ts';
export type { AppEnv };
/**
* 免鉴权白名单(ADR-010)—— 只三条,其余一律 401。
* 入站邮件是**外界边界**:它代替真实 IMAP 拉件,不是人的请求,没有会话可言。
*/
const INGEST_PREFIX = ingestMailRoute.path.slice(0, ingestMailRoute.path.indexOf(':'));
function isPublic(method: string, path: string): boolean {
if (method === 'GET' && path === healthRoute.path) return true;
if (method === 'POST' && path === loginRoute.path) return true;
if (method === 'POST' && path.startsWith(INGEST_PREFIX)) return true;
return false;
}
export function createServer(container: Container): Hono<AppEnv> {
const app = new Hono<AppEnv>();
/**
* 鉴权中间件(编码规范 §4:中间件在 app 层,身份判定读 `org` 出口;ADR-010)。
*
* 身份**只能**来自服务端会话表:cookie 里是一枚不透明 token,改它只会让会话解不出来,
* 冒充不了任何人 —— 这正是明文 `jcai_account` cookie 被废止的理由。
*/
app.use('*', async (c, next) => {
const token = readSessionToken(c.req.header('cookie'));
const session = token === undefined ? undefined : await container.org.resolveSession(token);
c.set('session', session);
c.set('account', session?.staff.account ?? '');
if (session === undefined && !isPublic(c.req.method, c.req.path)) {
const body: ApiError = { code: 'unauthenticated', message: '尚未登录' };
return c.json(body, 401);
}
await next();
return undefined;
});
app.onError((error, c) => {
const body: ApiError = {
code: 'internal',
message: error instanceof Error ? error.message : String(error),
};
process.stderr.write(`[api] ${body.message}\n`);
return c.json(body, 500);
});
app.notFound((c) => {
const body: ApiError = { code: 'not-found', message: `无此路由:${c.req.path}` };
return c.json(body, 404);
});
registerWorkbenchRoutes(app, container);
registerGovernanceRoutes(app, container);
registerDeskRoutes(app, container);
registerDeskElementRoutes(app, container);
registerDeskApprovalRoutes(app, container);
registerRegistrationRoutes(app, container);
registerGovernanceOpsRoutes(app, container);
registerIngestRoutes(app, container);
registerStreamRoute(app, container.broadcaster);
return app;
}