auth graph
This commit is contained in:
@@ -12,6 +12,8 @@ import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||
import { User } from '../users/entities/user.entity';
|
||||
import { AdminAuthController } from './controllers/admin-auth.controller';
|
||||
import { AdminAuthGuard } from './guards/adminAuth.guard';
|
||||
import { AuthResolver } from './graphql/resolvers/auth.resolver';
|
||||
import { AdminAuthResolver } from './graphql/resolvers/admin-auth.resolver';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -32,7 +34,7 @@ import { AdminAuthGuard } from './guards/adminAuth.guard';
|
||||
MikroOrmModule.forFeature([User]),
|
||||
],
|
||||
controllers: [AuthController, AdminAuthController],
|
||||
providers: [AuthService, TokensService, AdminAuthGuard],
|
||||
providers: [AuthService, TokensService, AdminAuthGuard, AuthResolver, AdminAuthResolver],
|
||||
exports: [AdminAuthGuard],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Inputs
|
||||
export * from './inputs/request-otp.input';
|
||||
export * from './inputs/verify-otp.input';
|
||||
export * from './inputs/refresh-token.input';
|
||||
|
||||
// Objects
|
||||
export * from './objects/token-response.object';
|
||||
export * from './objects/restaurant-info.object';
|
||||
export * from './objects/admin-auth-response.object';
|
||||
export * from './objects/auth-response.object';
|
||||
export * from './objects/otp-response.object';
|
||||
|
||||
// Resolvers
|
||||
export * from './resolvers/auth.resolver';
|
||||
export * from './resolvers/admin-auth.resolver';
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class RefreshTokenInput {
|
||||
@Field(() => String, { description: 'Refresh token' })
|
||||
@IsString({ message: 'Refresh token must be a string' })
|
||||
@IsNotEmpty({ message: 'Refresh token is required' })
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsString, IsMobilePhone } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class RequestOtpInput {
|
||||
@Field(() => String, { description: 'Mobile number' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@IsMobilePhone('fa-IR')
|
||||
phone: string;
|
||||
|
||||
@Field(() => String, { description: 'Restaurant slug' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
slug: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsNotEmpty, IsString, Length, Matches } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class VerifyOtpInput {
|
||||
@Field(() => String, { description: 'Mobile number' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@Matches(/^09\d{9}$/, {
|
||||
message: 'Mobile number must be a valid Iranian phone number (e.g., 09123456789)',
|
||||
})
|
||||
phone: string;
|
||||
|
||||
@Field(() => String, { description: 'OTP code' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@Length(5)
|
||||
otp: string;
|
||||
|
||||
@Field(() => String, { description: 'Restaurant slug' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
slug: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { TokenResponse } from './token-response.object';
|
||||
import { RestaurantInfo } from './restaurant-info.object';
|
||||
|
||||
@ObjectType()
|
||||
export class AdminInfo {
|
||||
@Field(() => String)
|
||||
id: string;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
firstName?: string;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
lastName?: string;
|
||||
|
||||
@Field(() => String)
|
||||
phone: string;
|
||||
|
||||
@Field(() => String)
|
||||
role: string;
|
||||
|
||||
@Field(() => [String])
|
||||
permissions: string[];
|
||||
|
||||
@Field(() => RestaurantInfo, { nullable: true })
|
||||
restaurant?: RestaurantInfo;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class AdminAuthResponse {
|
||||
@Field(() => TokenResponse)
|
||||
tokens: TokenResponse;
|
||||
|
||||
@Field(() => AdminInfo)
|
||||
admin: AdminInfo;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { TokenResponse } from './token-response.object';
|
||||
import { RestaurantInfo } from './restaurant-info.object';
|
||||
|
||||
@ObjectType()
|
||||
export class UserInfo {
|
||||
@Field(() => String)
|
||||
id: string;
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
lastName?: string;
|
||||
|
||||
@Field(() => String)
|
||||
phone: string;
|
||||
|
||||
@Field(() => Number)
|
||||
wallet: number;
|
||||
|
||||
@Field(() => Number)
|
||||
points: number;
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
isActive?: boolean;
|
||||
|
||||
@Field(() => RestaurantInfo)
|
||||
restaurant: RestaurantInfo;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class AuthResponse {
|
||||
@Field(() => TokenResponse)
|
||||
tokens: TokenResponse;
|
||||
|
||||
@Field(() => UserInfo)
|
||||
user: UserInfo;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class OtpResponse {
|
||||
@Field(() => String, { description: 'OTP code (for development/testing purposes)' })
|
||||
code: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class RestaurantInfo {
|
||||
@Field(() => String)
|
||||
id: string;
|
||||
|
||||
@Field(() => String)
|
||||
name: string;
|
||||
|
||||
@Field(() => String)
|
||||
slug: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class TokenInfo {
|
||||
@Field(() => String)
|
||||
token: string;
|
||||
|
||||
@Field(() => Number)
|
||||
expire: number;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class TokenResponse {
|
||||
@Field(() => TokenInfo)
|
||||
accessToken: TokenInfo;
|
||||
|
||||
@Field(() => TokenInfo)
|
||||
refreshToken: TokenInfo;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Resolver, Mutation, Args } from '@nestjs/graphql';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { RequestOtpInput } from '../inputs/request-otp.input';
|
||||
import { VerifyOtpInput } from '../inputs/verify-otp.input';
|
||||
import { RefreshTokenInput } from '../inputs/refresh-token.input';
|
||||
import { OtpResponse } from '../objects/otp-response.object';
|
||||
import { AdminAuthResponse } from '../objects/admin-auth-response.object';
|
||||
import { TokenResponse } from '../objects/token-response.object';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
import { RefreshTokenRateLimit } from 'src/common/decorators/rate-limit.decorator';
|
||||
|
||||
@Resolver()
|
||||
export class AdminAuthResolver {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@Throttle({ default: { limit: 3, ttl: 180_000 } })
|
||||
@Mutation(() => OtpResponse, {
|
||||
description: 'Request OTP for admin login',
|
||||
})
|
||||
async adminRequestOtp(@Args('input') input: RequestOtpInput): Promise<OtpResponse> {
|
||||
const code = await this.authService.requestOtpAdmin(input);
|
||||
return { code };
|
||||
}
|
||||
|
||||
@Mutation(() => AdminAuthResponse, {
|
||||
description: 'Verify OTP code for admin login',
|
||||
})
|
||||
async adminVerifyOtp(@Args('input') input: VerifyOtpInput): Promise<AdminAuthResponse> {
|
||||
return this.authService.verifyOtpAdmin(input.phone, input.slug, input.otp);
|
||||
}
|
||||
|
||||
@RefreshTokenRateLimit()
|
||||
@Mutation(() => TokenResponse, {
|
||||
description: 'Refresh the admin access token and refresh token',
|
||||
})
|
||||
async adminRefreshToken(@Args('input') input: RefreshTokenInput): Promise<TokenResponse> {
|
||||
return this.authService.refreshTokenAdmin(input.refreshToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Resolver, Mutation, Query, Args } from '@nestjs/graphql';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { RequestOtpInput } from '../inputs/request-otp.input';
|
||||
import { VerifyOtpInput } from '../inputs/verify-otp.input';
|
||||
import { RefreshTokenInput } from '../inputs/refresh-token.input';
|
||||
import { OtpResponse } from '../objects/otp-response.object';
|
||||
import { AuthResponse } from '../objects/auth-response.object';
|
||||
import { TokenResponse } from '../objects/token-response.object';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
import { RefreshTokenRateLimit } from 'src/common/decorators/rate-limit.decorator';
|
||||
|
||||
@Resolver()
|
||||
export class AuthResolver {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@Query(() => String, {
|
||||
description: 'Health check query',
|
||||
})
|
||||
async health(): Promise<string> {
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
@Throttle({ default: { limit: 3, ttl: 180_000 } })
|
||||
@Mutation(() => OtpResponse, {
|
||||
description: 'Request OTP for login or signup',
|
||||
})
|
||||
async requestOtp(@Args('input') input: RequestOtpInput): Promise<OtpResponse> {
|
||||
return this.authService.requestOtp(input);
|
||||
}
|
||||
|
||||
@Mutation(() => AuthResponse, {
|
||||
description: 'Verify OTP code',
|
||||
})
|
||||
async verifyOtp(@Args('input') input: VerifyOtpInput): Promise<AuthResponse> {
|
||||
return this.authService.verifyOtp(input.phone, input.slug, input.otp);
|
||||
}
|
||||
|
||||
@RefreshTokenRateLimit()
|
||||
@Mutation(() => TokenResponse, {
|
||||
description: 'Refresh the user access token and refresh token',
|
||||
})
|
||||
async refreshToken(@Args('input') input: RefreshTokenInput): Promise<TokenResponse> {
|
||||
return this.authService.refreshToken(input.refreshToken);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user