fix: Prisma issue fix + Auth done

This commit is contained in:
SauravDhakal
2026-04-12 08:06:05 +05:45
parent aa8deadf1f
commit 2f01adeade
7 changed files with 67 additions and 31 deletions

View File

@@ -5,7 +5,7 @@ import { UserModule } from './user/user.module';
import { AuthModule } from './auth/auth.module'; import { AuthModule } from './auth/auth.module';
import { RequestContextMiddleware } from 'core/als/request-context.middleware'; import { RequestContextMiddleware } from 'core/als/request-context.middleware';
import { RequestContextModule } from 'core/als/request-context.module'; import { RequestContextModule } from 'core/als/request-context.module';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { PrismaModule } from './prisma/prisma.module'; import { PrismaModule } from './prisma/prisma.module';
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core'; import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { ResponseInterceptor } from 'common/interceptors/response.interceptor'; import { ResponseInterceptor } from 'common/interceptors/response.interceptor';
@@ -72,7 +72,26 @@ import { ExpressAdapter } from '@bull-board/express';
], ],
}) })
export class AppModule implements NestModule { export class AppModule implements NestModule {
constructor(private readonly configService: ConfigService) { };
configure(consumer: MiddlewareConsumer) { configure(consumer: MiddlewareConsumer) {
consumer.apply(RequestContextMiddleware).forRoutes('*paths'); consumer.apply(RequestContextMiddleware).forRoutes('*paths');
} }
// Make sure all required env vars are present
onModuleInit() {
const requiredEnvVars = [
"TOKEN_SECRET",
"DATABASE_URL",
"BULL_MQ_REDIS_HOST",
"BULL_MQ_REDIS_PORT"
]
const missingEnvVars = requiredEnvVars.filter((envVar) => !(this.configService.get<string | number>(envVar)))
if (missingEnvVars.length > 0) {
Logger.error(`One or more env variables are missing. Add: ${missingEnvVars.join(', ')} to env file.`)
process.exit(1)
}
}
} }

View File

