feat(auth): implement Auth module with register, login, JWT, guards, and CQRS

- Add RefreshToken and OAuthAccount models to Prisma schema
- Implement clean architecture: domain (entities, VOs, events, repo interfaces),
  infrastructure (Prisma repos, Passport strategies, token service),
  application (CQRS command/query handlers), presentation (controller, guards, DTOs)
- Endpoints: POST /auth/register, /auth/login, /auth/refresh, GET /auth/profile,
  GET /auth/profile/agent, PATCH /auth/kyc
- JWT access + refresh token rotation with family-based revocation
- Role-based guards (BUYER, SELLER, AGENT, ADMIN)
- 16 unit tests (value objects, entity) + integration test suite
- All 80 tests passing, clean TypeScript build

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 00:24:42 +07:00
parent c981bff771
commit 391c040100
63 changed files with 2194 additions and 33 deletions

View File

@@ -0,0 +1,65 @@
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
// Domain
import { USER_REPOSITORY } from './domain/repositories/user.repository';
import { REFRESH_TOKEN_REPOSITORY } from './domain/repositories/refresh-token.repository';
// Infrastructure
import { PrismaUserRepository } from './infrastructure/repositories/prisma-user.repository';
import { PrismaRefreshTokenRepository } from './infrastructure/repositories/prisma-refresh-token.repository';
import { JwtStrategy } from './infrastructure/strategies/jwt.strategy';
import { LocalStrategy } from './infrastructure/strategies/local.strategy';
import { TokenService } from './infrastructure/services/token.service';
// Application
import { RegisterUserHandler } from './application/commands/register-user/register-user.handler';
import { LoginUserHandler } from './application/commands/login-user/login-user.handler';
import { RefreshTokenHandler } from './application/commands/refresh-token/refresh-token.handler';
import { VerifyKycHandler } from './application/commands/verify-kyc/verify-kyc.handler';
import { GetProfileHandler } from './application/queries/get-profile/get-profile.handler';
import { GetAgentByUserIdHandler } from './application/queries/get-agent-by-user-id/get-agent-by-user-id.handler';
// Presentation
import { AuthController } from './presentation/controllers/auth.controller';
const CommandHandlers = [
RegisterUserHandler,
LoginUserHandler,
RefreshTokenHandler,
VerifyKycHandler,
];
const QueryHandlers = [GetProfileHandler, GetAgentByUserIdHandler];
@Module({
imports: [
CqrsModule,
PassportModule,
JwtModule.register({
secret: process.env['JWT_SECRET'] || 'goodgo-jwt-secret-change-in-production',
signOptions: { expiresIn: '15m' },
}),
],
controllers: [AuthController],
providers: [
// Repositories
{ provide: USER_REPOSITORY, useClass: PrismaUserRepository },
{ provide: REFRESH_TOKEN_REPOSITORY, useClass: PrismaRefreshTokenRepository },
// Strategies
JwtStrategy,
LocalStrategy,
// Services
TokenService,
// CQRS
...CommandHandlers,
...QueryHandlers,
],
exports: [TokenService, USER_REPOSITORY],
})
export class AuthModule {}