diff --git a/package.json b/package.json index 9626eb5..9cff2db 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@nestjs/cache-manager": "2.1.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.0.0", @@ -27,6 +28,7 @@ "@nestjs/mapped-types": "*", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", + "@nestjs/swagger": "^11.0.2", "@nestjs/typeorm": "^10.0.2", "@types/bcrypt": "^5.0.2", "@types/passport-github2": "^1.2.9", @@ -34,6 +36,7 @@ "axios": "^1.7.9", "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", + "cache-manager": "4.1.0", "dotenv": "^16.4.7", "install": "^0.13.0", "mysql2": "^3.12.0", @@ -43,6 +46,7 @@ "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "swagger-ui-express": "^5.0.1", "typeorm": "^0.3.20", "uuid": "^11.0.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94babe3..9e691a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,7 @@ lockfileVersion: 5.4 specifiers: + '@nestjs/cache-manager': 2.1.0 '@nestjs/cli': ^10.0.0 '@nestjs/common': ^10.0.0 '@nestjs/config': ^3.3.0 @@ -10,6 +11,7 @@ specifiers: '@nestjs/passport': ^10.0.3 '@nestjs/platform-express': ^10.0.0 '@nestjs/schematics': ^10.0.0 + '@nestjs/swagger': ^11.0.2 '@nestjs/testing': ^10.0.0 '@nestjs/typeorm': ^10.0.2 '@types/bcrypt': ^5.0.2 @@ -25,6 +27,7 @@ specifiers: axios: ^1.7.9 bcrypt: ^5.1.1 bcryptjs: ^2.4.3 + cache-manager: 4.1.0 dotenv: ^16.4.7 eslint: ^8.42.0 eslint-config-prettier: ^9.0.0 @@ -41,6 +44,7 @@ specifiers: rxjs: ^7.8.1 source-map-support: ^0.5.21 supertest: ^6.3.3 + swagger-ui-express: ^5.0.1 ts-jest: ^29.1.0 ts-loader: ^9.4.3 ts-node: ^10.9.1 @@ -50,6 +54,7 @@ specifiers: uuid: ^11.0.4 dependencies: + '@nestjs/cache-manager': 2.1.0_inm4aznx37zugyc3b7aqzqhqfi '@nestjs/common': 10.4.15_rcbhqa4so6dtcde4mhxkgzk6je '@nestjs/config': 3.3.0_4ts62un553jxr7h6yhonkwt3ou '@nestjs/core': 10.4.15_akwwzhtnoftcbx5g7sbkce2laq @@ -57,6 +62,7 @@ dependencies: '@nestjs/mapped-types': 2.0.6_lhexpmbaszs2uqyaxe2vaiymm4 '@nestjs/passport': 10.0.3_zkygu43hvfc54d4x4wrnlrmh5q '@nestjs/platform-express': 10.4.15_5u4hn6whjn5aawl2edmluzh4i4 + '@nestjs/swagger': 11.0.2_f2iyopv64iutag35mprrixece4 '@nestjs/typeorm': 10.0.2_5ay6scu5luhdsc23ie3iqrw3sm '@types/bcrypt': 5.0.2 '@types/passport-github2': 1.2.9 @@ -64,6 +70,7 @@ dependencies: axios: 1.7.9 bcrypt: 5.1.1 bcryptjs: 2.4.3 + cache-manager: 4.1.0 dotenv: 16.4.7 install: 0.13.0 mysql2: 3.12.0 @@ -73,6 +80,7 @@ dependencies: passport-local: 1.0.0 reflect-metadata: 0.1.14 rxjs: 7.8.1 + swagger-ui-express: 5.0.1 typeorm: 0.3.20_35miekcjczcapbnupftunbhdjm uuid: 11.0.4 @@ -883,6 +891,26 @@ packages: - supports-color dev: false + /@microsoft/tsdoc/0.15.0: + resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + dev: false + + /@nestjs/cache-manager/2.1.0_inm4aznx37zugyc3b7aqzqhqfi: + resolution: {integrity: sha512-9kep3a8Mq5cMuXN/anGhSYc0P48CRBXk5wyJJRBFxhNkCH8AIzZF4CASGVDIEMmm3OjVcEUHojjyJwCODS17Qw==} + peerDependencies: + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + cache-manager: <=5 + reflect-metadata: ^0.1.12 + rxjs: ^7.0.0 + dependencies: + '@nestjs/common': 10.4.15_rcbhqa4so6dtcde4mhxkgzk6je + '@nestjs/core': 10.4.15_akwwzhtnoftcbx5g7sbkce2laq + cache-manager: 4.1.0 + reflect-metadata: 0.1.14 + rxjs: 7.8.1 + dev: false + /@nestjs/cli/10.4.9: resolution: {integrity: sha512-s8qYd97bggqeK7Op3iD49X2MpFtW4LVNLAwXFkfbRxKME6IYT7X0muNTJ2+QfI8hpbNx9isWkrLWIp+g5FOhiA==} engines: {node: '>= 16.14'} @@ -1011,6 +1039,23 @@ packages: reflect-metadata: 0.1.14 dev: false + /@nestjs/mapped-types/2.1.0_lhexpmbaszs2uqyaxe2vaiymm4: + resolution: {integrity: sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + class-transformer: ^0.4.0 || ^0.5.0 + class-validator: ^0.13.0 || ^0.14.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + '@nestjs/common': 10.4.15_rcbhqa4so6dtcde4mhxkgzk6je + reflect-metadata: 0.1.14 + dev: false + /@nestjs/passport/10.0.3_zkygu43hvfc54d4x4wrnlrmh5q: resolution: {integrity: sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==} peerDependencies: @@ -1067,6 +1112,34 @@ packages: - chokidar dev: true + /@nestjs/swagger/11.0.2_f2iyopv64iutag35mprrixece4: + resolution: {integrity: sha512-zuV7n/Oc1X4gsl3uEnYC7xCrMgB8s4hglu6hw4SKDeFiLg3y4ZywkbYNDeyY+PRjeZCB8oDLwzO+WmQjefwbHQ==} + peerDependencies: + '@fastify/static': ^8.0.0 + '@nestjs/common': ^11.0.1 + '@nestjs/core': ^11.0.1 + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + '@fastify/static': + optional: true + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + '@microsoft/tsdoc': 0.15.0 + '@nestjs/common': 10.4.15_rcbhqa4so6dtcde4mhxkgzk6je + '@nestjs/core': 10.4.15_akwwzhtnoftcbx5g7sbkce2laq + '@nestjs/mapped-types': 2.1.0_lhexpmbaszs2uqyaxe2vaiymm4 + js-yaml: 4.1.0 + lodash: 4.17.21 + path-to-regexp: 8.2.0 + reflect-metadata: 0.1.14 + swagger-ui-dist: 5.18.2 + dev: false + /@nestjs/testing/10.4.15_ulskiam2rhl5zwwkbzxlbea4si: resolution: {integrity: sha512-eGlWESkACMKti+iZk1hs6FUY/UqObmMaa8HAN9JLnaYkoLf1Jeh+EuHlGnfqo/Rq77oznNLIyaA3PFjrFDlNUg==} peerDependencies: @@ -1146,6 +1219,11 @@ packages: engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} dev: true + /@scarf/scarf/1.4.0: + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + requiresBuild: true + dev: false + /@sinclair/typebox/0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true @@ -1834,7 +1912,6 @@ packages: /argparse/2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /array-flatten/1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} @@ -1852,6 +1929,10 @@ packages: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} dev: true + /async/3.2.3: + resolution: {integrity: sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==} + dev: false + /async/3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} dev: true @@ -2081,6 +2162,14 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + /cache-manager/4.1.0: + resolution: {integrity: sha512-ZGM6dLxrP65bfOZmcviWMadUOCICqpLs92+P/S5tj8onz+k+tB7Gr+SAgOUHCQtfm2gYEQDHiKeul4+tYPOJ8A==} + dependencies: + async: 3.2.3 + lodash.clonedeep: 4.5.0 + lru-cache: 7.18.3 + dev: false + /call-bind-apply-helpers/1.0.1: resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} engines: {node: '>= 0.4'} @@ -3983,7 +4072,6 @@ packages: hasBin: true dependencies: argparse: 2.0.1 - dev: true /jsesc/3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} @@ -4111,6 +4199,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash.clonedeep/4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: false + /lodash.includes/4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} dev: false @@ -4687,6 +4779,11 @@ packages: /path-to-regexp/3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + /path-to-regexp/8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + dev: false + /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5273,6 +5370,21 @@ packages: engines: {node: '>= 0.4'} dev: true + /swagger-ui-dist/5.18.2: + resolution: {integrity: sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==} + dependencies: + '@scarf/scarf': 1.4.0 + dev: false + + /swagger-ui-express/5.0.1: + resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==} + engines: {node: '>= v0.10.32'} + peerDependencies: + express: '>=4.0.0 || >=5.0.0-beta' + dependencies: + swagger-ui-dist: 5.18.2 + dev: false + /symbol-observable/4.0.0: resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} engines: {node: '>=0.10'} diff --git a/src/app.module.ts b/src/app.module.ts index 4b97128..011e92a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { CacheModule } from '@nestjs/cache-manager'; import { AppController } from './app.controller'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { AppService } from './app.service'; @@ -11,13 +12,17 @@ import { LarkModule } from './lark/lark.module'; @Module({ imports: [ ConfigModule.forRoot(), + CacheModule.register({ + isGlobal: true, + store: 'memory', + }), TypeOrmModule.forRootAsync({ imports: [ConfigModule], useFactory: (configService: ConfigService) => ({ type: 'mysql', host: configService.get('SERVER_HOST'), port: configService.get('SERVER_PORT'), - username: 'root', + username: configService.get('SERVER_USER'), password: configService.get('PASSWORD'), database: 'auth_db', entities: [User], diff --git a/src/lark/lark.service.ts b/src/lark/lark.service.ts index c990158..43aaf25 100644 --- a/src/lark/lark.service.ts +++ b/src/lark/lark.service.ts @@ -1,8 +1,11 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Inject } from '@nestjs/common'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Cache } from 'cache-manager'; import axios from 'axios'; @Injectable() export class LarkService { + constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {} private readonly appId = process.env.FEISHU_APP_ID; private readonly appSecret = process.env.FEISHU_APP_SECRET; @@ -13,6 +16,10 @@ export class LarkService { } async getAccessToken(authCode: string): Promise { + const cachedToken = await this.cacheManager.get('tokenData'); + if (cachedToken) { + return cachedToken; // 如果缓存中有 token,直接返回 + } const url = 'https://open.feishu.cn/open-apis/authen/v1/access_token'; const data = { grant_type: 'authorization_code', @@ -22,6 +29,13 @@ export class LarkService { }; const response = await axios.post(url, data); - return response.data; + const tokenData = response.data; + if (tokenData && tokenData.code === 0) { + // 将 token 存储到缓存中,设置过期时间 + await this.cacheManager.set('tokenData', tokenData, 3600); // 缓存1小时 + return tokenData; + } else { + throw new Error('获取 token 失败'); + } } }