add host and minglar admin APIs for registration, login, and retrieval of host details

This commit is contained in:
paritosh18
2025-11-13 14:59:50 +05:30
parent 8e19bb566d
commit a14f1388f6
11 changed files with 718 additions and 688 deletions

View File

@@ -166,6 +166,67 @@ functions:
path: /host/add-payment-details
method: post
getHostById:
handler: src/modules/host/handlers/getbyidhandler.handler
package:
patterns:
- 'src/modules/host/**'
- 'common/**'
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
- 'node_modules/@prisma/client/**'
- 'prisma/schema.prisma'
events:
- httpApi:
path: /host/{id}
method: get
# 👇 Minglar Admin Module
minglarRegistration:
handler: src/modules/minglaradmin/handlers/registration.handler
package:
patterns:
- 'src/modules/minglaradmin/**'
- 'common/**'
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
- 'node_modules/@prisma/client/**'
- 'node_modules/@aws-sdk/**'
- 'prisma/schema.prisma'
events:
- httpApi:
path: /minglaradmin/registration
method: post
minglarLoginForAdmin:
handler: src/modules/minglaradmin/handlers/loginForMinglar.handler
package:
patterns:
- 'src/modules/minglaradmin/**'
- 'common/**'
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
- 'node_modules/@prisma/client/**'
- 'node_modules/@aws-sdk/**'
- 'prisma/schema.prisma'
events:
- httpApi:
path: /minglaradmin/login
method: post
minglarCreatePassword:
handler: src/modules/minglaradmin/handlers/createPassword.handler
package:
patterns:
- 'src/modules/minglaradmin/**'
- 'common/**'
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
- 'node_modules/@prisma/client/**'
- 'node_modules/@aws-sdk/**'
- 'prisma/schema.prisma'
events:
- httpApi:
path: /minglaradmin/create-password
method: post
addCompanyDetails:
handler: src/modules/minglaradmin/handlers/addCompanyDetails.handler
package:

View File

@@ -0,0 +1,50 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import { PrismaService } from '../../../common/database/prisma.service';
import { HostService } from '../services/host.service';
import ApiError from '../../../common/utils/helper/ApiError';
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
const prismaService = new PrismaService();
const hostService = new HostService(prismaService);
export const handler = safeHandler(async (
event: APIGatewayProxyEvent,
context?: Context
): Promise<APIGatewayProxyResult> => {
// Get host ID from path parameters
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token']
if(!token) {
throw new ApiError(400, 'This is a protected route. Please provide a valid token.');
}
const userInfo = await verifyHostToken(token);
const id = Number(userInfo.id)
if (!id) {
throw new ApiError(400, 'Host ID is required');
}
if (isNaN(id)) {
throw new ApiError(400, 'Invalid host ID format');
}
const hostDetails = await hostService.getHostById(id);
if (!hostDetails) {
throw new ApiError(404, 'Host not found');
}
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: true,
message: 'Host details retrieved successfully',
data: hostDetails,
}),
};
});

View File

