@roomkit/gateway 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +182 -47
  2. package/dist/src/config/config.module.d.ts +3 -0
  3. package/dist/src/config/config.module.d.ts.map +1 -1
  4. package/dist/src/config/config.module.js +16 -6
  5. package/dist/src/config/config.module.js.map +1 -1
  6. package/dist/src/config/config.service.d.ts +17 -1
  7. package/dist/src/config/config.service.d.ts.map +1 -1
  8. package/dist/src/config/config.service.js +17 -16
  9. package/dist/src/config/config.service.js.map +1 -1
  10. package/dist/src/config/gateway.config.d.ts +2 -0
  11. package/dist/src/config/gateway.config.d.ts.map +1 -1
  12. package/dist/src/config/gateway.config.js +1 -0
  13. package/dist/src/config/gateway.config.js.map +1 -1
  14. package/dist/src/gateway.module.d.ts +2 -0
  15. package/dist/src/gateway.module.d.ts.map +1 -1
  16. package/dist/src/gateway.module.js +1 -1
  17. package/dist/src/gateway.module.js.map +1 -1
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js +21 -16
  20. package/dist/src/index.js.map +1 -1
  21. package/dist/src/ws/message-router.service.d.ts.map +1 -1
  22. package/dist/src/ws/message-router.service.js +6 -6
  23. package/dist/src/ws/message-router.service.js.map +1 -1
  24. package/package.json +25 -21
  25. package/dist/test/connection/connection.service.spec.d.ts +0 -2
  26. package/dist/test/connection/connection.service.spec.d.ts.map +0 -1
  27. package/dist/test/connection/connection.service.spec.js +0 -204
  28. package/dist/test/connection/connection.service.spec.js.map +0 -1
  29. package/dist/test/e2e/gateway-worker.e2e.spec.d.ts +0 -2
  30. package/dist/test/e2e/gateway-worker.e2e.spec.d.ts.map +0 -1
  31. package/dist/test/e2e/gateway-worker.e2e.spec.js +0 -412
  32. package/dist/test/e2e/gateway-worker.e2e.spec.js.map +0 -1
  33. package/dist/test/integration/admin-api.spec.d.ts +0 -2
  34. package/dist/test/integration/admin-api.spec.d.ts.map +0 -1
  35. package/dist/test/integration/admin-api.spec.js +0 -218
  36. package/dist/test/integration/admin-api.spec.js.map +0 -1
  37. package/dist/test/ratelimit/rate-limiter.service.spec.d.ts +0 -2
  38. package/dist/test/ratelimit/rate-limiter.service.spec.d.ts.map +0 -1
  39. package/dist/test/ratelimit/rate-limiter.service.spec.js +0 -139
  40. package/dist/test/ratelimit/rate-limiter.service.spec.js.map +0 -1
  41. package/dist/test/setup.d.ts +0 -2
  42. package/dist/test/setup.d.ts.map +0 -1
  43. package/dist/test/setup.js +0 -56
  44. package/dist/test/setup.js.map +0 -1
  45. package/dist/test/ws/message-router.service.spec.d.ts +0 -2
  46. package/dist/test/ws/message-router.service.spec.d.ts.map +0 -1
  47. package/dist/test/ws/message-router.service.spec.js +0 -403
  48. package/dist/test/ws/message-router.service.spec.js.map +0 -1
