autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
241 lines (240 loc) • 8.35 kB
JavaScript
import EventEmitter from 'node:events';
import { v4 as uuidv4 } from 'uuid';
import Logger from '../../infrastructure/logging/Logger.js';
import { InternalError } from '../../shared/errors/BaseError.js';
/**
* Gateway - 统一网关
* 所有操作的唯一入口。
*
* Pipeline (4 步):
* validate → guard → route → audit
*/
export class Gateway extends EventEmitter {
auditLogger;
config;
constitution;
constitutionValidator;
eventBus;
logger;
permissionManager;
routes;
constructor(config) {
super();
this.config = config;
this.logger = Logger.getInstance();
this.routes = new Map();
// 依赖注入(稍后设置)
this.constitution = null;
this.constitutionValidator = null;
this.permissionManager = null;
this.auditLogger = null;
this.eventBus = null; // 可选:外部注入 EventBus 实例
}
/** 设置依赖 */
setDependencies({ constitution, constitutionValidator, permissionManager, auditLogger, }) {
this.constitution = constitution ?? null;
this.constitutionValidator = constitutionValidator ?? null;
this.permissionManager = permissionManager ?? null;
this.auditLogger = auditLogger ?? null;
}
/** 注册路由处理器 */
register(action, handler) {
if (this.routes.has(action)) {
throw new Error(`Action '${action}' is already registered`);
}
this.routes.set(action, handler);
this.logger.debug(`Route registered: ${action}`);
}
/** 获取已注册的 action 列表 */
getRegisteredActions() {
return [...this.routes.keys()];
}
/** 执行操作(主入口) */
async execute(request) {
const requestId = uuidv4();
const startTime = Date.now();
const context = {
requestId,
actor: request.actor,
action: request.action,
resource: request.resource,
data: request.data || {},
session: request.session,
startTime,
};
this.logger.info('Gateway: Request received', {
requestId,
actor: context.actor,
action: context.action,
});
try {
// 1. validate — 请求格式
this.validateRequest(request);
// 2. guard — 权限 + 宪法规则
await this.guard(context);
// 3. route — 路由到处理器
const result = await this.routeToHandler(context);
// 4. audit — 记录成功
await this.auditSuccess(context, result);
const duration = Date.now() - startTime;
this.logger.info('Gateway: Request completed', {
requestId,
duration: `${duration}ms`,
});
return {
success: true,
requestId,
data: result,
duration,
};
}
catch (error) {
const errMsg = error instanceof Error ? error.message : String(error);
const errLike = error;
await this.auditFailure(context, {
message: errMsg,
code: errLike?.code,
statusCode: errLike?.statusCode,
});
const duration = Date.now() - startTime;
this.logger.error('Gateway: Request failed', {
requestId,
error: errMsg,
duration: `${duration}ms`,
});
return {
success: false,
requestId,
error: {
message: errMsg,
code: errLike?.code || 'INTERNAL_ERROR',
statusCode: errLike?.statusCode || 500,
},
duration,
};
}
}
/**
* 仅检查权限与宪法(不执行业务逻辑)
* 用于 MCP Gateway gating
*/
async checkOnly(request) {
const requestId = uuidv4();
const startTime = Date.now();
const context = {
requestId,
actor: request.actor,
action: request.action,
resource: request.resource,
data: request.data || {},
session: request.session,
startTime,
};
try {
this.validateRequest(request);
await this.guard(context);
await this.auditSuccess(context, { checkOnly: true });
return { success: true, requestId };
}
catch (error) {
const errMsg = error instanceof Error ? error.message : String(error);
const errLike = error;
await this.auditFailure(context, {
message: errMsg,
code: errLike?.code,
statusCode: errLike?.statusCode,
});
return {
success: false,
requestId,
error: {
message: errMsg,
code: errLike?.code || 'INTERNAL_ERROR',
statusCode: errLike?.statusCode || 500,
},
};
}
}
// ─── Pipeline Steps ────────────────────────────────────
/** validate — 验证请求格式 */
validateRequest(request) {
if (!request.actor) {
throw new InternalError('Missing required field: actor');
}
if (!request.action) {
throw new InternalError('Missing required field: action');
}
}
/** guard — 权限检查 + 宪法验证 */
async guard(context) {
// 权限检查
if (this.permissionManager) {
this.permissionManager.enforce(context.actor, context.action, context.resource);
}
// 宪法数据完整性规则
if (this.constitutionValidator) {
await this.constitutionValidator.enforce({
actor: context.actor,
action: context.action,
resource: context.resource,
data: context.data,
});
}
}
/** route — 路由到处理器 */
async routeToHandler(context) {
const handler = this.routes.get(context.action);
if (!handler) {
throw new InternalError(`No handler found for action: ${context.action}`);
}
return await handler(context);
}
/** audit — 记录成功 */
async auditSuccess(context, result) {
if (!this.auditLogger) {
return;
}
const entry = {
requestId: context.requestId,
actor: context.actor,
action: context.action,
resource: context.resource,
result: 'success',
duration: Date.now() - context.startTime,
context: { session: context.session },
};
await this.auditLogger.log(entry);
// 向 EventBus 发送 Gateway 操作完成事件(供 SignalCollector 等监听)
if (this.eventBus) {
this.emit('gateway:action:completed', { ...entry, timestamp: Date.now() });
this.eventBus.emit('gateway:action:completed', { ...entry, timestamp: Date.now() });
}
}
/** audit — 记录失败 */
async auditFailure(context, error) {
if (!this.auditLogger) {
return;
}
const entry = {
requestId: context.requestId,
actor: context.actor,
action: context.action,
resource: context.resource,
result: 'failure',
error: error.message,
duration: Date.now() - context.startTime,
context: { session: context.session },
};
await this.auditLogger.log(entry);
// 向 EventBus 发送 Gateway 操作失败事件
if (this.eventBus) {
this.emit('gateway:action:failed', { ...entry, timestamp: Date.now() });
this.eventBus.emit('gateway:action:failed', { ...entry, timestamp: Date.now() });
}
}
/** 获取所有注册的路由 */
getRoutes() {
return Array.from(this.routes.keys());
}
}
export default Gateway;