@@ -44,7 +44,7 @@ export const handler = safeHandler(async (
newUser = user;
} else {
// ✅ No user found → create new one
newUser = await hostService.createHostUser(email);
newUser = await hostService.createMinglarUser(email);
}
const otpResult = await generateOtpHelper(

View File

@@ -19,10 +19,71 @@ export class HostService {
}
async getHostById(id: number) {
const host = await this.prisma.user.findUnique({ where: { id } });
const host = await this.prisma.user.findUnique({
where: { id },
include: {
HostHeader: {
select: {
id: true,
companyName: true,
hostRefNumber: true,
address1: true,
address2: true,
cityXid: true,
stateXid: true,
countryXid: true,
pinCode: true,
logoPath: true,
isSubsidairy: true,
registrationNumber: true,
panNumber: true,
gstNumber: true,
formationDate: true,
companyType: true,
websiteUrl: true,
instagramUrl: true,
facebookUrl: true,
linkedinUrl: true,
twitterUrl: true,
currencyXid: true,
stepper: true,
hostStatusInternal: true,
hostStatusDisplay: true,
adminStatusInternal: true,
adminStatusDisplay: true,
amStatus: true,
agreementAccepted: true,
accountManagerXid: true,
isApproved: true,
agreementStartDate: true,
durationNumber: true,
durationFrequency: true,
isCommisionBase: true,
commisionPer: true,
amountPerBooking: true,
isActive: true,
createdAt: true,
updatedAt: true,
deletedAt: true,
currencies: true,
cities: true,
states: true,
countries: true,
HostBankDetails: true,
HostDocuments: true,
HostSuggestion: true,
hostParent: true,
HostTrack: true,
Activities: true,
},
},
},
});
if (!host || host.roleXid !== 4) {
throw new ApiError(404, 'Host not found');
}
return host;
}
@@ -108,9 +169,9 @@ export class HostService {
return existingUser;
}
async createHostUser(email: string) {
async createMinglarUser(email: string) {
const newUser = await this.prisma.user.create({
data: { emailAddress: email, roleXid: 4 },
data: { emailAddress: email, roleXid: 1 },
});
return newUser;
}

View File

@@ -0,0 +1,93 @@
// src/modules/host/dto/host.dto.ts
import { IsInt, IsOptional, IsString, IsBoolean, IsEmail } from 'class-validator';
export class CreateMinglarDto {
@IsString()
firstName: string;
@IsString()
lastName: string;
@IsEmail()
emailAddress: string;
@IsOptional()
@IsString()
isdCode?: string;
@IsOptional()
@IsString()
mobileNumber?: string;
@IsOptional()
@IsString()
userPassword?: string;
@IsOptional()
@IsInt()
roleXid?: number;
@IsOptional()
@IsBoolean()
isActive?: boolean;
}
export class UpdateMinglarDto {
@IsOptional()
@IsString()
firstName?: string;
@IsOptional()
@IsString()
lastName?: string;
@IsOptional()
@IsEmail()
emailAddress?: string;
@IsOptional()
@IsBoolean()
isActive?: boolean;
}
export class GetMinglarLoginResponseDTO {
id: number;
firstName: string | null;
lastName: string | null;
emailAddress: string;
mobileNumber: string | null;
isActive: boolean;
roleXid: number;
accessToken: string;
refreshToken: string;
constructor(user: any, accessToken: string, refreshToken: string) {
this.id = user.id;
this.firstName = user.firstName;
this.lastName = user.lastName;
this.emailAddress = user.emailAddress;
this.mobileNumber = user.mobileNumber;
this.isActive = user.isActive;
this.roleXid = user.roleXid;
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}
export class AddPaymentDetailsDTO {
bankXid: number;
bankBranchXid: number;
accountNumber: number;
accountHolderName: string;
ifscCode: string;
hostXid: number;
constructor(bankXid: number, bankBranchXid: number, accountNumber: number, accountHolderName: string, ifscCode: string, hostXid: number) {
this.bankXid = bankXid;
this.bankBranchXid = bankBranchXid;
this.accountNumber = accountNumber;
this.accountHolderName = accountHolderName;
this.ifscCode = ifscCode;
this.hostXid = hostXid;
}
}

View File

@@ -1,70 +0,0 @@
// import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
// import { safeHandler } from '../../../common/utils/handlers/safeHandler';
// import { PrismaService } from '../../../common/database/prisma.service';
// import { HostService } from '../services/host.service';
// import { TokenService } from '../services/token.service';
// import { GetHostLoginResponseDTO } from '../dto/host.dto';
// import ApiError from '../../../common/utils/helper/ApiError';
// import * as bcrypt from 'bcryptjs';
// const prismaService = new PrismaService();
// const hostService = new HostService(prismaService);
// const tokenService = new TokenService();
// export const handler = safeHandler(async (
// event: APIGatewayProxyEvent,
// context?: Context
// ): Promise<APIGatewayProxyResult> => {
// // Parse request body
// let body: { emailAddress?: string; userPassword?: string };
// try {
// body = event.body ? JSON.parse(event.body) : {};
// } catch (error) {
// throw new ApiError(400, 'Invalid JSON in request body');
// }
// const { emailAddress } = body;
// if (!emailAddress) {
// throw new ApiError(400, 'Email is required');
// }
// const loginForHost = await hostService.loginForHost(emailAddress);
// if (!loginForHost) {
// throw new ApiError(400, 'Failed to login');
// }
// if (!matchPassword) {
// throw new ApiError(401, 'Invalid credentials');
// }
// const generateTokenForHost = await tokenService.generateAuthToken(
// loginForHost.id
// );
// if (!generateTokenForHost) {
// throw new ApiError(500, 'Failed to generate token');
// }
// const loginForHostResponse = new GetHostLoginResponseDTO(
// loginForHost,
// generateTokenForHost.access.token,
// generateTokenForHost.refresh.token
// );
// return {
// statusCode: 200,
// headers: {
// 'Content-Type': 'application/json',
// 'Access-Control-Allow-Origin': '*',
// },
// body: JSON.stringify({
// success: true,
// message: 'Login successful',
// data: loginForHostResponse,
// }),
// };
// });

View File

@@ -0,0 +1,75 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import { PrismaService } from '../../../common/database/prisma.service';
import { MinglarService } from '../services/minglar.service';
import { TokenService } from "../services/token.service";
import { GetMinglarLoginResponseDTO } from '../dto/minglar.dto';
import ApiError from '../../../common/utils/helper/ApiError';
import * as bcrypt from 'bcryptjs';
const prismaService = new PrismaService();
const minglarSerivce = new MinglarService(prismaService);
const tokenService = new TokenService();
export const handler = safeHandler(async (
event: APIGatewayProxyEvent,
context?: Context
): Promise<APIGatewayProxyResult> => {
// Parse request body
let body: { emailAddress?: string; userPassword?: string };
try {
body = event.body ? JSON.parse(event.body) : {};
} catch (error) {
throw new ApiError(400, 'Invalid JSON in request body');
}
const { emailAddress ,userPassword} = body;
if (!emailAddress) {
throw new ApiError(400, 'Email is required');
}
const loginForMinglar = await minglarSerivce.loginForMinglar(emailAddress ,userPassword);
if (!loginForMinglar) {
throw new ApiError(400, 'Failed to login');
}
const matchPassword = await bcrypt.compare(
userPassword,
loginForMinglar.userPassword
);
if (!matchPassword) {
throw new ApiError(401, 'Invalid credentials');
}
const generateTokenForHost = await tokenService.generateAuthToken(
loginForMinglar.id
);
if (!generateTokenForHost) {
throw new ApiError(500, 'Failed to generate token');
}
const loginForHostResponse = new GetMinglarLoginResponseDTO(
loginForMinglar,
generateTokenForHost.access.token,
generateTokenForHost.refresh.token
);
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: true,
message: 'Login successful',
data: loginForHostResponse,
}),
};
});

View File

@@ -0,0 +1,75 @@
import { MinglarService } from './../services/minglar.service';
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import { PrismaService } from '../../../common/database/prisma.service';
import ApiError from '../../../common/utils/helper/ApiError';
import * as bcrypt from 'bcryptjs';
import { generateOtpHelper } from '../../../common/utils/helper/sendOtp';
const prismaService = new PrismaService();
const minglarService = new MinglarService(prismaService);
export const handler = safeHandler(async (
event: APIGatewayProxyEvent,
context?: Context
): Promise<APIGatewayProxyResult> => {
// Parse request body
let body: { email?: string };
try {
body = event.body ? JSON.parse(event.body) : {};
} catch (error) {
throw new ApiError(400, 'Invalid JSON in request body');
}
const { email } = body;
if (!email) {
throw new ApiError(400, 'Email is required');
}
const user = await prismaService.user.findUnique({
where: { emailAddress: email },
select: { emailAddress: true, id: true, userPassword: true },
});
if (user && user.userPassword) {
throw new ApiError(404, 'User is already registered. Please login.');
}
let newUser;
if (user && !user.userPassword) {
// ✅ User already exists but without password → reuse record
newUser = user;
} else {
// ✅ No user found → create new one
newUser = await minglarService.createHostUser(email);
}
const otpResult = await generateOtpHelper(
Number(newUser?.id),
newUser?.emailAddress,
'Register',
6,
5
);
if (!otpResult || !otpResult.otp) {
throw new ApiError(500, 'Failed to send OTP');
}
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: true,
message: 'OTP sent successfully.',
data: {},
}),
};
});

