hydro-ai-helper 1.8.5

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 (182) hide show
  1. package/.eslintrc.json +31 -0
  2. package/.github/workflows/sync-releases-to-gitee.yml +119 -0
  3. package/.github/workflows/sync-to-gitee.yml +53 -0
  4. package/.vercelignore +31 -0
  5. package/CONTRIBUTING.md +165 -0
  6. package/README.md +273 -0
  7. package/TROUBLESHOOTING.md +355 -0
  8. package/api/badge-active.ts +88 -0
  9. package/api/badge-installs.ts +75 -0
  10. package/api/report.ts +242 -0
  11. package/assets/screenshots/1.png +0 -0
  12. package/assets/screenshots/2.png +0 -0
  13. package/assets/screenshots/3.png +0 -0
  14. package/assets/screenshots/4.png +0 -0
  15. package/assets/screenshots/5.png +0 -0
  16. package/assets/screenshots/6.png +0 -0
  17. package/assets/screenshots/7.png +0 -0
  18. package/dist/__tests__/__mocks__/hydrooj.js +29 -0
  19. package/dist/__tests__/__mocks__/hydrooj.js.map +1 -0
  20. package/dist/constants/jailbreakRules.js +29 -0
  21. package/dist/constants/jailbreakRules.js.map +1 -0
  22. package/dist/constants/permissions.js +42 -0
  23. package/dist/constants/permissions.js.map +1 -0
  24. package/dist/handlers/adminConfigHandler.js +347 -0
  25. package/dist/handlers/adminConfigHandler.js.map +1 -0
  26. package/dist/handlers/adminHandler.js +378 -0
  27. package/dist/handlers/adminHandler.js.map +1 -0
  28. package/dist/handlers/analyticsHandler.js +433 -0
  29. package/dist/handlers/analyticsHandler.js.map +1 -0
  30. package/dist/handlers/dashboardHandler.js +22 -0
  31. package/dist/handlers/dashboardHandler.js.map +1 -0
  32. package/dist/handlers/exportHandler.js +118 -0
  33. package/dist/handlers/exportHandler.js.map +1 -0
  34. package/dist/handlers/studentHandler.js +430 -0
  35. package/dist/handlers/studentHandler.js.map +1 -0
  36. package/dist/handlers/teacherHandler.js +246 -0
  37. package/dist/handlers/teacherHandler.js.map +1 -0
  38. package/dist/handlers/testHandler.js +27 -0
  39. package/dist/handlers/testHandler.js.map +1 -0
  40. package/dist/handlers/updateHandler.js +84 -0
  41. package/dist/handlers/updateHandler.js.map +1 -0
  42. package/dist/handlers/versionHandler.js +61 -0
  43. package/dist/handlers/versionHandler.js.map +1 -0
  44. package/dist/index.d.ts +22 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +151 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/lib/crypto.js +110 -0
  49. package/dist/lib/crypto.js.map +1 -0
  50. package/dist/lib/httpHelpers.js +61 -0
  51. package/dist/lib/httpHelpers.js.map +1 -0
  52. package/dist/lib/queryHelpers.js +81 -0
  53. package/dist/lib/queryHelpers.js.map +1 -0
  54. package/dist/models/aiConfig.js +255 -0
  55. package/dist/models/aiConfig.js.map +1 -0
  56. package/dist/models/conversation.js +209 -0
  57. package/dist/models/conversation.js.map +1 -0
  58. package/dist/models/jailbreakLog.js +68 -0
  59. package/dist/models/jailbreakLog.js.map +1 -0
  60. package/dist/models/message.js +174 -0
  61. package/dist/models/message.js.map +1 -0
  62. package/dist/models/pluginInstall.js +95 -0
  63. package/dist/models/pluginInstall.js.map +1 -0
  64. package/dist/models/rateLimitRecord.js +54 -0
  65. package/dist/models/rateLimitRecord.js.map +1 -0
  66. package/dist/models/versionCache.js +89 -0
  67. package/dist/models/versionCache.js.map +1 -0
  68. package/dist/services/effectivenessService.js +131 -0
  69. package/dist/services/effectivenessService.js.map +1 -0
  70. package/dist/services/exportService.js +152 -0
  71. package/dist/services/exportService.js.map +1 -0
  72. package/dist/services/migrationService.js +90 -0
  73. package/dist/services/migrationService.js.map +1 -0
  74. package/dist/services/openaiClient.js +412 -0
  75. package/dist/services/openaiClient.js.map +1 -0
  76. package/dist/services/promptService.js +467 -0
  77. package/dist/services/promptService.js.map +1 -0
  78. package/dist/services/rateLimitService.js +108 -0
  79. package/dist/services/rateLimitService.js.map +1 -0
  80. package/dist/services/telemetryService.js +173 -0
  81. package/dist/services/telemetryService.js.map +1 -0
  82. package/dist/services/updateService.js +388 -0
  83. package/dist/services/updateService.js.map +1 -0
  84. package/dist/services/versionService.js +289 -0
  85. package/dist/services/versionService.js.map +1 -0
  86. package/dist/types/hydrooj.js +7 -0
  87. package/dist/types/hydrooj.js.map +1 -0
  88. package/dist/types/mongo.js +7 -0
  89. package/dist/types/mongo.js.map +1 -0
  90. package/dist/utils/domainHelper.js +16 -0
  91. package/dist/utils/domainHelper.js.map +1 -0
  92. package/dist/utils/mongo.js +16 -0
  93. package/dist/utils/mongo.js.map +1 -0
  94. package/frontend/admin/ConfigPanel.tsx +1379 -0
  95. package/frontend/admin/VersionBadge.tsx +655 -0
  96. package/frontend/ai_helper.page.tsx +52 -0
  97. package/frontend/ai_helper_admin_config.page.tsx +47 -0
  98. package/frontend/ai_helper_analytics.page.tsx +52 -0
  99. package/frontend/ai_helper_conversation_detail.page.tsx +55 -0
  100. package/frontend/ai_helper_conversations.page.tsx +56 -0
  101. package/frontend/components/AIHelperDashboard.tsx +125 -0
  102. package/frontend/problem_detail.page.tsx +395 -0
  103. package/frontend/student/AIAssistantPanel.tsx +1839 -0
  104. package/frontend/student/AIChatPanel.tsx +944 -0
  105. package/frontend/student/ThreeColumnLayout.tsx +240 -0
  106. package/frontend/teacher/AnalyticsPage.tsx +614 -0
  107. package/frontend/teacher/ConversationDetail.tsx +504 -0
  108. package/frontend/teacher/ConversationList.tsx +568 -0
  109. package/frontend/teacher/ExportDialog.tsx +291 -0
  110. package/frontend/test.page.ts +88 -0
  111. package/frontend/utils/domainUtils.ts +37 -0
  112. package/index.js +6 -0
  113. package/jest.config.js +17 -0
  114. package/lib/mongodb.ts +60 -0
  115. package/locales/en.yaml +4 -0
  116. package/locales/zh.yaml +77 -0
  117. package/package.json +48 -0
  118. package/public/.gitkeep +0 -0
  119. package/public/README.md +1 -0
  120. package/src/__tests__/__mocks__/hydrooj.ts +27 -0
  121. package/src/__tests__/services/exportService.test.ts +156 -0
  122. package/src/__tests__/services/promptService.test.ts +141 -0
  123. package/src/__tests__/services/rateLimitService.test.ts +145 -0
  124. package/src/__tests__/utils/domainHelper.test.ts +47 -0
  125. package/src/constants/jailbreakRules.ts +28 -0
  126. package/src/constants/permissions.ts +43 -0
  127. package/src/handlers/adminConfigHandler.ts +363 -0
  128. package/src/handlers/adminHandler.ts +427 -0
  129. package/src/handlers/analyticsHandler.ts +517 -0
  130. package/src/handlers/dashboardHandler.ts +20 -0
  131. package/src/handlers/exportHandler.ts +134 -0
  132. package/src/handlers/studentHandler.ts +483 -0
  133. package/src/handlers/teacherHandler.ts +317 -0
  134. package/src/handlers/testHandler.ts +25 -0
  135. package/src/handlers/updateHandler.ts +85 -0
  136. package/src/handlers/versionHandler.ts +62 -0
  137. package/src/index.ts +198 -0
  138. package/src/lib/crypto.ts +125 -0
  139. package/src/lib/httpHelpers.ts +75 -0
  140. package/src/lib/queryHelpers.ts +108 -0
  141. package/src/models/aiConfig.ts +375 -0
  142. package/src/models/conversation.ts +328 -0
  143. package/src/models/jailbreakLog.ts +108 -0
  144. package/src/models/message.ts +246 -0
  145. package/src/models/pluginInstall.ts +138 -0
  146. package/src/models/rateLimitRecord.ts +81 -0
  147. package/src/models/versionCache.ts +127 -0
  148. package/src/services/effectivenessService.ts +161 -0
  149. package/src/services/exportService.ts +191 -0
  150. package/src/services/migrationService.ts +111 -0
  151. package/src/services/openaiClient.ts +548 -0
  152. package/src/services/promptService.ts +573 -0
  153. package/src/services/rateLimitService.ts +122 -0
  154. package/src/services/telemetryService.ts +218 -0
  155. package/src/services/updateService.ts +441 -0
  156. package/src/services/versionService.ts +351 -0
  157. package/src/types/hydrooj-extensions.d.ts +55 -0
  158. package/src/types/hydrooj.ts +38 -0
  159. package/src/types/mongo.ts +23 -0
  160. package/src/utils/domainHelper.ts +16 -0
  161. package/src/utils/mongo.ts +15 -0
  162. package/templates/ai-helper/admin_config.html +8 -0
  163. package/templates/ai-helper/analytics.html +8 -0
  164. package/templates/ai-helper/dashboard.html +8 -0
  165. package/templates/ai-helper/teacher_conversation_detail.html +8 -0
  166. package/templates/ai-helper/teacher_conversations.html +8 -0
  167. package/tests/integration/README.md +86 -0
  168. package/tests/integration/curl/01-admin-config-set.sh +56 -0
  169. package/tests/integration/curl/02-student-chat.sh +67 -0
  170. package/tests/integration/curl/03-teacher-list.sh +52 -0
  171. package/tests/integration/curl/04-teacher-detail.sh +37 -0
  172. package/tests/integration/curl/05-export.sh +49 -0
  173. package/tests/integration/curl/06-analytics.sh +43 -0
  174. package/tests/performance/README.md +85 -0
  175. package/tests/performance/data/prepare-sample-data.md +57 -0
  176. package/tests/performance/scripts/export-csv-k6.js +41 -0
  177. package/tests/performance/scripts/student-chat-k6.js +44 -0
  178. package/tests/performance/scripts/teacher-conversations-k6.js +33 -0
  179. package/tsconfig.json +42 -0
  180. package/tsconfig.vercel.json +27 -0
  181. package/types/hydrooj/index.d.ts +73 -0
  182. package/vercel.json +3 -0
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * Crypto 加解密工具
4
+ *
5
+ * 使用 AES-256-GCM 加密 API Key 等敏感信息
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.encrypt = encrypt;
9
+ exports.decrypt = decrypt;
10
+ exports.maskApiKey = maskApiKey;
11
+ const crypto_1 = require("crypto");
12
+ /**
13
+ * 加密密钥(32 字节)
14
+ * 从环境变量读取,若不存在则使用默认值(仅开发环境)
15
+ */
16
+ function getEncryptionKey() {
17
+ const keyRaw = process.env.ENCRYPTION_KEY || 'dev-encryption-key-please-change-me-32';
18
+ // 如果环境变量不存在,打印警告(但不打印密钥本身)
19
+ if (!process.env.ENCRYPTION_KEY) {
20
+ console.warn('[Crypto] ⚠️ ENCRYPTION_KEY 环境变量未设置,使用默认密钥(仅开发环境)');
21
+ console.warn('[Crypto] ⚠️ 生产环境请务必设置 ENCRYPTION_KEY 环境变量!');
22
+ }
23
+ // 使用 SHA-256 哈希生成固定 32 字节密钥
24
+ return (0, crypto_1.createHash)('sha256').update(keyRaw).digest();
25
+ }
26
+ const ENCRYPTION_KEY = getEncryptionKey();
27
+ const ALGORITHM = 'aes-256-gcm';
28
+ const IV_LENGTH = 12; // GCM 推荐 12 字节 IV
29
+ const AUTH_TAG_LENGTH = 16; // GCM 认证标签长度
30
+ /**
31
+ * 加密文本
32
+ * @param text 明文
33
+ * @returns Base64 编码的密文(包含 IV + AuthTag + Encrypted)
34
+ */
35
+ function encrypt(text) {
36
+ if (!text) {
37
+ throw new Error('加密失败:输入文本不能为空');
38
+ }
39
+ try {
40
+ // 生成随机 IV
41
+ const iv = (0, crypto_1.randomBytes)(IV_LENGTH);
42
+ // 创建加密器
43
+ const cipher = (0, crypto_1.createCipheriv)(ALGORITHM, ENCRYPTION_KEY, iv);
44
+ // 加密数据
45
+ const encrypted = Buffer.concat([
46
+ cipher.update(text, 'utf8'),
47
+ cipher.final()
48
+ ]);
49
+ // 获取认证标签
50
+ const authTag = cipher.getAuthTag();
51
+ // 拼接: IV(12) + AuthTag(16) + Encrypted(N)
52
+ const combined = Buffer.concat([iv, authTag, encrypted]);
53
+ // 返回 Base64 编码
54
+ return combined.toString('base64');
55
+ }
56
+ catch (error) {
57
+ throw new Error(`加密失败: ${error instanceof Error ? error.message : String(error)}`);
58
+ }
59
+ }
60
+ /**
61
+ * 解密文本
62
+ * @param cipherText Base64 编码的密文
63
+ * @returns 解密后的明文
64
+ */
65
+ function decrypt(cipherText) {
66
+ if (!cipherText) {
67
+ throw new Error('解密失败:输入密文不能为空');
68
+ }
69
+ try {
70
+ // 解码 Base64
71
+ const combined = Buffer.from(cipherText, 'base64');
72
+ // 分离 IV + AuthTag + Encrypted
73
+ const iv = combined.subarray(0, IV_LENGTH);
74
+ const authTag = combined.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
75
+ const encrypted = combined.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
76
+ // 创建解密器
77
+ const decipher = (0, crypto_1.createDecipheriv)(ALGORITHM, ENCRYPTION_KEY, iv);
78
+ decipher.setAuthTag(authTag);
79
+ // 解密数据
80
+ const decrypted = Buffer.concat([
81
+ decipher.update(encrypted),
82
+ decipher.final()
83
+ ]);
84
+ return decrypted.toString('utf8');
85
+ }
86
+ catch (error) {
87
+ // 解密失败可能是密钥错误或数据损坏
88
+ throw new Error(`解密失败: ${error instanceof Error ? error.message : String(error)}`);
89
+ }
90
+ }
91
+ /**
92
+ * API Key 脱敏显示
93
+ * @param apiKeyPlain 明文 API Key
94
+ * @returns 脱敏后的字符串(如 "sk-12****abcd")
95
+ */
96
+ function maskApiKey(apiKeyPlain) {
97
+ if (!apiKeyPlain) {
98
+ return '';
99
+ }
100
+ const len = apiKeyPlain.length;
101
+ // 如果长度 <= 8,简单返回 ****
102
+ if (len <= 8) {
103
+ return '****';
104
+ }
105
+ // 保留前 4 和后 4 位,中间用 **** 替换
106
+ const start = apiKeyPlain.slice(0, 4);
107
+ const end = apiKeyPlain.slice(-4);
108
+ return `${start}****${end}`;
109
+ }
110
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/lib/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * HTTP 响应工具函数
4
+ * 提供统一的响应格式和错误处理
5
+ *
6
+ * @module lib/httpHelpers
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.setJsonResponse = setJsonResponse;
10
+ exports.setErrorResponse = setErrorResponse;
11
+ exports.setTemplateResponse = setTemplateResponse;
12
+ exports.expectsJson = expectsJson;
13
+ /**
14
+ * 设置 JSON 响应
15
+ * 统一设置响应类型和状态码
16
+ *
17
+ * @param handler Handler 实例
18
+ * @param data 响应数据
19
+ * @param status HTTP 状态码,默认 200
20
+ */
21
+ function setJsonResponse(handler, data, status = 200) {
22
+ handler.response.status = status;
23
+ handler.response.body = data;
24
+ handler.response.type = 'application/json';
25
+ }
26
+ /**
27
+ * 设置错误响应
28
+ * 统一的错误响应格式
29
+ *
30
+ * @param handler Handler 实例
31
+ * @param code 错误代码,如 'INVALID_INPUT'
32
+ * @param message 错误信息
33
+ * @param status HTTP 状态码,默认 400
34
+ */
35
+ function setErrorResponse(handler, code, message, status = 400) {
36
+ setJsonResponse(handler, { error: { code, message } }, status);
37
+ }
38
+ /**
39
+ * 设置模板响应(HTML 页面)
40
+ * 统一设置模板和数据
41
+ *
42
+ * @param handler Handler 实例
43
+ * @param template 模板路径
44
+ * @param data 模板数据
45
+ */
46
+ function setTemplateResponse(handler, template, data = {}) {
47
+ handler.response.template = template;
48
+ handler.response.body = data;
49
+ }
50
+ /**
51
+ * 判断请求是否期望 JSON 响应
52
+ * 根据 Accept 头部判断
53
+ *
54
+ * @param handler Handler 实例
55
+ * @returns 是否期望 JSON
56
+ */
57
+ function expectsJson(handler) {
58
+ const accept = handler.request.headers.accept || '';
59
+ return accept.includes('application/json');
60
+ }
61
+ //# sourceMappingURL=httpHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpHelpers.js","sourceRoot":"","sources":["../../src/lib/httpHelpers.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAsBH,0CAIC;AAWD,4CAOC;AAUD,kDAGC;AASD,kCAGC;AAvDD;;;;;;;GAOG;AACH,SAAgB,eAAe,CAAC,OAAgB,EAAE,IAAa,EAAE,SAAiB,GAAG;IACnF,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG,kBAAkB,CAAC;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,gBAAgB,CAC9B,OAAgB,EAChB,IAAY,EACZ,OAAe,EACf,SAAiB,GAAG;IAEpB,eAAe,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,OAAgB,EAAE,QAAgB,EAAE,OAAgB,EAAE;IACxF,OAAO,CAAC,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,OAAgB;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IACpD,OAAO,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ /**
3
+ * 查询参数解析工具
4
+ * 提供统一的查询参数解析和验证
5
+ *
6
+ * @module lib/queryHelpers
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.parsePaginationParams = parsePaginationParams;
10
+ exports.parseDateRangeParams = parseDateRangeParams;
11
+ exports.buildFilters = buildFilters;
12
+ /**
13
+ * 解析分页参数
14
+ * 从查询参数中提取并验证分页参数
15
+ *
16
+ * @param pageStr 页码字符串,默认 '1'
17
+ * @param limitStr 每页数量字符串,默认 '50'
18
+ * @param maxLimit 最大每页数量,默认 100
19
+ * @returns 解析后的分页参数
20
+ */
21
+ function parsePaginationParams(pageStr = '1', limitStr = '50', maxLimit = 100) {
22
+ const page = Math.max(1, parseInt(pageStr, 10) || 1);
23
+ const limit = Math.min(maxLimit, Math.max(1, parseInt(limitStr, 10) || 50));
24
+ return { page, limit };
25
+ }
26
+ /**
27
+ * 解析日期范围参数
28
+ * 从查询参数中提取并验证日期范围
29
+ *
30
+ * @param startDateStr 开始日期字符串 (ISO 8601 格式)
31
+ * @param endDateStr 结束日期字符串 (ISO 8601 格式)
32
+ * @returns 解析后的日期范围参数
33
+ */
34
+ function parseDateRangeParams(startDateStr, endDateStr) {
35
+ const result = {};
36
+ if (startDateStr) {
37
+ try {
38
+ const date = new Date(startDateStr);
39
+ if (!isNaN(date.getTime())) {
40
+ result.startDate = date;
41
+ }
42
+ }
43
+ catch (err) {
44
+ // 忽略无效日期
45
+ console.error('[queryHelpers] Invalid startDate:', startDateStr);
46
+ }
47
+ }
48
+ if (endDateStr) {
49
+ try {
50
+ const date = new Date(endDateStr);
51
+ if (!isNaN(date.getTime())) {
52
+ result.endDate = date;
53
+ }
54
+ }
55
+ catch (err) {
56
+ // 忽略无效日期
57
+ console.error('[queryHelpers] Invalid endDate:', endDateStr);
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ /**
63
+ * 构建筛选条件对象
64
+ * 从查询参数中构建筛选条件,过滤掉空值
65
+ *
66
+ * @param params 原始查询参数对象
67
+ * @param allowedFields 允许的字段列表
68
+ * @returns 筛选条件对象
69
+ */
70
+ function buildFilters(params, allowedFields) {
71
+ const filters = {};
72
+ for (const field of allowedFields) {
73
+ const value = params[field];
74
+ if (value !== undefined && value !== null && value !== '') {
75
+ // @ts-expect-error - 动态字段赋值
76
+ filters[field] = value;
77
+ }
78
+ }
79
+ return filters;
80
+ }
81
+ //# sourceMappingURL=queryHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryHelpers.js","sourceRoot":"","sources":["../../src/lib/queryHelpers.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AA2BH,sDASC;AAUD,oDA+BC;AAUD,oCAeC;AApFD;;;;;;;;GAQG;AACH,SAAgB,qBAAqB,CACnC,UAAkB,GAAG,EACrB,WAAmB,IAAI,EACvB,WAAmB,GAAG;IAEtB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAE5E,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,oBAAoB,CAClC,YAAqB,EACrB,UAAmB;IAEnB,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC;YACpC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;YAC1B,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS;YACT,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,YAAY,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;YACxB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS;YACT,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,UAAU,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,YAAY,CAC1B,MAA+B,EAC/B,aAAuB;IAEvB,MAAM,OAAO,GAAe,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAC1D,4BAA4B;YAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ /**
3
+ * AI Config Model - AI 服务配置数据模型
4
+ *
5
+ * 管理全局 AI 服务配置(API Key、模型名称等)
6
+ * 约定:数据库中最多只有一条配置记录(固定 ID = 'default')
7
+ *
8
+ * v2 新增:支持多 API 端点配置、模型自动获取、Fallback 机制
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.AIConfigModel = exports.CURRENT_CONFIG_VERSION = void 0;
12
+ const crypto_1 = require("crypto");
13
+ /** 当前配置版本号 */
14
+ exports.CURRENT_CONFIG_VERSION = 2;
15
+ /**
16
+ * AI Config Model 操作类
17
+ * 封装 AI 配置的 CRUD 操作
18
+ */
19
+ class AIConfigModel {
20
+ constructor(db) {
21
+ this.FIXED_ID = 'default'; // 固定配置记录 ID
22
+ this.collection = db.collection('ai_config');
23
+ }
24
+ /**
25
+ * 确保索引已创建
26
+ * (单条记录无需复杂索引,仅用于一致性)
27
+ */
28
+ async ensureIndexes() {
29
+ // 创建 _id 索引(MongoDB 自动创建,此处仅占位)
30
+ console.log('[AIConfigModel] Collection initialized');
31
+ }
32
+ /**
33
+ * 获取当前配置(自动执行懒迁移)
34
+ * @returns 配置对象或 null(若尚未配置)
35
+ */
36
+ async getConfig() {
37
+ const rawConfig = await this.collection.findOne({ _id: this.FIXED_ID });
38
+ if (!rawConfig) {
39
+ return null;
40
+ }
41
+ // 懒迁移:检测并迁移旧版配置
42
+ if (this.needsMigration(rawConfig)) {
43
+ const migratedConfig = this.migrateFromLegacy(rawConfig);
44
+ // 持久化迁移后的配置
45
+ await this.collection.updateOne({ _id: this.FIXED_ID }, { $set: migratedConfig });
46
+ return migratedConfig;
47
+ }
48
+ return rawConfig;
49
+ }
50
+ /**
51
+ * 检测是否需要迁移
52
+ */
53
+ needsMigration(config) {
54
+ // 无 configVersion 或版本低于当前版本需要迁移
55
+ return !config.configVersion || config.configVersion < exports.CURRENT_CONFIG_VERSION;
56
+ }
57
+ /**
58
+ * 从旧版配置迁移到新版
59
+ */
60
+ migrateFromLegacy(legacy) {
61
+ console.log('[AIConfigModel] Migrating from legacy config to v2...');
62
+ // 如果已有 endpoints,规范化并保留现有数据
63
+ if (legacy.endpoints && legacy.endpoints.length > 0) {
64
+ const normalizedEndpoints = legacy.endpoints.map((endpoint, index) => ({
65
+ id: endpoint.id || (0, crypto_1.randomUUID)(),
66
+ name: endpoint.name || `端点 ${index + 1}`,
67
+ apiBaseUrl: endpoint.apiBaseUrl || legacy.apiBaseUrl || '',
68
+ apiKeyEncrypted: endpoint.apiKeyEncrypted || legacy.apiKeyEncrypted || '',
69
+ models: Array.isArray(endpoint.models) ? endpoint.models : [],
70
+ modelsLastFetched: endpoint.modelsLastFetched,
71
+ enabled: endpoint.enabled !== false,
72
+ }));
73
+ const endpointIds = new Set(normalizedEndpoints.map(ep => ep.id));
74
+ let selectedModels = (legacy.selectedModels || []).filter(sm => endpointIds.has(sm.endpointId));
75
+ // 如果没有有效的 selectedModels,尝试从旧配置或端点模型列表推导
76
+ if (selectedModels.length === 0) {
77
+ if (legacy.modelName && normalizedEndpoints[0]) {
78
+ selectedModels = [{ endpointId: normalizedEndpoints[0].id, modelName: legacy.modelName }];
79
+ }
80
+ else {
81
+ selectedModels = normalizedEndpoints
82
+ .filter(ep => ep.models.length > 0)
83
+ .map(ep => ({ endpointId: ep.id, modelName: ep.models[0] }));
84
+ }
85
+ }
86
+ return {
87
+ ...legacy,
88
+ configVersion: exports.CURRENT_CONFIG_VERSION,
89
+ endpoints: normalizedEndpoints,
90
+ selectedModels,
91
+ };
92
+ }
93
+ // 从旧字段创建默认端点
94
+ const defaultEndpoint = {
95
+ id: (0, crypto_1.randomUUID)(),
96
+ name: '默认端点',
97
+ apiBaseUrl: legacy.apiBaseUrl || '',
98
+ apiKeyEncrypted: legacy.apiKeyEncrypted || '',
99
+ models: legacy.modelName ? [legacy.modelName] : [],
100
+ modelsLastFetched: undefined,
101
+ enabled: true,
102
+ };
103
+ // 创建默认选中模型
104
+ const selectedModels = legacy.modelName
105
+ ? [{ endpointId: defaultEndpoint.id, modelName: legacy.modelName }]
106
+ : [];
107
+ const migratedConfig = {
108
+ _id: legacy._id,
109
+ configVersion: exports.CURRENT_CONFIG_VERSION,
110
+ endpoints: legacy.apiBaseUrl ? [defaultEndpoint] : [],
111
+ selectedModels,
112
+ rateLimitPerMinute: legacy.rateLimitPerMinute,
113
+ timeoutSeconds: legacy.timeoutSeconds,
114
+ systemPromptTemplate: legacy.systemPromptTemplate,
115
+ extraJailbreakPatternsText: legacy.extraJailbreakPatternsText,
116
+ updatedAt: legacy.updatedAt,
117
+ // 保留旧字段便于回滚
118
+ apiBaseUrl: legacy.apiBaseUrl,
119
+ modelName: legacy.modelName,
120
+ apiKeyEncrypted: legacy.apiKeyEncrypted,
121
+ };
122
+ console.log('[AIConfigModel] Migration complete. Created endpoint:', defaultEndpoint.id);
123
+ return migratedConfig;
124
+ }
125
+ /**
126
+ * 更新配置(若不存在则创建)
127
+ * @param partial 要更新的配置字段(部分更新)
128
+ * @returns 更新后的配置对象
129
+ */
130
+ async updateConfig(partial) {
131
+ const now = new Date();
132
+ // 确保 configVersion 为最新
133
+ const updateData = {
134
+ ...partial,
135
+ configVersion: exports.CURRENT_CONFIG_VERSION,
136
+ updatedAt: now
137
+ };
138
+ // 使用 upsert 更新或创建配置
139
+ await this.collection.updateOne({ _id: this.FIXED_ID }, { $set: updateData }, { upsert: true });
140
+ // 查询更新后的配置
141
+ const config = await this.collection.findOne({ _id: this.FIXED_ID });
142
+ if (!config) {
143
+ throw new Error('配置更新失败:无法读取更新后的配置');
144
+ }
145
+ return config;
146
+ }
147
+ /**
148
+ * 添加新端点
149
+ */
150
+ async addEndpoint(endpoint) {
151
+ const config = await this.getConfig();
152
+ const newEndpoint = {
153
+ ...endpoint,
154
+ id: (0, crypto_1.randomUUID)(),
155
+ };
156
+ const endpoints = config?.endpoints || [];
157
+ endpoints.push(newEndpoint);
158
+ await this.updateConfig({ endpoints });
159
+ return newEndpoint;
160
+ }
161
+ /**
162
+ * 更新端点
163
+ */
164
+ async updateEndpoint(endpointId, updates) {
165
+ const config = await this.getConfig();
166
+ if (!config) {
167
+ throw new Error('配置不存在');
168
+ }
169
+ const endpoints = config.endpoints.map(ep => ep.id === endpointId ? { ...ep, ...updates } : ep);
170
+ await this.updateConfig({ endpoints });
171
+ }
172
+ /**
173
+ * 删除端点
174
+ */
175
+ async deleteEndpoint(endpointId) {
176
+ const config = await this.getConfig();
177
+ if (!config) {
178
+ throw new Error('配置不存在');
179
+ }
180
+ const endpoints = config.endpoints.filter(ep => ep.id !== endpointId);
181
+ // 同时移除引用该端点的 selectedModels
182
+ const selectedModels = config.selectedModels.filter(sm => sm.endpointId !== endpointId);
183
+ await this.updateConfig({ endpoints, selectedModels });
184
+ }
185
+ /**
186
+ * 更新选中的模型列表
187
+ */
188
+ async updateSelectedModels(selectedModels) {
189
+ await this.updateConfig({ selectedModels });
190
+ }
191
+ /**
192
+ * 根据端点 ID 获取端点配置
193
+ */
194
+ async getEndpointById(endpointId) {
195
+ const config = await this.getConfig();
196
+ if (!config) {
197
+ return null;
198
+ }
199
+ return config.endpoints.find(ep => ep.id === endpointId) || null;
200
+ }
201
+ /**
202
+ * 删除配置(用于测试或重置)
203
+ */
204
+ async deleteConfig() {
205
+ await this.collection.deleteOne({ _id: this.FIXED_ID });
206
+ }
207
+ /**
208
+ * 初始化默认配置(若不存在)
209
+ * @param defaults 默认配置值
210
+ */
211
+ async initializeDefaultConfig(defaults) {
212
+ const existing = await this.getConfig();
213
+ if (!existing) {
214
+ await this.collection.insertOne({
215
+ _id: this.FIXED_ID,
216
+ configVersion: exports.CURRENT_CONFIG_VERSION,
217
+ endpoints: [],
218
+ selectedModels: [],
219
+ ...defaults,
220
+ updatedAt: new Date()
221
+ });
222
+ console.log('[AIConfigModel] Default config initialized');
223
+ }
224
+ else {
225
+ console.log('[AIConfigModel] Config already exists, skipping initialization');
226
+ }
227
+ }
228
+ /**
229
+ * 获取解析后的模型配置(用于 MultiModelClient)
230
+ * 返回按 fallback 顺序排列的完整模型配置
231
+ */
232
+ async getResolvedModelConfigs() {
233
+ const config = await this.getConfig();
234
+ if (!config) {
235
+ return [];
236
+ }
237
+ const results = [];
238
+ for (const selected of config.selectedModels) {
239
+ const endpoint = config.endpoints.find(ep => ep.id === selected.endpointId);
240
+ if (endpoint && endpoint.enabled !== false) {
241
+ results.push({
242
+ endpointId: endpoint.id,
243
+ endpointName: endpoint.name,
244
+ apiBaseUrl: endpoint.apiBaseUrl,
245
+ apiKeyEncrypted: endpoint.apiKeyEncrypted,
246
+ modelName: selected.modelName,
247
+ timeoutSeconds: config.timeoutSeconds,
248
+ });
249
+ }
250
+ }
251
+ return results;
252
+ }
253
+ }
254
+ exports.AIConfigModel = AIConfigModel;
255
+ //# sourceMappingURL=aiConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiConfig.js","sourceRoot":"","sources":["../../src/models/aiConfig.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAGH,mCAAoC;AAEpC,cAAc;AACD,QAAA,sBAAsB,GAAG,CAAC,CAAC;AA4DxC;;;GAGG;AACH,MAAa,aAAa;IAIxB,YAAY,EAAM;QAFD,aAAQ,GAAG,SAAS,CAAC,CAAC,YAAY;QAGjD,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,CAAW,WAAW,CAAC,CAAC;IACzD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa;QACjB,gCAAgC;QAChC,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;IACxD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAExE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QAED,gBAAgB;QAChB,IAAI,IAAI,CAAC,cAAc,CAAC,SAAsC,CAAC,EAAE,CAAC;YAChE,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAsC,CAAC,CAAC;YACtF,YAAY;YACZ,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAC7B,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,EACtB,EAAE,IAAI,EAAE,cAAc,EAAE,CACzB,CAAC;YACF,OAAO,cAAc,CAAC;QACxB,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,MAAsB;QAC3C,gCAAgC;QAChC,OAAO,CAAC,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,GAAG,8BAAsB,CAAC;IAChF,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,MAAsB;QAC9C,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;QAErE,4BAA4B;QAC5B,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpD,MAAM,mBAAmB,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;gBACrE,EAAE,EAAE,QAAQ,CAAC,EAAE,IAAI,IAAA,mBAAU,GAAE;gBAC/B,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,MAAM,KAAK,GAAG,CAAC,EAAE;gBACxC,UAAU,EAAE,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,EAAE;gBAC1D,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,MAAM,CAAC,eAAe,IAAI,EAAE;gBACzE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;gBAC7D,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;gBAC7C,OAAO,EAAE,QAAQ,CAAC,OAAO,KAAK,KAAK;aACpC,CAAC,CAAC,CAAC;YAEJ,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAClE,IAAI,cAAc,GAAG,CAAC,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;YAEhG,yCAAyC;YACzC,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,IAAI,MAAM,CAAC,SAAS,IAAI,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/C,cAAc,GAAG,CAAC,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC5F,CAAC;qBAAM,CAAC;oBACN,cAAc,GAAG,mBAAmB;yBACjC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;yBAClC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;YAED,OAAO;gBACL,GAAG,MAAM;gBACT,aAAa,EAAE,8BAAsB;gBACrC,SAAS,EAAE,mBAAmB;gBAC9B,cAAc;aACH,CAAC;QAChB,CAAC;QAED,aAAa;QACb,MAAM,eAAe,GAAgB;YACnC,EAAE,EAAE,IAAA,mBAAU,GAAE;YAChB,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,EAAE;YACnC,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,EAAE;YAC7C,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE;YAClD,iBAAiB,EAAE,SAAS;YAC5B,OAAO,EAAE,IAAI;SACd,CAAC;QAEF,WAAW;QACX,MAAM,cAAc,GAAoB,MAAM,CAAC,SAAS;YACtD,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;YACnE,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,cAAc,GAAa;YAC/B,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,aAAa,EAAE,8BAAsB;YACrC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE;YACrD,cAAc;YACd,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;YAC7C,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;YACjD,0BAA0B,EAAE,MAAM,CAAC,0BAA0B;YAC7D,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,YAAY;YACZ,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,eAAe,EAAE,MAAM,CAAC,eAAe;SACxC,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,uDAAuD,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;QACzF,OAAO,cAAc,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,OAAqD;QACtE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,uBAAuB;QACvB,MAAM,UAAU,GAAG;YACjB,GAAG,OAAO;YACV,aAAa,EAAE,8BAAsB;YACrC,SAAS,EAAE,GAAG;SACf,CAAC;QAEF,oBAAoB;QACpB,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAC7B,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,EACtB,EAAE,IAAI,EAAE,UAAU,EAAE,EACpB,EAAE,MAAM,EAAE,IAAI,EAAE,CACjB,CAAC;QAEF,WAAW;QACX,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAErE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,QAAiC;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,MAAM,WAAW,GAAgB;YAC/B,GAAG,QAAQ;YACX,EAAE,EAAE,IAAA,mBAAU,GAAE;SACjB,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC;QAC1C,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE5B,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;QACvC,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,UAAkB,EAAE,OAAyC;QAChF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAC1C,EAAE,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAClD,CAAC;QAEF,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,UAAkB;QACrC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;QACtE,4BAA4B;QAC5B,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;QAExF,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;IACzD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,oBAAoB,CAAC,cAA+B;QACxD,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,UAAkB;QACtC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,UAAU,CAAC,IAAI,IAAI,CAAC;IACnE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,uBAAuB,CAAC,QAAgG;QAC5H,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAExC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;gBAC9B,GAAG,EAAE,IAAI,CAAC,QAAQ;gBAClB,aAAa,EAAE,8BAAsB;gBACrC,SAAS,EAAE,EAAE;gBACb,cAAc,EAAE,EAAE;gBAClB,GAAG,QAAQ;gBACX,SAAS,EAAE,IAAI,IAAI,EAAE;aACV,CAAC,CAAC;YAEf,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,uBAAuB;QAQ3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,OAAO,GAOR,EAAE,CAAC;QAER,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC5E,IAAI,QAAQ,IAAI,QAAQ,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;gBAC3C,OAAO,CAAC,IAAI,CAAC;oBACX,UAAU,EAAE,QAAQ,CAAC,EAAE;oBACvB,YAAY,EAAE,QAAQ,CAAC,IAAI;oBAC3B,UAAU,EAAE,QAAQ,CAAC,UAAU;oBAC/B,eAAe,EAAE,QAAQ,CAAC,eAAe;oBACzC,SAAS,EAAE,QAAQ,CAAC,SAAS;oBAC7B,cAAc,EAAE,MAAM,CAAC,cAAc;iBACtC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAzSD,sCAySC"}