@@ -1,412 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- /// <reference types="jest" />
40
- /**
41
- * Gateway-Worker 端到端集成测试
42
- * 测试完整的消息流:Client -> Gateway -> Redis -> Worker -> Redis -> Gateway -> Client
43
- */
44
- const dotenv = __importStar(require("dotenv"));
45
- const path = __importStar(require("path"));
46
- // 加载根目录的 .env 文件
47
- dotenv.config({ path: path.resolve(__dirname, '../../../../.env') });
48
- const testing_1 = require("@nestjs/testing");
49
- const ws_1 = __importDefault(require("ws"));
50
- const ioredis_1 = __importDefault(require("ioredis"));
51
- const gateway_module_1 = require("../../src/gateway.module");
52
- const core_1 = require("@roomkit/core");
53
- // 模拟Worker服务
54
- class MockWorkerService {
55
- redisClient;
56
- redisSub;
57
- workerId;
58
- constructor(workerId) {
59
- this.workerId = workerId;
60
- this.redisClient = new ioredis_1.default({
61
- host: process.env.REDIS_HOST || 'localhost',
62
- port: parseInt(process.env.REDIS_PORT || '16379'),
63
- password: process.env.REDIS_PASSWORD,
64
- });
65
- this.redisSub = this.redisClient.duplicate();
66
- }
67
- async start() {
68
- // 注册Worker
69
- await this.redisClient.hset(`gf:worker:${this.workerId}`, 'status', 'active', 'connectedAt', Date.now().toString());
70
- // 订阅Worker的RPC通道
71
- await this.redisSub.subscribe(`gf:rpc:worker:${this.workerId}`);
72
- this.redisSub.on('message', this.handleMessage.bind(this));
73
- }
74
- async handleMessage(channel, message) {
75
- try {
76
- const msg = JSON.parse(message);
77
- switch (msg.type) {
78
- case 'auth':
79
- await this.handleAuth(msg);
80
- break;
81
- case 'create-room':
82
- await this.handleCreateRoom(msg);
83
- break;
84
- case 'join-room':
85
- await this.handleJoinRoom(msg);
86
- break;
87
- case 'client-message':
88
- await this.handleClientMessage(msg);
89
- break;
90
- }
91
- }
92
- catch (error) {
93
- console.error('MockWorker error:', error);
94
- }
95
- }
96
- async handleAuth(msg) {
97
- const { gatewayId, connectionId, payload } = msg;
98
- // 模拟token验证
99
- const isValid = payload.token && payload.token.startsWith('token_');
100
- const userId = isValid ? payload.token.replace('token_', 'user_') : null;
101
- const response = {
102
- type: 'auth-response',
103
- workerId: this.workerId,
104
- requestId: msg.requestId,
105
- connectionId,
106
- success: isValid,
107
- userId,
108
- error: isValid ? undefined : 'INVALID_TOKEN',
109
- timestamp: Date.now(),
110
- };
111
- await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
112
- }
113
- async handleCreateRoom(msg) {
114
- const { gatewayId, connectionId, payload } = msg;
115
- const roomId = payload.roomId || `room_${Date.now()}`;
116
- // 注册房间到Worker的映射
117
- await this.redisClient.set(`gf:room:${roomId}:worker`, this.workerId);
118
- const response = {
119
- type: 'client-response',
120
- workerId: this.workerId,
121
- connectionId,
122
- msgId: core_1.MessageId.CREATE_ROOM_RES,
123
- payload: {
124
- success: true,
125
- roomId,
126
- workerId: this.workerId,
127
- },
128
- timestamp: Date.now(),
129
- };
130
- await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
131
- }
132
- async handleJoinRoom(msg) {
133
- const { gatewayId, connectionId, userId, payload } = msg;
134
- const { roomId } = payload;
135
- // 验证房间是否存在
136
- const workerId = await this.redisClient.get(`gf:room:${roomId}:worker`);
137
- if (!workerId) {
138
- const errorResponse = {
139
- type: 'client-response',
140
- workerId: this.workerId,
141
- connectionId,
142
- msgId: core_1.MessageId.ERROR,
143
- payload: {
144
- error: 'ROOM_NOT_FOUND',
145
- message: 'Room does not exist',
146
- },
147
- timestamp: Date.now(),
148
- };
149
- await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(errorResponse));
150
- return;
151
- }
152
- // 添加成员到房间
153
- await this.redisClient.hset(`gf:room:${roomId}:members`, userId, JSON.stringify({ gatewayId, connectionId, joinedAt: Date.now() }));
154
- // 发送加入成功响应
155
- const response = {
156
- type: 'client-response',
157
- workerId: this.workerId,
158
- connectionId,
159
- msgId: core_1.MessageId.JOIN_ROOM_RES,
160
- payload: {
161
- success: true,
162
- roomId,
163
- players: await this.getRoomPlayers(roomId),
164
- },
165
- timestamp: Date.now(),
166
- };
167
- await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
168
- // 广播玩家加入消息
169
- const broadcastMsg = {
170
- type: 'room-broadcast',
171
- workerId: this.workerId,
172
- roomId,
173
- msgId: core_1.MessageId.PLAYER_JOINED,
174
- payload: {
175
- userId,
176
- timestamp: Date.now(),
177
- },
178
- excludeUserId: userId,
179
- timestamp: Date.now(),
180
- };
181
- await this.redisClient.publish('gf:broadcast', JSON.stringify(broadcastMsg));
182
- }
183
- async handleClientMessage(msg) {
184
- const { gatewayId, connectionId, msgId, payload } = msg;
185
- // 简单回显消息
186
- const response = {
187
- type: 'client-response',
188
- workerId: this.workerId,
189
- connectionId,
190
- msgId: msgId + 1, // 响应ID = 请求ID + 1
191
- payload: {
192
- echo: payload,
193
- serverTime: Date.now(),
194
- },
195
- timestamp: Date.now(),
196
- };
197
- await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
198
- }
199
- async getRoomPlayers(roomId) {
200
- const members = await this.redisClient.hgetall(`gf:room:${roomId}:members`);
201
- return Object.entries(members).map(([userId, data]) => ({
202
- userId,
203
- ...JSON.parse(data),
204
- }));
205
- }
206
- async stop() {
207
- await this.redisSub.unsubscribe();
208
- await this.redisSub.quit();
209
- await this.redisClient.del(`gf:worker:${this.workerId}`);
210
- await this.redisClient.quit();
211
- }
212
- }
213
- // 测试客户端
214
- class TestClient {
215
- ws = null;
216
- messages = [];
217
- messageResolvers = new Map();
218
- async connect(url) {
219
- return new Promise((resolve, reject) => {
220
- this.ws = new ws_1.default(url);
221
- this.ws.on('open', () => resolve());
222
- this.ws.on('error', reject);
223
- this.ws.on('message', (data) => {
224
- const msg = JSON.parse(data.toString());
225
- this.messages.push(msg);
226
- const resolver = this.messageResolvers.get(msg.msgId);
227
- if (resolver) {
228
- resolver(msg);
229
- this.messageResolvers.delete(msg.msgId);
230
- }
231
- });
232
- });
233
- }
234
- send(msgId, payload) {
235
- if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
236
- throw new Error('WebSocket not connected');
237
- }
238
- this.ws.send(JSON.stringify({ msgId, payload }));
239
- }
240
- async sendAndWait(msgId, payload, expectedResMsgId, timeout = 5000) {
241
- return new Promise((resolve, reject) => {
242
- const timer = setTimeout(() => {
243
- this.messageResolvers.delete(expectedResMsgId);
244
- reject(new Error(`Timeout waiting for message ${expectedResMsgId}`));
245
- }, timeout);
246
- this.messageResolvers.set(expectedResMsgId, (msg) => {
247
- clearTimeout(timer);
248
- resolve(msg);
249
- });
250
- this.send(msgId, payload);
251
- });
252
- }
253
- getMessages() {
254
- return this.messages;
255
- }
256
- clearMessages() {
257
- this.messages = [];
258
- }
259
- disconnect() {
260
- if (this.ws) {
261
- this.ws.close();
262
- this.ws = null;
263
- }
264
- }
265
- }
266
- describe('Gateway-Worker E2E Tests', () => {
267
- let app;
268
- let mockWorker;
269
- let redis;
270
- const workerId = 'worker-e2e-test';
271
- const gatewayUrl = 'ws://localhost:3001/ws';
272
- beforeAll(async () => {
273
- // 创建Gateway应用
274
- const moduleFixture = await testing_1.Test.createTestingModule({
275
- imports: [gateway_module_1.GatewayModule],
276
- }).compile();
277
- app = moduleFixture.createNestApplication();
278
- await app.listen(3001);
279
- // 创建Redis客户端
280
- redis = new ioredis_1.default({
281
- host: process.env.REDIS_HOST || 'localhost',
282
- port: parseInt(process.env.REDIS_PORT || '6379'),
283
- });
284
- // 启动模拟Worker
285
- mockWorker = new MockWorkerService(workerId);
286
- await mockWorker.start();
287
- // 等待服务初始化
288
- await new Promise((resolve) => setTimeout(resolve, 1000));
289
- });
290
- afterAll(async () => {
291
- await mockWorker.stop();
292
- await redis.quit();
293
- await app.close();
294
- });
295
- beforeEach(async () => {
296
- // 清理测试数据
297
- const keys = await redis.keys('gf:*');
298
- if (keys.length > 0) {
299
- await redis.del(...keys);
300
- }
301
- // 重新注册Worker
302
- await redis.hset(`gf:worker:${workerId}`, 'status', 'active', 'connectedAt', Date.now().toString());
303
- });
304
- describe('鉴权流程', () => {
305
- it('应该成功完成鉴权', async () => {
306
- const client = new TestClient();
307
- await client.connect(gatewayUrl);
308
- const authRes = await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_test123' }, core_1.MessageId.AUTH_RES);
309
- expect(authRes.payload.success).toBe(true);
310
- expect(authRes.payload.userId).toBe('user_test123');
311
- client.disconnect();
312
- });
313
- it('应该拒绝无效token', async () => {
314
- const client = new TestClient();
315
- await client.connect(gatewayUrl);
316
- const authRes = await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'invalid' }, core_1.MessageId.AUTH_RES);
317
- expect(authRes.payload.success).toBe(false);
318
- expect(authRes.payload.error).toBe('INVALID_TOKEN');
319
- client.disconnect();
320
- });
321
- });
322
- describe('房间管理', () => {
323
- it('应该成功创建房间', async () => {
324
- const client = new TestClient();
325
- await client.connect(gatewayUrl);
326
- // 先鉴权
327
- await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_test123' }, core_1.MessageId.AUTH_RES);
328
- // 创建房间
329
- const createRes = await client.sendAndWait(core_1.MessageId.CREATE_ROOM_REQ, { gameType: 'test', maxPlayers: 4 }, core_1.MessageId.CREATE_ROOM_RES);
330
- expect(createRes.payload.success).toBe(true);
331
- expect(createRes.payload.roomId).toBeDefined();
332
- expect(createRes.payload.workerId).toBe(workerId);
333
- client.disconnect();
334
- });
335
- it('应该成功加入房间', async () => {
336
- const client1 = new TestClient();
337
- const client2 = new TestClient();
338
- await client1.connect(gatewayUrl);
339
- await client2.connect(gatewayUrl);
340
- // Client1 鉴权并创建房间
341
- await client1.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_user1' }, core_1.MessageId.AUTH_RES);
342
- const createRes = await client1.sendAndWait(core_1.MessageId.CREATE_ROOM_REQ, { roomId: 'test-room-123', gameType: 'test', maxPlayers: 4 }, core_1.MessageId.CREATE_ROOM_RES);
343
- const roomId = createRes.payload.roomId;
344
- // Client2 鉴权并加入房间
345
- await client2.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_user2' }, core_1.MessageId.AUTH_RES);
346
- client1.clearMessages();
347
- client2.clearMessages();
348
- const joinRes = await client2.sendAndWait(core_1.MessageId.JOIN_ROOM_REQ, { roomId }, core_1.MessageId.JOIN_ROOM_RES);
349
- expect(joinRes.payload.success).toBe(true);
350
- expect(joinRes.payload.roomId).toBe(roomId);
351
- // 等待广播消息
352
- await new Promise((resolve) => setTimeout(resolve, 500));
353
- // Client1 应该收到玩家加入的广播
354
- const client1Messages = client1.getMessages();
355
- const joinBroadcast = client1Messages.find((msg) => msg.msgId === core_1.MessageId.PLAYER_JOINED);
356
- expect(joinBroadcast).toBeDefined();
357
- expect(joinBroadcast.payload.userId).toBe('user_user2');
358
- client1.disconnect();
359
- client2.disconnect();
360
- });
361
- });
362
- describe('消息路由', () => {
363
- it('应该正确转发并响应客户端消息', async () => {
364
- const client = new TestClient();
365
- await client.connect(gatewayUrl);
366
- // 鉴权
367
- await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_test123' }, core_1.MessageId.AUTH_RES);
368
- // 创建房间
369
- await client.sendAndWait(core_1.MessageId.CREATE_ROOM_REQ, { gameType: 'test', maxPlayers: 4 }, core_1.MessageId.CREATE_ROOM_RES);
370
- // 发送自定义消息
371
- const customMsgId = 3001;
372
- const customPayload = { action: 'test', data: 'hello' };
373
- const response = await client.sendAndWait(customMsgId, customPayload, customMsgId + 1);
374
- expect(response.payload.echo).toEqual(customPayload);
375
- expect(response.payload.serverTime).toBeDefined();
376
- client.disconnect();
377
- });
378
- });
379
- describe('心跳机制', () => {
380
- it('应该正确响应心跳', async () => {
381
- const client = new TestClient();
382
- await client.connect(gatewayUrl);
383
- const timestamp = Date.now();
384
- const heartbeatRes = await client.sendAndWait(core_1.MessageId.HEARTBEAT, { timestamp }, core_1.MessageId.HEARTBEAT_ACK);
385
- expect(heartbeatRes.payload.timestamp).toBe(timestamp);
386
- expect(heartbeatRes.payload.serverTime).toBeDefined();
387
- client.disconnect();
388
- });
389
- });
390
- describe('并发场景', () => {
391
- it('应该处理多个客户端同时连接', async () => {
392
- const clients = [];
393
- const clientCount = 10;
394
- // 创建并连接多个客户端
395
- for (let i = 0; i < clientCount; i++) {
396
- const client = new TestClient();
397
- await client.connect(gatewayUrl);
398
- clients.push(client);
399
- }
400
- // 所有客户端同时鉴权
401
- const authPromises = clients.map((client, i) => client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: `token_user${i}` }, core_1.MessageId.AUTH_RES));
402
- const authResults = await Promise.all(authPromises);
403
- authResults.forEach((res, i) => {
404
- expect(res.payload.success).toBe(true);
405
- expect(res.payload.userId).toBe(`user_user${i}`);
406
- });
407
- // 清理
408
- clients.forEach((client) => client.disconnect());
409
- });
410
- });
411
- });
412
- //# sourceMappingURL=gateway-worker.e2e.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gateway-worker.e2e.spec.js","sourceRoot":"","sources":["../../../test/e2e/gateway-worker.e2e.spec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAA8B;AAC9B;;;GAGG;AACH,+CAAiC;AACjC,2CAA6B;AAE7B,iBAAiB;AACjB,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;AAErE,6CAAsD;AAEtD,4CAA2B;AAC3B,sDAA4B;AAC5B,6DAAyD;AACzD,wCAA0C;AAE1C,aAAa;AACb,MAAM,iBAAiB;IACb,WAAW,CAAQ;IACnB,QAAQ,CAAQ;IAChB,QAAQ,CAAS;IAEzB,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,IAAI,iBAAK,CAAC;YAC3B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;YAC3C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;YACjD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;SACrC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,KAAK;QACT,WAAW;QACX,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CACzB,aAAa,IAAI,CAAC,QAAQ,EAAE,EAC5B,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CACtB,CAAC;QAEF,iBAAiB;QACjB,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,iBAAiB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,OAAe,EAAE,OAAe;QAC1D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAEhC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;gBACjB,KAAK,MAAM;oBACT,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;oBAC3B,MAAM;gBACR,KAAK,aAAa;oBAChB,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;oBACjC,MAAM;gBACR,KAAK,WAAW;oBACd,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;oBAC/B,MAAM;gBACR,KAAK,gBAAgB;oBACnB,MAAM,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;oBACpC,MAAM;YACV,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,GAAQ;QAC/B,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QAEjD,YAAY;QACZ,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAEzE,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,eAAe;YACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,YAAY;YACZ,OAAO,EAAE,OAAO;YAChB,MAAM;YACN,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe;YAC5C,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,GAAQ;QACrC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QACjD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,QAAQ,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAEtD,iBAAiB;QACjB,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,MAAM,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEtE,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY;YACZ,KAAK,EAAE,gBAAS,CAAC,eAAe;YAChC,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI;gBACb,MAAM;gBACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB;YACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,GAAQ;QACnC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QACzD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAE3B,WAAW;QACX,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,MAAM,SAAS,CAAC,CAAC;QACxE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,aAAa,GAAG;gBACpB,IAAI,EAAE,iBAAiB;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,YAAY;gBACZ,KAAK,EAAE,gBAAS,CAAC,KAAK;gBACtB,OAAO,EAAE;oBACP,KAAK,EAAE,gBAAgB;oBACvB,OAAO,EAAE,qBAAqB;iBAC/B;gBACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;YACF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAC9B,CAAC;YACF,OAAO;QACT,CAAC;QAED,UAAU;QACV,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CACzB,WAAW,MAAM,UAAU,EAC3B,MAAM,EACN,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAClE,CAAC;QAEF,WAAW;QACX,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY;YACZ,KAAK,EAAE,gBAAS,CAAC,aAAa;YAC9B,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI;gBACb,MAAM;gBACN,OAAO,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;aAC3C;YACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;QAEF,WAAW;QACX,MAAM,YAAY,GAAG;YACnB,IAAI,EAAE,gBAAgB;YACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM;YACN,KAAK,EAAE,gBAAS,CAAC,aAAa;YAC9B,OAAO,EAAE;gBACP,MAAM;gBACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB;YACD,aAAa,EAAE,MAAM;YACrB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/E,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,GAAQ;QACxC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QAExD,SAAS;QACT,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY;YACZ,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,kBAAkB;YACpC,OAAO,EAAE;gBACP,IAAI,EAAE,OAAO;gBACb,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;aACvB;YACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,MAAc;QACzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC;QAC5E,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM;YACN,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;SACpB,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAChC,CAAC;CACF;AAED,QAAQ;AACR,MAAM,UAAU;IACN,EAAE,GAAqB,IAAI,CAAC;IAC5B,QAAQ,GAAU,EAAE,CAAC;IACrB,gBAAgB,GAAG,IAAI,GAAG,EAA8B,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,EAAE,GAAG,IAAI,YAAS,CAAC,GAAG,CAAC,CAAC;YAE7B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACxC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACtD,IAAI,QAAQ,EAAE,CAAC;oBACb,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,KAAa,EAAE,OAAY;QAC9B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,YAAS,CAAC,IAAI,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAa,EAAE,OAAY,EAAE,gBAAwB,EAAE,OAAO,GAAG,IAAI;QACrF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,KAAK,CAAC,+BAA+B,gBAAgB,EAAE,CAAC,CAAC,CAAC;YACvE,CAAC,EAAE,OAAO,CAAC,CAAC;YAEZ,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAClD,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,aAAa;QACX,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;CACF;AAED,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,IAAI,GAAqB,CAAC;IAC1B,IAAI,UAA6B,CAAC;IAClC,IAAI,KAAY,CAAC;IACjB,MAAM,QAAQ,GAAG,iBAAiB,CAAC;IACnC,MAAM,UAAU,GAAG,wBAAwB,CAAC;IAE5C,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,cAAc;QACd,MAAM,aAAa,GAAkB,MAAM,cAAI,CAAC,mBAAmB,CAAC;YAClE,OAAO,EAAE,CAAC,8BAAa,CAAC;SACzB,CAAC,CAAC,OAAO,EAAE,CAAC;QAEb,GAAG,GAAG,aAAa,CAAC,qBAAqB,EAAE,CAAC;QAC5C,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvB,aAAa;QACb,KAAK,GAAG,IAAI,iBAAK,CAAC;YAChB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;YAC3C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,CAAC;SACjD,CAAC,CAAC;QAEH,aAAa;QACb,UAAU,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;QAEzB,UAAU;QACV,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,SAAS;QACT,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,aAAa;QACb,MAAM,KAAK,CAAC,IAAI,CACd,aAAa,QAAQ,EAAE,EACvB,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CACtC,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAEpD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;YAC3B,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CACtC,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAEpD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM;YACN,MAAM,MAAM,CAAC,WAAW,CACtB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,OAAO;YACP,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,WAAW,CACxC,gBAAS,CAAC,eAAe,EACzB,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,EACnC,gBAAS,CAAC,eAAe,CAC1B,CAAC;YAEF,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC/C,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAElD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC;YAEjC,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAClC,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAElC,kBAAkB;YAClB,MAAM,OAAO,CAAC,WAAW,CACvB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,aAAa,EAAE,EACxB,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,CACzC,gBAAS,CAAC,eAAe,EACzB,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,EAC5D,gBAAS,CAAC,eAAe,CAC1B,CAAC;YAEF,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;YAExC,kBAAkB;YAClB,MAAM,OAAO,CAAC,WAAW,CACvB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,aAAa,EAAE,EACxB,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,OAAO,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,CAAC,aAAa,EAAE,CAAC;YAExB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,CACvC,gBAAS,CAAC,aAAa,EACvB,EAAE,MAAM,EAAE,EACV,gBAAS,CAAC,aAAa,CACxB,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAE5C,SAAS;YACT,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAEzD,sBAAsB;YACtB,MAAM,eAAe,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9C,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CACxC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,gBAAS,CAAC,aAAa,CAC/C,CAAC;YACF,MAAM,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAExD,OAAO,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;YAC9B,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,KAAK;YACL,MAAM,MAAM,CAAC,WAAW,CACtB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,OAAO;YACP,MAAM,MAAM,CAAC,WAAW,CACtB,gBAAS,CAAC,eAAe,EACzB,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,EACnC,gBAAS,CAAC,eAAe,CAC1B,CAAC;YAEF,UAAU;YACV,MAAM,WAAW,GAAG,IAAI,CAAC;YACzB,MAAM,aAAa,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAExD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,WAAW,CACvC,WAAW,EACX,aAAa,EACb,WAAW,GAAG,CAAC,CAChB,CAAC;YAEF,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YACrD,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YAElD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,WAAW,CAC3C,gBAAS,CAAC,SAAS,EACnB,EAAE,SAAS,EAAE,EACb,gBAAS,CAAC,aAAa,CACxB,CAAC;YAEF,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YAEtD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;YAC7B,MAAM,OAAO,GAAiB,EAAE,CAAC;YACjC,MAAM,WAAW,GAAG,EAAE,CAAC;YAEvB,aAAa;YACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAED,YAAY;YACZ,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAC7C,MAAM,CAAC,WAAW,CAChB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,aAAa,CAAC,EAAE,EAAE,EAC3B,gBAAS,CAAC,QAAQ,CACnB,CACF,CAAC;YAEF,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAEpD,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;gBAC7B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;YAEH,KAAK;YACL,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=admin-api.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin-api.spec.d.ts","sourceRoot":"","sources":["../../../test/integration/admin-api.spec.ts"],"names":[],"mappings":""}
@@ -1,218 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- /// <reference types="jest" />
7
- const testing_1 = require("@nestjs/testing");
8
- const supertest_1 = __importDefault(require("supertest"));
9
- const admin_controller_1 = require("../../src/admin/admin.controller");
10
- const admin_service_1 = require("../../src/admin/admin.service");
11
- const config_service_1 = require("../../src/config/config.service");
12
- const connection_service_1 = require("../../src/connection/connection.service");
13
- const redis_service_1 = require("../../src/redis/redis.service");
14
- const rate_limiter_service_1 = require("../../src/ratelimit/rate-limiter.service");
15
- /**
16
- * Gateway Admin API 集成测试
17
- * 只测试 Admin API 模块,不启动 WebSocket
18
- */
19
- describe('Gateway Admin API (Integration)', () => {
20
- let app;
21
- const mockConfigService = {
22
- gatewayId: 'gateway-test-integration',
23
- port: 18080,
24
- adminPort: 18081,
25
- redisHost: process.env.REDIS_HOST || 'localhost',
26
- redisPort: parseInt(process.env.REDIS_PORT || '6379', 10),
27
- redisPassword: process.env.REDIS_PASSWORD,
28
- heartbeatInterval: 30000,
29
- heartbeatTimeout: 90000,
30
- maxConnections: 10000,
31
- connectionRateLimit: 100,
32
- messageRateLimitPerConnection: 50,
33
- messageBurstLimit: 100,
34
- globalMessageRateLimit: 50000,
35
- };
36
- const mockConnectionService = {
37
- getConnectionCount: jest.fn().mockReturnValue(0),
38
- getAuthenticatedCount: jest.fn().mockReturnValue(0),
39
- getAllConnections: jest.fn().mockReturnValue(new Map()),
40
- getConnection: jest.fn().mockReturnValue(null),
41
- getConnectionByUserId: jest.fn().mockReturnValue(null),
42
- getConnectionIdByUserId: jest.fn().mockReturnValue(null),
43
- getAllUsers: jest.fn().mockReturnValue([]),
44
- broadcast: jest.fn().mockReturnValue({ success: 0, failed: 0 }),
45
- removeConnection: jest.fn(),
46
- send: jest.fn(),
47
- };
48
- const mockRedisService = {
49
- client: {
50
- status: 'ready',
51
- },
52
- };
53
- const mockRateLimiterService = {
54
- getCurrentConnectionRate: jest.fn().mockReturnValue(0),
55
- getCurrentGlobalMessageRate: jest.fn().mockReturnValue(0),
56
- getConnectionRateLimitStats: jest.fn().mockReturnValue({
57
- count: 0,
58
- window: 1000,
59
- limit: 100,
60
- }),
61
- getMessageRateLimitStats: jest.fn().mockReturnValue({
62
- trackedConnections: 0,
63
- totalMessages: 0,
64
- }),
65
- getGlobalMessageRateLimitStats: jest.fn().mockReturnValue({
66
- count: 0,
67
- window: 1000,
68
- limit: 50000,
69
- }),
70
- getTrackedConnectionsCount: jest.fn().mockReturnValue(0),
71
- getConfig: jest.fn().mockReturnValue({
72
- connectionRateLimit: 100,
73
- messageRateLimitPerConnection: 50,
74
- messageBurstLimit: 100,
75
- globalMessageRateLimit: 50000,
76
- }),
77
- getStats: jest.fn().mockReturnValue({
78
- connectionRate: {
79
- current: 0,
80
- limit: 100,
81
- window: 1000,
82
- },
83
- globalMessageRate: {
84
- current: 0,
85
- limit: 50000,
86
- window: 1000,
87
- },
88
- trackedConnections: 0,
89
- }),
90
- };
91
- beforeAll(async () => {
92
- const moduleFixture = await testing_1.Test.createTestingModule({
93
- controllers: [admin_controller_1.AdminController],
94
- providers: [
95
- admin_service_1.AdminService,
96
- { provide: config_service_1.ConfigService, useValue: mockConfigService },
97
- { provide: connection_service_1.ConnectionService, useValue: mockConnectionService },
98
- { provide: redis_service_1.RedisService, useValue: mockRedisService },
99
- { provide: rate_limiter_service_1.RateLimiterService, useValue: mockRateLimiterService },
100
- ],
101
- }).compile();
102
- app = moduleFixture.createNestApplication();
103
- await app.init();
104
- });
105
- afterAll(async () => {
106
- await app.close();
107
- });
108
- describe('GET /admin/health', () => {
109
- it('should return health status', async () => {
110
- const response = await (0, supertest_1.default)(app.getHttpServer())
111
- .get('/admin/health')
112
- .expect(200);
113
- expect(response.body).toHaveProperty('status', 'ok');
114
- expect(response.body).toHaveProperty('gatewayId');
115
- expect(response.body).toHaveProperty('timestamp');
116
- });
117
- });
118
- describe('GET /admin/status', () => {
119
- it('should return system status', async () => {
120
- const response = await (0, supertest_1.default)(app.getHttpServer())
121
- .get('/admin/status')
122
- .expect(200);
123
- expect(response.body).toHaveProperty('gatewayId');
124
- expect(response.body).toHaveProperty('uptime');
125
- expect(response.body).toHaveProperty('connections');
126
- expect(response.body.connections).toHaveProperty('total');
127
- expect(response.body.connections).toHaveProperty('authenticated');
128
- expect(response.body).toHaveProperty('memory');
129
- });
130
- });
131
- describe('GET /admin/connections', () => {
132
- it('should return empty connections list initially', async () => {
133
- const response = await (0, supertest_1.default)(app.getHttpServer())
134
- .get('/admin/connections')
135
- .expect(200);
136
- expect(response.body).toHaveProperty('count', 0);
137
- expect(response.body).toHaveProperty('connections');
138
- expect(response.body.connections).toEqual([]);
139
- });
140
- });
141
- describe('GET /admin/connections/:id', () => {
142
- it('should return 404 for non-existent connection', async () => {
143
- await (0, supertest_1.default)(app.getHttpServer())
144
- .get('/admin/connections/non-existent-id')
145
- .expect(404);
146
- });
147
- });
148
- describe('GET /admin/ratelimit/stats', () => {
149
- it('should return rate limit statistics', async () => {
150
- const response = await (0, supertest_1.default)(app.getHttpServer())
151
- .get('/admin/ratelimit/stats')
152
- .expect(200);
153
- expect(response.body).toHaveProperty('connectionRate');
154
- expect(response.body).toHaveProperty('globalMessageRate');
155
- expect(response.body).toHaveProperty('trackedConnections');
156
- expect(response.body).toHaveProperty('config');
157
- });
158
- });
159
- describe('GET /admin/redis/status', () => {
160
- it('should return Redis status', async () => {
161
- const response = await (0, supertest_1.default)(app.getHttpServer())
162
- .get('/admin/redis/status')
163
- .expect(200);
164
- expect(response.body).toHaveProperty('status');
165
- expect(response.body).toHaveProperty('host');
166
- expect(response.body).toHaveProperty('port');
167
- });
168
- });
169
- describe('GET /admin/users', () => {
170
- it('should return empty users list initially', async () => {
171
- const response = await (0, supertest_1.default)(app.getHttpServer())
172
- .get('/admin/users')
173
- .expect(200);
174
- expect(response.body).toHaveProperty('count', 0);
175
- expect(response.body).toHaveProperty('users');
176
- expect(response.body.users).toEqual([]);
177
- });
178
- });
179
- describe('GET /admin/users/:id', () => {
180
- it('should return 404 for non-existent user', async () => {
181
- await (0, supertest_1.default)(app.getHttpServer())
182
- .get('/admin/users/non-existent-user')
183
- .expect(404);
184
- });
185
- });
186
- describe('POST /admin/users/:id/kick', () => {
187
- it('should return 404 for non-existent user', async () => {
188
- await (0, supertest_1.default)(app.getHttpServer())
189
- .post('/admin/users/non-existent-user/kick')
190
- .send({ reason: 'test' })
191
- .expect(404);
192
- });
193
- });
194
- describe('POST /admin/broadcast', () => {
195
- it('should broadcast message (0 recipients when no connections)', async () => {
196
- const response = await (0, supertest_1.default)(app.getHttpServer())
197
- .post('/admin/broadcast')
198
- .send({ msgId: 9999, payload: { message: 'test' } })
199
- .expect(200);
200
- expect(response.body).toHaveProperty('success', true);
201
- expect(response.body).toHaveProperty('sent', 0);
202
- expect(response.body).toHaveProperty('total', 0);
203
- });
204
- });
205
- describe('GET /admin/metrics', () => {
206
- it('should return metrics data', async () => {
207
- const response = await (0, supertest_1.default)(app.getHttpServer())
208
- .get('/admin/metrics')
209
- .expect(200);
210
- expect(response.body).toHaveProperty('timestamp');
211
- expect(response.body).toHaveProperty('gateway');
212
- expect(response.body).toHaveProperty('connections');
213
- expect(response.body).toHaveProperty('rateLimit');
214
- expect(response.body).toHaveProperty('memory');
215
- });
216
- });
217
- });
218
- //# sourceMappingURL=admin-api.spec.js.map