diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 71ca3c0..7a4988f 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -5,17 +5,10 @@ import { TokenService } from '@/modules/token/token.service' import { UserRepository } from '@/shared/utils/repositories' import { AuthController } from './auth.controller' -import { AuthRepository } from './auth.repository' import { AuthService } from './auth.service' @Module({ controllers: [AuthController], - providers: [ - AuthService, - AuthRepository, - UserRepository, - OtpService, - TokenService - ] + providers: [AuthService, UserRepository, OtpService, TokenService] }) export class AuthModule {} diff --git a/src/modules/auth/auth.repository.ts b/src/modules/auth/auth.repository.ts deleted file mode 100644 index 7cb7e02..0000000 --- a/src/modules/auth/auth.repository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable } from '@nestjs/common' -import { AccountCreateInput } from '@prisma/generated/models/Account' - -import { PrismaService } from '@/infra/prisma/prisma.service' - -@Injectable() -export class AuthRepository { - public constructor(private readonly prismaService: PrismaService) {} - - public createAccount(data: AccountCreateInput) { - return this.prismaService.account.create({ data }) - } -} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index a07addb..edd62b1 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -16,12 +16,9 @@ import { UserRepository } from '@/shared/utils/repositories' import { TokenService } from '../token/token.service' -import { AuthRepository } from './auth.repository' - @Injectable() export class AuthService { public constructor( - private readonly authRepository: AuthRepository, private readonly userRepository: UserRepository, private readonly otpService: OtpService, private readonly tokenService: TokenService @@ -41,7 +38,7 @@ export class AuthService { } if (!account) { - account = await this.authRepository.createAccount({ + account = await this.userRepository.createAccount({ email: isEmailType ? identifier : undefined, phone: isPhoneType ? identifier : undefined }) diff --git a/src/modules/telegram/telegram.controller.ts b/src/modules/telegram/telegram.controller.ts index 13cd91e..e4996ab 100644 --- a/src/modules/telegram/telegram.controller.ts +++ b/src/modules/telegram/telegram.controller.ts @@ -1,6 +1,10 @@ import { Controller } from '@nestjs/common' import { GrpcMethod } from '@nestjs/microservices' import type { + TelegramCompleteRequest, + TelegramCompleteResponse, + TelegramConsumeRequest, + TelegramConsumeResponse, TelegramInitResponse, TelegramVerifyRequest, TelegramVerifyResponse @@ -21,4 +25,18 @@ export class TelegramController { public verify(data: TelegramVerifyRequest): Promise { return this.telegramService.verify(data) } + + @GrpcMethod('AuthService', 'TelegramComplete') + public complete( + data: TelegramCompleteRequest + ): Promise { + return this.telegramService.complete(data) + } + + @GrpcMethod('AuthService', 'TelegramConsume') + public consumeSession( + data: TelegramConsumeRequest + ): Promise { + return this.telegramService.consumeSession(data) + } } diff --git a/src/modules/telegram/telegram.module.ts b/src/modules/telegram/telegram.module.ts index 09e9a3e..818683c 100644 --- a/src/modules/telegram/telegram.module.ts +++ b/src/modules/telegram/telegram.module.ts @@ -2,12 +2,13 @@ import { Module } from '@nestjs/common' import { TelegramRepository } from '@/modules/telegram/telegram.repository' import { TokenService } from '@/modules/token/token.service' +import { UserRepository } from '@/shared/utils/repositories' import { TelegramController } from './telegram.controller' import { TelegramService } from './telegram.service' @Module({ controllers: [TelegramController], - providers: [TelegramService, TelegramRepository, TokenService] + providers: [TelegramService, TelegramRepository, TokenService, UserRepository] }) export class TelegramModule {} diff --git a/src/modules/telegram/telegram.service.ts b/src/modules/telegram/telegram.service.ts index 2cd28a4..1e1ca8e 100644 --- a/src/modules/telegram/telegram.service.ts +++ b/src/modules/telegram/telegram.service.ts @@ -3,6 +3,10 @@ import { ConfigService } from '@nestjs/config' import { RpcException } from '@nestjs/microservices' import { RpcStatus } from '@teacinema/common' import { + TelegramCompleteRequest, + TelegramCompleteResponse, + TelegramConsumeRequest, + TelegramConsumeResponse, TelegramInitResponse, TelegramVerifyRequest } from '@teacinema/contracts/gen/auth' @@ -11,6 +15,7 @@ import { createHash, createHmac, randomBytes } from 'node:crypto' import { AllConfigs } from '@/config' import { RedisService } from '@/infra/redis/redis.service' import { TokenService } from '@/modules/token/token.service' +import { UserRepository } from '@/shared/utils/repositories' import { TelegramRepository } from './telegram.repository' @@ -25,7 +30,8 @@ export class TelegramService { private readonly redisService: RedisService, private readonly configService: ConfigService, private readonly telegramRepository: TelegramRepository, - private readonly tokenService: TokenService + private readonly tokenService: TokenService, + private readonly userRepository: UserRepository ) { this.BOT_ID = configService.get('telegram.telegramBotId', { infer: true }) this.BOT_TOKEN = configService.get('telegram.telegramBotToken', { @@ -94,7 +100,7 @@ export class TelegramService { const dataCheckString = dataCheckArr.join('\n') const secretKey = createHash('sha256') .update(`${this.BOT_ID}:${this.BOT_TOKEN}`) - .digest('hex') + .digest() const hmac = createHmac('sha256', secretKey) .update(dataCheckString) @@ -102,4 +108,67 @@ export class TelegramService { return hmac === hash } + + public async complete( + data: TelegramCompleteRequest + ): Promise { + const { phone, sessionId } = data + const raw = await this.redisService.get(`telegram_session:${sessionId}`) + + if (!raw) { + throw new RpcException({ + code: RpcStatus.NOT_FOUND, + message: 'Session not found' + }) + } + + const { telegramId }: Record<'telegramId' | 'username', string> = + JSON.parse(raw) + + let user = await this.userRepository.findByPhone(phone) + + if (!user) { + user = await this.userRepository.createAccount({ + phone, + telegramId + }) + } + + await this.userRepository.updateAccount(user.id, { + telegramId, + isPhoneVerified: true + }) + + const tokens = this.tokenService.generateTokens(user.id) + + await this.redisService.set( + `telegram_tokens:${sessionId}`, + JSON.stringify(tokens), + 'EX', + 120 + ) + await this.redisService.del(`telegram_session:${sessionId}`) + + return { sessionId } + } + + public async consumeSession( + data: TelegramConsumeRequest + ): Promise { + const { sessionId } = data + const redisKey = `telegram_tokens:${sessionId}` + const raw = await this.redisService.get(redisKey) + if (!raw) { + throw new RpcException({ + code: RpcStatus.NOT_FOUND, + message: 'Session not found' + }) + } + + const tokens: Record<'accessToken' | 'refreshToken', string> = + JSON.parse(raw) + await this.redisService.del(redisKey) + + return tokens + } } diff --git a/src/shared/utils/repositories/user.repository.ts b/src/shared/utils/repositories/user.repository.ts index 33a94eb..0da9796 100644 --- a/src/shared/utils/repositories/user.repository.ts +++ b/src/shared/utils/repositories/user.repository.ts @@ -1,6 +1,9 @@ import { Injectable } from '@nestjs/common' import { Account } from '@prisma/generated/client' -import { AccountUpdateInput } from '@prisma/generated/models/Account' +import { + AccountCreateInput, + AccountUpdateInput +} from '@prisma/generated/models/Account' import { PrismaService } from '@/infra/prisma/prisma.service' @@ -26,4 +29,8 @@ export class UserRepository { data }) } + + public createAccount(data: AccountCreateInput) { + return this.prismaService.account.create({ data }) + } } diff --git a/yarn.lock b/yarn.lock index e970d2f..6d30dc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1383,9 +1383,9 @@ integrity sha512-q3DURJbSk3k8MNWFIYaSM4LEcBgPbWa+HJmBz/nzYT4kuYitJVSXxpZ97kr0Ea+81AZwAU3JhQKlkK0SBWUi0A== "@teacinema/contracts@^1.0.0": - version "1.0.8" - resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.0.8/contracts-1.0.8.tgz#4e31c3a370b0808629d9c93900c5f27958ef82e6" - integrity sha512-xyPyifbVwWelgo4MsvhhRl//To+b0Tnp8DtANCzwLBUhwPj3ZG19QgfPB7Ih7PsBU5TD6F80aNlITDxJbFgkFQ== + version "1.0.9" + resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.0.9/contracts-1.0.9.tgz#14b136d0c08552147b0de637a7af6d340d538527" + integrity sha512-9BwWvXaDKgRcef6+1ep9vUDWBdOndqheeTX5mXK5OOvLXbLOlzYoqAUoT0ZwaRwOCD+Je/LDrqcdti6eoss71Q== dependencies: "@nestjs/microservices" "^11.1.12" protoc "33.4.0"