From 55378f4c9485e85b13f092cc0485e81f4cf5933c Mon Sep 17 00:00:00 2001 From: xiongxiao Date: Wed, 25 Mar 2026 00:57:18 +0800 Subject: [PATCH] feat: remove old data import scripts and JSON files - Deleted `import-data.ts` and `import-life.ts` scripts for importing short-link and life JSON data into the database. - Removed `ncode-list.json` containing short-link data. - Added new script `mv-resources.ts` for migrating resources from username-based paths to userId-based paths in the database and object storage. - Introduced `UserId` module for fetching user IDs and usernames from the database with caching. - Updated `UserApp` class to use user IDs instead of usernames for resource paths. - Modified routes to include a new endpoint for retrieving user IDs based on usernames. --- package.json | 6 +- pnpm-lock.yaml | 218 ++++++++++++++--------------- scripts/import-data.ts | 78 ----------- scripts/import-life.ts | 83 ----------- scripts/mv-resources.ts | 106 ++++++++++++++ scripts/ncode-list.json | 104 -------------- src/aura/libs/auc.ts | 2 +- src/modules/user-app/index.ts | 11 +- src/routes/app-manager/list.ts | 2 +- src/routes/user/list.ts | 26 ++++ src/routes/user/modules/user-id.ts | 41 ++++++ 11 files changed, 294 insertions(+), 383 deletions(-) delete mode 100644 scripts/import-data.ts delete mode 100644 scripts/import-life.ts create mode 100644 scripts/mv-resources.ts delete mode 100644 scripts/ncode-list.json create mode 100644 src/routes/user/modules/user-id.ts diff --git a/package.json b/package.json index e3d3f29..5fdd0de 100644 --- a/package.json +++ b/package.json @@ -59,9 +59,9 @@ }, "devDependencies": { "@ai-sdk/openai-compatible": "^2.0.37", - "@aws-sdk/client-s3": "^3.1014.0", + "@aws-sdk/client-s3": "^3.1015.0", "@kevisual/api": "^0.0.65", - "@kevisual/cnb": "^0.0.59", + "@kevisual/cnb": "^0.0.60", "@kevisual/context": "^0.0.8", "@kevisual/local-app-manager": "0.1.32", "@kevisual/logger": "^0.0.4", @@ -78,7 +78,7 @@ "@types/pg": "^8.20.0", "@types/semver": "^7.7.1", "@types/xml2js": "^0.4.14", - "ai": "^6.0.134", + "ai": "^6.0.137", "archiver": "^7.0.1", "convex": "^1.34.0", "crypto-js": "^4.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83c1397..7e21350 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,14 +63,14 @@ importers: specifier: ^2.0.37 version: 2.0.37(zod@4.3.6) '@aws-sdk/client-s3': - specifier: ^3.1014.0 - version: 3.1014.0 + specifier: ^3.1015.0 + version: 3.1015.0 '@kevisual/api': specifier: ^0.0.65 version: 0.0.65(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@kevisual/cnb': - specifier: ^0.0.59 - version: 0.0.59(dotenv@17.3.1)(idb-keyval@6.2.2)(ioredis@5.10.0) + specifier: ^0.0.60 + version: 0.0.60(dotenv@17.3.1)(idb-keyval@6.2.2)(ioredis@5.10.0) '@kevisual/context': specifier: ^0.0.8 version: 0.0.8 @@ -120,8 +120,8 @@ importers: specifier: ^0.4.14 version: 0.4.14 ai: - specifier: ^6.0.134 - version: 6.0.134(zod@4.3.6) + specifier: ^6.0.137 + version: 6.0.137(zod@4.3.6) archiver: specifier: ^7.0.1 version: 7.0.1 @@ -207,8 +207,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@3.0.77': - resolution: {integrity: sha512-UdwIG2H2YMuntJQ5L+EmED5XiwnlvDT3HOmKfVFxR4Nq/RSLFA/HcchhwfNXHZ5UJjyuL2VO0huLbWSZ9ijemQ==} + '@ai-sdk/gateway@3.0.79': + resolution: {integrity: sha512-Wk2QJpqd0em5YcR49uoMCy9msyANAYgjXdlRcqqRt2fz4rNLnMMrKOlLwAXoFzR1ElR3bj4e/k6hscRfjpzSGA==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -264,48 +264,48 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.1014.0': - resolution: {integrity: sha512-0XLrOT4Cm3NEhhiME7l/8LbTXS4KdsbR4dSrY207KNKTcHLLTZ9EXt4ZpgnTfLvWQF3pGP2us4Zi1fYLo0N+Ow==} + '@aws-sdk/client-s3@3.1015.0': + resolution: {integrity: sha512-yo+Y+/fq5/E684SynTRO+VA3a+98MeE/hs7J52XpNI5SchOCSrLhLtcDKVASlGhHQdNLGLzblRgps1OZaf8sbA==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.23': - resolution: {integrity: sha512-aoJncvD1XvloZ9JLnKqTRL9dBy+Szkryoag9VT+V1TqsuUgIxV9cnBVM/hrDi2vE8bDqLiDR8nirdRcCdtJu0w==} + '@aws-sdk/core@3.973.24': + resolution: {integrity: sha512-vvf82RYQu2GidWAuQq+uIzaPz9V0gSCXVqdVzRosgl5rXcspXOpSD3wFreGGW6AYymPr97Z69kjVnLePBxloDw==} engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.972.5': resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.21': - resolution: {integrity: sha512-BkAfKq8Bd4shCtec1usNz//urPJF/SZy14qJyxkSaRJQ/Vv1gVh0VZSTmS7aE6aLMELkFV5wHHrS9ZcdG8Kxsg==} + '@aws-sdk/credential-provider-env@3.972.22': + resolution: {integrity: sha512-cXp0VTDWT76p3hyK5D51yIKEfpf6/zsUvMfaB8CkyqadJxMQ8SbEeVroregmDlZbtG31wkj9ei0WnftmieggLg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.23': - resolution: {integrity: sha512-4XZ3+Gu5DY8/n8zQFHBgcKTF7hWQl42G6CY9xfXVo2d25FM/lYkpmuzhYopYoPL1ITWkJ2OSBQfYEu5JRfHOhA==} + '@aws-sdk/credential-provider-http@3.972.24': + resolution: {integrity: sha512-h694K7+tRuepSRJr09wTvQfaEnjzsKZ5s7fbESrVds02GT/QzViJ94/HCNwM7bUfFxqpPXHxulZfL6Cou0dwPg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.23': - resolution: {integrity: sha512-PZLSmU0JFpNCDFReidBezsgL5ji9jOBry8CnZdw4Jj6d0K2z3Ftnp44NXgADqYx5BLMu/ZHujfeJReaDoV+IwQ==} + '@aws-sdk/credential-provider-ini@3.972.24': + resolution: {integrity: sha512-O46fFmv0RDFWiWEA9/e6oW92BnsyAXuEgTTasxHligjn2RCr9L/DK773m/NoFaL3ZdNAUz8WxgxunleMnHAkeQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.23': - resolution: {integrity: sha512-OmE/pSkbMM3dCj1HdOnZ5kXnKK+R/Yz+kbBugraBecp0pGAs21eEURfQRz+1N2gzIHLVyGIP1MEjk/uSrFsngg==} + '@aws-sdk/credential-provider-login@3.972.24': + resolution: {integrity: sha512-sIk8oa6AzDoUhxsR11svZESqvzGuXesw62Rl2oW6wguZx8i9cdGCvkFg+h5K7iucUZP8wyWibUbJMc+J66cu5g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.24': - resolution: {integrity: sha512-9Jwi7aps3AfUicJyF5udYadPypPpCwUZ6BSKr/QjRbVCpRVS1wc+1Q6AEZ/qz8J4JraeRd247pSzyMQSIHVebw==} + '@aws-sdk/credential-provider-node@3.972.25': + resolution: {integrity: sha512-m7dR0Dsva2P+VUpL+VkC0WwiDby5pgmWXkRVDB5rlwv0jXJrQJf7YMtCoM8Wjk0H9jPeCYOxOXXcIgp/qp5Alg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.21': - resolution: {integrity: sha512-nRxbeOJ1E1gVA0lNQezuMVndx+ZcuyaW/RB05pUsznN5BxykSlH6KkZ/7Ca/ubJf3i5N3p0gwNO5zgPSCzj+ww==} + '@aws-sdk/credential-provider-process@3.972.22': + resolution: {integrity: sha512-Os32s8/4gTZjBk5BtoS/cuTILaj+K72d0dVG7TCJX/fC4598cxwLDmf1AEHEpER5oL3K//yETjvFaz0V8oO5Xw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.23': - resolution: {integrity: sha512-APUccADuYPLL0f2htpM8Z4czabSmHOdo4r41W6lKEZdy++cNJ42Radqy6x4TopENzr3hR6WYMyhiuiqtbf/nAA==} + '@aws-sdk/credential-provider-sso@3.972.24': + resolution: {integrity: sha512-PaFv7snEfypU2yXkpvfyWgddEbDLtgVe51wdZlinhc2doubBjUzJZZpgwuF2Jenl1FBydMhNpMjD6SBUM3qdSA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.23': - resolution: {integrity: sha512-H5JNqtIwOu/feInmMMWcK0dL5r897ReEn7n2m16Dd0DPD9gA2Hg8Cq4UDzZ/9OzaLh/uqBM6seixz0U6Fi2Eag==} + '@aws-sdk/credential-provider-web-identity@3.972.24': + resolution: {integrity: sha512-J6H4R1nvr3uBTqD/EeIPAskrBtET4WFfNhpFySr2xW7bVZOXpQfPjrLSIx65jcNjBmLXzWq8QFLdVoGxiGG/SA==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-bucket-endpoint@3.972.8': @@ -316,8 +316,8 @@ packages: resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.974.3': - resolution: {integrity: sha512-fB7FNLH1+VPUs0QL3PLrHW+DD4gKu6daFgWtyq3R0Y0Lx8DLZPvyGAxCZNFBxH+M2xt9KvBJX6USwjuqvitmCQ==} + '@aws-sdk/middleware-flexible-checksums@3.974.4': + resolution: {integrity: sha512-fhCbZXPAyy8btnNbnBlR7Cc1nD54cETSvGn2wey71ehsM89AKPO8Dpco9DBAAgvrUdLrdHQepBXcyX4vxC5OwA==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.8': @@ -336,32 +336,32 @@ packages: resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.972.23': - resolution: {integrity: sha512-50QgHGPQAb2veqFOmTF1A3GsAklLHZXL47KbY35khIkfbXH5PLvqpEc/gOAEBPj/yFxrlgxz/8mqWcWTNxBkwQ==} + '@aws-sdk/middleware-sdk-s3@3.972.24': + resolution: {integrity: sha512-4sXxVC/enYgMkZefNMOzU6C6KtAXEvwVJLgNcUx1dvROH6GvKB5Sm2RGnGzTp0/PwkibIyMw4kOzF8tbLfaBAQ==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.8': resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.24': - resolution: {integrity: sha512-dLTWy6IfAMhNiSEvMr07g/qZ54be6pLqlxVblbF6AzafmmGAzMMj8qMoY9B4+YgT+gY9IcuxZslNh03L6PyMCQ==} + '@aws-sdk/middleware-user-agent@3.972.25': + resolution: {integrity: sha512-QxiMPofvOt8SwSynTOmuZfvvPM1S9QfkESBxB22NMHTRXCJhR5BygLl8IXfC4jELiisQgwsgUby21GtXfX3f/g==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.996.13': - resolution: {integrity: sha512-ptZ1HF4yYHNJX8cgFF+8NdYO69XJKZn7ft0/ynV3c0hCbN+89fAbrLS+fqniU2tW8o9Kfqhj8FUh+IPXb2Qsuw==} + '@aws-sdk/nested-clients@3.996.14': + resolution: {integrity: sha512-fSESKvh1VbfjtV3QMnRkCPZWkUbQof6T/DOpiLp33yP2wA+rbwwnZeG3XT3Ekljgw2I8X4XaQPnw+zSR8yxJ5Q==} engines: {node: '>=20.0.0'} '@aws-sdk/region-config-resolver@3.972.9': resolution: {integrity: sha512-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.996.11': - resolution: {integrity: sha512-SKgZY7x6AloLUXO20FJGnkKJ3a6CXzNDt6PYs2yqoPzgU0xKWcUoGGJGEBTsfM5eihKW42lbwp+sXzACLbSsaA==} + '@aws-sdk/signature-v4-multi-region@3.996.12': + resolution: {integrity: sha512-abRObSqjVeKUUHIZfAp78PTYrEsxCgVKDs/YET357pzT5C02eDDEvmWyeEC2wglWcYC4UTbBFk22gd2YJUlCQg==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1014.0': - resolution: {integrity: sha512-gHTHNUoaOGNrSWkl32A7wFsU78jlNTlqMccLu0byUk5CysYYXaxNMIonIVr4YcykC7vgtDS5ABuz83giy6fzJA==} + '@aws-sdk/token-providers@3.1015.0': + resolution: {integrity: sha512-3OSD4y110nisRhHzFOjoEeHU4GQL4KpzkX9PxzWaiZe0Yg2+thZKM0Pn9DjYwezH5JYfh/K++xK/SE0IHGrmCQ==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.6': @@ -383,8 +383,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.972.8': resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.973.10': - resolution: {integrity: sha512-E99zeTscCc+pTMfsvnfi6foPpKmdD1cZfOC7/P8UUrjsoQdg9VEWPRD+xdFduKnfPXwcvby58AlO9jwwF6U96g==} + '@aws-sdk/util-user-agent-node@3.973.11': + resolution: {integrity: sha512-1qdXbXo2s5MMLpUvw00284LsbhtlQ4ul7Zzdn5n+7p4WVgCMLqhxImpHIrjSoc72E/fyc4Wq8dLtUld2Gsh+lA==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -871,8 +871,8 @@ packages: '@kevisual/auth@2.0.3': resolution: {integrity: sha512-4xpijaIhlCTr/DlJaV/gmkCQeg45EO1yxWpRvUX+1jCdVbuxSR0wZrF0SD9oybnjmKWMKDNPLsXyduFjMGcItA==} - '@kevisual/cnb@0.0.59': - resolution: {integrity: sha512-pz35KfZK7dSMEgasRBxxOHgNfd92DfJYKKYTRYwOEnW5LC58qmhl3b+ApC/pFNjGsuLmpaxxjqTgtvFzkSIc/A==} + '@kevisual/cnb@0.0.60': + resolution: {integrity: sha512-I13DKPntnneyM+o+eJ5hemIQGhaScCNPQiinaSlrxPHpsCKU8XFookjzfMA6T+D4hi0pWfZYILtXnpOHGh3k/Q==} hasBin: true '@kevisual/context@0.0.8': @@ -1265,8 +1265,8 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} - ai@6.0.134: - resolution: {integrity: sha512-YalNEaavld/kE444gOcsMKXdVVRGEe0SK77fAFcWYcqLg+a7xKnEet8bdfrEAJTfnMjj01rhgrIL10903w1a5Q==} + ai@6.0.137: + resolution: {integrity: sha512-9e/mNMTmXmMkmldEJPIumy9FFBuUWHUyj2yF8EglC3VVOhVVBwlnpeHYSFKdNc13Jj6Hso3EJcZioMaofgCs4A==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -2729,7 +2729,7 @@ snapshots: '@ai-sdk/provider-utils': 4.0.19(zod@4.3.6) zod: 4.3.6 - '@ai-sdk/gateway@3.0.77(zod@4.3.6)': + '@ai-sdk/gateway@3.0.79(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) @@ -2813,29 +2813,29 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.1014.0': + '@aws-sdk/client-s3@3.1015.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.23 - '@aws-sdk/credential-provider-node': 3.972.24 + '@aws-sdk/core': 3.973.24 + '@aws-sdk/credential-provider-node': 3.972.25 '@aws-sdk/middleware-bucket-endpoint': 3.972.8 '@aws-sdk/middleware-expect-continue': 3.972.8 - '@aws-sdk/middleware-flexible-checksums': 3.974.3 + '@aws-sdk/middleware-flexible-checksums': 3.974.4 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-location-constraint': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-sdk-s3': 3.972.23 + '@aws-sdk/middleware-sdk-s3': 3.972.24 '@aws-sdk/middleware-ssec': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.24 + '@aws-sdk/middleware-user-agent': 3.972.25 '@aws-sdk/region-config-resolver': 3.972.9 - '@aws-sdk/signature-v4-multi-region': 3.996.11 + '@aws-sdk/signature-v4-multi-region': 3.996.12 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.10 + '@aws-sdk/util-user-agent-node': 3.973.11 '@smithy/config-resolver': 4.4.13 '@smithy/core': 3.23.12 '@smithy/eventstream-serde-browser': 4.2.12 @@ -2873,7 +2873,7 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.23': + '@aws-sdk/core@3.973.24': dependencies: '@aws-sdk/types': 3.973.6 '@aws-sdk/xml-builder': 3.972.15 @@ -2894,17 +2894,17 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.21': + '@aws-sdk/credential-provider-env@3.972.22': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.24 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.23': + '@aws-sdk/credential-provider-http@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.24 '@aws-sdk/types': 3.973.6 '@smithy/fetch-http-handler': 5.3.15 '@smithy/node-http-handler': 4.5.0 @@ -2915,16 +2915,16 @@ snapshots: '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.23': + '@aws-sdk/credential-provider-ini@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/credential-provider-env': 3.972.21 - '@aws-sdk/credential-provider-http': 3.972.23 - '@aws-sdk/credential-provider-login': 3.972.23 - '@aws-sdk/credential-provider-process': 3.972.21 - '@aws-sdk/credential-provider-sso': 3.972.23 - '@aws-sdk/credential-provider-web-identity': 3.972.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.24 + '@aws-sdk/credential-provider-env': 3.972.22 + '@aws-sdk/credential-provider-http': 3.972.24 + '@aws-sdk/credential-provider-login': 3.972.24 + '@aws-sdk/credential-provider-process': 3.972.22 + '@aws-sdk/credential-provider-sso': 3.972.24 + '@aws-sdk/credential-provider-web-identity': 3.972.24 + '@aws-sdk/nested-clients': 3.996.14 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -2934,10 +2934,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.23': + '@aws-sdk/credential-provider-login@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.24 + '@aws-sdk/nested-clients': 3.996.14 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/protocol-http': 5.3.12 @@ -2947,14 +2947,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.24': + '@aws-sdk/credential-provider-node@3.972.25': dependencies: - '@aws-sdk/credential-provider-env': 3.972.21 - '@aws-sdk/credential-provider-http': 3.972.23 - '@aws-sdk/credential-provider-ini': 3.972.23 - '@aws-sdk/credential-provider-process': 3.972.21 - '@aws-sdk/credential-provider-sso': 3.972.23 - '@aws-sdk/credential-provider-web-identity': 3.972.23 + '@aws-sdk/credential-provider-env': 3.972.22 + '@aws-sdk/credential-provider-http': 3.972.24 + '@aws-sdk/credential-provider-ini': 3.972.24 + '@aws-sdk/credential-provider-process': 3.972.22 + '@aws-sdk/credential-provider-sso': 3.972.24 + '@aws-sdk/credential-provider-web-identity': 3.972.24 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -2964,20 +2964,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.21': + '@aws-sdk/credential-provider-process@3.972.22': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.24 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.23': + '@aws-sdk/credential-provider-sso@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 - '@aws-sdk/token-providers': 3.1014.0 + '@aws-sdk/core': 3.973.24 + '@aws-sdk/nested-clients': 3.996.14 + '@aws-sdk/token-providers': 3.1015.0 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -2986,10 +2986,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.23': + '@aws-sdk/credential-provider-web-identity@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.24 + '@aws-sdk/nested-clients': 3.996.14 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -3015,12 +3015,12 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.974.3': + '@aws-sdk/middleware-flexible-checksums@3.974.4': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.24 '@aws-sdk/crc64-nvme': 3.972.5 '@aws-sdk/types': 3.973.6 '@smithy/is-array-buffer': 4.2.2 @@ -3059,9 +3059,9 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.23': + '@aws-sdk/middleware-sdk-s3@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.24 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-arn-parser': 3.972.3 '@smithy/core': 3.23.12 @@ -3082,9 +3082,9 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.24': + '@aws-sdk/middleware-user-agent@3.972.25': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.24 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@smithy/core': 3.23.12 @@ -3093,20 +3093,20 @@ snapshots: '@smithy/util-retry': 4.2.12 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.13': + '@aws-sdk/nested-clients@3.996.14': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.24 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.24 + '@aws-sdk/middleware-user-agent': 3.972.25 '@aws-sdk/region-config-resolver': 3.972.9 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.10 + '@aws-sdk/util-user-agent-node': 3.973.11 '@smithy/config-resolver': 4.4.13 '@smithy/core': 3.23.12 '@smithy/fetch-http-handler': 5.3.15 @@ -3144,19 +3144,19 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.11': + '@aws-sdk/signature-v4-multi-region@3.996.12': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.23 + '@aws-sdk/middleware-sdk-s3': 3.972.24 '@aws-sdk/types': 3.973.6 '@smithy/protocol-http': 5.3.12 '@smithy/signature-v4': 5.3.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1014.0': + '@aws-sdk/token-providers@3.1015.0': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.24 + '@aws-sdk/nested-clients': 3.996.14 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -3193,9 +3193,9 @@ snapshots: bowser: 2.13.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.10': + '@aws-sdk/util-user-agent-node@3.973.11': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.24 + '@aws-sdk/middleware-user-agent': 3.972.25 '@aws-sdk/types': 3.973.6 '@smithy/node-config-provider': 4.3.12 '@smithy/types': 4.13.1 @@ -3464,7 +3464,7 @@ snapshots: '@kevisual/logger': 0.0.4 '@kevisual/permission': 0.0.4 '@kevisual/query': 0.0.53 - ai: 6.0.134(zod@4.3.6) + ai: 6.0.137(zod@4.3.6) zod: 4.3.6 '@kevisual/api@0.0.65(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': @@ -3491,7 +3491,7 @@ snapshots: '@kevisual/auth@2.0.3': {} - '@kevisual/cnb@0.0.59(dotenv@17.3.1)(idb-keyval@6.2.2)(ioredis@5.10.0)': + '@kevisual/cnb@0.0.60(dotenv@17.3.1)(idb-keyval@6.2.2)(ioredis@5.10.0)': dependencies: '@kevisual/query': 0.0.53 '@kevisual/router': 0.1.4 @@ -4061,9 +4061,9 @@ snapshots: transitivePeerDependencies: - supports-color - ai@6.0.134(zod@4.3.6): + ai@6.0.137(zod@4.3.6): dependencies: - '@ai-sdk/gateway': 3.0.77(zod@4.3.6) + '@ai-sdk/gateway': 3.0.79(zod@4.3.6) '@ai-sdk/provider': 3.0.8 '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) '@opentelemetry/api': 1.9.0 diff --git a/scripts/import-data.ts b/scripts/import-data.ts deleted file mode 100644 index d5ade2c..0000000 --- a/scripts/import-data.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * 导入 short-link JSON 数据到数据库 - * 运行: bun run scripts/import-data.ts - */ - -import { drizzle } from 'drizzle-orm/node-postgres'; -import { shortLink } from '@/db/schemas/n-code-schema.ts'; -import { useConfig } from '@kevisual/use-config'; -import { readFileSync } from 'node:fs'; -import { resolve } from 'node:path'; - -const config = useConfig() as any; -const DATABASE_URL = config.DATABASE_URL || process.env.DATABASE_URL || ''; -if (!DATABASE_URL) { - console.error('缺少 DATABASE_URL 配置'); - process.exit(1); -} - -const db = drizzle(DATABASE_URL); - -// 读取 JSON 数据 -const jsonPath = resolve(import.meta.dir, 'ncode-list.json'); -const rawData = JSON.parse(readFileSync(jsonPath, 'utf-8')) as Array<{ - code: string; - data: Record; - description: string; - slug: string; - tags: string[]; - title: string; - type: string; - userId: string; - version: string; -}>; - -async function importData() { - console.log(`准备导入 ${rawData.length} 条 short-link 数据...`); - - let inserted = 0; - let skipped = 0; - - const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - - for (const item of rawData) { - const userId = item.userId && uuidRegex.test(item.userId) ? item.userId : null; - - try { - await db - .insert(shortLink) - .values({ - slug: item.slug, - code: item.code, - type: item.type || 'link', - version: item.version || '1.0', - title: item.title || '', - description: item.description || '', - tags: item.tags ?? [], - data: item.data ?? {}, - userId: userId as any, - }) - .onConflictDoNothing(); - - console.log(` ✓ 导入: slug=${item.slug}, code=${item.code}, title=${item.title}`); - inserted++; - } catch (err: any) { - const cause = err.cause || err; - console.warn(` ✗ 跳过: slug=${item.slug}, code=${item.code} — ${cause.message || err.message}`); - skipped++; - } - } - - console.log(`\n完成: 成功 ${inserted} 条,跳过 ${skipped} 条`); - process.exit(0); -} - -importData().catch((err) => { - console.error('导入失败:', err); - process.exit(1); -}); diff --git a/scripts/import-life.ts b/scripts/import-life.ts deleted file mode 100644 index d89d8eb..0000000 --- a/scripts/import-life.ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * 导入 life JSON 数据到数据库 - * 运行: bun run scripts/import-life.ts - */ - -import { drizzle } from 'drizzle-orm/node-postgres'; -import { life } from '@/db/schemas/life-schema.ts'; -import { useConfig } from '@kevisual/use-config'; -import { readFileSync } from 'node:fs'; -import { resolve } from 'node:path'; - -const config = useConfig() as any; -const DATABASE_URL = config.DATABASE_URL || process.env.DATABASE_URL || ''; -if (!DATABASE_URL) { - console.error('缺少 DATABASE_URL 配置'); - process.exit(1); -} - -const db = drizzle(DATABASE_URL); - -// 读取 JSON 数据 -const jsonPath = resolve(import.meta.dir, 'life-list.json'); -const rawData = JSON.parse(readFileSync(jsonPath, 'utf-8')) as Array<{ - data: Record; - description: string; - effectiveAt: string; - link: string; - prompt: string; - summary: string; - tags: string[]; - taskResult: Record; - taskType: string; - title: string; - type: string; - updatedAt: string; - userId: string; -}>; - -async function importData() { - console.log(`准备导入 ${rawData.length} 条 flowme-life 数据...`); - - let inserted = 0; - let skipped = 0; - - for (const item of rawData) { - const uid = item.userId; - - try { - await db - .insert(life) - .values({ - title: item.title || '', - summary: item.summary || '', - description: item.description || '', - tags: item.tags ?? [], - link: item.link || '', - data: item.data ?? {}, - effectiveAt: item.effectiveAt || '', - type: item.type || '', - prompt: item.prompt || '', - taskType: item.taskType || '', - taskResult: item.taskResult ?? {}, - uid: uid as any, - }) - .onConflictDoNothing(); - - console.log(` ✓ 导入: title=${item.title}, type=${item.type}`); - inserted++; - } catch (err: any) { - const cause = err.cause || err; - console.warn(` ✗ 跳过: title=${item.title} — ${cause.message || err.message}`); - skipped++; - } - } - - console.log(`\n完成: 成功 ${inserted} 条,跳过 ${skipped} 条`); - process.exit(0); -} - -importData().catch((err) => { - console.error('导入失败:', err); - process.exit(1); -}); diff --git a/scripts/mv-resources.ts b/scripts/mv-resources.ts new file mode 100644 index 0000000..532c103 --- /dev/null +++ b/scripts/mv-resources.ts @@ -0,0 +1,106 @@ +import { oss } from '@/modules/s3.ts'; +import { db, schema } from '../src/app.ts'; +import { eq } from 'drizzle-orm'; +import { UserId } from '@/routes/user/modules/user-id.ts'; +import { mvUserAToUserB } from '@/routes/file/index.ts'; + + +// 迁移资源,原本的是 ${username}/appKey 改为 ${userId}/appKey + +// 第一步,迁移 表kv_app 和 kv_app_list, 对应的 data 中,把data 对应的 files 中path 去掉第一个前缀,比如 +// { +// "files": [ +// { +// "name": "README.md", +// "path": "root/code-center/0.0.6/README.md" +// }, +// 把 path 中的 root 去掉,变成 code-center/0.0.6/README.md +// ] +// } + +type Data = { + files: { + name: string; + path: string; + }[]; +} +const BATCH_SIZE = 1000; + +// 迁移 kv_app; +const firstMigration = async () => { + let offset = 0; + while (true) { + const kvAppList = await db.select().from(schema.kvApp).limit(BATCH_SIZE).offset(offset); + if (kvAppList.length === 0) break; + + for (const kvApp of kvAppList) { + const data = kvApp.data as Data; + const uid = kvApp.uid; + const username = await UserId.getUserNameById(uid); + if (!data.files) continue; + for (const file of data.files) { + // const pathParts = file.path.split('/'); + // pathParts.shift(); + // file.path = pathParts.join('/'); + file.path = username + '/' + file.path; + } + await db.update(schema.kvApp).set({ data: { ...data } }).where(eq(schema.kvApp.id, kvApp.id)); + } + + console.log(`Processed ${offset + kvAppList.length} records`); + offset += BATCH_SIZE; + } +} + +// 迁移 kv_app_list +const secondMigration = async () => { + let offset = 0; + while (true) { + const kvAppList = await db.select().from(schema.kvAppList).limit(BATCH_SIZE).offset(offset); + if (kvAppList.length === 0) break; + + for (const kvApp of kvAppList) { + const data = kvApp.data as Data; + const uid = kvApp.uid; + const username = await UserId.getUserNameById(uid); + if (!data.files) continue; + for (const file of data.files) { + // const pathParts = file.path.split('/'); + // pathParts.shift(); + // file.path = pathParts.join('/'); + file.path = username + '/' + file.path; + } + await db.update(schema.kvAppList).set({ data: { ...data } }).where(eq(schema.kvAppList.id, kvApp.id)); + } + + console.log(`Processed ${offset + kvAppList.length} records`); + offset += BATCH_SIZE; + } +} + +/** + * 迁移对象存储 + */ +const migrateOss = async () => { + const list = await oss.listObjects('') + const _names = list.filter(item => item.size === 0) + type Name = { + prefix: string; + size: 0; + id?: string; + } + const names: Name[] = _names as any; + for (const name of names) { + const username = name.prefix.split('/')[0]; + const id = await UserId.getUserIdByName(username); + if (id) { + name.id = id; + mvUserAToUserB(username, `data/${id}`, true); + } + } +} +// await firstMigration(); +// await secondMigration(); +await migrateOss(); + +console.log('Migration completed'); \ No newline at end of file diff --git a/scripts/ncode-list.json b/scripts/ncode-list.json deleted file mode 100644 index fe49422..0000000 --- a/scripts/ncode-list.json +++ /dev/null @@ -1,104 +0,0 @@ -[ - { - "code": "anwjgg", - "data": { - "link": "https://kevisual.cn/root/name-card/" - }, - "description": "这是一个测试码", - "slug": "pmzsq4gp4g", - "tags": [], - "title": "测试码", - "type": "link", - "userId": "0e700dc8-90dd-41b7-91dd-336ea51de3d2", - "version": "1.0" - }, - { - "code": "0gmn12", - "data": { - "link": "https://kevisual.cn/root/v1/ha-api?path=ha&key=open-balcony-light", - "permission": { - "share": "public" - }, - "useOwnerToken": true - }, - "description": "test 阳台灯", - "slug": "3z1nbdogew", - "tags": [], - "title": "阳台灯", - "type": "link", - "userId": "0e700dc8-90dd-41b7-91dd-336ea51de3d2", - "version": "1.0" - }, - { - "code": "abc111", - "data": { - "link": "https://kevisual.cn/root/nfc/", - "permission": { - "share": "public" - } - }, - "description": "nfc link", - "slug": "0000000001", - "tags": [], - "title": "nfc link", - "type": "link", - "userId": "", - "version": "1.0" - }, - { - "code": "ej73jm", - "data": { - "link": "https://kevisual.cn/root/nfc/", - "permission": { - "share": "public" - } - }, - "description": "nfc link", - "slug": "001", - "tags": [], - "title": "nfc link", - "type": "link", - "userId": "", - "version": "1.0" - }, - { - "code": "09dd42", - "data": { - "details": [ - { - "description": "算法基于七卡瓦,自建", - "title": "beads(kevisual.cn)", - "url": "https://kevisual.cn/root/beads/" - }, - { - "description": "算法很不错,图片转图纸很好", - "title": "拼豆七卡瓦(zippland.com)", - "url": "https://perlerbeads.zippland.com/" - }, - { - "description": "功能偏向PS,画板类,像素类", - "title": "拼豆像素格子(zwpyyds.com)", - "url": "https://www.zwpyyds.com/" - }, - { - "description": "编辑不错,拼豆社区", - "title": "我嘞个豆(ohmybead.cn)", - "url": "https://ohmybead.cn/" - } - ], - "link": "https://kevisual.cn/root/nfc/way/", - "permission": { - "share": "public" - }, - "title": "拼豆图纸自取方案", - "type": "html-render" - }, - "description": "Pindou", - "slug": "pindou", - "tags": [], - "title": "Pindou", - "type": "link", - "userId": "", - "version": "1.0" - } -] \ No newline at end of file diff --git a/src/aura/libs/auc.ts b/src/aura/libs/auc.ts index b6573ef..296782a 100644 --- a/src/aura/libs/auc.ts +++ b/src/aura/libs/auc.ts @@ -29,7 +29,7 @@ export class Asr { this.url = AsrBaseURL } if (!this.appid || !this.token) { - throw new Error("VOLCENGINE_Asr_APPID or VOLCENGINE_Asr_TOKEN is not set") + console.error("VOLCENGINE_ASR_APPID or VOLCENGINE_ASR_TOKEN is not set") } } diff --git a/src/modules/user-app/index.ts b/src/modules/user-app/index.ts index eac4eab..66cd1f2 100644 --- a/src/modules/user-app/index.ts +++ b/src/modules/user-app/index.ts @@ -10,6 +10,7 @@ import { getAppLoadStatus, setAppLoadStatus } from './get-app-status.ts'; import { minioResources } from '../s3.ts'; import { downloadFileFromMinio, fetchApp, fetchDomain, fetchTest } from '@/modules/fm-manager/index.ts'; import { logger } from '../logger.ts'; +import { UserId } from '@/routes/user/modules/user-id.ts'; export * from './get-app-status.ts'; export * from './user-home.ts'; @@ -20,7 +21,7 @@ const wrapperResources = (resources: string, urlpath: string) => { if (urlpath.startsWith('http')) { return urlpath; } - return `${resources}/${urlpath}`; + return `${resources}/data/${urlpath}`; }; const demoData = { user: 'root', @@ -221,17 +222,19 @@ export class UserApp { const value = fetchData; await redis.set(key, JSON.stringify(value)); const version = value.version; - let indexHtml = resources + '/' + user + '/' + app + '/' + version + '/index.html'; + const uid = await UserId.getUserIdByName(user); + let indexHtml = resources + 'data/' + uid + '/' + app + '/' + version + '/index.html'; const files = value?.data?.files || []; const permission = value?.data?.permission || { share: 'private' }; const data = {}; // 将文件名和路径添加到 `data` 对象中 files.forEach((file) => { + const noUserPath = file.path.startsWith(`${user}/`) ? file.path.replace(`${user}/`, '') : file.path; if (file.name === 'index.html') { - indexHtml = wrapperResources(resources, file.path); + indexHtml = wrapperResources(resources, noUserPath); } - data[file.name] = wrapperResources(resources, file.path); + data[file.name] = wrapperResources(resources, noUserPath); }); await redis.set('user:app:exist:' + app + ':' + user, indexHtml + '||etag||true', 'EX', 60 * 60 * 24 * 7); // 7天 await redis.set('user:app:permission:' + app + ':' + user, JSON.stringify(permission), 'EX', 60 * 60 * 24 * 7); // 7天 diff --git a/src/routes/app-manager/list.ts b/src/routes/app-manager/list.ts index 33c4c5b..84d7354 100644 --- a/src/routes/app-manager/list.ts +++ b/src/routes/app-manager/list.ts @@ -464,7 +464,7 @@ app data: z.object({ id: z.string().optional(), key: z.string().optional(), - version: z.string().optional(), + user: z.string().optional().describe('用户名,默认为当前用户'), }) } } diff --git a/src/routes/user/list.ts b/src/routes/user/list.ts index 1cca469..c76e268 100644 --- a/src/routes/user/list.ts +++ b/src/routes/user/list.ts @@ -4,6 +4,8 @@ import { CustomError } from '@kevisual/router'; import { checkUsername } from './admin/user.ts'; import { nanoid } from 'nanoid'; import { sql } from 'drizzle-orm'; +import z from 'zod'; +import { UserId } from './modules/user-id.ts'; app .route({ @@ -91,3 +93,27 @@ app }; }) .addTo(app); + +app.route({ + path: 'user', + key: 'uid', + description: '根据用户名获取用户ID', + middleware: ['auth'], + metadata: { + args: { + username: z.string().describe('username is required'), + } + } +}).define(async (ctx) => { + const { username } = ctx.query || {}; + if (!username) { + ctx.throw(400, { message: 'username is required' }); + } + const userId = await UserId.getUserIdByName(username); + if (!userId) { + ctx.throw(404, { message: 'user not found' }); + } + ctx.body = { + id: userId, + }; +}).addTo(app); \ No newline at end of file diff --git a/src/routes/user/modules/user-id.ts b/src/routes/user/modules/user-id.ts new file mode 100644 index 0000000..381a65a --- /dev/null +++ b/src/routes/user/modules/user-id.ts @@ -0,0 +1,41 @@ +import { eq } from 'drizzle-orm'; +import { db, redis, schema } from '@/app.ts'; + +export class UserId { + static async getUserIdByName(username: string): Promise { + const cacheKey = `user:id:${username}`; + let userId = await redis.get(cacheKey); + if (!userId) { + const user = await db + .select({ id: schema.cfUser.id }) + .from(schema.cfUser) + .where(eq(schema.cfUser.username, username)) + .limit(1); + if (user.length > 0) { + userId = user[0].id; + await redis.set(cacheKey, userId, 'EX', 3600); // 缓存1小时 + } else { + return null; + } + } + return userId; + } + static async getUserNameById(userId: string): Promise { + const cacheKey = `user:name:${userId}`; + let username = await redis.get(cacheKey); + if (!username) { + const user = await db + .select({ username: schema.cfUser.username }) + .from(schema.cfUser) + .where(eq(schema.cfUser.id, userId)) + .limit(1); + if (user.length > 0) { + username = user[0].username; + await redis.set(cacheKey, username, 'EX', 3600); // 缓存1小时 + } else { + return null; + } + } + return username; + } +} \ No newline at end of file