diff --git a/backend/src/services/searchService.ts b/backend/src/services/searchService.ts index 470840b..129fc8c 100644 --- a/backend/src/services/searchService.ts +++ b/backend/src/services/searchService.ts @@ -56,23 +56,90 @@ export class SearchService { @handleError('搜索执行失败') public async search(keyword: string): Promise { logger.info('开始执行搜索', { keyword }); - const token = await this.getToken(); - const response = await axios.get( - `${this.baseUrl}${config.searchService.endpoints.search}`, - { - params: { keyword }, - headers: { - Authorization: `Bearer ${token}`, - }, + // 1. 尝试从Redis获取缓存 + const cacheKey = `search:${keyword}`; + const cachedResult = await this.redisService.get(cacheKey); + if (cachedResult) { + logger.info('从缓存中获取搜索结果', { keyword }); + return JSON.parse(cachedResult); + } + + // 2. 获取token并执行搜索 + let token = await this.getToken(); + let retryCount = 0; + const maxRetries = 1; + + while (retryCount <= maxRetries) { + try { + const response = await axios.get( + `${this.baseUrl}${config.searchService.endpoints.search}`, + { + params: { keyword }, + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + // 3. 处理搜索结果 + if (response.data.success && response.data.data) { + // 为每个结果项生成唯一ID并格式化数据 + const processedData = response.data.data.map(channelData => { + const processedList = channelData.list.map(item => { + const itemId = crypto.randomUUID(); + const processedItem = { + itemId, + driveType: item.cloudLinks?.[0]?.cloudType?.toLowerCase() || 'unknown', + publishDate: new Date(item.pubDate).toISOString(), + displayTitle: item.title, + ...item + }; + + // 将单个结果项存入Redis,TTL设置为1天 + this.redisService.set(`item:${itemId}`, JSON.stringify(processedItem), { EX: 86400 }) + .catch(err => logger.error('缓存单个结果项失败', { error: err, itemId })); + + return processedItem; + }); + + return { + ...channelData, + list: processedList + }; + }); + + // 4. 将完整搜索结果存入Redis,TTL设置为5分钟 + const searchResult = { + ...response.data, + data: processedData + }; + await this.redisService.set(cacheKey, JSON.stringify(searchResult), { EX: 3600 }); + + logger.info('搜索完成', { + keyword, + resultCount: processedData.reduce((acc, channel) => acc + channel.list.length, 0) + }); + + return searchResult; + } + + throw new AppError('搜索结果格式错误', 500); + } catch (error: any) { + // 处理token失效的情况 + if (error.response?.status === 401 || error.response?.status === 403) { + if (retryCount < maxRetries) { + logger.warn('Token已失效,尝试重新获取', { retryCount }); + token = await this.getToken(); + retryCount++; + continue; + } + } + throw error; } - ); + } - logger.info('搜索完成', { - keyword, - resultCount: response.data.data?.length || 0 - }); - return response.data; + throw new AppError('搜索失败,请稍后重试', 500); } @handleError('参数化搜索失败') diff --git a/doc/需求文档.md b/doc/需求文档.md index bb41f99..103f991 100644 --- a/doc/需求文档.md +++ b/doc/需求文档.md @@ -68,16 +68,47 @@ ## **5\. 功能需求 \- 后端 API** -### **5.1. GET /api/search** +你补充的这个细节非常重要!这会直接影响我们后端与外部搜索服务交互的方式。 -* **输入:** keyword (Query Parameter, String, Required) -* **外部API认证配置:** - * 后端应用需要安全地配置用于登录外部搜索接口 (http://192.168.1.99:8008/api/user/login) 的 username 和 password。这些凭证**绝不能**硬编码,应通过环境变量或加密配置文件提供。 - * 后端需要一个机制来存储和管理从外部登录接口获取的当前有效的 token (例如,存储在内存变量中,并考虑持久化到Redis以支持多实例部署和重启后恢复,或者每次应用启动时重新登录获取)。 -* **处理流程:** - 1. **确保外部API Token有效:** a. 检查当前是否已持有有效的 token。可以通过检查 token 的过期时间(如果JWT token中包含exp声明)或通过一个简单的状态标记来判断。 b. **如果 token 不存在、无效或即将过期:** i. 调用外部登录接口 POST http://192.168.1.99:8008/api/user/login,请求体包含配置好的 username 和 password。 ii. 如果登录成功,获取返回的新 token,并更新后端持有的当前 token 及其过期信息。记录登录成功日志。 iii.如果登录失败,记录严重错误日志(例如,无法连接外部认证服务,凭证错误),并向上游(即我们的前端)返回一个特定的错误码,表明搜索服务暂时不可用。 - 2. **执行搜索(结合缓存逻辑):** a. 构建Redis缓存Key (例如: search:${keyword})。尝试从Redis获取缓存。若命中,直接返回缓存数据(包含itemId的列表)。 b. 若未命中缓存: i. **调用外部搜索API (携带Token):** GET http://192.168.1.99:8008/api/search?keyword={keyword} 在请求头中添加 Authorization: Bearer \<当前有效的token\>。 ii. **处理Token失效(重试机制):** 如果调用外部搜索API返回401或403错误(表示Token无效或过期): 1\. 立即执行上述**步骤 1.b (重新登录获取新Token)**。 2\. 如果成功获取新Token,则**重试一次**步骤 2.b.i (调用外部搜索API)。 3\. 如果重试仍然失败,或重新登录失败,则记录严重错误日志,并向上游返回错误。 iii.处理外部API的响应数据。对于返回的每个资源项: 1\. 生成一个全局唯一的 itemId (例如: UUID)。 2\. **数据提取与格式化:** 确保包含 driveType (统一格式), publishDate (ISO 8601), displayTitle, 以及后续“获取链接”所需的原始信息(如 cloudLinks, 原始 messageId)。 3\. 将该项的**完整处理后信息**存入Redis,Key为 itemId,**TTL设置为 1-2 天**。 iv. 记录本次搜索操作(成功或因外部API问题失败)到 ActivityLog 表(包含关键词、来源IP等)。 v. 如果外部API调用成功,将包含所有处理后结果项(至少含 itemId, driveType, publishDate, displayTitle)的**完整列表**存入Redis,Key为 search:${keyword},**TTL建议设置为 5 分钟**。 vi. 将此完整列表返回给前端(或在发生不可恢复错误时返回错误响应)。 -* **输出:** \[{ itemId, displayTitle, driveType, publishDate, ... }, ...\] 或错误响应。 +我将更新需求文档中关于后端 `GET /api/search` 接口的部分,以包含这个认证流程。 + +--- + +**以下是针对需求文档的补充和修改:** + +**在章节 5. 功能需求 - 后端 API,子章节 5.1. `GET /api/search` 中,处理流程需要更新:** + +### 5.1. `GET /api/search` + +* **输入:** `keyword` (Query Parameter, String, Required) +* **外部API认证配置:** + * 后端应用需要安全地配置用于登录外部搜索接口 (`http://192.168.1.99:8008/api/user/login`) 的 `username` 和 `password`。这些凭证**绝不能**硬编码,应通过环境变量或加密配置文件提供。 + * 后端需要一个机制来存储和管理从外部登录接口获取的当前有效的 `token` (例如,存储在内存变量中,并考虑持久化到Redis以支持多实例部署和重启后恢复,或者每次应用启动时重新登录获取)。 +* **处理流程:** + 1. **确保外部API Token有效:** + a. 检查当前是否已持有有效的 `token`。可以通过检查 `token` 的过期时间(如果JWT token中包含`exp`声明)或通过一个简单的状态标记来判断。 + b. **如果 `token` 不存在、无效或即将过期:** + i. 调用外部登录接口 `POST http://192.168.1.99:8008/api/user/login`,请求体包含配置好的 `username` 和 `password`。 + ii. 如果登录成功,获取返回的新 `token`,并更新后端持有的当前 `token` 及其过期信息。记录登录成功日志。 + iii.如果登录失败,记录严重错误日志(例如,无法连接外部认证服务,凭证错误),并向上游(即我们的前端)返回一个特定的错误码,表明搜索服务暂时不可用。 + 2. **执行搜索(结合缓存逻辑):** + a. 构建Redis缓存Key (例如: `search:${keyword}`)。尝试从Redis获取缓存。若命中,直接返回缓存数据(包含`itemId`的列表)。 + b. 若未命中缓存: + i. **调用外部搜索API (携带Token):** + `GET http://192.168.1.99:8008/api/search?keyword={keyword}` + 在请求头中添加 `Authorization: Bearer <当前有效的token>`。 + ii. **处理Token失效(重试机制):** 如果调用外部搜索API返回401或403错误(表示Token无效或过期): + 1. 立即执行上述**步骤 1.b (重新登录获取新Token)**。 + 2. 如果成功获取新Token,则**重试一次**步骤 2.b.i (调用外部搜索API)。 + 3. 如果重试仍然失败,或重新登录失败,则记录严重错误日志,并向上游返回错误。 + iii.处理外部API的响应数据。对于返回的每个资源项: + 1. 生成一个全局唯一的 `itemId` (例如: UUID)。 + 2. **数据提取与格式化:** 确保包含 `driveType` (统一格式), `publishDate` (ISO 8601), `displayTitle`, 以及后续“获取链接”所需的原始信息(如 `cloudLinks`, 原始 `messageId`)。 + 3. 将该项的**完整处理后信息**存入Redis,Key为 `itemId`,**TTL设置为 1-2 天**。 + iv. 记录本次搜索操作(成功或因外部API问题失败)到 `ActivityLog` 表(包含关键词、来源IP等)。 + v. 如果外部API调用成功,将包含所有处理后结果项(至少含 `itemId`, `driveType`, `publishDate`, `displayTitle`)的**完整列表**存入Redis,Key为 `search:${keyword}`,**TTL建议设置为 5 分钟**。 + vi. 将此完整列表返回给前端(或在发生不可恢复错误时返回错误响应)。 +* **输出:** `[{ itemId, displayTitle, driveType, publishDate, ... }, ...]` 或错误响应。 * **并发管理 (建议):** 当多个并发请求发现token失效时,应有机制避免同时多次调用外部登录接口(例如,使用一个简单的锁或请求队列来管理token的更新过程)。 ### **5.2. POST /api/generate-link** diff --git a/package-lock.json b/package-lock.json index 7881c19..4d921f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,14 @@ "dependencies": { "@types/axios": "^0.14.4", "@types/redis": "^4.0.11", + "@types/uuid": "^10.0.0", "axios": "^1.9.0", "dotenv": "^16.5.0", + "ioredis": "^5.6.1", "redis": "^5.0.1", + "sequelize": "^6.37.7", + "typedi": "^0.10.0", + "uuid": "^11.1.0", "winston": "^3.17.0" }, "devDependencies": { @@ -50,6 +55,12 @@ "kuler": "^2.0.0" } }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "license": "MIT" + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -170,13 +181,26 @@ "axios": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.15.17", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -196,6 +220,18 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.0.tgz", + "integrity": "sha512-nh7nrWhLr6CBq9ldtw0wx+z9wKnnv/uTVLA9g/3/TcOYxbpOSZE+MhKPmWqU+K0NvThjhv12uD8MuqijB0WzEA==", + "license": "MIT" + }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", @@ -333,6 +369,23 @@ "dev": true, "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -341,6 +394,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -362,6 +424,12 @@ "url": "https://dotenvx.com" } }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", + "license": "MIT" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -557,12 +625,45 @@ "node": ">= 0.4" } }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ioredis": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", + "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -587,6 +688,24 @@ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "license": "MIT" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/logform": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", @@ -638,6 +757,27 @@ "node": ">= 0.6" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -653,6 +793,12 @@ "fn.name": "1.x.x" } }, + "node_modules/pg-connection-string": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", + "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -687,6 +833,33 @@ "node": ">= 18" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -716,6 +889,98 @@ "node": ">=10" } }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize": { + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -734,6 +999,12 @@ "node": "*" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -749,6 +1020,12 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -802,6 +1079,12 @@ } } }, + "node_modules/typedi": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/typedi/-/typedi-0.10.0.tgz", + "integrity": "sha512-v3UJF8xm68BBj6AF4oQML3ikrfK2c9EmZUyLOfShpJuItAqVBHWP/KtpGinkSsIiP6EZyyb6Z3NXyW9dgS9X1w==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -820,9 +1103,7 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -830,6 +1111,19 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -837,6 +1131,15 @@ "dev": true, "license": "MIT" }, + "node_modules/validator": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz", + "integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/winston": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", @@ -873,6 +1176,15 @@ "node": ">= 12.0.0" } }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index f1d82fd..f41ce1d 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,14 @@ "dependencies": { "@types/axios": "^0.14.4", "@types/redis": "^4.0.11", + "@types/uuid": "^10.0.0", "axios": "^1.9.0", "dotenv": "^16.5.0", + "ioredis": "^5.6.1", "redis": "^5.0.1", + "sequelize": "^6.37.7", + "typedi": "^0.10.0", + "uuid": "^11.1.0", "winston": "^3.17.0" }, "devDependencies": {