@@ -76,10 +76,17 @@ export class AuthController {
@IsTempToken() @IsTempToken()
@UseGuards(AuthGuard) @UseGuards(AuthGuard)
@Post('/complete-profile') @Post('/complete-profile')
async completeUserProfile(@Body() body: CompleteProfileSetupRequestDTO): Promise<string> { async completeUserProfile(@Body() body: CompleteProfileSetupRequestDTO) {
await this.authService.completeProfileSetup(body); const { accessToken, refreshToken, user } = await this.authService.completeProfileSetup(body);
return 'Welcome'; return {
message: "Welcome to our app",
data: {
accessToken,
refreshToken,
user
}
}
} }
logout() { } logout() { }

View File

@@ -4,10 +4,11 @@ import { AuthController } from './auth.controller';
import { APP_GUARD } from '@nestjs/core'; import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './guards/auth.guard'; import { AuthGuard } from './guards/auth.guard';
import { UserModule } from 'src/user/user.module'; import { UserModule } from 'src/user/user.module';
import { JwtModule } from '@nestjs/jwt';
import { RequestContextModule } from 'core/als/request-context.module'; import { RequestContextModule } from 'core/als/request-context.module';
import { BullModule } from '@nestjs/bullmq'; import { BullModule } from '@nestjs/bullmq';
import { PrismaModule } from 'src/prisma/prisma.module'; import { PrismaModule } from 'src/prisma/prisma.module';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Global() @Global()
@Module({ @Module({
@@ -23,8 +24,16 @@ import { PrismaModule } from 'src/prisma/prisma.module';
BullModule.registerQueue({ BullModule.registerQueue({
name: "mail" name: "mail"
}), }),
JwtModule.registerAsync({
global: true,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get<string>("TOKEN_SECRET"),
signOptions: { expiresIn: '7d' }
})
}),
UserModule, UserModule,
JwtModule,
RequestContextModule, RequestContextModule,
PrismaModule PrismaModule
], ],

View File

@@ -83,7 +83,7 @@ export class AuthService {
const now = Number(new Date()) / 1000; const now = Number(new Date()) / 1000;
if (!otpExists) if (!otpExists)
throw new BadRequestException("No user found") throw new BadRequestException("No OTP request found")
else if (otpExists.otp !== dto.otp) else if (otpExists.otp !== dto.otp)
throw new BadRequestException("Invalid OTP") throw new BadRequestException("Invalid OTP")
else if ((Number(otpExists.expiresAt) / 1000 < now)) { else if ((Number(otpExists.expiresAt) / 1000 < now)) {
@@ -217,43 +217,36 @@ export class AuthService {
resetPassword() { } resetPassword() { }
// TODO: Use nest jwt // TODO: If remember me is there, sign for like 30d maybe
private async genSignedTokens(token: TokenInputType) { private async genSignedTokens(token: TokenInputType) {
const accessToken = await this.jwtService.signAsync(token, { const accessToken = await this.jwtService.signAsync(token);
secret: 'demo',
});
const refreshToken = await this.jwtService.signAsync( const refreshToken = await this.jwtService.signAsync(
{ {
userId: token.userId, userId: token.userId,
}, },
{
secret: 'demo',
},
); );
return { accessToken, refreshToken }; return { accessToken, refreshToken };
} }
private async genSignedTempToken(token: OTPTokenInputType) { private async genSignedTempToken(token: OTPTokenInputType) {
const accessToken = await this.jwtService.signAsync(token, { const accessToken = await this.jwtService.signAsync(token);
secret: 'demo',
});
const refreshToken = await this.jwtService.signAsync( const refreshToken = await this.jwtService.signAsync(
{ {
userId: token.userId, userId: token.userId,
}, },
{
secret: 'demo',
},
); );
return { accessToken, refreshToken }; return { accessToken, refreshToken };
} }
private genOtp() { genOtp(): number {
return 123456; const array = new Uint32Array(1);
crypto.getRandomValues(array);
const otp = array[0] % 900000 + 100000;
return otp;
} }
} }

View File

@@ -13,6 +13,7 @@ import { Reflector } from '@nestjs/core';
import { PUBLIC_KEY, TEMP_TOKEN_KEY } from 'common/keys'; import { PUBLIC_KEY, TEMP_TOKEN_KEY } from 'common/keys';
import { UserService } from 'src/user/user.service'; import { UserService } from 'src/user/user.service';
import { USER_ACCOUNT_STATUS } from 'prisma/generated/prisma/enums'; import { USER_ACCOUNT_STATUS } from 'prisma/generated/prisma/enums';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class AuthGuard implements CanActivate { export class AuthGuard implements CanActivate {
@@ -21,6 +22,7 @@ export class AuthGuard implements CanActivate {
private readonly jwtService: JwtService, private readonly jwtService: JwtService,
private readonly requestContext: RequestContextService, private readonly requestContext: RequestContextService,
private readonly userService: UserService, private readonly userService: UserService,
private readonly configService: ConfigService,
) { } ) { }
async canActivate(context: ExecutionContext) { async canActivate(context: ExecutionContext) {
@@ -42,9 +44,12 @@ export class AuthGuard implements CanActivate {
try { try {
const payload: JwtPayload = await this.jwtService.verifyAsync(token, { const payload: JwtPayload = await this.jwtService.verifyAsync(token, {
secret: 'demo', secret: this.configService.get<string>("TOKEN_SECRET"),
}); });
if (isTempToken && payload.status !== USER_ACCOUNT_STATUS.pending)
throw new UnauthorizedException()
// TODO: Redis + Org too, blacklist token // TODO: Redis + Org too, blacklist token
const userExists = await this.userService.findById(payload.userId); const userExists = await this.userService.findById(payload.userId);

View File

@@ -16,6 +16,9 @@ import { TransactionClient } from 'prisma/generated/prisma/internal/prismaNamesp
export class PrismaService export class PrismaService
extends PrismaClient extends PrismaClient
implements OnModuleDestroy, OnModuleInit { implements OnModuleDestroy, OnModuleInit {
private readonly db: PrismaClient;
constructor( constructor(
private readonly ctx: RequestContextService, private readonly ctx: RequestContextService,
private readonly configService: ConfigService, private readonly configService: ConfigService,
@@ -31,6 +34,8 @@ export class PrismaService
adapter: adapter, adapter: adapter,
log: ['info', 'warn'], log: ['info', 'warn'],
}); });
this.db = new PrismaClient({ adapter, log: ['warn', 'info', 'error'] })
} }
async onModuleInit() { async onModuleInit() {
try { try {
@@ -51,6 +56,6 @@ export class PrismaService
* Else returns itself. * Else returns itself.
* */ * */
get client(): TransactionClient { get client(): TransactionClient {
return this.ctx.tx ?? this; return this.ctx.tx ?? this.db;
} }
} }

View File

@@ -56,14 +56,14 @@ export class UserService {
} }
async findUserForAuth(email: string) { async findUserForAuth(email: string) {
return await this.prisma.user.findUnique({ return await this.prisma.client.user.findUnique({
where: { email }, where: { email },
omit: { password: false }, // Password is omitted by default, we are just reverting it omit: { password: false }, // Password is omitted by default, we are just reverting it
}); });
} }
async findById(id: string) { async findById(id: string) {
return await this.prisma.user.findUnique({ return await this.prisma.client.user.findUnique({
where: { where: {
id: id, id: id,
}, },
@@ -71,8 +71,7 @@ export class UserService {
} }
async findByEmail(email: string) { async findByEmail(email: string) {
const client = this.prisma.client.user ?? this.prisma.user; return await this.prisma.client.user.findUnique({
return await client.findUnique({
where: { where: {
email: email, email: email,
}, },
@@ -80,7 +79,7 @@ export class UserService {
} }
async updateRefreshToken(id: string, refreshToken: string) { async updateRefreshToken(id: string, refreshToken: string) {
return await this.prisma.user.update({ return await this.prisma.client.user.update({
where: { id }, where: { id },
data: { refreshToken }, data: { refreshToken },
}); });
@@ -90,8 +89,7 @@ export class UserService {
* USER OTP SERVICES * USER OTP SERVICES
* */ * */
async findByEmailInOTP(email: string) { async findByEmailInOTP(email: string) {
const client = this.prisma.client.userOTP ?? this.prisma.userOTP; return await this.prisma.client.userOTP.findUnique({
return await client.findUnique({
where: { where: {
email, email,
}, },