fix(auth): add refresh & logout endpoints and ensure UsersService.findById handles fallback admin
All checks were successful
Deploy Canina / deploy (push) Successful in 1m53s

This commit is contained in:
parsa aghaei 2026-08-16 15:07:07 +03:30
parent 28fc660f69
commit 1a21c4c642
3 changed files with 93 additions and 47 deletions

View File

@ -1,15 +1,17 @@
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import { Controller, Post, Body, HttpCode, HttpStatus, UseGuards, Req } from '@nestjs/common';
import { AuthService } from './auth.service';
import { SendOtpDto } from './dto/send-otp.dto';
import { VerifyOtpDto } from './dto/verify-otp.dto';
import { RegisterDto } from './dto/register.dto';
import { LoginDto } from './dto/login.dto';
import { JwtAuthGuard } from './jwt-auth.guard';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiOkResponse,
ApiBadRequestResponse,
ApiBearerAuth,
} from '@nestjs/swagger';
import { AdminLoginDto } from './dto/admin-login.dto';
@ -43,59 +45,17 @@ export class AuthController {
},
},
})
@ApiBadRequestResponse({
description: 'فرمت شماره تلفن همراه نامعتبر است',
schema: {
example: {
success: false,
message: 'شماره موبایل نامعتبر است',
code: 'BAD_REQUEST',
details: {
message: ['شماره موبایل نامعتبر است'],
},
},
},
})
@ApiBadRequestResponse({ description: 'شماره تلفن همراه نامعتبر است' })
sendOtp(@Body() sendOtpDto: SendOtpDto) {
return this.authService.sendOtp(sendOtpDto);
}
@Post('verify-otp')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'تایید کد پیامکی و ورود/ثبت‌نام کاربر' })
@ApiOkResponse({
description: 'ورود موفق به همراه توکن دسترسی JWT و اطلاعات کاربر',
schema: {
example: {
success: true,
data: {
user: {
id: 'd8c4e428-cd2f-4c55-bfa3-dfa312bb6480',
firstName: 'کاربر',
lastName: 'جدید',
email: '09123456789@temp.local',
mobile: '09123456789',
role: 'User_PetOwner',
walletBalance: '0.00',
charityDonationTotal: '0.00',
createdAt: '2026-05-26T15:20:00.000Z',
updatedAt: '2026-05-26T15:20:00.000Z',
},
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
},
},
},
})
@ApiOperation({ summary: 'تایید کد پیامکی و ورود به سیستم' })
@ApiOkResponse({ description: 'کد تایید شد و ورود با موفقیت انجام گردید' })
@ApiBadRequestResponse({
description: 'کد تایید اشتباه یا منقضی شده است',
schema: {
example: {
success: false,
message: 'کد تایید اشتباه است',
code: 'OTP_INVALID',
details: {},
},
},
description: 'کد تایید اشتباه است یا منقضی شده است',
})
verifyOtp(@Body() verifyOtpDto: VerifyOtpDto) {
return this.authService.verifyOtp(verifyOtpDto);
@ -129,4 +89,22 @@ export class AuthController {
adminLogin(@Body() body: AdminLoginDto) {
return this.authService.adminLogin(body);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Post('refresh')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'تازه‌سازی توکن دسترسی (Refresh Token)' })
@ApiOkResponse({ description: 'توکن جدید با موفقیت صادر شد' })
refresh(@Req() req: { user: { id: string; email?: string; role?: string; mobile?: string } }) {
return this.authService.refresh(req.user);
}
@Post('logout')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'خروج از حساب کاربری' })
@ApiOkResponse({ description: 'با موفقیت خارج شدید' })
logout() {
return { success: true, message: 'با موفقیت خارج شدید' };
}
}

View File

@ -245,4 +245,21 @@ export class AuthService {
error: 'INVALID_ADMIN_CREDENTIALS',
});
}
async refresh(user: { id: string; email?: string; role?: string; mobile?: string }) {
const payload = {
sub: user.id,
email: user.email,
role: user.role,
phoneNumber: user.mobile,
};
return {
success: true,
data: {
user,
accessToken: this.jwtService.sign(payload),
},
};
}
}

View File

@ -18,6 +18,57 @@ export class UsersService {
constructor(private prisma: PrismaService) {}
async findById(id: string) {
if (id === '12345678-1234-1234-1234-123456789012') {
const dbUser = await this.prisma.user.findUnique({
where: { id },
include: {
pets: {
include: {
medicalConditions: true,
reminders: {
include: {
completions: true,
},
},
healthLogs: true,
},
},
orders: {
orderBy: { createdAt: 'desc' },
include: {
orderItems: {
include: { product: true },
},
},
},
addresses: true,
walletTransactions: {
orderBy: { createdAt: 'desc' },
take: 50,
},
},
});
if (dbUser) return dbUser;
return {
id: '12345678-1234-1234-1234-123456789012',
firstName: 'مدیر',
lastName: 'سیستم',
email: process.env.ADMIN_EMAIL || 'admin@canina-iran.com',
mobile: '09120000000',
role: 'Admin',
walletBalance: new Prisma.Decimal(0),
charityDonationTotal: new Prisma.Decimal(0),
pets: [],
orders: [],
addresses: [],
walletTransactions: [],
createdAt: new Date(),
updatedAt: new Date(),
};
}
return this.prisma.user.findUnique({
where: { id },
include: {