View File

@@ -4,6 +4,8 @@ import ApiError from '../../../common/utils/helper/ApiError';
import * as bcrypt from 'bcryptjs';
import { z } from 'zod';
import { hostCompanyDetailsSchema } from '../../../common/utils/validation/host/hostCompanyDetails.validation';
import { CreateMinglarDto, UpdateMinglarDto } from '../dto/minglar.dto';
import { User } from '@prisma/client';
type HostCompanyDetailsInput = z.infer<typeof hostCompanyDetailsSchema>;
@@ -47,6 +49,134 @@ export class MinglarService {
return true;
}
async createHost(data: CreateMinglarDto) {
return this.prisma.user.create({ data });
}
async getAllHosts() {
return this.prisma.user.findMany({ where: { roleXid: 3 } });
}
async getHostById(id: number) {
const host = await this.prisma.user.findUnique({
where: { id },
include: {
HostHeader: {
include: {
currencies: true,
cities: true,
states: true,
countries: true,
HostBankDetails: true,
HostDocuments: true,
HostSuggestion: true,
hostParent: true,
HostTrack: true,
Activities: true,
},
},
UserOtp: true,
Token: true,
},
});
if (!host || host.roleXid !== 4) {
throw new ApiError(404, 'Host not found');
}
return host;
}
async updateHost(id: number, data: UpdateMinglarDto) {
return this.prisma.user.update({
where: { id },
data,
});
}
async deleteHost(id: number) {
return this.prisma.user.delete({ where: { id } });
}
async getHostByEmail(email: string): Promise<User> {
return this.prisma.user.findUnique({ where: { emailAddress: email } });
}
async verifyHostOtp(email: string, otp: string): Promise<boolean> {
const user = await this.prisma.user.findUnique({
where: { emailAddress: email },
select: {
id: true,
emailAddress: true,
UserOtp: {
where: { isActive: true, isVerified: false },
orderBy: { createdAt: 'desc' },
take: 1,
},
},
});
if (!user) {
throw new ApiError(404, 'User not found.');
}
const userOtp = user.UserOtp[0];
if (!userOtp) {
throw new ApiError(400, 'No OTP found.');
}
if (new Date() > userOtp.expiresOn) {
throw new ApiError(400, 'OTP has expired.');
}
const isMatch = await bcrypt.compare(otp, userOtp.otpCode);
if (!isMatch) {
throw new ApiError(400, 'Invalid OTP.');
}
await this.prisma.userOtp.update({
where: { id: userOtp.id },
data: {
isVerified: true,
verifiedOn: new Date(),
isActive: false,
},
});
return true;
}
async loginForMinglar(emailAddress: string, userPassword: string) {
const existingUser = await this.prisma.user.findUnique({
where: { emailAddress: emailAddress },
});
if (!existingUser) {
throw new ApiError(404, 'User not found');
}
if (existingUser.roleXid !== 4) {
throw new ApiError(403, 'Access denied. Not a host user.');
}
const matchPassword = await bcrypt.compare(userPassword, existingUser.userPassword);
if (!matchPassword) {
throw new ApiError(401, 'Invalid credentials');
}
return existingUser;
}
async createHostUser(email: string) {
const newUser = await this.prisma.user.create({
data: { emailAddress: email, roleXid: 4 },
});
return newUser;
}
async addCompanyDetails(
companyData: HostCompanyDetailsInput,
documents: HostDocumentInput[] // Documents with S3 URLs

View File

@@ -0,0 +1,159 @@
import { PrismaClient } from "@prisma/client";
import jwt, { JwtPayload } from "jsonwebtoken";
import moment from "moment";
import config from "../../../config/config";
const prisma = new PrismaClient();
export class TokenService {
private generateToken(
user_xid: number,
expiresIn: Date,
type: string,
secret: string
): { token: string; expires: Date } {
const token = jwt.sign(
{
sub: user_xid,
iat: moment().unix(),
exp: moment(expiresIn).unix(),
type,
},
secret
);
return { token, expires: expiresIn };
}
async generateAuthToken(
user_xid: number,
): Promise<{
access: { token: string; expires: Date };
refresh: { token: string; expires: Date };
}> {
const accessTokenExpires = moment()
.add(config.jwt.accessExpirationMinutes, "minutes")
.toDate();
const refreshTokenExpires = moment()
.add(config.jwt.refreshExpirationDays, "days")
.toDate();
const accessToken = this.generateToken(
user_xid,
accessTokenExpires,
"access",
config.jwt.secret
);
const refreshToken = this.generateToken(
user_xid,
refreshTokenExpires,
"refresh",
config.jwt.secret
);
await prisma.token.create({
data: {
token: refreshToken.token,
expiringAt: refreshToken.expires,
tokenType: "refresh",
isBlackListed: false,
user: {
connect: { id: user_xid },
},
},
});
return {
access: accessToken,
refresh: refreshToken,
};
}
async generateAuthTokenAdmin(
user_xid: number
): Promise<{
access: { token: string; expires: Date };
refresh: { token: string; expires: Date };
}> {
const accessTokenExpires = moment()
.add(config.jwt.accessExpirationMinutes, "minutes")
.toDate();
const refreshTokenExpires = moment()
.add(config.jwt.refreshExpirationDays, "days")
.toDate();
const accessToken = this.generateToken(
user_xid,
accessTokenExpires,
"access",
config.jwt.secret
);
const refreshToken = this.generateToken(
user_xid,
refreshTokenExpires,
"refresh",
config.jwt.secret
);
await prisma.token.create({
data: {
token: refreshToken.token,
expiringAt: refreshToken.expires,
tokenType: "refresh",
isBlackListed: false,
user: {
connect: { id: user_xid },
},
},
});
return {
access: accessToken,
refresh: refreshToken,
};
}
async revokeToken(user_xid: number, deviceId: string): Promise<boolean> {
const existingToken = await prisma.token.findFirst({
where: {
id: user_xid,
deviceId,
},
});
if (!existingToken) return false;
await prisma.token.delete({ where: { id: existingToken.id } });
return true;
}
async isTokenBlackListed(token: string): Promise<boolean> {
const existing = await prisma.token.findUnique({
where: { token },
});
return existing ? true : false;
}
async verifyRefreshToken(
token: string
): Promise<string | JwtPayload | null> {
try {
return jwt.verify(token, config.jwt.secret);
} catch {
return null;
}
}
async decodeToken(token: string): Promise<string | JwtPayload | null> {
try {
return jwt.decode(token);
} catch {
return null;
}
}
}

View File

@@ -1,631 +1,27 @@
{
"openapi": "3.0.0",
"paths": {},
"info": {
"title": "Minglar API",
"description": "NestJS Backend for Minglar with AWS Lambda endpoints",
"description": "NestJS Backend for Minglar with Lambda-ready endpoints",
"version": "1.0.0",
"contact": {
"name": "API Support"
}
"contact": {}
},
"tags": [],
"servers": [
{
"url": "https://api.minglar.com",
"description": "Production Server"
},
{
"url": "http://localhost:3000",
"description": "Local Development Server"
"url": "http://localhost:3000/",
"description": "Local Server"
}
],
"tags": [
{
"name": "Host",
"description": "Host management endpoints"
},
{
"name": "Authentication",
"description": "Authentication and authorization endpoints"
}
],
"paths": {
"/host": {
"get": {
"tags": ["Host"],
"summary": "Get all hosts",
"description": "Retrieves a list of all host headers with basic information",
"operationId": "getHosts",
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/HostHeader"
}
},
"example": [
{
"hostParent": "Example Host",
"hostRefNumber": "HOST001",
"hostStatusDisplay": "Active",
"accountManager": "John Doe"
}
]
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/host/registration": {
"post": {
"tags": ["Authentication"],
"summary": "Register a new host",
"description": "Initiates host registration by sending an OTP to the provided email address",
"operationId": "registrationOfHost",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegistrationRequest"
},
"example": {
"email": "host@example.com"
}
}
}
},
"responses": {
"200": {
"description": "OTP sent successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SuccessResponse"
},
"example": {
"success": true,
"message": "OTP sent successfully.",
"data": {}
}
}
}
},
"400": {
"description": "Bad request - Email is required",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"message": "Email is required",
"data": null,
"error": {
"code": 400,
"description": "Email is required",
"statusCode": 400
}
}
}
}
},
"404": {
"description": "User already registered",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"message": "User is already registered. Please login.",
"data": null,
"error": {
"code": 404,
"description": "User is already registered. Please login.",
"statusCode": 404
}
}
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/host/verify-otp": {
"post": {
"tags": ["Authentication"],
"summary": "Verify OTP",
"description": "Verifies the OTP sent to the user's email during registration",
"operationId": "verifyOtp",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/VerifyOtpRequest"
},
"example": {
"email": "host@example.com",
"otp": "123456"
}
}
}
},
"responses": {
"200": {
"description": "OTP verified successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SuccessResponse"
},
"example": {
"success": true,
"message": "OTP verified successfully",
"data": null
}
}
}
},
"400": {
"description": "Bad request - Invalid OTP or missing fields",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"examples": {
"missingFields": {
"value": {
"success": false,
"message": "Email and OTP are required",
"data": null,
"error": {
"code": 400,
"description": "Email and OTP are required",
"statusCode": 400
}
}
},
"invalidOtp": {
"value": {
"success": false,
"message": "Invalid OTP.",
"data": null,
"error": {
"code": 400,
"description": "Invalid OTP.",
"statusCode": 400
}
}
},
"expiredOtp": {
"value": {
"success": false,
"message": "OTP has expired.",
"data": null,
"error": {
"code": 400,
"description": "OTP has expired.",
"statusCode": 400
}
}
}
}
}
}
},
"404": {
"description": "User not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"message": "User not found.",
"data": null,
"error": {
"code": 404,
"description": "User not found.",
"statusCode": 404
}
}
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/host/login": {
"post": {
"tags": ["Authentication"],
"summary": "Login for host",
"description": "Authenticates a host user and returns an access token",
"operationId": "loginForHost",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoginRequest"
},
"example": {
"emailAddress": "host@example.com",
"userPassword": "password123"
}
}
}
},
"responses": {
"200": {
"description": "Login successful",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoginResponse"
},
"example": {
"success": true,
"message": "Login successful",
"data": {
"id": 1,
"firstName": "John",
"lastName": "Doe",
"emailAddress": "host@example.com",
"mobileNumber": "+1234567890",
"isActive": true,
"roleXid": 4,
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
}
}
},
"400": {
"description": "Bad request - Missing fields or failed login",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"message": "Email and password are required",
"data": null,
"error": {
"code": 400,
"description": "Email and password are required",
"statusCode": 400
}
}
}
}
},
"401": {
"description": "Unauthorized - Invalid credentials",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"message": "Invalid credentials",
"data": null,
"error": {
"code": 401,
"description": "Invalid credentials",
"statusCode": 401
}
}
}
}
},
"403": {
"description": "Forbidden - Not a host user",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"message": "Access denied. Not a host user.",
"data": null,
"error": {
"code": 403,
"description": "Access denied. Not a host user.",
"statusCode": 403
}
}
}
}
},
"404": {
"description": "User not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"message": "User not found",
"data": null,
"error": {
"code": 404,
"description": "User not found",
"statusCode": 404
}
}
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"bearer": {
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "JWT token obtained from login endpoint"
"type": "http"
}
},
"schemas": {
"HostHeader": {
"type": "object",
"properties": {
"hostParent": {
"type": "string",
"description": "Host parent name",
"example": "Example Host"
},
"hostRefNumber": {
"type": "string",
"description": "Host reference number",
"example": "HOST001"
},
"hostStatusDisplay": {
"type": "string",
"description": "Host status display",
"example": "Active"
},
"accountManager": {
"type": "string",
"description": "Account manager name",
"example": "John Doe"
}
}
},
"RegistrationRequest": {
"type": "object",
"required": ["email"],
"properties": {
"email": {
"type": "string",
"format": "email",
"description": "Email address for registration",
"example": "host@example.com"
}
}
},
"VerifyOtpRequest": {
"type": "object",
"required": ["email", "otp"],
"properties": {
"email": {
"type": "string",
"format": "email",
"description": "Email address used during registration",
"example": "host@example.com"
},
"otp": {
"type": "string",
"description": "6-digit OTP code sent to email",
"pattern": "^[0-9]{6}$",
"example": "123456"
}
}
},
"LoginRequest": {
"type": "object",
"required": ["emailAddress", "userPassword"],
"properties": {
"emailAddress": {
"type": "string",
"format": "email",
"description": "Host user email address",
"example": "host@example.com"
},
"userPassword": {
"type": "string",
"format": "password",
"description": "User password",
"minLength": 8,
"example": "password123"
}
}
},
"LoginResponseData": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "User ID",
"example": 1
},
"firstName": {
"type": "string",
"nullable": true,
"description": "User first name",
"example": "John"
},
"lastName": {
"type": "string",
"nullable": true,
"description": "User last name",
"example": "Doe"
},
"emailAddress": {
"type": "string",
"format": "email",
"description": "User email address",
"example": "host@example.com"
},
"mobileNumber": {
"type": "string",
"nullable": true,
"description": "User mobile number",
"example": "+1234567890"
},
"isActive": {
"type": "boolean",
"description": "User active status",
"example": true
},
"roleXid": {
"type": "integer",
"description": "User role ID (4 for host)",
"example": 4
},
"accessToken": {
"type": "string",
"description": "JWT access token for authentication",
"example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
},
"LoginResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"message": {
"type": "string",
"example": "Login successful"
},
"data": {
"$ref": "#/components/schemas/LoginResponseData"
}
}
},
"SuccessResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": true
},
"message": {
"type": "string",
"example": "Operation successful"
},
"data": {
"type": "object",
"description": "Response data (can be null or empty object)"
}
}
},
"ErrorResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"example": false
},
"message": {
"type": "string",
"description": "Error message",
"example": "An error occurred"
},
"data": {
"type": "object",
"nullable": true,
"example": null
},
"error": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"description": "HTTP status code",
"example": 400
},
"description": {
"type": "string",
"description": "Error description",
"example": "Bad request"
},
"statusCode": {
"type": "integer",
"description": "HTTP status code",
"example": 400
},
"debug": {
"type": "string",
"description": "Debug information (only in non-production environments)",
"example": "Stack trace..."
}
},
"required": ["code", "description", "statusCode"]
}
},
"required": ["success", "message", "data", "error"]
}
}
"schemas": {}
}
}
}