made register and login apis for host
This commit is contained in:
130
package-lock.json
generated
130
package-lock.json
generated
@@ -21,11 +21,14 @@
|
||||
"@types/http-status": "^1.1.2",
|
||||
"ajv": "8.12.0",
|
||||
"aws-lambda": "^1.0.7",
|
||||
"bcrypt": "^6.0.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"helmet": "^7.1.0",
|
||||
"http-status": "^2.1.0",
|
||||
"moment": "^2.30.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
@@ -33,7 +36,8 @@
|
||||
"rxjs": "^7.8.1",
|
||||
"serverless": "4.17.0",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"yup": "^1.7.1"
|
||||
"yup": "^1.7.1",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.3.0",
|
||||
@@ -61,6 +65,7 @@
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
},
|
||||
@@ -813,7 +818,6 @@
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -830,7 +834,6 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -847,7 +850,6 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -864,7 +866,6 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -881,7 +882,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -898,7 +898,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -915,7 +914,6 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -932,7 +930,6 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -949,7 +946,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -966,7 +962,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -983,7 +978,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1000,7 +994,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1017,7 +1010,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1034,7 +1026,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1051,7 +1042,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1068,7 +1058,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1085,7 +1074,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1102,7 +1090,6 @@
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1119,7 +1106,6 @@
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1136,7 +1122,6 @@
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1153,7 +1138,6 @@
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1170,7 +1154,6 @@
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1187,7 +1170,6 @@
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1204,7 +1186,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1221,7 +1202,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1238,7 +1218,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -4131,6 +4110,27 @@
|
||||
"baseline-browser-mapping": "dist/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/bcrypt": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz",
|
||||
"integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-addon-api": "^8.3.0",
|
||||
"node-gyp-build": "^4.8.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/bcrypt/node_modules/node-addon-api": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz",
|
||||
"integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==",
|
||||
"engines": {
|
||||
"node": "^18 || ^20 || >= 21"
|
||||
}
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||
@@ -4996,6 +4996,15 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
@@ -5342,7 +5351,6 @@
|
||||
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
@@ -6311,6 +6319,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
|
||||
"integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
@@ -8235,6 +8255,14 @@
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -8329,6 +8357,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/node-int64": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||
@@ -9161,6 +9199,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve.exports": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
|
||||
@@ -10464,6 +10511,25 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.20.6",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz",
|
||||
"integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
@@ -11168,6 +11234,14 @@
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
|
||||
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
package.json
10
package.json
@@ -22,7 +22,8 @@
|
||||
"prisma:push": "prisma db push",
|
||||
"prisma:migrate": "prisma migrate dev",
|
||||
"prisma:studio": "prisma studio",
|
||||
"prisma:seed": "ts-node prisma/seed.ts"
|
||||
"prisma:seed": "ts-node prisma/seed.ts",
|
||||
"seeder": "tsx prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.3.0",
|
||||
@@ -37,11 +38,14 @@
|
||||
"@types/http-status": "^1.1.2",
|
||||
"ajv": "8.12.0",
|
||||
"aws-lambda": "^1.0.7",
|
||||
"bcrypt": "^6.0.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"helmet": "^7.1.0",
|
||||
"http-status": "^2.1.0",
|
||||
"moment": "^2.30.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
@@ -49,7 +53,8 @@
|
||||
"rxjs": "^7.8.1",
|
||||
"serverless": "4.17.0",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"yup": "^1.7.1"
|
||||
"yup": "^1.7.1",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.3.0",
|
||||
@@ -77,6 +82,7 @@
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"jest": {
|
||||
|
||||
6
prisma/prisma.ts
Normal file
6
prisma/prisma.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
export const prisma = new PrismaClient();
|
||||
process.on('SIGINT', async () => {
|
||||
await prisma.$disconnect();
|
||||
process.exit(0);
|
||||
});
|
||||
@@ -19,7 +19,8 @@ model User {
|
||||
emailAddress String @unique @map("email_address")
|
||||
isdCode String? @map("isd_code")
|
||||
mobileNumber String? @map("mobile_number")
|
||||
userPasscode String? @map("user_password")
|
||||
userPassword String? @map("user_password")
|
||||
userPasscode String? @map("user_passcode")
|
||||
isEmailVerfied Boolean? @default(false) @map("is_email_verified")
|
||||
isMobileVerfied Boolean? @default(false) @map("is_mobile_verified")
|
||||
isActive Boolean? @default(true) @map("is_active")
|
||||
|
||||
291
prisma/seed.ts
Normal file
291
prisma/seed.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
// ✅ Countries
|
||||
const india = await prisma.countries.upsert({
|
||||
where: { countryName: 'India' },
|
||||
update: {},
|
||||
create: {
|
||||
countryName: 'India',
|
||||
countryCode: 'IN',
|
||||
countryFlag: '🇮🇳',
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
|
||||
const usa = await prisma.countries.upsert({
|
||||
where: { countryName: 'United States' },
|
||||
update: {},
|
||||
create: {
|
||||
countryName: 'United States',
|
||||
countryCode: 'US',
|
||||
countryFlag: '🇺🇸',
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ Currencies
|
||||
await prisma.currencies.createMany({
|
||||
data: [
|
||||
{ countryXid: india.id, currencyName: 'Indian Rupee', currencySymbol: '₹' },
|
||||
{ countryXid: usa.id, currencyName: 'US Dollar', currencySymbol: '$' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ States
|
||||
const maharashtra = await prisma.states.upsert({
|
||||
where: { stateName: 'Maharashtra' },
|
||||
update: {},
|
||||
create: { countryXid: india.id, stateName: 'Maharashtra' },
|
||||
});
|
||||
|
||||
const california = await prisma.states.upsert({
|
||||
where: { stateName: 'California' },
|
||||
update: {},
|
||||
create: { countryXid: usa.id, stateName: 'California' },
|
||||
});
|
||||
|
||||
// ✅ Cities
|
||||
await prisma.cities.createMany({
|
||||
data: [
|
||||
{ stateXid: maharashtra.id, cityName: 'Mumbai' },
|
||||
{ stateXid: california.id, cityName: 'Los Angeles' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ Taxes
|
||||
await prisma.taxes.createMany({
|
||||
data: [
|
||||
{ countryXid: india.id, taxName: 'GST', taxPer: 18 },
|
||||
{ countryXid: usa.id, taxName: 'VAT', taxPer: 10 },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ Banks
|
||||
const hdfc = await prisma.banks.upsert({
|
||||
where: { bankName: 'HDFC Bank' },
|
||||
update: {},
|
||||
create: { countryXid: india.id, bankName: 'HDFC Bank' },
|
||||
});
|
||||
|
||||
const chase = await prisma.banks.upsert({
|
||||
where: { bankName: 'Chase Bank' },
|
||||
update: {},
|
||||
create: { countryXid: usa.id, bankName: 'Chase Bank' },
|
||||
});
|
||||
|
||||
// ✅ Bank Branches
|
||||
await prisma.bankBranches.createMany({
|
||||
data: [
|
||||
{
|
||||
bankXid: hdfc.id,
|
||||
stateXid: maharashtra.id,
|
||||
cityXid: (await prisma.cities.findFirst({ where: { cityName: 'Mumbai' } }))!.id,
|
||||
branchAddress: 'HDFC Fort Branch, Mumbai',
|
||||
ifscCode: 'HDFC0001234',
|
||||
},
|
||||
{
|
||||
bankXid: chase.id,
|
||||
stateXid: california.id,
|
||||
cityXid: (await prisma.cities.findFirst({ where: { cityName: 'Los Angeles' } }))!.id,
|
||||
branchAddress: 'Chase Downtown LA',
|
||||
ifscCode: 'CHASUS12345',
|
||||
},
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ Interests + Activity Types
|
||||
const chillandzen = await prisma.interests.upsert({
|
||||
where: { interestName: 'Chill and Zen' },
|
||||
update: {},
|
||||
create: { interestName: 'Chill and Zen', displayOrder: 1 },
|
||||
});
|
||||
const sweatmode = await prisma.interests.upsert({
|
||||
where: { interestName: 'Sweat Mode' },
|
||||
update: {},
|
||||
create: { interestName: 'Sweat Mode', displayOrder: 2 },
|
||||
});
|
||||
const gameon = await prisma.interests.upsert({
|
||||
where: { interestName: 'Game On' },
|
||||
update: {},
|
||||
create: { interestName: 'Game On', displayOrder: 3 },
|
||||
});
|
||||
const partycentral = await prisma.interests.upsert({
|
||||
where: { interestName: 'Party Central' },
|
||||
update: {},
|
||||
create: { interestName: 'Party Central', displayOrder: 4 },
|
||||
});
|
||||
const artsy = await prisma.interests.upsert({
|
||||
where: { interestName: 'Artsy' },
|
||||
update: {},
|
||||
create: { interestName: 'Artsy', displayOrder: 5 },
|
||||
});
|
||||
const foodiediaries = await prisma.interests.upsert({
|
||||
where: { interestName: 'Foodie Diaries' },
|
||||
update: {},
|
||||
create: { interestName: 'Foodie Diaries', displayOrder: 6 },
|
||||
});
|
||||
|
||||
await prisma.activityTypes.createMany({
|
||||
data: [
|
||||
{ interestXid: chillandzen.id, activityTypeName: 'Cricket' },
|
||||
{ interestXid: chillandzen.id, activityTypeName: 'Football' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ Document Types
|
||||
await prisma.documentType.createMany({
|
||||
data: [
|
||||
{ documentTypeName: 'GST Certificate', displayOrder: 1 },
|
||||
{ documentTypeName: 'PAN / BIN Card', displayOrder: 2 },
|
||||
{ documentTypeName: 'Registration Certification', displayOrder: 3 },
|
||||
{ documentTypeName: 'Aadhaar Card', displayOrder: 4 },
|
||||
{ documentTypeName: 'Fire Insurance', displayOrder: 5 },
|
||||
{ documentTypeName: 'ATOAI Certification', displayOrder: 6 },
|
||||
{ documentTypeName: 'FASSI Certification', displayOrder: 7 },
|
||||
{ documentTypeName: 'Safety Certification', displayOrder: 8 },
|
||||
{ documentTypeName: 'Others', displayOrder: 9 },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
// ✅ Amenities
|
||||
await prisma.amenities.createMany({
|
||||
data: [
|
||||
{ amenitiesName: 'Wi-Fi' },
|
||||
{ amenitiesName: 'Air Conditioner' },
|
||||
{ amenitiesName: 'Phone Charger' },
|
||||
{ amenitiesName: 'Mobile Network' },
|
||||
],
|
||||
skipDuplicates: true, // prevents error if already seeded
|
||||
});
|
||||
|
||||
// ✅ Food Types
|
||||
await prisma.foodTypes.createMany({
|
||||
data: [
|
||||
{ foodTypeName: 'Veg' },
|
||||
{ foodTypeName: 'Non-Veg' },
|
||||
{ foodTypeName: 'Vegan' },
|
||||
{ foodTypeName: 'Jain' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ AGE RESTRICTIONS
|
||||
await prisma.ageRestrictions.createMany({
|
||||
data: [
|
||||
{ ageRestrictionName: 'Below 18', minAge: 1, maxAge: 17 },
|
||||
{ ageRestrictionName: 'Above 18', minAge: 18, maxAge: 200 },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ ALLOWED ENTRY TYPES
|
||||
await prisma.allowedEntryTypes.createMany({
|
||||
data: [
|
||||
{ allowedEntryTypeName: 'Solo' },
|
||||
{ allowedEntryTypeName: 'Couple' },
|
||||
{ allowedEntryTypeName: 'Group' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ ROLES
|
||||
await prisma.roles.createMany({
|
||||
data: [
|
||||
{ roleName: 'Admin' },
|
||||
{ roleName: 'Co_Admin' },
|
||||
{ roleName: 'Account_Manager' },
|
||||
{ roleName: 'Host' },
|
||||
{ roleName: 'Operator' },
|
||||
{ roleName: 'User' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ Frequencies
|
||||
await prisma.frequencies.createMany({
|
||||
data: [
|
||||
{ frequencyName: 'Single Event' },
|
||||
{ frequencyName: 'Multiple Events Per Day' },
|
||||
{ frequencyName: 'Training' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ Navigation Modes
|
||||
await prisma.navigationModes.createMany({
|
||||
data: [
|
||||
{ navigationModeName: 'Elephant Ride', navigationModeIcon: '🚗' },
|
||||
{ navigationModeName: 'Horse Ride', navigationModeIcon: '🏍️' },
|
||||
{ navigationModeName: 'Camel Ride', navigationModeIcon: '🚶' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ Transport Modes
|
||||
await prisma.transportModes.createMany({
|
||||
data: [
|
||||
{ transportModeName: 'Open Jeep / Car / SUV - 4 seater', transportModeIcon: '🚌' },
|
||||
{ transportModeName: 'Open Jeep / Car / SUV - 6 seater', transportModeIcon: '🚌' },
|
||||
{ transportModeName: '4W Jeep / SUV - 4 seater', transportModeIcon: '🚆' },
|
||||
{ transportModeName: '4W Jeep / SUV - 6 seater', transportModeIcon: '🚆' },
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
// ✅ PQQ Categories + Questions + Answers
|
||||
const category = await prisma.pQQCategories.upsert({
|
||||
where: { categoryName: 'General' },
|
||||
update: {},
|
||||
create: {
|
||||
categoryName: 'General',
|
||||
subCategoryName: 'Basic',
|
||||
categoryTitle: 'General Information',
|
||||
displayOrder: 1,
|
||||
isActive: true,
|
||||
PQQQuestions: {
|
||||
create: [
|
||||
{
|
||||
questionName: 'Do you have insurance?',
|
||||
displayOrder: 1,
|
||||
PQQAnswers: {
|
||||
create: [
|
||||
{ answerName: 'Yes', answerScore: '10', displayOrder: 1 },
|
||||
{ answerName: 'No', answerScore: '0', displayOrder: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
questionName: 'Do you have ISO certification?',
|
||||
displayOrder: 2,
|
||||
PQQAnswers: {
|
||||
create: [
|
||||
{ answerName: 'Yes', answerScore: '20', displayOrder: 1 },
|
||||
{ answerName: 'No', answerScore: '0', displayOrder: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
include: { PQQQuestions: true },
|
||||
});
|
||||
|
||||
console.log('✅ Seed data inserted successfully!');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
@@ -7,6 +7,11 @@ provider:
|
||||
versionFunctions: false
|
||||
environment:
|
||||
DATABASE_URL: ${env:DATABASE_URL}
|
||||
DB_USERNAME: ${env:DB_USERNAME}
|
||||
DB_PASSWORD: ${env:DB_PASSWORD}
|
||||
DB_DATABASE_NAME: ${env:DB_DATABASE_NAME}
|
||||
DB_HOSTNAME: ${env:DB_HOSTNAME}
|
||||
DB_PORT: ${env:DB_PORT}
|
||||
BY_PASS_EMAIL: ${env:BY_PASS_EMAIL}
|
||||
BYPASS_OTP: ${env:BYPASS_OTP}
|
||||
BREVO_EMAIL_API_KEY: ${env:BREVO_EMAIL_API_KEY}
|
||||
@@ -19,6 +24,8 @@ provider:
|
||||
REFRESH_TOKEN_SECRET: ${env:REFRESH_TOKEN_SECRET}
|
||||
JWT_SECRET: ${env:JWT_SECRET}
|
||||
SALT_ROUNDS: ${env:SALT_ROUNDS}
|
||||
NODE_ENV: ${env:NODE_ENV}
|
||||
|
||||
|
||||
httpApi:
|
||||
cors:
|
||||
@@ -71,3 +78,59 @@ functions:
|
||||
- httpApi:
|
||||
path: /host
|
||||
method: get
|
||||
|
||||
verifyOtp:
|
||||
handler: src/modules/host/handlers/verifyOtp.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/verify-otp
|
||||
method: post
|
||||
|
||||
loginForHost:
|
||||
handler: src/modules/host/handlers/loginForHost.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/login
|
||||
method: post
|
||||
|
||||
registrationOfHost:
|
||||
handler: src/modules/host/handlers/registration.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/registration
|
||||
method: post
|
||||
|
||||
createPasswordForHost:
|
||||
handler: src/modules/host/handlers/createPassword.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/create-password
|
||||
method: post
|
||||
@@ -8,7 +8,8 @@ import config from '../../../config/config';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
interface DecodedToken {
|
||||
id: number;
|
||||
id?: number;
|
||||
sub?: string | number;
|
||||
role?: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
@@ -26,7 +27,59 @@ declare module 'express-serve-static-core' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies JWT and validates Host user (role_xid = 3)
|
||||
* Core authentication function - verifies JWT and validates Host user
|
||||
* Can be used by both Express middleware and Lambda handlers
|
||||
*/
|
||||
export async function verifyHostToken(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 Host role (role_xid = 4)
|
||||
if (user.roleXid !== 4) {
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Access restricted to host users only');
|
||||
}
|
||||
|
||||
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 = 4)
|
||||
*/
|
||||
const verifyCallback = async (
|
||||
req: Request,
|
||||
@@ -35,62 +88,22 @@ const verifyCallback = async (
|
||||
) => {
|
||||
const token = req.header('x-auth-token') || req.cookies?.accessToken;
|
||||
|
||||
if (!token) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate'));
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, config.jwt.secret) as DecodedToken;
|
||||
|
||||
if (!decoded?.id) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Invalid token payload'));
|
||||
}
|
||||
|
||||
// ✅ Fetch user from Prisma (Host user only)
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: decoded.id },
|
||||
include: { role: true },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'User not found'));
|
||||
}
|
||||
|
||||
// ✅ Check if user is active
|
||||
if (!user.isActive) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Your account is deactivated by admin.')
|
||||
);
|
||||
}
|
||||
|
||||
// ✅ Check Host role (role_xid = 3)
|
||||
if (user.roleXid !== 3) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Access restricted to host users only')
|
||||
);
|
||||
}
|
||||
const userInfo = await verifyHostToken(token);
|
||||
|
||||
// Attach user to request
|
||||
req.user = { id: user.id.toString(), role: user.role?.roleName };
|
||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||
|
||||
resolve();
|
||||
} catch (error) {
|
||||
if (error instanceof jwt.TokenExpiredError) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.UNAUTHORIZED, 'Your session has expired. Please log in again.')
|
||||
);
|
||||
}
|
||||
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Invalid or expired authentication token.')
|
||||
);
|
||||
return reject(error as Error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Express middleware — use as `auth()` in routes
|
||||
*/
|
||||
const auth =
|
||||
const authForHost =
|
||||
() =>
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -100,4 +113,4 @@ const auth =
|
||||
.catch((err) => next(err));
|
||||
};
|
||||
|
||||
export default auth;
|
||||
export default authForHost;
|
||||
|
||||
@@ -26,24 +26,19 @@ declare module 'express-serve-static-core' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies JWT and validates Host user (role_xid = 3)
|
||||
* Core authentication function - verifies JWT and validates Host user
|
||||
* Can be used by both Express middleware and Lambda handlers
|
||||
*/
|
||||
const verifyCallback = async (
|
||||
req: Request,
|
||||
resolve: (value?: unknown) => void,
|
||||
reject: (reason?: Error) => void
|
||||
) => {
|
||||
const token = req.header('x-auth-token') || req.cookies?.accessToken;
|
||||
|
||||
export async function verifyHostToken(token: string): Promise<{ id: number; role?: string }> {
|
||||
if (!token) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate'));
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, config.jwt.secret) as DecodedToken;
|
||||
|
||||
if (!decoded?.id) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Invalid token payload'));
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Invalid token payload');
|
||||
}
|
||||
|
||||
// ✅ Fetch user from Prisma (Host user only)
|
||||
@@ -53,44 +48,59 @@ const verifyCallback = async (
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'User not found'));
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||
}
|
||||
|
||||
// ✅ Check if user is active
|
||||
if (!user.isActive) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Your account is deactivated by admin.')
|
||||
);
|
||||
if (user.isActive === false) {
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Your account is deactivated by admin.');
|
||||
}
|
||||
|
||||
// ✅ Check Admin role (role_xid = 2)
|
||||
if (user.roleXid !== 2) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Access restricted to host users only')
|
||||
);
|
||||
// ✅ Check Host role (role_xid = 1)
|
||||
if (user.roleXid !== 1) {
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Access restricted to host users only');
|
||||
}
|
||||
|
||||
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 = 4)
|
||||
*/
|
||||
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 verifyHostToken(token);
|
||||
|
||||
// Attach user to request
|
||||
req.user = { id: user.id.toString(), role: user.role?.roleName };
|
||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||
|
||||
resolve();
|
||||
} catch (error) {
|
||||
if (error instanceof jwt.TokenExpiredError) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.UNAUTHORIZED, 'Your session has expired. Please log in again.')
|
||||
);
|
||||
}
|
||||
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Invalid or expired authentication token.')
|
||||
);
|
||||
return reject(error as Error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Express middleware — use as `auth()` in routes
|
||||
*/
|
||||
const auth =
|
||||
const authForHost =
|
||||
() =>
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -100,4 +110,4 @@ const auth =
|
||||
.catch((err) => next(err));
|
||||
};
|
||||
|
||||
export default auth;
|
||||
export default authForHost;
|
||||
|
||||
@@ -26,24 +26,19 @@ declare module 'express-serve-static-core' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies JWT and validates Host user (role_xid = 3)
|
||||
* Core authentication function - verifies JWT and validates Host user
|
||||
* Can be used by both Express middleware and Lambda handlers
|
||||
*/
|
||||
const verifyCallback = async (
|
||||
req: Request,
|
||||
resolve: (value?: unknown) => void,
|
||||
reject: (reason?: Error) => void
|
||||
) => {
|
||||
const token = req.header('x-auth-token') || req.cookies?.accessToken;
|
||||
|
||||
export async function verifyHostToken(token: string): Promise<{ id: number; role?: string }> {
|
||||
if (!token) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate'));
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, config.jwt.secret) as DecodedToken;
|
||||
|
||||
if (!decoded?.id) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Invalid token payload'));
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Invalid token payload');
|
||||
}
|
||||
|
||||
// ✅ Fetch user from Prisma (Host user only)
|
||||
@@ -53,44 +48,59 @@ const verifyCallback = async (
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'User not found'));
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||
}
|
||||
|
||||
// ✅ Check if user is active
|
||||
if (!user.isActive) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Your account is deactivated by admin.')
|
||||
);
|
||||
if (user.isActive === false) {
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Your account is deactivated by admin.');
|
||||
}
|
||||
|
||||
// ✅ Check User role (role_xid = 1)
|
||||
if (user.roleXid !== 1) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Access restricted to host users only')
|
||||
);
|
||||
// ✅ Check Host role (role_xid = 6)
|
||||
if (user.roleXid !== 6) {
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Access restricted to host users only');
|
||||
}
|
||||
|
||||
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 = 4)
|
||||
*/
|
||||
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 verifyHostToken(token);
|
||||
|
||||
// Attach user to request
|
||||
req.user = { id: user.id.toString(), role: user.role?.roleName };
|
||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||
|
||||
resolve();
|
||||
} catch (error) {
|
||||
if (error instanceof jwt.TokenExpiredError) {
|
||||
return reject(
|
||||
new ApiError(httpStatus.UNAUTHORIZED, 'Your session has expired. Please log in again.')
|
||||
);
|
||||
}
|
||||
|
||||
return reject(
|
||||
new ApiError(httpStatus.FORBIDDEN, 'Invalid or expired authentication token.')
|
||||
);
|
||||
return reject(error as Error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Express middleware — use as `auth()` in routes
|
||||
*/
|
||||
const auth =
|
||||
const authForHost =
|
||||
() =>
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -100,4 +110,4 @@ const auth =
|
||||
.catch((err) => next(err));
|
||||
};
|
||||
|
||||
export default auth;
|
||||
export default authForHost;
|
||||
|
||||
37
src/common/utils/helper/CodeGenerator.ts
Normal file
37
src/common/utils/helper/CodeGenerator.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
const algorithm = 'aes-256-cbc';
|
||||
const secretKey = crypto.scryptSync('your-secret-password', 'salt', 32);
|
||||
const ivLength = 16;
|
||||
|
||||
// Encrypt function
|
||||
export function encryptUserId(id: string): string {
|
||||
const iv = crypto.randomBytes(ivLength);
|
||||
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
|
||||
let encrypted = cipher.update(id, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return `${iv.toString('hex')}:${encrypted}`;
|
||||
}
|
||||
|
||||
// Decrypt function
|
||||
export function decryptUserId(encryptedId: string): string | null {
|
||||
try {
|
||||
const parts = encryptedId.split(':');
|
||||
if (parts.length !== 2) {
|
||||
console.error('Invalid encryptedId format:', encryptedId);
|
||||
return null;
|
||||
}
|
||||
|
||||
const iv = Buffer.from(parts[0], 'hex');
|
||||
const encryptedText = Buffer.from(parts[1], 'hex');
|
||||
|
||||
const decipher = crypto.createDecipheriv(algorithm, secretKey, iv);
|
||||
let decrypted = decipher.update(encryptedText);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
|
||||
return decrypted.toString('utf8');
|
||||
} catch (error) {
|
||||
console.error('Decryption failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
18
src/common/utils/helper/OtpGenerator.ts
Normal file
18
src/common/utils/helper/OtpGenerator.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import config from '../../../config/config';
|
||||
|
||||
export class OtpGenerator {
|
||||
static generateOtp(): string {
|
||||
if (config.byPassOTP) {
|
||||
return '1234';
|
||||
}
|
||||
return Math.floor(1000 + Math.random() * 9000).toString();
|
||||
}
|
||||
}
|
||||
export class OtpGeneratorSixDigit {
|
||||
static generateOtp(): string {
|
||||
if (config.byPassOTP) {
|
||||
return '123456';
|
||||
}
|
||||
return Math.floor(100000 + Math.random() * 900000).toString();
|
||||
}
|
||||
}
|
||||
91
src/common/utils/helper/sendOtp.ts
Normal file
91
src/common/utils/helper/sendOtp.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import * as bcrypt from "bcryptjs";
|
||||
import { OtpGenerator, OtpGeneratorSixDigit } from "./OtpGenerator";
|
||||
import { encryptUserId } from "./CodeGenerator";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export interface OtpResult {
|
||||
otp: string; // Plain OTP (for sending)
|
||||
hashedOtp: string; // Hashed OTP (for DB storage)
|
||||
expiry: Date; // Expiry timestamp
|
||||
encryptedId: string; // Encrypted user ID
|
||||
emailMessage: string; // Message body for email
|
||||
emailSubject: string; // Subject line for email
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate OTP, store it in DB, and send email.
|
||||
* @param userId The user’s ID
|
||||
* @param email Recipient email
|
||||
* @param emailPurpose For which flow (e.g. "Register", "Login", "ForgotPassword")
|
||||
* @param otpLength OTP length (4 or 6)
|
||||
* @param expiryMinutes Expiry time in minutes
|
||||
*/
|
||||
export async function generateOtpHelper(
|
||||
userId: number,
|
||||
email: string,
|
||||
emailPurpose: "Register" | "Login" | "ForgotPassword",
|
||||
otpLength: 4 | 6 = 4,
|
||||
expiryMinutes: number = 5
|
||||
): Promise<OtpResult> {
|
||||
// Generate OTP
|
||||
const otp =
|
||||
otpLength === 6
|
||||
? OtpGeneratorSixDigit.generateOtp()
|
||||
: OtpGenerator.generateOtp();
|
||||
console.log("Generated OTP:", otp);
|
||||
// Hash OTP
|
||||
const hashedOtp = await bcrypt.hash(otp, 10);
|
||||
|
||||
// Expiry time
|
||||
const expiry = new Date(Date.now() + expiryMinutes * 60 * 1000);
|
||||
|
||||
// Encrypt user ID
|
||||
const encryptedId = encryptUserId(userId.toString());
|
||||
|
||||
// 🔹 First delete old OTPs for this user & purpose
|
||||
await prisma.userOtp.deleteMany({
|
||||
where: {
|
||||
userXid: userId,
|
||||
otpType: emailPurpose,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Save OTP into user_otps table
|
||||
await prisma.userOtp.create({
|
||||
data: {
|
||||
userXid: userId,
|
||||
otpType: emailPurpose,
|
||||
otpCode: hashedOtp,
|
||||
expiresOn: expiry,
|
||||
isVerified: false,
|
||||
isActive: true,
|
||||
// sendOn will default to now()
|
||||
// createdAt will default to now()
|
||||
},
|
||||
});
|
||||
|
||||
// Build email content
|
||||
const emailSubject = `${emailPurpose} OTP Verification`;
|
||||
const emailMessage = `Your OTP for ${emailPurpose} is ${otp}. It will expire in ${expiryMinutes} minutes.`;
|
||||
|
||||
// Send email
|
||||
// await sendBulkEmailForOTP([
|
||||
// {
|
||||
// to: [{ email: email }],
|
||||
// subject: emailSubject,
|
||||
// htmlContent: emailMessage,
|
||||
// },
|
||||
// ]);
|
||||
|
||||
return {
|
||||
otp,
|
||||
hashedOtp,
|
||||
expiry,
|
||||
encryptedId,
|
||||
emailMessage,
|
||||
emailSubject,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// validations/hostBankDetails.validation.ts
|
||||
import { z } from "zod";
|
||||
|
||||
export const hostBankDetailsSchema = z.object({
|
||||
accountNumber: z
|
||||
.number()
|
||||
.int({ message: "Account number must be an integer" })
|
||||
.positive({ message: "Account number must be a positive number" }),
|
||||
|
||||
accountHolderName: z
|
||||
.string()
|
||||
.nonempty("Account holder name is required")
|
||||
.min(2, { message: "Account holder name must be at least 2 characters" }),
|
||||
|
||||
ifscCode: z
|
||||
.string()
|
||||
.nonempty("IFSC code is required")
|
||||
.regex(/^[A-Z]{4}0[A-Z0-9]{6}$/, { message: "Invalid IFSC code format" }),
|
||||
|
||||
bankXid: z
|
||||
.number()
|
||||
.int({ message: "Bank ID must be an integer" })
|
||||
.positive({ message: "Bank ID must be a positive number" }),
|
||||
|
||||
hostXid: z
|
||||
.number()
|
||||
.int({ message: "Host ID must be an integer" })
|
||||
.positive({ message: "Host ID must be a positive number" }),
|
||||
|
||||
bankBranchXid: z
|
||||
.number()
|
||||
.int({ message: "Bank branch ID must be an integer" })
|
||||
.positive({ message: "Bank branch ID must be a positive number" }),
|
||||
});
|
||||
|
||||
export type HostBankDetailsSchema = z.infer<typeof hostBankDetailsSchema>;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { z } from "zod";
|
||||
|
||||
// Allowed document types (must match your DocumentType master table IDs)
|
||||
export const REQUIRED_DOC_TYPES = {
|
||||
PAN: 1,
|
||||
GST: 2,
|
||||
REGISTRATION: 3,
|
||||
AADHAAR: 4,
|
||||
};
|
||||
|
||||
export const hostCompanyDetailsSchema = z.object({
|
||||
companyName: z.string().min(1, "Company name is required"),
|
||||
address1: z.string().min(1, "Address1 is required"),
|
||||
address2: z.string().optional(),
|
||||
hostRefNumber: z.string().min(1, "Host reference number is required"),
|
||||
cityXid: z.number().min(1, "City is required"),
|
||||
stateXid: z.number().min(1, "State is required"),
|
||||
countryXid: z.number().min(1,"Country is required"),
|
||||
pinCode: z.string().min(4, "Pincode/Zipcode is required"),
|
||||
logoPath: z.string().optional(),
|
||||
isSubsidairy: z.boolean(),
|
||||
registrationNumber: z.string().min(1, "Registration number is required"),
|
||||
panNumber: z.string().min(1, "PAN number is required"),
|
||||
gstNumber: z.string().optional(),
|
||||
formationDate: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||
message: "Formation date must be a valid date",
|
||||
}),
|
||||
companyType: z.string().min(1, "Company type is required"),
|
||||
websiteUrl: z.url().optional(),
|
||||
instagramUrl: z.url().optional(),
|
||||
facebookUrl: z.url().optional(),
|
||||
linkedinUrl: z.url().optional(),
|
||||
twitterUrl: z.url().optional(),
|
||||
currencyXid: z.number().min(1, "Currency is required"),
|
||||
});
|
||||
|
||||
// Validation for documents
|
||||
export const hostDocumentsSchema = z.array(
|
||||
z.object({
|
||||
documentTypeXid: z.number(),
|
||||
documentName: z.string(),
|
||||
filePath: z.string(),
|
||||
})
|
||||
);
|
||||
20
src/common/utils/validation/host/login.validation.ts
Normal file
20
src/common/utils/validation/host/login.validation.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// validations/hostBankDetails.validation.ts
|
||||
import { z } from "zod";
|
||||
|
||||
export const loginForHostSchema = z.object({
|
||||
|
||||
|
||||
emailAddress : z
|
||||
.string()
|
||||
.nonempty("Email is required"),
|
||||
|
||||
userPassword : z
|
||||
.string()
|
||||
.nonempty("Password is required")
|
||||
.min(8, { message: "Password must be at least 8 characters" }),
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
export type loginForHostSchema = z.infer<typeof loginForHostSchema>;
|
||||
@@ -12,7 +12,6 @@ const envVarsSchema = yup
|
||||
.oneOf(['production', 'development', 'test'])
|
||||
.required(),
|
||||
PORT: yup.number().default(3000),
|
||||
BASEURL: yup.string().required('Base URL is required'),
|
||||
// FRONTEND_URL: yup.string().required('Frontend URL is required'),
|
||||
//JWT
|
||||
JWT_SECRET: yup.string().required('JWT secret key is required'),
|
||||
|
||||
@@ -21,7 +21,7 @@ export class CreateHostDto {
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
userPasscode?: string;
|
||||
userPassword?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@@ -49,3 +49,27 @@ export class UpdateHostDto {
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export class GetHostLoginResponseDTO {
|
||||
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;
|
||||
}
|
||||
}
|
||||
65
src/modules/host/handlers/createPassword.ts
Normal file
65
src/modules/host/handlers/createPassword.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
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> => {
|
||||
// Extract token from headers
|
||||
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.');
|
||||
}
|
||||
|
||||
// Authenticate user using the shared authForHost function
|
||||
const userInfo = await verifyHostToken(token);
|
||||
const user_xid = userInfo.id;
|
||||
|
||||
// Parse request body
|
||||
let body: { password?: string; confirmPassword?: string };
|
||||
|
||||
try {
|
||||
body = event.body ? JSON.parse(event.body) : {};
|
||||
} catch (error) {
|
||||
throw new ApiError(400, 'Invalid JSON in request body');
|
||||
}
|
||||
|
||||
const { password, confirmPassword } = body;
|
||||
|
||||
if (!password || !confirmPassword) {
|
||||
throw new ApiError(400, 'Password and confirm password are required');
|
||||
}
|
||||
|
||||
// Validate password match
|
||||
if (password !== confirmPassword) {
|
||||
throw new ApiError(400, 'Password and confirm password do not match');
|
||||
}
|
||||
|
||||
// Validate password length
|
||||
if (password.length < 8) {
|
||||
throw new ApiError(400, 'Password must be at least 8 characters long');
|
||||
}
|
||||
|
||||
await hostService.createPassword(user_xid, password);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
message: 'Password created successfully',
|
||||
data: null,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
79
src/modules/host/handlers/loginForHost.ts
Normal file
79
src/modules/host/handlers/loginForHost.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
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, userPassword } = body;
|
||||
|
||||
if (!emailAddress || !userPassword) {
|
||||
throw new ApiError(400, 'Email and password are required');
|
||||
}
|
||||
|
||||
const loginForHost = await hostService.loginForHost(emailAddress, userPassword);
|
||||
|
||||
if (!loginForHost) {
|
||||
throw new ApiError(400, 'Failed to login');
|
||||
}
|
||||
|
||||
if (!loginForHost.userPassword) {
|
||||
throw new ApiError(401, 'Invalid credentials');
|
||||
}
|
||||
|
||||
const matchPassword = await bcrypt.compare(
|
||||
userPassword,
|
||||
loginForHost.userPassword
|
||||
);
|
||||
|
||||
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,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
75
src/modules/host/handlers/registration.ts
Normal file
75
src/modules/host/handlers/registration.ts
Normal 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 { HostService } from '../services/host.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 hostService = new HostService(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 hostService.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: {},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
52
src/modules/host/handlers/verifyOtp.ts
Normal file
52
src/modules/host/handlers/verifyOtp.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
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 { TokenService } from '../services/token.service';
|
||||
|
||||
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: { email?: string; otp?: string };
|
||||
|
||||
try {
|
||||
body = event.body ? JSON.parse(event.body) : {};
|
||||
} catch (error) {
|
||||
throw new ApiError(400, 'Invalid JSON in request body');
|
||||
}
|
||||
|
||||
const { email, otp } = body;
|
||||
|
||||
if (!email || !otp) {
|
||||
throw new ApiError(400, 'Email and OTP are required');
|
||||
}
|
||||
|
||||
await hostService.verifyHostOtp(email, otp);
|
||||
const user = await hostService.getHostByEmail(email);
|
||||
const generateTokenForHost = await tokenService.generateAuthToken(
|
||||
user.id
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
message: 'OTP verified successfully',
|
||||
accessToken: generateTokenForHost.access.token,
|
||||
refreshToken: generateTokenForHost.refresh.token,
|
||||
data: null,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../common/database/prisma.service';
|
||||
import { CreateHostDto, UpdateHostDto } from '../dto/host.dto';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import ApiError from '../../../common/utils/helper/ApiError';
|
||||
import { User } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class HostService {
|
||||
@@ -17,7 +20,9 @@ export class HostService {
|
||||
|
||||
async getHostById(id: number) {
|
||||
const host = await this.prisma.user.findUnique({ where: { id } });
|
||||
if (!host || host.roleXid !== 3) throw new Error('Host not found');
|
||||
if (!host || host.roleXid !== 4) {
|
||||
throw new ApiError(404, 'Host not found');
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
@@ -31,4 +36,111 @@ export class HostService {
|
||||
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 loginForHost(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 createPassword(user_xid: number, password: string): Promise<boolean> {
|
||||
// Find user by id
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id: user_xid },
|
||||
select: { id: true, emailAddress: true, userPassword: true },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError(404, 'User not found');
|
||||
}
|
||||
|
||||
// Check if password already exists
|
||||
if (user.userPassword) {
|
||||
throw new ApiError(400, 'Password already exists. Use update password instead.');
|
||||
}
|
||||
|
||||
// Hash the password
|
||||
const saltRounds = parseInt(process.env.SALT_ROUNDS || '10', 10);
|
||||
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
||||
|
||||
// Update user with hashed password
|
||||
await this.prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { userPassword: hashedPassword },
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
159
src/modules/host/services/token.service.ts
Normal file
159
src/modules/host/services/token.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
630
swagger.json
630
swagger.json
@@ -1,27 +1,631 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"paths": {},
|
||||
"info": {
|
||||
"title": "Minglar API",
|
||||
"description": "NestJS Backend for Minglar with Lambda-ready endpoints",
|
||||
"description": "NestJS Backend for Minglar with AWS Lambda endpoints",
|
||||
"version": "1.0.0",
|
||||
"contact": {}
|
||||
"contact": {
|
||||
"name": "API Support"
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://localhost:3000/",
|
||||
"description": "Local Server"
|
||||
"url": "https://api.minglar.com",
|
||||
"description": "Production Server"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:3000",
|
||||
"description": "Local Development Server"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"bearer": {
|
||||
"scheme": "bearer",
|
||||
"bearerFormat": "JWT",
|
||||
"type": "http"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"schemas": {}
|
||||
"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",
|
||||
"scheme": "bearer",
|
||||
"bearerFormat": "JWT",
|
||||
"description": "JWT token obtained from login endpoint"
|
||||
}
|
||||
},
|
||||
"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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user