made get score api for pqq
This commit is contained in:
@@ -97,14 +97,15 @@ model UserAddressDetails {
|
||||
}
|
||||
|
||||
model UserDocuments {
|
||||
id Int @id @default(autoincrement())
|
||||
userXid Int @map("user_xid")
|
||||
user User @relation(fields: [userXid], references: [id], onDelete: Cascade)
|
||||
fileName String @map("file_name") @db.VarChar(500)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
id Int @id @default(autoincrement())
|
||||
userXid Int @map("user_xid")
|
||||
user User @relation(fields: [userXid], references: [id], onDelete: Cascade)
|
||||
// documentTypeName String @map("document_type_name") @db.VarChar(50)
|
||||
fileName String @map("file_name") @db.VarChar(500)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
|
||||
@@map("user_documents")
|
||||
@@schema("usr")
|
||||
@@ -654,8 +655,8 @@ model HostHeader {
|
||||
cities Cities @relation(fields: [cityXid], references: [id], onDelete: Restrict)
|
||||
stateXid Int @map("state_xid")
|
||||
states States @relation(fields: [stateXid], references: [id], onDelete: Restrict)
|
||||
countryXid Int @map("country_xid")
|
||||
countries Countries @relation(fields: [countryXid], references: [id], onDelete: Restrict)
|
||||
countryXid Int? @map("country_xid")
|
||||
countries Countries? @relation(fields: [countryXid], references: [id], onDelete: Restrict)
|
||||
pinCode String @map("pin_code") @db.VarChar(30)
|
||||
logoPath String? @map("logo_path") @db.VarChar(400)
|
||||
isSubsidairy Boolean @default(false) @map("is_subsidairy")
|
||||
@@ -669,14 +670,14 @@ model HostHeader {
|
||||
facebookUrl String? @map("facebook_url") @db.VarChar(80)
|
||||
linkedinUrl String? @map("linkedin_url") @db.VarChar(80)
|
||||
twitterUrl String? @map("twitter_url") @db.VarChar(80)
|
||||
currencyXid Int @map("currency_xid")
|
||||
currencies Currencies @relation(fields: [currencyXid], references: [id], onDelete: Restrict)
|
||||
stepper Int @default(1) @map("stepper")
|
||||
currencyXid Int? @map("currency_xid")
|
||||
currencies Currencies? @relation(fields: [currencyXid], references: [id], onDelete: Restrict)
|
||||
stepper Int? @default(1) @map("stepper")
|
||||
hostStatusInternal String @default("pending") @map("host_status_internal") @db.VarChar(20)
|
||||
hostStatusDisplay String @default("pending") @map("host_status_Display") @db.VarChar(20)
|
||||
adminStatusInternal String @default("pending") @map("admin_status_internal") @db.VarChar(20)
|
||||
adminStatusDisplay String @default("pending") @map("admin_status_display") @db.VarChar(20)
|
||||
amStatus String @default("pending") @map("am_status") @db.VarChar(20)
|
||||
amStatus String? @default("pending") @map("am_status") @db.VarChar(20)
|
||||
agreementAccepted Boolean @default(false) @map("agreement_accepted")
|
||||
accountManagerXid Int? @map("account_manager_xid")
|
||||
accountManager User? @relation("AccountManager", fields: [accountManagerXid], references: [id], onDelete: Restrict)
|
||||
|
||||
102
serverless.yml
102
serverless.yml
@@ -91,7 +91,7 @@ functions:
|
||||
- 'common/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -107,7 +107,7 @@ functions:
|
||||
- 'common/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -123,7 +123,7 @@ functions:
|
||||
- 'common/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
events:
|
||||
- httpApi:
|
||||
path: /host/login
|
||||
@@ -138,7 +138,7 @@ functions:
|
||||
- 'common/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
events:
|
||||
- httpApi:
|
||||
path: /host/registration
|
||||
@@ -168,7 +168,7 @@ functions:
|
||||
- 'common/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -183,7 +183,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -198,7 +198,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -213,7 +213,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -228,7 +228,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -243,7 +243,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -258,7 +258,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -275,7 +275,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -289,7 +289,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -303,7 +303,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -317,7 +317,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -331,7 +331,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -347,7 +347,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
- 'node_modules/@aws-sdk/**'
|
||||
- 'node_modules/@smithy/**'
|
||||
- 'node_modules/tslib/**'
|
||||
@@ -365,7 +365,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -379,7 +379,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -393,7 +393,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -407,7 +407,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -421,7 +421,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -435,14 +435,13 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
path: /minglaradmin/get-all-coadmin-and-am-details
|
||||
method: get
|
||||
|
||||
|
||||
|
||||
getAllInvitedCoadminAndAMDetails:
|
||||
handler: src/modules/minglaradmin/handlers/getAllInvitedCoadminAndAM.handler
|
||||
package:
|
||||
@@ -450,7 +449,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -464,7 +463,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -478,7 +477,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -492,7 +491,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -506,7 +505,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -520,7 +519,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -534,7 +533,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -548,7 +547,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -562,7 +561,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -576,7 +575,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -590,7 +589,7 @@ functions:
|
||||
- 'src/modules/minglaradmin/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
@@ -605,7 +604,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
- 'node_modules/@aws-sdk/**'
|
||||
- 'node_modules/@smithy/**'
|
||||
- 'node_modules/tslib/**'
|
||||
@@ -632,7 +631,7 @@ functions:
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
- 'node_modules/@aws-sdk/**'
|
||||
- 'node_modules/@smithy/**'
|
||||
- 'node_modules/tslib/**'
|
||||
@@ -650,3 +649,30 @@ functions:
|
||||
- httpApi:
|
||||
path: /host/submit-pqq-ans
|
||||
method: patch
|
||||
|
||||
submitFinalPqqAnswer:
|
||||
handler: src/modules/host/handlers/getPQQScore.handler
|
||||
package:
|
||||
patterns:
|
||||
- 'src/modules/host/handlers/getPQQScore.*'
|
||||
- 'src/modules/host/services/**'
|
||||
- 'src/common/**'
|
||||
- 'node_modules/@prisma/client/**'
|
||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||
- 'node_modules/@aws-sdk/**'
|
||||
- 'node_modules/@smithy/**'
|
||||
- 'node_modules/tslib/**'
|
||||
- 'node_modules/fast-xml-parser/**'
|
||||
- 'node_modules/lambda-multipart-parser/**'
|
||||
- 'node_modules/busboy/**'
|
||||
- 'node_modules/@aws-crypto/**'
|
||||
- 'node_modules/uuid/**'
|
||||
- 'node_modules/@aws/util-uri-escape/**'
|
||||
- 'node_modules/@aws/util-middleware/**'
|
||||
- 'node_modules/@aws/smithy-client/**'
|
||||
- 'node_modules/@aws/lambda-invoke-store/**'
|
||||
|
||||
events:
|
||||
- httpApi:
|
||||
path: /host/submit-final-pqq-ans
|
||||
method: patch
|
||||
|
||||
118
src/common/middlewares/jwt/authForMinglarAdmin&Host.ts
Normal file
118
src/common/middlewares/jwt/authForMinglarAdmin&Host.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import httpStatus from 'http-status';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import ApiError from '../../utils/helper/ApiError';
|
||||
import config from '../../../config/config';
|
||||
import { ROLE } from '@/common/utils/constants/common.constant';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
interface DecodedToken {
|
||||
id?: number;
|
||||
sub?: string | number;
|
||||
role?: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
interface UserPayload {
|
||||
id: string;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
declare module 'express-serve-static-core' {
|
||||
interface Request {
|
||||
user?: UserPayload;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Core authentication function - verifies JWT and validates Host user
|
||||
* Can be used by both Express middleware and Lambda handlers
|
||||
*/
|
||||
export async function verifyMinglarAdminHostToken(token: string): Promise<{ id: number; role?: string }> {
|
||||
if (!token) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, config.jwt.secret) as unknown as DecodedToken;
|
||||
|
||||
const userId = decoded.id ?? (decoded.sub ? Number(decoded.sub) : null);
|
||||
|
||||
if (!userId) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Invalid token payload');
|
||||
}
|
||||
|
||||
// ✅ Fetch user from Prisma (Host user only)
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: { role: true },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||
}
|
||||
|
||||
// ✅ Check if user is active
|
||||
if (user.isActive === false) {
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Your account is deactivated by admin.');
|
||||
}
|
||||
|
||||
// ✅ Check Minglar Roles (role_xid = 1, 2, 3)
|
||||
if (![ROLE.MINGLAR_ADMIN, ROLE.CO_ADMIN, ROLE.ACCOUNT_MANAGER, ROLE.HOST].includes(user.roleXid)) {
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
||||
}
|
||||
|
||||
|
||||
return { id: user.id, role: user.role?.roleName };
|
||||
} catch (error) {
|
||||
if (error instanceof jwt.TokenExpiredError) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Your session has expired. Please log in again.');
|
||||
}
|
||||
|
||||
if (error instanceof ApiError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Invalid or expired authentication token.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies JWT and validates Host user (role_xid = 1)
|
||||
*/
|
||||
const verifyCallback = async (
|
||||
req: Request,
|
||||
resolve: (value?: unknown) => void,
|
||||
reject: (reason?: Error) => void
|
||||
) => {
|
||||
const token = req.header('x-auth-token') || req.cookies?.accessToken;
|
||||
|
||||
try {
|
||||
const userInfo = await verifyMinglarAdminHostToken(token);
|
||||
|
||||
// Attach user to request
|
||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||
|
||||
resolve();
|
||||
} catch (error) {
|
||||
return reject(error as Error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Express middleware — use as `auth()` in routes
|
||||
*/
|
||||
const authForHost =
|
||||
() =>
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
verifyCallback(req, resolve, reject);
|
||||
})
|
||||
.then(() => next())
|
||||
.catch((err) => next(err));
|
||||
};
|
||||
|
||||
export default authForHost;
|
||||
@@ -21,4 +21,8 @@ export const STEPPER = {
|
||||
BANK_DETAILS_UPDATED: 4,
|
||||
AGREEMENT_ACCEPTED: 5,
|
||||
REJECTED: 6
|
||||
}
|
||||
|
||||
export const LAST_QUESTION_ID = {
|
||||
Q_ID: 55
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { PrismaService } from '../../../common/database/prisma.service';
|
||||
import ApiError from '../../../common/utils/helper/ApiError';
|
||||
import { verifyHostToken } from '../../../common/middlewares/jwt/authForHost';
|
||||
import { HostService } from '../services/host.service';
|
||||
import { verifyMinglarAdminHostToken } from '@/common/middlewares/jwt/authForMinglarAdmin&Host';
|
||||
|
||||
const prismaService = new PrismaService();
|
||||
const hostService = new HostService(prismaService);
|
||||
@@ -18,7 +19,7 @@ export const handler = safeHandler(async (
|
||||
}
|
||||
|
||||
// Verify token and get user info
|
||||
const userInfo = await verifyHostToken(token);
|
||||
const userInfo = await verifyMinglarAdminHostToken(token);
|
||||
const userId = Number(userInfo.id);
|
||||
|
||||
let body: { question_xid: number, activity_xid: number };
|
||||
|
||||
310
src/modules/host/handlers/getPQQScore.ts
Normal file
310
src/modules/host/handlers/getPQQScore.ts
Normal file
@@ -0,0 +1,310 @@
|
||||
import config from '@/config/config';
|
||||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
||||
import AWS from 'aws-sdk';
|
||||
import Busboy from 'busboy';
|
||||
import crypto from 'crypto';
|
||||
import { PrismaService } from '../../../common/database/prisma.service';
|
||||
import { verifyHostToken } from '../../../common/middlewares/jwt/authForHost';
|
||||
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
|
||||
import ApiError from '../../../common/utils/helper/ApiError';
|
||||
import { HostService } from '../services/host.service';
|
||||
import { LAST_QUESTION_ID } from '@/common/utils/constants/host.constant';
|
||||
|
||||
const prisma = new PrismaService();
|
||||
const pqqService = new HostService(prisma);
|
||||
|
||||
const s3 = new AWS.S3({ region: config.aws.region });
|
||||
|
||||
// Function to extract S3 key from URL
|
||||
function getS3KeyFromUrl(url: string): string {
|
||||
const bucketBaseUrl = `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/`;
|
||||
return url.replace(bucketBaseUrl, '');
|
||||
}
|
||||
|
||||
// Function to delete file from S3
|
||||
async function deleteFromS3(s3Key: string): Promise<void> {
|
||||
try {
|
||||
await s3.deleteObject({
|
||||
Bucket: config.aws.bucketName,
|
||||
Key: s3Key,
|
||||
}).promise();
|
||||
console.log(`✅ File deleted from S3: ${s3Key}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Error deleting file from S3: ${s3Key}`, error);
|
||||
// Don't throw error here, continue with upload
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, prefix: string, existingUrl?: string): Promise<string> {
|
||||
let s3Key: string;
|
||||
|
||||
// If existing URL provided, use the same S3 key to replace the file
|
||||
if (existingUrl) {
|
||||
s3Key = getS3KeyFromUrl(existingUrl);
|
||||
// Delete existing file first
|
||||
await deleteFromS3(s3Key);
|
||||
} else {
|
||||
// Generate new unique key for new file
|
||||
const uniqueKey = `${crypto.randomUUID()}_${originalName}`;
|
||||
s3Key = `${prefix}/${uniqueKey}`;
|
||||
}
|
||||
|
||||
// Upload new file (replaces existing if same key)
|
||||
await s3.upload({
|
||||
Bucket: config.aws.bucketName,
|
||||
Key: s3Key,
|
||||
Body: buffer,
|
||||
ContentType: mimeType,
|
||||
ACL: 'private'
|
||||
}).promise();
|
||||
|
||||
console.log(`✅ File uploaded to S3: ${s3Key}`);
|
||||
return `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${s3Key}`;
|
||||
}
|
||||
|
||||
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
||||
try {
|
||||
// 1) Auth
|
||||
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||
if (!token) throw new ApiError(401, 'Missing token.');
|
||||
const user = await verifyHostToken(token);
|
||||
|
||||
// 2) Content-Type check
|
||||
const contentType = event.headers["content-type"] || event.headers["Content-Type"];
|
||||
if (!contentType?.startsWith("multipart/form-data"))
|
||||
throw new ApiError(400, "Content-Type must be multipart/form-data");
|
||||
|
||||
if (!event.isBase64Encoded)
|
||||
throw new ApiError(400, "Body must be base64 encoded");
|
||||
|
||||
const bodyBuffer = Buffer.from(event.body!, "base64");
|
||||
|
||||
const fields: any = {};
|
||||
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
|
||||
|
||||
// 3) Parse multipart data
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const bb = Busboy({ headers: { 'content-type': contentType } });
|
||||
|
||||
bb.on('file', (fieldname, file, info) => {
|
||||
const { filename, mimeType } = info;
|
||||
|
||||
// Skip if no filename (empty file field)
|
||||
if (!filename) {
|
||||
file.resume();
|
||||
return;
|
||||
}
|
||||
|
||||
const chunks: Buffer[] = [];
|
||||
let totalSize = 0;
|
||||
const MAX_SIZE = 5 * 1024 * 1024; // 5 MB
|
||||
|
||||
file.on('data', (chunk) => {
|
||||
totalSize += chunk.length;
|
||||
if (totalSize > MAX_SIZE) {
|
||||
file.resume();
|
||||
return reject(new ApiError(400, `File ${filename} exceeds 5MB limit.`));
|
||||
}
|
||||
chunks.push(chunk);
|
||||
});
|
||||
|
||||
file.on('end', () => {
|
||||
// Only add file if we have data
|
||||
if (chunks.length > 0) {
|
||||
files.push({
|
||||
buffer: Buffer.concat(chunks),
|
||||
mimeType,
|
||||
fileName: filename,
|
||||
fieldName: fieldname,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
file.on('error', (err) => {
|
||||
reject(new ApiError(400, `File upload error: ${err.message}`));
|
||||
});
|
||||
});
|
||||
|
||||
bb.on('field', (fieldname, val) => {
|
||||
// Handle empty or null values
|
||||
if (val === '' || val === 'null' || val === 'undefined') {
|
||||
fields[fieldname] = null;
|
||||
} else {
|
||||
try {
|
||||
fields[fieldname] = JSON.parse(val);
|
||||
} catch {
|
||||
fields[fieldname] = val;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bb.on('close', () => {
|
||||
console.log("✅ Busboy parsing completed");
|
||||
console.log("📌 Fields:", fields);
|
||||
console.log("📁 Files:", files.length);
|
||||
resolve();
|
||||
});
|
||||
|
||||
bb.on('error', (err) => {
|
||||
console.error("❌ Busboy error:", err);
|
||||
reject(new ApiError(400, `Multipart parsing error: ${err.message}`));
|
||||
});
|
||||
|
||||
bb.end(bodyBuffer);
|
||||
});
|
||||
|
||||
// 4) Extract required fields - only activityXid, pqqQuestionXid, pqqAnswerXid are required
|
||||
const activityXid = Number(fields.activityXid);
|
||||
const pqqQuestionXid = Number(fields.pqqQuestionXid);
|
||||
const pqqAnswerXid = Number(fields.pqqAnswerXid);
|
||||
|
||||
// Comments and files are optional
|
||||
const comments = fields.comments || null;
|
||||
|
||||
// Validate required fields
|
||||
if (!activityXid || isNaN(activityXid)) throw new ApiError(400, "Valid activityXid is required");
|
||||
if (!pqqQuestionXid || isNaN(pqqQuestionXid)) throw new ApiError(400, "Valid pqqQuestionXid is required");
|
||||
if (pqqQuestionXid !== LAST_QUESTION_ID.Q_ID) throw new ApiError(400, "Wrong question id.")
|
||||
if (!pqqAnswerXid || isNaN(pqqAnswerXid)) throw new ApiError(400, "Valid pqqAnswerXid is required");
|
||||
|
||||
// console.log(`📝 Processing - Activity: ${activityXid}, Question: ${pqqQuestionXid}, Answer: ${pqqAnswerXid}`);
|
||||
// console.log(`💬 Comments: ${comments ? 'Provided' : 'Not provided'}`);
|
||||
// console.log(`📎 Files: ${files.length}`);
|
||||
|
||||
// 5) UPSERT: Check if header already exists for this combination
|
||||
const existingHeader = await pqqService.findHeaderByCompositeKey(
|
||||
activityXid,
|
||||
pqqQuestionXid,
|
||||
pqqAnswerXid
|
||||
);
|
||||
|
||||
let header;
|
||||
if (existingHeader) {
|
||||
console.log("🔄 Updating existing PQQ header");
|
||||
// Update existing header (comments can be null)
|
||||
const header = await pqqService.updateHeader(
|
||||
existingHeader.id,
|
||||
comments
|
||||
);
|
||||
} else {
|
||||
console.log("🆕 Creating new PQQ header");
|
||||
// Create new header (comments can be null)
|
||||
const header = await pqqService.createHeader(
|
||||
activityXid,
|
||||
pqqQuestionXid,
|
||||
pqqAnswerXid,
|
||||
comments
|
||||
);
|
||||
}
|
||||
// Calculate score after answer submission
|
||||
const score = await pqqService.calculatePqqScoreForUser(activityXid);
|
||||
|
||||
|
||||
// 6) Get existing supporting files for this header
|
||||
const existingSupportingFiles = await pqqService.getSupportingFilesByHeaderId(header.id);
|
||||
console.log(`📁 Found ${existingSupportingFiles.length} existing supporting files`);
|
||||
|
||||
// 7) Handle file UPSERT - only if files are provided
|
||||
const uploadedFiles: any[] = [];
|
||||
|
||||
if (files.length > 0) {
|
||||
console.log("📤 Processing file uploads...");
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const existingFile = existingSupportingFiles[i] || null;
|
||||
|
||||
const url = await uploadToS3(
|
||||
file.buffer,
|
||||
file.mimeType,
|
||||
file.fileName,
|
||||
`ActivityOnboarding/supportings/${activityXid}`,
|
||||
existingFile ? existingFile.mediaFileName : undefined
|
||||
);
|
||||
|
||||
let supporting;
|
||||
if (existingFile) {
|
||||
// Update existing supporting file record
|
||||
supporting = await pqqService.updateSupportingFile(
|
||||
existingFile.id,
|
||||
file.mimeType,
|
||||
url
|
||||
);
|
||||
console.log(`🔄 Updated supporting file: ${existingFile.id}`);
|
||||
} else {
|
||||
// Create new supporting file record
|
||||
supporting = await pqqService.addSupportingFile(
|
||||
header.id,
|
||||
file.mimeType,
|
||||
url
|
||||
);
|
||||
console.log(`🆕 Created new supporting file: ${supporting.id}`);
|
||||
}
|
||||
|
||||
uploadedFiles.push(supporting);
|
||||
}
|
||||
|
||||
// 8) Delete any remaining existing files that weren't replaced
|
||||
if (existingSupportingFiles.length > files.length) {
|
||||
const filesToDelete = existingSupportingFiles.slice(files.length);
|
||||
console.log(`🗑️ Deleting ${filesToDelete.length} unused supporting files`);
|
||||
|
||||
for (const fileToDelete of filesToDelete) {
|
||||
await pqqService.deleteSupportingFile(fileToDelete.id);
|
||||
// Also delete from S3
|
||||
const s3Key = getS3KeyFromUrl(fileToDelete.mediaFileName);
|
||||
await deleteFromS3(s3Key);
|
||||
console.log(`🗑️ Deleted supporting file: ${fileToDelete.id}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("📭 No files provided in request");
|
||||
|
||||
// If no files provided but existing files exist, delete them (cleanup)
|
||||
if (existingSupportingFiles.length > 0) {
|
||||
console.log(`🗑️ No new files provided, deleting ${existingSupportingFiles.length} existing files`);
|
||||
for (const fileToDelete of existingSupportingFiles) {
|
||||
await pqqService.deleteSupportingFile(fileToDelete.id);
|
||||
const s3Key = getS3KeyFromUrl(fileToDelete.mediaFileName);
|
||||
await deleteFromS3(s3Key);
|
||||
console.log(`🗑️ Deleted supporting file: ${fileToDelete.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9) Prepare response
|
||||
const responseMessage = existingHeader ? "PQQ answer updated successfully" : "PQQ answer submitted successfully";
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
message: responseMessage,
|
||||
data: {
|
||||
headerId: header.id,
|
||||
activityXid,
|
||||
pqqQuestionXid,
|
||||
pqqAnswerXid,
|
||||
comments: comments,
|
||||
score,
|
||||
files: {
|
||||
uploaded: uploadedFiles,
|
||||
total: uploadedFiles.length
|
||||
},
|
||||
operation: existingHeader ? 'updated' : 'created',
|
||||
fileOperation: files.length > 0 ?
|
||||
(existingSupportingFiles.length > 0 ? 'replaced' : 'added') :
|
||||
(existingSupportingFiles.length > 0 ? 'removed' : 'unchanged')
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
} catch (error: any) {
|
||||
console.error("❌ Error in submitPqqAnswer:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
import { verifyMinglarAdminHostToken } from '@/common/middlewares/jwt/authForMinglarAdmin&Host';
|
||||
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 { safeHandler } from '../../../common/utils/handlers/safeHandler';
|
||||
import ApiError from '../../../common/utils/helper/ApiError';
|
||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
||||
import { HostService } from '../services/host.service';
|
||||
|
||||
const prismaService = new PrismaService();
|
||||
const hostService = new HostService(prismaService);
|
||||
@@ -18,7 +18,7 @@ export const handler = safeHandler(async (
|
||||
throw new ApiError(400, 'This is a protected route. Please provide a valid token.');
|
||||
}
|
||||
|
||||
const userInfo = await verifyHostToken(token);
|
||||
const userInfo = await verifyMinglarAdminHostToken(token);
|
||||
const id = Number(userInfo.id)
|
||||
|
||||
if (!id) {
|
||||
|
||||
@@ -48,9 +48,17 @@ export class HostService {
|
||||
include: {
|
||||
hostParent: true,
|
||||
HostBankDetails: true,
|
||||
HostDocuments: true,
|
||||
HostDocuments: {
|
||||
include: {
|
||||
documentType: true,
|
||||
},
|
||||
},
|
||||
HostSuggestion: true,
|
||||
HostTrack: true,
|
||||
countries: true,
|
||||
currencies: true,
|
||||
states: true,
|
||||
cities: true,
|
||||
}
|
||||
});
|
||||
|
||||
@@ -241,7 +249,8 @@ export class HostService {
|
||||
select: {
|
||||
pqqQuestionXid: true,
|
||||
pqqAnswerXid: true,
|
||||
ActivityPQQSupportings: true
|
||||
ActivityPQQSupportings: true,
|
||||
ActivityPQQSuggestions: true
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -293,14 +302,14 @@ export class HostService {
|
||||
|
||||
const createdHost = await tx.hostHeader.create({
|
||||
data: {
|
||||
userXid: user_xid,
|
||||
user: { connect: { id: user_xid } },
|
||||
companyName: companyData.companyName,
|
||||
hostRefNumber: refNumber,
|
||||
address1: companyData.address1,
|
||||
address2: companyData.address2,
|
||||
cityXid: companyData.cityXid,
|
||||
stateXid: companyData.stateXid,
|
||||
countryXid: companyData.countryXid,
|
||||
cities: { connect: { id: companyData.cityXid } },
|
||||
states: { connect: { id: companyData.stateXid } },
|
||||
countries: { connect: { id: companyData.countryXid } },
|
||||
pinCode: companyData.pinCode,
|
||||
logoPath: companyData.logoPath || null,
|
||||
isSubsidairy: companyData.isSubsidairy,
|
||||
@@ -314,7 +323,7 @@ export class HostService {
|
||||
facebookUrl: companyData.facebookUrl || null,
|
||||
linkedinUrl: companyData.linkedinUrl || null,
|
||||
twitterUrl: companyData.twitterUrl || null,
|
||||
currencyXid: companyData.currencyXid,
|
||||
// currencyXid: companyData.currencyXid,
|
||||
stepper: STEPPER.UNDER_REVIEW,
|
||||
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
|
||||
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
|
||||
@@ -383,9 +392,9 @@ export class HostService {
|
||||
companyName: companyData.companyName,
|
||||
address1: companyData.address1,
|
||||
address2: companyData.address2,
|
||||
cityXid: companyData.cityXid,
|
||||
stateXid: companyData.stateXid,
|
||||
countryXid: companyData.countryXid,
|
||||
cities: { connect: { id: companyData.cityXid } },
|
||||
states: { connect: { id: companyData.stateXid } },
|
||||
countries: { connect: { id: companyData.countryXid } },
|
||||
pinCode: companyData.pinCode,
|
||||
logoPath: companyData.logoPath || null,
|
||||
isSubsidairy: companyData.isSubsidairy,
|
||||
@@ -399,7 +408,7 @@ export class HostService {
|
||||
facebookUrl: companyData.facebookUrl || null,
|
||||
linkedinUrl: companyData.linkedinUrl || null,
|
||||
twitterUrl: companyData.twitterUrl || null,
|
||||
currencyXid: companyData.currencyXid,
|
||||
// currencyXid: companyData.currencyXid,
|
||||
stepper: STEPPER.UNDER_REVIEW
|
||||
// hostRefNumber: DO NOT UPDATE
|
||||
},
|
||||
@@ -628,6 +637,87 @@ export class HostService {
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
async calculatePqqScoreForUser(activityXid: number) {
|
||||
// 1. Get all headers for this activity (user's answers)
|
||||
const answers = await this.prisma.activityPQQheader.findMany({
|
||||
where: { activityXid },
|
||||
include: {
|
||||
pqqQuestions: {
|
||||
include: {
|
||||
pqqSubCategories: {
|
||||
include: {
|
||||
category: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pqqAnswers: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!answers.length) {
|
||||
return {
|
||||
overallPercentage: 0,
|
||||
categoryWise: {}
|
||||
};
|
||||
}
|
||||
|
||||
// Prepare accumulators
|
||||
let totalUserPoints = 0;
|
||||
let totalMaxPoints = 0;
|
||||
|
||||
// For category-wise scoring
|
||||
const categories: any = {}; // { [categoryId]: { userPoints, maxPoints, name } }
|
||||
|
||||
for (const item of answers) {
|
||||
const question = item.pqqQuestions;
|
||||
const answer = item.pqqAnswers;
|
||||
|
||||
const maxPoints = question.maxPoints;
|
||||
const userPoints = answer.answerPoints;
|
||||
|
||||
totalUserPoints += userPoints;
|
||||
totalMaxPoints += maxPoints;
|
||||
|
||||
// Category info
|
||||
const category = question.pqqSubCategories.category;
|
||||
const categoryId = category.id;
|
||||
|
||||
if (!categories[categoryId]) {
|
||||
categories[categoryId] = {
|
||||
categoryId,
|
||||
categoryName: category.categoryName,
|
||||
userPoints: 0,
|
||||
maxPoints: 0
|
||||
};
|
||||
}
|
||||
|
||||
categories[categoryId].userPoints += userPoints;
|
||||
categories[categoryId].maxPoints += maxPoints;
|
||||
}
|
||||
|
||||
// Overall percent
|
||||
const overallPercentage = totalMaxPoints > 0
|
||||
? (totalUserPoints / totalMaxPoints) * 100
|
||||
: 0;
|
||||
|
||||
// Category percentages
|
||||
const categoryWise: any = {};
|
||||
|
||||
for (const catId in categories) {
|
||||
const c = categories[catId];
|
||||
categoryWise[c.categoryName] =
|
||||
c.maxPoints > 0 ? (c.userPoints / c.maxPoints) * 100 : 0;
|
||||
}
|
||||
|
||||
return {
|
||||
overallPercentage,
|
||||
categoryWise
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async createHeader(
|
||||
activityXid: number,
|
||||
pqqQuestionXid: number,
|
||||
|
||||
@@ -485,82 +485,87 @@ export class MinglarService {
|
||||
})
|
||||
}
|
||||
|
||||
async getAllHostApplications(userId: number, userRoleXid: number, search?: string, userStatus?: string) {
|
||||
// Build where clause based on user role
|
||||
const whereClause: any = {
|
||||
async getAllHostApplications(
|
||||
userId: number,
|
||||
userRoleXid: number,
|
||||
search?: string,
|
||||
userStatus?: string
|
||||
) {
|
||||
const filters: any = {
|
||||
isActive: true,
|
||||
user: {
|
||||
roleXid: {
|
||||
notIn: [ROLE.CO_ADMIN, ROLE.ACCOUNT_MANAGER]
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add search filter if search query is provided
|
||||
if (search && search.trim() !== '') {
|
||||
const searchTerm = search.trim();
|
||||
/** -----------------------------------
|
||||
* SEARCH FILTER (ID, EMAIL, NAME)
|
||||
* ----------------------------------- */
|
||||
if (search?.trim()) {
|
||||
const term = search.trim();
|
||||
|
||||
// Check if search term is a number (for ID search)
|
||||
const isNumeric = /^\d+$/.test(searchTerm);
|
||||
|
||||
if (isNumeric) {
|
||||
// Search by host ID
|
||||
whereClause.id = parseInt(searchTerm);
|
||||
if (/^\d+$/.test(term)) {
|
||||
// Search by Host ID
|
||||
filters.id = Number(term);
|
||||
} else {
|
||||
// Search by email or name
|
||||
whereClause.user = {
|
||||
...whereClause.user,
|
||||
filters.user = {
|
||||
...filters.user,
|
||||
OR: [
|
||||
{ emailAddress: { contains: searchTerm, mode: 'insensitive' } },
|
||||
{ firstName: { contains: searchTerm, mode: 'insensitive' } },
|
||||
{ lastName: { contains: searchTerm, mode: 'insensitive' } }
|
||||
{ emailAddress: { contains: term, mode: "insensitive" } },
|
||||
{ firstName: { contains: term, mode: "insensitive" } },
|
||||
{ lastName: { contains: term, mode: "insensitive" } }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
// Apply userStatus filter (case-insensitive) e.g. userStatus=new / NEW / New
|
||||
if (userStatus && userStatus.trim().toLowerCase() === MINGLAR_STATUS_DISPLAY.NEW.toLowerCase()) {
|
||||
whereClause.adminStatusDisplay = MINGLAR_STATUS_DISPLAY.NEW;
|
||||
|
||||
/** -----------------------------------
|
||||
* USER STATUS FILTER (NEW)
|
||||
* ----------------------------------- */
|
||||
if (
|
||||
userStatus &&
|
||||
userStatus.trim().toLowerCase() === MINGLAR_STATUS_DISPLAY.NEW.toLowerCase()
|
||||
) {
|
||||
filters.adminStatusDisplay = MINGLAR_STATUS_DISPLAY.NEW;
|
||||
}
|
||||
|
||||
/** -----------------------------------
|
||||
* ROLE-BASED FILTER:
|
||||
* CO_ADMIN & ACCOUNT_MANAGER only see assigned hosts
|
||||
* ----------------------------------- */
|
||||
if (userRoleXid === ROLE.CO_ADMIN || userRoleXid === ROLE.ACCOUNT_MANAGER) {
|
||||
whereClause.accountManagerXid = userId;
|
||||
filters.accountManagerXid = userId;
|
||||
}
|
||||
|
||||
const hostHeaders = await this.prisma.hostHeader.findMany({
|
||||
where: whereClause,
|
||||
/** -----------------------------------
|
||||
* MAIN QUERY
|
||||
* ----------------------------------- */
|
||||
const results = await this.prisma.hostHeader.findMany({
|
||||
where: filters,
|
||||
select: {
|
||||
id: true,
|
||||
hostStatusInternal: true,
|
||||
hostStatusDisplay: true,
|
||||
adminStatusDisplay: true,
|
||||
adminStatusInternal: true,
|
||||
createdAt: true,
|
||||
companyName: true,
|
||||
assignedOn: true,
|
||||
cities: {
|
||||
select: {
|
||||
id: true,
|
||||
cityName: true,
|
||||
}
|
||||
},
|
||||
adminStatusDisplay: true,
|
||||
countries: {
|
||||
select: {
|
||||
id: true,
|
||||
countryName: true,
|
||||
}
|
||||
},
|
||||
states: {
|
||||
select: {
|
||||
id: true,
|
||||
stateName: true,
|
||||
}
|
||||
},
|
||||
|
||||
cities: { select: { id: true, cityName: true } },
|
||||
states: { select: { id: true, stateName: true } },
|
||||
countries: { select: { id: true, countryName: true } },
|
||||
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
emailAddress: true,
|
||||
mobileNumber: true,
|
||||
mobileNumber: true
|
||||
}
|
||||
},
|
||||
accountManager: {
|
||||
@@ -570,30 +575,34 @@ export class MinglarService {
|
||||
lastName: true,
|
||||
emailAddress: true,
|
||||
mobileNumber: true,
|
||||
roleXid: true,
|
||||
roleXid: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
orderBy: { createdAt: "desc" }
|
||||
});
|
||||
|
||||
// Transform the data to return host, hostStatusDisplay, submittedOn, and accountManager details
|
||||
return hostHeaders.map(host => ({
|
||||
hostId: host.id,
|
||||
host: host.user,
|
||||
hostStatusDisplay: host.hostStatusDisplay,
|
||||
submittedOn: host.createdAt,
|
||||
accountManager: host.accountManager || null,
|
||||
companyName: host.companyName || null,
|
||||
city: host.cities || null,
|
||||
state: host.states || null,
|
||||
country: host.countries || null,
|
||||
assignedOn: host.assignedOn || null,
|
||||
/** -----------------------------------
|
||||
* TRANSFORM RESPONSE
|
||||
* ----------------------------------- */
|
||||
return results.map(h => ({
|
||||
hostId: h.id,
|
||||
host: h.user,
|
||||
hostStatusDisplay: h.hostStatusDisplay,
|
||||
hostStatusInternal: h.hostStatusInternal,
|
||||
adminStatusDisplay: h.adminStatusDisplay,
|
||||
adminStatusInternal: h.adminStatusInternal,
|
||||
submittedOn: h.createdAt,
|
||||
accountManager: h.accountManager || null,
|
||||
companyName: h.companyName || null,
|
||||
city: h.cities || null,
|
||||
state: h.states || null,
|
||||
country: h.countries || null,
|
||||
assignedOn: h.assignedOn || null
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
async getAllCoadminAndAM() {
|
||||
// 1. Fetch all required users (Admin, Co-Admin, AM)
|
||||
const users = await this.prisma.user.findMany({
|
||||
|
||||
Reference in New Issue
Block a user