diff --git a/.env b/.env index a2936b2..ef625ab 100644 --- a/.env +++ b/.env @@ -3,4 +3,12 @@ SERVER_HOST='192.144.32.178' SERVER_PORT=3306 SERVER_USER='root' PASSWORD='lichao1314' -DB_NAME='auth_db' \ No newline at end of file +DB_NAME='auth_db' + +GITHUB_CLIENT_ID=Ov23lihk723FlNAwlFg6 +GITHUB_CLIENT_SECRET=b839f50bba1f006ffdd43fb73c5ae221a54e1e2e +GITHUB_CALLBACK_URL=http://localhost:3030/auth/github/callback + + +FEISHU_APP_ID=cli_a66a897f687a5013 +FEISHU_APP_SECRET=s106GAbbCZk66OcHN69Rng5TaLK6fiH2 diff --git a/package.json b/package.json index 3c4ccf2..9626eb5 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,16 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.2", "@types/bcrypt": "^5.0.2", + "@types/passport-github2": "^1.2.9", "@types/uuid": "^10.0.0", + "axios": "^1.7.9", "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", + "dotenv": "^16.4.7", "install": "^0.13.0", "mysql2": "^3.12.0", "passport": "^0.7.0", + "passport-github2": "^0.1.12", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ab8ba2..94babe3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,13 +16,16 @@ specifiers: '@types/express': ^4.17.17 '@types/jest': ^29.5.2 '@types/node': ^20.3.1 + '@types/passport-github2': ^1.2.9 '@types/passport-jwt': ^4.0.1 '@types/supertest': ^6.0.0 '@types/uuid': ^10.0.0 '@typescript-eslint/eslint-plugin': ^6.0.0 '@typescript-eslint/parser': ^6.0.0 + axios: ^1.7.9 bcrypt: ^5.1.1 bcryptjs: ^2.4.3 + dotenv: ^16.4.7 eslint: ^8.42.0 eslint-config-prettier: ^9.0.0 eslint-plugin-prettier: ^5.0.0 @@ -30,6 +33,7 @@ specifiers: jest: ^29.5.0 mysql2: ^3.12.0 passport: ^0.7.0 + passport-github2: ^0.1.12 passport-jwt: ^4.0.1 passport-local: ^1.0.0 prettier: ^3.0.0 @@ -55,12 +59,16 @@ dependencies: '@nestjs/platform-express': 10.4.15_5u4hn6whjn5aawl2edmluzh4i4 '@nestjs/typeorm': 10.0.2_5ay6scu5luhdsc23ie3iqrw3sm '@types/bcrypt': 5.0.2 + '@types/passport-github2': 1.2.9 '@types/uuid': 10.0.0 + axios: 1.7.9 bcrypt: 5.1.1 bcryptjs: 2.4.3 + dotenv: 16.4.7 install: 0.13.0 mysql2: 3.12.0 passport: 0.7.0 + passport-github2: 0.1.12 passport-jwt: 4.0.1 passport-local: 1.0.0 reflect-metadata: 0.1.14 @@ -1210,13 +1218,11 @@ packages: dependencies: '@types/connect': 3.4.38 '@types/node': 20.17.12 - dev: true /@types/connect/3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: '@types/node': 20.17.12 - dev: true /@types/cookiejar/2.1.5: resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} @@ -1247,7 +1253,6 @@ packages: '@types/qs': 6.9.17 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - dev: true /@types/express/4.17.21: resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} @@ -1256,7 +1261,6 @@ packages: '@types/express-serve-static-core': 4.19.6 '@types/qs': 6.9.17 '@types/serve-static': 1.15.7 - dev: true /@types/graceful-fs/4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -1266,7 +1270,6 @@ packages: /@types/http-errors/2.0.4: resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - dev: true /@types/istanbul-lib-coverage/2.0.6: resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -1306,13 +1309,26 @@ packages: /@types/mime/1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - dev: true /@types/node/20.17.12: resolution: {integrity: sha512-vo/wmBgMIiEA23A/knMfn/cf37VnuF52nZh5ZoW0GWt4e4sxNquibrMRJ7UQsA06+MBx9r/H1jsI9grYjQCQlw==} dependencies: undici-types: 6.19.8 + /@types/oauth/0.9.6: + resolution: {integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==} + dependencies: + '@types/node': 20.17.12 + dev: false + + /@types/passport-github2/1.2.9: + resolution: {integrity: sha512-/nMfiPK2E6GKttwBzwj0Wjaot8eHrM57hnWxu52o6becr5/kXlH/4yE2v2rh234WGvSgEEzIII02Nc5oC5xEHA==} + dependencies: + '@types/express': 4.17.21 + '@types/passport': 1.0.17 + '@types/passport-oauth2': 1.4.17 + dev: false + /@types/passport-jwt/4.0.1: resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==} dependencies: @@ -1320,6 +1336,14 @@ packages: '@types/passport-strategy': 0.2.38 dev: true + /@types/passport-oauth2/1.4.17: + resolution: {integrity: sha512-ODiAHvso6JcWJ6ZkHHroVp05EHGhqQN533PtFNBkg8Fy5mERDqsr030AX81M0D69ZcaMvhF92SRckEk2B0HYYg==} + dependencies: + '@types/express': 4.17.21 + '@types/oauth': 0.9.6 + '@types/passport': 1.0.17 + dev: false + /@types/passport-strategy/0.2.38: resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==} dependencies: @@ -1331,15 +1355,12 @@ packages: resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==} dependencies: '@types/express': 4.17.21 - dev: true /@types/qs/6.9.17: resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} - dev: true /@types/range-parser/1.2.7: resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - dev: true /@types/semver/7.5.8: resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} @@ -1350,7 +1371,6 @@ packages: dependencies: '@types/mime': 1.3.5 '@types/node': 20.17.12 - dev: true /@types/serve-static/1.15.7: resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} @@ -1358,7 +1378,6 @@ packages: '@types/http-errors': 2.0.4 '@types/node': 20.17.12 '@types/send': 0.17.4 - dev: true /@types/stack-utils/2.0.3: resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -1839,13 +1858,22 @@ packages: /asynckit/0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: true /aws-ssl-profiles/1.1.2: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} dev: false + /axios/1.7.9: + resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.1 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + /babel-jest/29.7.0_@babel+core@7.26.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1927,6 +1955,11 @@ packages: /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /base64url/3.0.1: + resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} + engines: {node: '>=6.0.0'} + dev: false + /bcrypt/5.1.1: resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} engines: {node: '>= 10.0.0'} @@ -2239,7 +2272,6 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - dev: true /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2426,7 +2458,6 @@ packages: /delayed-stream/1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dev: true /delegates/1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -2951,6 +2982,16 @@ packages: resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} dev: true + /follow-redirects/1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + /foreground-child/3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} @@ -2988,7 +3029,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: true /formidable/2.1.2: resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} @@ -4438,6 +4478,10 @@ packages: set-blocking: 2.0.0 dev: false + /oauth/0.10.0: + resolution: {integrity: sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==} + dev: false + /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -4567,6 +4611,13 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + /passport-github2/0.1.12: + resolution: {integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw==} + engines: {node: '>= 0.8.0'} + dependencies: + passport-oauth2: 1.8.0 + dev: false + /passport-jwt/4.0.1: resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==} dependencies: @@ -4581,6 +4632,17 @@ packages: passport-strategy: 1.0.0 dev: false + /passport-oauth2/1.8.0: + resolution: {integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==} + engines: {node: '>= 0.4.0'} + dependencies: + base64url: 3.0.1 + oauth: 0.10.0 + passport-strategy: 1.0.0 + uid2: 0.0.4 + utils-merge: 1.0.1 + dev: false + /passport-strategy/1.0.0: resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} engines: {node: '>= 0.4.0'} @@ -4710,6 +4772,10 @@ packages: forwarded: 0.2.0 ipaddr.js: 1.9.1 + /proxy-from-env/1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + /punycode/2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -5567,6 +5633,10 @@ packages: dependencies: '@lukeed/csprng': 1.1.0 + /uid2/0.0.4: + resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} + dev: false + /undici-types/6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts deleted file mode 100644 index d22f389..0000000 --- a/src/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe('root', () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); -}); diff --git a/src/app.module.ts b/src/app.module.ts index 0712b57..4b97128 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -5,6 +5,8 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { AppService } from './app.service'; import { UserModule } from './user/user.module'; import { User } from './user/entities/user.entity'; +import { AuthModule } from './auth/auth.module'; +import { LarkModule } from './lark/lark.module'; @Module({ imports: [ @@ -25,6 +27,8 @@ import { User } from './user/entities/user.entity'; }), TypeOrmModule.forFeature([User]), UserModule, + AuthModule, + LarkModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts new file mode 100644 index 0000000..d3d4542 --- /dev/null +++ b/src/auth/auth.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; +import { Response } from 'express'; + +@Controller('auth') +export class AuthController { + @Get('github') + @UseGuards(AuthGuard('github')) + async githubLogin() { + // GitHub 登录重定向 + } + + @Get('github/callback') + @UseGuards(AuthGuard('github')) + async githubCallback(@Req() req, @Res() res: Response) { + const user = req.user; + + // 构造前端 URL,附带用户信息或 Token + const frontendUrl = `http://localhost:8000/?username=${user.username}&email=${user.email}`; + // 重定向到前端 + return res.redirect(frontendUrl); + } +} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts new file mode 100644 index 0000000..a484e06 --- /dev/null +++ b/src/auth/auth.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; +import { AuthController } from './auth.controller'; +import { GitHubStrategy } from './github.strategy'; + +@Module({ + imports: [PassportModule], + controllers: [AuthController], + providers: [GitHubStrategy], +}) +export class AuthModule {} diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts new file mode 100644 index 0000000..a41c649 --- /dev/null +++ b/src/auth/auth.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AuthService {} diff --git a/src/auth/github.strategy.ts b/src/auth/github.strategy.ts new file mode 100644 index 0000000..7a58392 --- /dev/null +++ b/src/auth/github.strategy.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Strategy } from 'passport-github2'; + +@Injectable() +export class GitHubStrategy extends PassportStrategy(Strategy, 'github') { + constructor() { + super({ + clientID: process.env.GITHUB_CLIENT_ID, // 从环境变量加载 + clientSecret: process.env.GITHUB_CLIENT_SECRET, + callbackURL: process.env.GITHUB_CALLBACK_URL, // 回调 URL + scope: ['user:email'], // 需要的权限 + }); + } + + async validate(accessToken: string, refreshToken: string, profile: any) { + // 返回用户信息,或者直接处理用户登录逻辑 + const { id, username, emails } = profile; + return { + id, + username, + email: emails?.[0]?.value || null, + }; + } +} diff --git a/src/lark/lark.controller.ts b/src/lark/lark.controller.ts new file mode 100644 index 0000000..6f9dc8f --- /dev/null +++ b/src/lark/lark.controller.ts @@ -0,0 +1,30 @@ +import { Controller, Get, Query, Res } from '@nestjs/common'; +import { LarkService } from './lark.service'; +import { Response } from 'express'; + +@Controller('lark') +export class LarkController { + constructor(private readonly larkService: LarkService) {} + + @Get('login') + async login(@Query('redirectUri') redirectUri: string, @Res() res: Response) { + const loginUrl = await this.larkService.getLoginUrl(redirectUri); + return res.redirect(loginUrl); + } + + @Get('callback') + async callback(@Query('code') code: string, @Res() res: Response) { + if (!code) { + return res.status(400).send('Authorization code is missing'); + } + + const tokenData = await this.larkService.getAccessToken(code); + + if (tokenData && tokenData.code === 0) { + const user = tokenData.data; + return res.json({ message: 'Login successful', user }); + } else { + return res.status(400).send('Failed to retrieve access token'); + } + } +} diff --git a/src/lark/lark.module.ts b/src/lark/lark.module.ts new file mode 100644 index 0000000..f1f0deb --- /dev/null +++ b/src/lark/lark.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { LarkService } from './lark.service'; +import { LarkController } from './lark.controller'; + +@Module({ + controllers: [LarkController], + providers: [LarkService], +}) +export class LarkModule {} diff --git a/src/lark/lark.service.ts b/src/lark/lark.service.ts new file mode 100644 index 0000000..c990158 --- /dev/null +++ b/src/lark/lark.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; + +@Injectable() +export class LarkService { + private readonly appId = process.env.FEISHU_APP_ID; + private readonly appSecret = process.env.FEISHU_APP_SECRET; + + async getLoginUrl(redirectUri: string): Promise { + return `https://open.feishu.cn/open-apis/authen/v1/index?app_id=${this.appId}&redirect_uri=${encodeURIComponent( + redirectUri, + )}`; + } + + async getAccessToken(authCode: string): Promise { + const url = 'https://open.feishu.cn/open-apis/authen/v1/access_token'; + const data = { + grant_type: 'authorization_code', + code: authCode, + app_id: this.appId, + app_secret: this.appSecret, + }; + + const response = await axios.post(url, data); + return response.data; + } +} diff --git a/src/main.ts b/src/main.ts index 34f1701..124a695 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,6 +2,9 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as mysql from 'mysql2/promise'; import { ConfigService } from '@nestjs/config'; +import * as dotenv from 'dotenv'; + +dotenv.config(); async function bootstrap() { // 启动 Nest 应用 diff --git a/src/user/user.controller.spec.ts b/src/user/user.controller.spec.ts deleted file mode 100644 index 1f38440..0000000 --- a/src/user/user.controller.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { UserController } from './user.controller'; -import { UserService } from './user.service'; - -describe('UserController', () => { - let controller: UserController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [UserController], - providers: [UserService], - }).compile(); - - controller = module.get(UserController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -});