/me
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Post } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AllowWithoutSubscription } from '../auth/decorators/allow-without-subscription.decorator';
|
||||
import {
|
||||
@@ -18,6 +18,7 @@ import { Public } from './decorators/public.decorator';
|
||||
import { AuthTokensResponseDto } from './dto/auth-tokens-response.dto';
|
||||
import { CheckSalonMobileDto } from './dto/check-salon-mobile.dto';
|
||||
import { CheckSalonMobileResponseDto } from './dto/check-salon-mobile-response.dto';
|
||||
import { MeResponseDto } from './dto/me-response.dto';
|
||||
import { RefreshTokenDto } from './dto/refresh-token.dto';
|
||||
import { RequestOtpDto } from './dto/request-otp.dto';
|
||||
import { RequestOtpResponseDto } from './dto/request-otp-response.dto';
|
||||
@@ -89,6 +90,14 @@ export class AuthController {
|
||||
return this.authService.refresh(dto.refreshToken);
|
||||
}
|
||||
|
||||
@AllowWithoutSubscription()
|
||||
@ApiProtectedEndpoint('Get current authenticated user profile')
|
||||
@ApiStandardOkResponse(MeResponseDto)
|
||||
@Get('me')
|
||||
getMe(@CurrentUser() user: AuthUser) {
|
||||
return this.authService.getMe(user);
|
||||
}
|
||||
|
||||
@AllowWithoutSubscription()
|
||||
@ApiProtectedEndpoint('Logout and invalidate refresh token')
|
||||
@ApiStandardEmptyResponse()
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AuthorizationModule } from '../authorization/authorization.module';
|
||||
import { Customer } from '../customers/entities/customer.entity';
|
||||
import { Salon } from '../salons/entities/salon.entity';
|
||||
import { SmsModule } from '../sms/sms.module';
|
||||
import { SubscriptionsModule } from '../subscriptions/subscriptions.module';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
@@ -33,6 +34,7 @@ import { JwtStrategy } from './strategies/jwt.strategy';
|
||||
}),
|
||||
UsersModule,
|
||||
SmsModule,
|
||||
SubscriptionsModule,
|
||||
AuthorizationModule,
|
||||
],
|
||||
controllers: [AuthController],
|
||||
|
||||
@@ -15,6 +15,8 @@ import { Repository, IsNull } from 'typeorm';
|
||||
import { Customer } from '../customers/entities/customer.entity';
|
||||
import { Salon } from '../salons/entities/salon.entity';
|
||||
import { SmsService } from '../sms/sms.service';
|
||||
import { SalonSubscription } from '../subscriptions/entities/salon-subscription.entity';
|
||||
import { SalonSubscriptionsService } from '../subscriptions/salon-subscriptions.service';
|
||||
import { UserRole } from '../users/enums/user-role.enum';
|
||||
import { User } from '../users/entities/user.entity';
|
||||
import { UsersService } from '../users/users.service';
|
||||
@@ -22,6 +24,7 @@ import { VerifyOtpDto } from './dto/verify-otp.dto';
|
||||
import { VerifySalonOtpDto } from './dto/verify-salon-otp.dto';
|
||||
import { Otp } from './entities/otp.entity';
|
||||
import { RefreshToken } from './entities/refresh-token.entity';
|
||||
import { AuthUser } from './interfaces/auth-user.interface';
|
||||
import { JwtPayload } from './interfaces/jwt-payload.interface';
|
||||
|
||||
export interface AuthTokensResponse {
|
||||
@@ -40,6 +43,12 @@ export interface SalonAuthResponse extends AuthTokensResponse {
|
||||
salon?: Salon;
|
||||
}
|
||||
|
||||
export interface MeResponse {
|
||||
user: AuthTokensResponse['user'];
|
||||
salons?: Salon[];
|
||||
activeSubscription?: SalonSubscription | null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private readonly logger = new Logger(AuthService.name);
|
||||
@@ -60,6 +69,7 @@ export class AuthService {
|
||||
@InjectRepository(Salon)
|
||||
private readonly salonsRepository: Repository<Salon>,
|
||||
private readonly usersService: UsersService,
|
||||
private readonly salonSubscriptionsService: SalonSubscriptionsService,
|
||||
private readonly smsService: SmsService,
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly configService: ConfigService,
|
||||
@@ -336,6 +346,33 @@ export class AuthService {
|
||||
throw new UnauthorizedException('Invalid refresh token');
|
||||
}
|
||||
|
||||
async getMe(authUser: AuthUser): Promise<MeResponse> {
|
||||
const user = await this.usersService.findOne(authUser.id);
|
||||
|
||||
const response: MeResponse = {
|
||||
user: {
|
||||
id: user.id,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
mobile: user.mobile,
|
||||
role: user.role,
|
||||
},
|
||||
};
|
||||
|
||||
if (user.role === UserRole.SALON) {
|
||||
response.salons = await this.salonsRepository.find({
|
||||
where: { ownerId: user.id },
|
||||
order: { createdAt: 'ASC' },
|
||||
});
|
||||
response.activeSubscription =
|
||||
await this.salonSubscriptionsService.findActiveSubscriptionForOwner(
|
||||
user.id,
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private async resolveUserForLogin(dto: VerifyOtpDto): Promise<User> {
|
||||
const existingUser = await this.usersService.findByMobile(dto.mobile);
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Salon } from '../../salons/entities/salon.entity';
|
||||
import { SalonSubscription } from '../../subscriptions/entities/salon-subscription.entity';
|
||||
import { UserRole } from '../../users/enums/user-role.enum';
|
||||
|
||||
class MeUserResponseDto {
|
||||
@ApiProperty({ format: 'uuid' })
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty()
|
||||
mobile: string;
|
||||
|
||||
@ApiProperty({ enum: UserRole })
|
||||
role: UserRole;
|
||||
}
|
||||
|
||||
export class MeResponseDto {
|
||||
@ApiProperty({ type: MeUserResponseDto })
|
||||
user: MeUserResponseDto;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
type: Salon,
|
||||
isArray: true,
|
||||
description: 'Present for salon owners',
|
||||
})
|
||||
salons?: Salon[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
type: SalonSubscription,
|
||||
nullable: true,
|
||||
description: 'Active subscription for salon owners, null if none',
|
||||
})
|
||||
activeSubscription?: SalonSubscription | null;
|
||||
}
|
||||
@@ -154,25 +154,34 @@ export class SalonSubscriptionsService {
|
||||
}
|
||||
|
||||
async hasActiveSubscriptionForOwner(ownerId: string): Promise<boolean> {
|
||||
const subscription = await this.findActiveSubscriptionForOwner(ownerId);
|
||||
return subscription !== null;
|
||||
}
|
||||
|
||||
async findActiveSubscriptionForOwner(
|
||||
ownerId: string,
|
||||
): Promise<SalonSubscription | null> {
|
||||
const subscriptions = await this.subscriptionsRepository
|
||||
.createQueryBuilder('subscription')
|
||||
.innerJoin('subscription.salon', 'salon')
|
||||
.innerJoinAndSelect('subscription.salon', 'salon')
|
||||
.innerJoinAndSelect('subscription.plan', 'plan')
|
||||
.where('salon.ownerId = :ownerId', { ownerId })
|
||||
.andWhere('subscription.status = :status', {
|
||||
status: SalonSubscriptionStatus.ACTIVE,
|
||||
})
|
||||
.orderBy('subscription.createdAt', 'DESC')
|
||||
.getMany();
|
||||
|
||||
const now = new Date();
|
||||
for (const subscription of subscriptions) {
|
||||
if (subscription.endDate > now) {
|
||||
return true;
|
||||
return subscription;
|
||||
}
|
||||
subscription.status = SalonSubscriptionStatus.EXPIRED;
|
||||
await this.subscriptionsRepository.save(subscription);
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
async consumeSms(salonId: string, count = 1): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user