19 Commits

Author SHA1 Message Date
paritosh18
4a61e2c412 prepopulate change 2026-03-16 11:19:54 +05:30
paritosh18
7167eae07e fixed the path in the user yml file 2026-03-13 17:31:29 +05:30
paritosh18
4f3d7fd737 Merge branch 'mayankSprint2' of http://git.wdipl.com/Mayank.Mishra/MinglarBackendNestJS into Split-lambda 2026-03-13 17:30:27 +05:30
paritosh18
199013b0f5 split lambda 2026-03-13 17:27:51 +05:30
0b81dbf7b1 made a new getAllBucketActivities api 2026-03-13 13:09:25 +05:30
paritosh18
9722e1988c Merge branch 'mayankSprint2' of http://git.wdipl.com/Mayank.Mishra/MinglarBackendNestJS into Split-lambda 2026-03-13 13:05:14 +05:30
cf2bbbf138 fixed the response structure in the specific api 2026-03-11 15:53:54 +05:30
paritosh18
2ca785248f add serverless-plugin-split-stacks and update serverless configuration; refactor host function definitions and improve updateHostProfile handler 2026-03-11 15:42:41 +05:30
0aa2b9b53e fixed the search api for the specific and sending by default 15 km radius activities in the nearby activity api 2026-03-11 13:41:15 +05:30
b5cdb20c4f sending the latestactivity image in the bucket 2026-03-10 21:30:43 +05:30
00e07113e5 sending the latest added activity image in the add to bucket api and fixed the presignedurl in the getFilteredLandingPageAllDetails api and fixed the logic to send the activities from the selected activityxids 2026-03-10 19:36:44 +05:30
c8f0f93792 sending the duration cheapprice per person and sustanibility score in the surprise me api 2026-03-10 18:12:26 +05:30
87779664d1 sending the activity type xids in the search api of I am specific 2026-03-10 15:26:57 +05:30
5b31e5f2a9 sending the activity count in the submit personal info 2026-03-09 18:49:40 +05:30
2a073c44a2 Merge branch 'paritosh-main1' of http://git.wdipl.com/Mayank.Mishra/MinglarBackendNestJS into mayankSprint2 2026-03-09 16:09:36 +05:30
paritosh18
220d309087 Added profile image upload functionality to updateHostProfile API, including multipart form data handling and S3 integration for image storage. 2026-03-09 16:09:20 +05:30
f45c33ba83 sending the random activities in the getconnection api 2026-03-09 16:00:31 +05:30
22b3593150 sending 5 random activities in the get surprise me api 2026-03-09 15:57:14 +05:30
d186681ee4 added isActive condition 2026-03-09 15:51:58 +05:30
21 changed files with 1569 additions and 340 deletions

533
package-lock.json generated
View File

@@ -13,7 +13,7 @@
"@aws-crypto/sha256-browser": "^5.2.0",
"@aws-crypto/sha256-js": "^5.2.0",
"@aws-sdk/client-s3": "^3.928.0",
"@aws-sdk/s3-request-presigner": "^3.310.0",
"@aws-sdk/s3-request-presigner": "^3.928.0",
"@aws/lambda-invoke-store": "^0.2.1",
"@nestjs/common": "^10.3.0",
"@nestjs/config": "^3.1.1",
@@ -31,18 +31,27 @@
"@types/http-status": "^1.1.2",
"ajv": "8.12.0",
"aws-lambda": "^1.0.7",
"aws-sdk": "^2.1692.0",
"bcrypt": "^6.0.0",
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"date-fns": "^4.1.0",
"dayjs": "^1.11.19",
"docx": "^9.6.0",
"docxtemplater": "^3.68.3",
"fast-xml-parser": "^5.3.1",
"fs": "^0.0.1-security",
"helmet": "^7.1.0",
"http-status": "^2.1.0",
"moment": "^2.30.1",
"number-to-words": "^1.2.4",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"path": "^0.12.7",
"pdf-lib": "^1.17.1",
"pizzip": "^3.2.0",
"prisma": "^7.0.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
@@ -73,6 +82,7 @@
"prettier": "^3.2.5",
"serverless-esbuild": "^1.55.1",
"serverless-offline": "^14.4.0",
"serverless-plugin-split-stacks": "^1.14.0",
"source-map-support": "^0.5.21",
"supertest": "^6.3.4",
"ts-jest": "^29.1.2",
@@ -4650,6 +4660,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.6"
}
},
"node_modules/@pdf-lib/upng": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.10"
}
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -5751,6 +5779,13 @@
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"license": "MIT"
},
"node_modules/@tootallnate/quickjs-emscripten": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node10": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
@@ -6546,6 +6581,15 @@
"@xtuc/long": "4.2.2"
}
},
"node_modules/@xmldom/xmldom": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz",
"integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==",
"license": "MIT",
"engines": {
"node": ">=14.6"
}
},
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -6624,6 +6668,16 @@
"node": ">=0.4.0"
}
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -6964,6 +7018,19 @@
"dev": true,
"license": "MIT"
},
"node_modules/ast-types": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
"dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.0.1"
},
"engines": {
"node": ">=4"
}
},
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
@@ -6992,6 +7059,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/aws-info": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/aws-info/-/aws-info-1.3.0.tgz",
"integrity": "sha512-dYE3J2GQOMXjirx54IonDisZ6Ok4vBSYjNklNAGGDj2FzGHkWpGOlGAn5/BC8TRh8ttmYRy+Fsmxc8EJMnzSCg==",
"dev": true,
"license": "MIT"
},
"node_modules/aws-lambda": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/aws-lambda/-/aws-lambda-1.0.7.tgz",
@@ -7257,6 +7331,16 @@
"baseline-browser-mapping": "dist/cli.js"
}
},
"node_modules/basic-ftp": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz",
"integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/bcrypt": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz",
@@ -8386,7 +8470,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true,
"license": "MIT"
},
"node_modules/cors": {
@@ -8539,6 +8622,12 @@
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -8633,6 +8722,21 @@
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT"
},
"node_modules/degenerator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
"integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ast-types": "^0.13.4",
"escodegen": "^2.1.0",
"esprima": "^4.0.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -8763,6 +8867,50 @@
"node": ">=6.0.0"
}
},
"node_modules/docx": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/docx/-/docx-9.6.0.tgz",
"integrity": "sha512-y6EaJJMDvt4P7wgGQB9KsZf4wsRkQMJfkc9LlNufRshggI5BT35hGNkXBCAeEoI3MLMwApKguxzjdqqVcBCqNA==",
"license": "MIT",
"dependencies": {
"@types/node": "^25.2.3",
"hash.js": "^1.1.7",
"jszip": "^3.10.1",
"nanoid": "^5.1.3",
"xml": "^1.0.1",
"xml-js": "^1.6.8"
},
"engines": {
"node": ">=10"
}
},
"node_modules/docx/node_modules/@types/node": {
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
"integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.18.0"
}
},
"node_modules/docx/node_modules/undici-types": {
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"license": "MIT"
},
"node_modules/docxtemplater": {
"version": "3.68.3",
"resolved": "https://registry.npmjs.org/docxtemplater/-/docxtemplater-3.68.3.tgz",
"integrity": "sha512-hTZfGcHgN60A09w68Qj0EQRCnF5kf2/ohFlZlUVqAOozCFwx9QMm8naCTvmTsXafuO3nG9qpS4pQWSjFdaCWfQ==",
"license": "MIT",
"dependencies": {
"@xmldom/xmldom": "^0.9.8"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
@@ -9031,6 +9179,39 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/escodegen": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
"node": ">=6.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
"node_modules/escodegen/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/eslint": {
"version": "8.57.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
@@ -10022,6 +10203,12 @@
"node": ">= 0.8"
}
},
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==",
"license": "ISC"
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -10199,6 +10386,31 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/get-uri": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
"integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"basic-ftp": "^5.0.2",
"data-uri-to-buffer": "^6.0.2",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/get-uri/node_modules/data-uri-to-buffer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
"integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/giget": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
@@ -10437,6 +10649,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -10499,6 +10721,20 @@
"node": ">= 0.8"
}
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/http-status": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/http-status/-/http-status-2.1.0.tgz",
@@ -10514,6 +10750,20 @@
"integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==",
"license": "MIT"
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -10556,7 +10806,6 @@
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"dev": true,
"license": "MIT"
},
"node_modules/import-fresh": {
@@ -10661,6 +10910,16 @@
"node": ">=12.0.0"
}
},
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -11986,7 +12245,6 @@
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dev": true,
"license": "(MIT OR GPL-3.0-or-later)",
"dependencies": {
"lie": "~3.3.0",
@@ -11999,7 +12257,6 @@
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
@@ -12015,14 +12272,12 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT"
},
"node_modules/jszip/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
@@ -12149,7 +12404,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"immediate": "~3.0.5"
@@ -12548,6 +12802,12 @@
"node": ">=6"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
"license": "ISC"
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
@@ -12698,6 +12958,24 @@
"node": ">=12"
}
},
"node_modules/nanoid": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
"integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -12722,6 +13000,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/netmask": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/nock": {
"version": "13.5.6",
"resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
@@ -12873,6 +13161,12 @@
"node": ">=8"
}
},
"node_modules/number-to-words": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/number-to-words/-/number-to-words-1.2.4.tgz",
"integrity": "sha512-/fYevVkXRcyBiZDg6yzZbm0RuaD6i0qRfn8yr+6D0KgBMOndFPxuW10qCHpzs50nN8qKuv78k8MuotZhcVX6Pw==",
"license": "MIT"
},
"node_modules/nypm": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz",
@@ -13118,6 +13412,40 @@
"node": ">=6"
}
},
"node_modules/pac-proxy-agent": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
"integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@tootallnate/quickjs-emscripten": "^0.23.0",
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"get-uri": "^6.0.1",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.6",
"pac-resolver": "^7.0.1",
"socks-proxy-agent": "^8.0.5"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pac-resolver": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
"integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
"dev": true,
"license": "MIT",
"dependencies": {
"degenerator": "^5.0.0",
"netmask": "^2.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@@ -13128,7 +13456,6 @@
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true,
"license": "(MIT AND Zlib)"
},
"node_modules/parent-module": {
@@ -13219,6 +13546,16 @@
"node": ">= 0.4.0"
}
},
"node_modules/path": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
"integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
"license": "MIT",
"dependencies": {
"process": "^0.11.1",
"util": "^0.10.3"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -13293,6 +13630,21 @@
"node": ">=8"
}
},
"node_modules/path/node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
"license": "ISC"
},
"node_modules/path/node_modules/util": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
"license": "MIT",
"dependencies": {
"inherits": "2.0.3"
}
},
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -13304,6 +13656,24 @@
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
},
"node_modules/pdf-lib": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
"license": "MIT",
"dependencies": {
"@pdf-lib/standard-fonts": "^1.0.0",
"@pdf-lib/upng": "^1.0.1",
"pako": "^1.0.11",
"tslib": "^1.11.1"
}
},
"node_modules/pdf-lib/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"license": "0BSD"
},
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
@@ -13438,6 +13808,21 @@
"node": ">= 6"
}
},
"node_modules/pizzip": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/pizzip/-/pizzip-3.2.0.tgz",
"integrity": "sha512-X4NPNICxCfIK8VYhF6wbksn81vTiziyLbvKuORVAmolvnUzl1A1xmz9DAWKxPRq9lZg84pJOOAMq3OE61bD8IQ==",
"license": "(MIT OR GPL-3.0)",
"dependencies": {
"pako": "^2.1.0"
}
},
"node_modules/pizzip/node_modules/pako": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
"license": "(MIT AND Zlib)"
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -13689,11 +14074,19 @@
}
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true,
"license": "MIT"
},
"node_modules/prompts": {
@@ -13756,6 +14149,36 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-agent": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
"integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"http-proxy-agent": "^7.0.1",
"https-proxy-agent": "^7.0.6",
"lru-cache": "^7.14.1",
"pac-proxy-agent": "^7.1.0",
"proxy-from-env": "^1.1.0",
"socks-proxy-agent": "^8.0.5"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/proxy-agent/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -14756,6 +15179,23 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/serverless-plugin-split-stacks": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/serverless-plugin-split-stacks/-/serverless-plugin-split-stacks-1.14.0.tgz",
"integrity": "sha512-VksNqvJUPnGHqef0jHNiN0BzTVr0Hy0cWaLxCG75HiQ3vnIog8qeyiu7uWH6LKNhJnGP1jiTNh0YcheCN8kaKA==",
"dev": true,
"license": "MIT",
"dependencies": {
"aws-info": "^1.2.0",
"lodash": "^4.17.21",
"proxy-agent": "^6.3.1",
"semver": "^7.3.5",
"throat": "^6.0.1"
},
"peerDependencies": {
"serverless": "1 || 2 || 3 || 4"
}
},
"node_modules/serverless/node_modules/rimraf": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
@@ -14792,7 +15232,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
"dev": true,
"license": "MIT"
},
"node_modules/setprototypeof": {
@@ -14923,6 +15362,47 @@
"node": ">=8"
}
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks": {
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ip-address": "^10.0.1",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"socks": "^2.8.3"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/sorted-array-functions": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
@@ -15504,6 +15984,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/throat": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz",
"integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==",
"dev": true,
"license": "MIT"
},
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -17076,6 +17563,30 @@
}
}
},
"node_modules/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
"license": "MIT"
},
"node_modules/xml-js": {
"version": "1.6.11",
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
"license": "MIT",
"dependencies": {
"sax": "^1.2.4"
},
"bin": {
"xml-js": "bin/cli.js"
}
},
"node_modules/xml-js/node_modules/sax": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
"license": "BlueOak-1.0.0"
},
"node_modules/xml2js": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",

View File

@@ -99,6 +99,7 @@
"prettier": "^3.2.5",
"serverless-esbuild": "^1.55.1",
"serverless-offline": "^14.4.0",
"serverless-plugin-split-stacks": "^1.14.0",
"source-map-support": "^0.5.21",
"supertest": "^6.3.4",
"ts-jest": "^29.1.2",

View File

@@ -1,7 +1,8 @@
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "rhel-openssl-3.0.x"] // Add Linux target
binaryTargets = ["native", "rhel-openssl-3.0.x"] // Lambda Node 18/20 (Amazon Linux) target
previewFeatures = ["multiSchema"]
engineType = "library"
}
datasource db {

13
serverless.host.yml Normal file
View File

@@ -0,0 +1,13 @@
service: minglar-host
useDotenv: ${file(./serverless/common.yml):useDotenv}
params: ${file(./serverless/common.yml):params}
provider: ${file(./serverless/common.yml):provider}
build: ${file(./serverless/common.yml):build}
package: ${file(./serverless/common.yml):package}
plugins: ${file(./serverless/common.yml):plugins}
custom: ${file(./serverless/common.yml):custom}
functions:
- ${file(./serverless/functions/host.yml)}

View File

@@ -0,0 +1,13 @@
service: minglar-admin
useDotenv: ${file(./serverless/common.yml):useDotenv}
params: ${file(./serverless/common.yml):params}
provider: ${file(./serverless/common.yml):provider}
build: ${file(./serverless/common.yml):build}
package: ${file(./serverless/common.yml):package}
plugins: ${file(./serverless/common.yml):plugins}
custom: ${file(./serverless/common.yml):custom}
functions:
- ${file(./serverless/functions/minglaradmin.yml)}

View File

@@ -0,0 +1,13 @@
service: minglar-prepopulate
useDotenv: ${file(./serverless/common.yml):useDotenv}
params: ${file(./serverless/common.yml):params}
provider: ${file(./serverless/common.yml):provider}
build: ${file(./serverless/common.yml):build}
package: ${file(./serverless/common.yml):package}
plugins: ${file(./serverless/common.yml):plugins}
custom: ${file(./serverless/common.yml):custom}
functions:
- ${file(./serverless/functions/prepopulate.yml)}

View File

@@ -0,0 +1,32 @@
service: minglar-prisma-layer
useDotenv: true
params:
dev:
stage: dev
test:
stage: test
uat:
stage: uat
provider:
name: aws
runtime: nodejs22.x
region: ap-south-1
stage: ${opt:stage, 'dev'}
deploymentBucket:
# use a fixed bucket name to prevent Serverless from creating/quashing a resource
name: serverless-framework-deployments-ap-south-1-50264b8e-d2b9
# optionally uncomment below to enable serverless to create if missing
# serverSideEncryption: AES256
versionFunctions: false
layers:
prisma:
path: layers/prisma
name: ${self:service}-prisma-layer-${sls:stage}
description: Prisma 7 client with pg driver adapter (no binary engines)
compatibleRuntimes:
- nodejs22.x
retain: false

13
serverless.user.yml Normal file
View File

@@ -0,0 +1,13 @@
service: minglar-user
useDotenv: ${file(./serverless/common.yml):useDotenv}
params: ${file(./serverless/common.yml):params}
provider: ${file(./serverless/common.yml):provider}
build: ${file(./serverless/common.yml):build}
package: ${file(./serverless/common.yml):package}
plugins: ${file(./serverless/common.yml):plugins}
custom: ${file(./serverless/common.yml):custom}
functions:
- ${file(./serverless/functions/user.yml)}

View File

@@ -1,3 +1,4 @@
# Legacy monolith config. For new deployments use serverless.*.yml files.
service: minglarDev
@@ -16,6 +17,11 @@ provider:
runtime: nodejs22.x
region: ap-south-1
stage: ${opt:stage, 'dev'}
deploymentBucket:
# use a fixed bucket name to prevent Serverless from creating/quashing a resource
name: serverless-framework-deployments-ap-south-1-50264b8e-d2b9
# optionally uncomment below to enable serverless to create if missing
# serverSideEncryption: AES256
versionFunctions: false
memorySize: 512
# Apply Prisma layer to all functions
@@ -73,10 +79,6 @@ provider:
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}'
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}/*'
custom:
serverless-offline:
reloadHandler: true
build:
esbuild:
bundle: true
@@ -145,8 +147,18 @@ functions:
- ${file(./serverless/functions/host.yml)}
- ${file(./serverless/functions/minglaradmin.yml)}
- ${file(./serverless/functions/prepopulate.yml)}
- ${file(./serverless/functions/pqq.yml)}
- ${file(./serverless/functions/user.yml)}
plugins:
- serverless-offline
- serverless-offline
- serverless-plugin-split-stacks
custom:
serverless-offline:
reloadHandler: true
# split-stacks configuration to avoid CloudFormation resource limit
splitStacks:
perFunction: true
perType: true
perGroupFunction: false

134
serverless/common.yml Normal file
View File

@@ -0,0 +1,134 @@
useDotenv: true
params:
dev:
stage: dev
test:
stage: test
uat:
stage: uat
provider:
name: aws
runtime: nodejs22.x
region: ap-south-1
stage: ${opt:stage, 'dev'}
deploymentBucket:
# use a fixed bucket name to prevent Serverless from creating/quashing a resource
name: serverless-framework-deployments-ap-south-1-50264b8e-d2b9
# optionally uncomment below to enable serverless to create if missing
# serverSideEncryption: AES256
versionFunctions: false
memorySize: 512
# Apply Prisma layer to all functions
# Reference the layer defined in the dedicated layer stack
layers:
- ${cf:minglar-prisma-layer-${sls:stage}.PrismaLambdaLayerQualifiedArn}
apiGateway:
binaryMediaTypes:
- '*/*'
minimumCompressionSize: 1024
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}
BREVO_API_BASEURL: ${env:BREVO_API_BASEURL}
BREVO_FROM_EMAIL: ${env:BREVO_FROM_EMAIL}
BREVO_SMTP_HOST: ${env:BREVO_SMTP_HOST}
BREVO_SMTP_PORT: ${env:BREVO_SMTP_PORT}
BREVO_SMTP_USER: ${env:BREVO_SMTP_USER}
BREVO_SMTP_PASS: ${env:BREVO_SMTP_PASS}
REFRESH_TOKEN_SECRET: ${env:REFRESH_TOKEN_SECRET}
JWT_SECRET: ${env:JWT_SECRET}
JWT_ACCESS_EXPIRATION_MINUTES: ${env:JWT_ACCESS_EXPIRATION_MINUTES}
JWT_REFRESH_EXPIRATION_DAYS: ${env:JWT_REFRESH_EXPIRATION_DAYS}
JWT_RESET_PASSWORD_EXPIRATION_MINUTES: ${env:JWT_RESET_PASSWORD_EXPIRATION_MINUTES}
JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: ${env:JWT_VERIFY_EMAIL_EXPIRATION_MINUTES}
SALT_ROUNDS: ${env:SALT_ROUNDS}
NODE_ENV: ${env:NODE_ENV}
S3_BUCKET_NAME: ${env:S3_BUCKET_NAME}
MINGLAR_ADMIN_NAME: ${env:MINGLAR_ADMIN_NAME}
MINGLAR_ADMIN_EMAIL: ${env:MINGLAR_ADMIN_EMAIL}
AM_INVITATION_LINK: ${env:AM_INVITATION_LINK}
HOST_LINK: ${env:HOST_LINK}
HOST_LINK_PQ: ${env:HOST_LINK_PQ}
iam:
role:
statements:
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:DeleteObject
- s3:ListBucket
Resource:
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}'
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}/*'
build:
esbuild:
bundle: true
minify: true
sourcemap: false
target: node20
platform: node
# Mark as external so they're not bundled into the JS
external:
- '@prisma/client'
- '.prisma/client'
- '.prisma'
- '@prisma/adapter-pg'
- 'pg'
- 'zod'
- '@aws-sdk/*'
- '@smithy/*'
- '@aws-crypto/*'
# Exclude prevents npm install of these packages in the zip
exclude:
- 'aws-sdk'
- '@aws-sdk/*'
- '@smithy/*'
- '@aws-crypto/*'
- '@prisma/adapter-pg'
- '@prisma/client'
- '.prisma'
- '.prisma/client'
- 'pg'
- 'zod'
- 'pg-*'
- 'postgres-*'
- 'pgpass'
- 'split2'
- 'xtend'
package:
individually: true
patterns:
- '!node_modules/**'
- '!node_modules/@prisma/**'
- '!node_modules/.prisma/**'
- '!**/*.test.js'
- '!**/*.spec.js'
- '!**/test/**'
- '!**/__tests__/**'
- '!package-lock.json'
- '!yarn.lock'
- '!README.md'
- '!*.config.js'
- '!.git/**'
- '!.github/**'
plugins:
- serverless-offline
custom:
serverless-offline:
reloadHandler: true

View File

@@ -1,21 +1,21 @@
# Host Module Functions
# All authentication and host management endpoints
# getHosts:
# handler: src/modules/host/handlers/host.handler
# memorySize: 384
# package:
# patterns:
# - 'src/modules/host/handlers/host.*'
# - 'src/modules/host/services/**'
# - ${file(./serverless/patterns/base.yml):pattern1}
# - ${file(./serverless/patterns/base.yml):pattern2}
# - ${file(./serverless/patterns/base.yml):pattern3}
# - ${file(./serverless/patterns/base.yml):pattern4}
# events:
# - httpApi:
# path: /host
# method: get
getHosts:
handler: src/modules/host/handlers/host.handler
memorySize: 384
package:
patterns:
- 'src/modules/host/handlers/host.*'
- 'src/modules/host/services/**'
- ${file(./serverless/patterns/base.yml):pattern1}
- ${file(./serverless/patterns/base.yml):pattern2}
- ${file(./serverless/patterns/base.yml):pattern3}
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /
method: get
verifyOTP:
handler: src/modules/host/handlers/Host_Admin/onboarding/verifyOTP.handler
@@ -30,7 +30,7 @@ verifyOTP:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Host_Admin/onboarding/verify-otp
path: /Host_Admin/onboarding/verify-otp
method: post
login:
@@ -46,7 +46,7 @@ login:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Host_Admin/onboarding/login
path: /Host_Admin/onboarding/login
method: post
signUp:
@@ -62,7 +62,7 @@ signUp:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Host_Admin/onboarding/registration
path: /Host_Admin/onboarding/registration
method: post
createPassword:
@@ -78,7 +78,7 @@ createPassword:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Host_Admin/onboarding/create-password
path: /Host_Admin/onboarding/create-password
method: post
updateBankDetails:
@@ -94,7 +94,7 @@ updateBankDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Host_Admin/onboarding/add-payment-details
path: /Host_Admin/onboarding/add-payment-details
method: post
saveActivity_ForPQQ:
@@ -110,7 +110,7 @@ saveActivity_ForPQQ:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/add-activity
path: /Activity_Hub/OnBoarding/add-activity
method: post
getHostById:
@@ -126,7 +126,7 @@ getHostById:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/getById
path: /getById
method: get
getPQQ_ByQuestionId:
@@ -142,7 +142,7 @@ getPQQ_ByQuestionId:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/get-pqq-question-details
path: /Activity_Hub/OnBoarding/get-pqq-question-details
method: get
getPQQ_LastUpdatedQuestion:
@@ -158,7 +158,7 @@ getPQQ_LastUpdatedQuestion:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/get-latest-pqq-question-details
path: /Activity_Hub/OnBoarding/get-latest-pqq-question-details
method: get
prePopulateNewActivity:
@@ -174,7 +174,7 @@ prePopulateNewActivity:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/prepopulate-new-activity
path: /Activity_Hub/OnBoarding/prepopulate-new-activity
method: get
createNewActivity:
@@ -191,7 +191,7 @@ createNewActivity:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/create-new-activity
path: /Activity_Hub/OnBoarding/create-new-activity
method: patch
showSuggestion:
@@ -207,7 +207,7 @@ showSuggestion:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/get-suggestion
path: /get-suggestion
method: get
getAllActivitySuggestion:
@@ -223,7 +223,7 @@ getAllActivitySuggestion:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/get-Activity-suggestion
path: /get-Activity-suggestion
method: get
getAllHostActivity:
@@ -239,7 +239,7 @@ getAllHostActivity:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/get-all-host-activity
path: /Activity_Hub/OnBoarding/get-all-host-activity
method: get
acceptAggrement:
@@ -255,7 +255,7 @@ acceptAggrement:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Host_Admin/onboarding/accept-agreement
path: /Host_Admin/onboarding/accept-agreement
method: patch
getLatestAgreement:
@@ -271,7 +271,7 @@ getLatestAgreement:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Host_Admin/onboarding/get-latest-agreement
path: /Host_Admin/onboarding/get-latest-agreement
method: get
getStepperInfo:
@@ -305,7 +305,7 @@ updateHostProfile:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/profile
path: /profile
method: patch
# Functions with S3/AWS SDK dependencies
@@ -320,7 +320,7 @@ submitCompanyDetails:
- 'src/common/**'
events:
- httpApi:
path: /host/Host_Admin/onboarding/add-company-details
path: /Host_Admin/onboarding/add-company-details
method: patch
submitPQQ_Answer:
@@ -336,7 +336,7 @@ submitPQQ_Answer:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/submit-pqq-answer
path: /Activity_Hub/OnBoarding/submit-pqq-answer
method: patch
updatePQQ_LastAnswer:
@@ -352,7 +352,7 @@ updatePQQ_LastAnswer:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/submit-final-pqq-answer
path: /Activity_Hub/OnBoarding/submit-final-pqq-answer
method: post
submitPQQForReview:
@@ -368,7 +368,7 @@ submitPQQForReview:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/submit-pqq-for-review
path: /Activity_Hub/OnBoarding/submit-pqq-for-review
method: patch
getAllPQQwithSubmittedAns:
@@ -383,7 +383,7 @@ getAllPQQwithSubmittedAns:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/get-all-pqq-ques-submited-ans
path: /Activity_Hub/OnBoarding/get-all-pqq-ques-submited-ans
method: get
getAllDetailsOfActivityAndVenue:
@@ -398,7 +398,7 @@ getAllDetailsOfActivityAndVenue:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/get-all-details-activity-venue/{activityXid}
path: /Activity_Hub/OnBoarding/get-all-details-activity-venue/{activityXid}
method: get
updateSuggestionAsReviewed:
@@ -413,7 +413,7 @@ updateSuggestionAsReviewed:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/update-suggestion-reviewed
path: /Activity_Hub/OnBoarding/update-suggestion-reviewed
method: patch
resendOTPmail:
@@ -554,4 +554,34 @@ openCanceledSlotForActivity:
events:
- httpApi:
path: /scheduling/open-canceled-slot
method: patch
method: patch
createActivityAndAllQuestionsEntry:
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/createActivityAndAllQuestionsEntry.handler
memorySize: 384
package:
patterns:
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/createActivityAndAllQuestionsEntry**'
- ${file(./serverless/patterns/base.yml):pattern1}
- ${file(./serverless/patterns/base.yml):pattern2}
- ${file(./serverless/patterns/base.yml):pattern3}
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /Activity_Hub/OnBoarding/create-activity
method: post
submitPQAnswer:
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQAnswer.handler
memorySize: 384
package:
patterns:
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQAnswer**'
- ${file(./serverless/patterns/base.yml):pattern1}
- ${file(./serverless/patterns/base.yml):pattern2}
- ${file(./serverless/patterns/base.yml):pattern3}
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /Activity_Hub/OnBoarding/submit-pq-answer
method: patch

View File

@@ -1,29 +0,0 @@
createActivityAndAllQuestionsEntry:
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/createActivityAndAllQuestionsEntry.handler
memorySize: 384
package:
patterns:
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/createActivityAndAllQuestionsEntry**'
- ${file(./serverless/patterns/base.yml):pattern1}
- ${file(./serverless/patterns/base.yml):pattern2}
- ${file(./serverless/patterns/base.yml):pattern3}
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/create-activity
method: post
submitPQAnswer:
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQAnswer.handler
memorySize: 384
package:
patterns:
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQAnswer**'
- ${file(./serverless/patterns/base.yml):pattern1}
- ${file(./serverless/patterns/base.yml):pattern2}
- ${file(./serverless/patterns/base.yml):pattern3}
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /host/Activity_Hub/OnBoarding/submit-pq-answer
method: patch

View File

@@ -13,7 +13,7 @@ getAllBankAndCurrencyDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /prepopulate/get-all-bank-currency-details
path: /get-all-bank-currency-details
method: get
getCityByState:
@@ -29,7 +29,7 @@ getCityByState:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /prepopulate/get-city-by-state
path: /get-city-by-state
method: get
getBranchByBankXid:
@@ -45,7 +45,7 @@ getBranchByBankXid:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /prepopulate/get-branch-by-bank
path: /get-branch-by-bank
method: get
getAllDocumentCountryStateCityDetails:
@@ -60,7 +60,7 @@ getAllDocumentCountryStateCityDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /prepopulate/get-all-doc-country
path: /get-all-doc-country
method: get
getAllPqqQuesAns:
@@ -75,7 +75,7 @@ getAllPqqQuesAns:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /prepopulate/get-all-pqq-ques-ans
path: /get-all-pqq-ques-ans
method: get
getFrequenciesOfActivity:
@@ -90,7 +90,7 @@ getFrequenciesOfActivity:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /prepopulate/get-all-Frequencies
path: /get-all-Frequencies
method: get
getAddActivityPrePopulate:
@@ -105,5 +105,5 @@ getAddActivityPrePopulate:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /prepopulate/get-add-activity-prepopulate
path: /get-add-activity-prepopulate
method: get

View File

@@ -13,7 +13,7 @@ registerUser:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/register
path: /register
method: post
submitPersonalInfo:
@@ -28,7 +28,7 @@ submitPersonalInfo:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/submit-personal-info
path: /submit-personal-info
method: post
verifyOtpForUser:
@@ -43,7 +43,7 @@ verifyOtpForUser:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/verify-otp
path: /verify-otp
method: post
generateAccessFromRefreshToken:
@@ -58,7 +58,7 @@ generateAccessFromRefreshToken:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/generate-access-from-refresh
path: /generate-access-from-refresh
method: post
@@ -74,7 +74,7 @@ setPasscodeForMobile:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/set-passcode
path: /set-passcode
method: post
@@ -90,7 +90,7 @@ verifyPasscode:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/verify-passcode
path: /verify-passcode
method: post
setUserInterest:
@@ -105,7 +105,7 @@ setUserInterest:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/set-interests
path: /set-interests
method: post
setUserLocationss:
@@ -120,7 +120,7 @@ setUserLocationss:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/set-location-user
path: /set-location-user
method: post
getLandingPageDetails:
@@ -135,7 +135,7 @@ getLandingPageDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/get-landing-page-details
path: /activities/get-landing-page-details
method: get
getSurpriseMePageDetails:
@@ -150,7 +150,7 @@ getSurpriseMePageDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/get-surprise-me-page-details
path: /activities/get-surprise-me-page-details
method: get
getActivityDetailsById:
@@ -165,7 +165,7 @@ getActivityDetailsById:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/get-activity-details-by-id/{activity_xid}
path: /activities/get-activity-details-by-id/{activity_xid}
method: get
checkAvailabilityDetails:
@@ -180,7 +180,7 @@ checkAvailabilityDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/check-availability/{activity_xid}
path: /activities/check-availability/{activity_xid}
method: get
searchActivities:
@@ -195,7 +195,7 @@ searchActivities:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/specific-search
path: /activities/specific-search
method: get
searchSchoolsAndCompanies:
@@ -210,7 +210,7 @@ searchSchoolsAndCompanies:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/connections/search-schools-companies
path: /connections/search-schools-companies
method: get
searchCities:
@@ -225,7 +225,7 @@ searchCities:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/connections/search-cities
path: /connections/search-cities
method: get
addSchoolCompanyDetail:
@@ -240,7 +240,7 @@ addSchoolCompanyDetail:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/connections/add-school-company
path: /connections/add-school-company
method: post
removeConnectionDetails:
@@ -255,7 +255,7 @@ removeConnectionDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/connections/remove-connection-details
path: /connections/remove-connection-details
method: delete
getAllConnectionOfUser:
@@ -270,7 +270,7 @@ getAllConnectionOfUser:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/connections/get-all-connections-details
path: /connections/get-all-connections-details
method: get
getActivityFromConnectionsInterest:
@@ -285,7 +285,7 @@ getActivityFromConnectionsInterest:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/connections/get-activity-from-connections-interest
path: /connections/get-activity-from-connections-interest
method: get
viewMoreActivitiesByInterest:
@@ -300,7 +300,7 @@ viewMoreActivitiesByInterest:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/view-more-activities
path: /activities/view-more-activities
method: get
viewMoreActivitiesUpperSection:
@@ -315,7 +315,7 @@ viewMoreActivitiesUpperSection:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/view-more-activities-upper-section
path: /activities/view-more-activities-upper-section
method: get
getRandomActiveActivity:
@@ -330,7 +330,7 @@ getRandomActiveActivity:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/get-random-active-activity
path: /activities/get-random-active-activity
method: get
getNearbyActivities:
@@ -345,7 +345,7 @@ getNearbyActivities:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/get-nearby-activities
path: /activities/get-nearby-activities
method: get
addActivityToBucketInterested:
@@ -360,7 +360,7 @@ addActivityToBucketInterested:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/add-to-bucket-interested
path: /activities/add-to-bucket-interested
method: post
removeActivityFromBucketInterested:
@@ -375,7 +375,7 @@ removeActivityFromBucketInterested:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/remove-from-bucket-interested
path: /activities/remove-from-bucket-interested
method: post
getFilteredLandingPageAllDetails:
@@ -390,5 +390,20 @@ getFilteredLandingPageAllDetails:
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /user/activities/get-filtered-landing-page-details
path: /activities/get-filtered-landing-page-details
method: get
getAllBucketActivities:
handler: src/modules/user/handlers/activities/getAllBucketActivities.handler
memorySize: 512
package:
patterns:
- 'src/modules/user/**'
- ${file(./serverless/patterns/base.yml):pattern1}
- ${file(./serverless/patterns/base.yml):pattern2}
- ${file(./serverless/patterns/base.yml):pattern3}
- ${file(./serverless/patterns/base.yml):pattern4}
events:
- httpApi:
path: /activities/get-all-bucket-activities
method: get

View File

@@ -1,11 +1,18 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import AWS from 'aws-sdk';
import dayjs from 'dayjs';
import { z } from 'zod';
import { prismaClient } from '../../../common/database/prisma.lambda.service';
import { verifyHostToken } from '../../../common/middlewares/jwt/authForHost';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import ApiError from '../../../common/utils/helper/ApiError';
import { ROLE } from '../../../common/utils/constants/common.constant';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import ApiError from "../../../common/utils/helper/ApiError";
import { parseMultipartFormData } from '../../../common/utils/helper/parseMultipartFormData';
import config from '../../../config/config';
const s3 = new AWS.S3({
region: config.aws.region,
});
const updateHostProfileSchema = z
.strictObject({
@@ -17,6 +24,8 @@ const updateHostProfileSchema = z
mobileNumber: z.string().min(5).max(15).optional(),
dateOfBirth: z.string().min(1).optional(),
profileImage: z.string().url().optional(),
// Address
address1: z.string().min(1).optional(),
address2: z.string().min(1).optional(),
@@ -30,6 +39,33 @@ const updateHostProfileSchema = z
})
.strip();
async function uploadProfileImageToS3(buffer: Buffer, mimeType: string, originalName: string, userId: number) {
const sanitizeFileName = (name: string) => {
return name
.toLowerCase()
.replace(/[^a-z0-9.]/g, '_')
.replace(/_+/g, '_')
.replace(/^_+|_+$/g, '');
};
const fileExtension = originalName.split('.').pop() || 'jpg';
const fileName = `profile_image.${fileExtension}`;
const sanitizedFileName = sanitizeFileName(fileName);
const s3Key = `Host/ProfileImages/${userId}/${sanitizedFileName}`;
await s3
.upload({
Bucket: config.aws.bucketName,
Key: s3Key,
Body: buffer,
ContentType: mimeType,
ACL: 'private',
})
.promise();
return `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${s3Key}`;
}
function parseDob(dateOfBirth: string): Date {
const parsed = dayjs(dateOfBirth, ['YYYY-MM-DD', 'MM/DD/YYYY', 'DD/MM/YYYY'], true);
if (!parsed.isValid()) {
@@ -105,7 +141,18 @@ async function ensureHostUser(tx: any, userId: number) {
if (user.roleXid !== ROLE.HOST) throw new ApiError(403, 'Access denied.');
}
async function updateUserIfNeeded(tx: any, userId: number, input: { firstName?: string; lastName?: string | null; isdCode?: string; mobileNumber?: string; dateOfBirth?: string }) {
async function updateUserIfNeeded(
tx: any,
userId: number,
input: {
firstName?: string;
lastName?: string | null;
isdCode?: string;
mobileNumber?: string;
dateOfBirth?: string;
profileImage?: string;
},
) {
const userUpdateData: any = {};
if (input.firstName !== undefined) userUpdateData.firstName = input.firstName || null;
if (input.lastName !== undefined) userUpdateData.lastName = input.lastName;
@@ -114,6 +161,9 @@ async function updateUserIfNeeded(tx: any, userId: number, input: { firstName?:
if (input.dateOfBirth !== undefined) {
userUpdateData.dateOfBirth = input.dateOfBirth ? parseDob(input.dateOfBirth) : null;
}
if (input.profileImage !== undefined) {
userUpdateData.profileImage = input.profileImage || null;
}
if (!hasAnyDefined(userUpdateData)) return;
@@ -210,7 +260,56 @@ export const handler = safeHandler(async (
throw new ApiError(400, 'Invalid user id');
}
const body = parseJsonBody(event);
const contentType = event.headers['Content-Type'] || event.headers['content-type'] || '';
const isMultipart = contentType.includes('multipart/form-data');
let body: any;
if (isMultipart) {
const isBase64Encoded = event.isBase64Encoded || false;
const { fields, files } = parseMultipartFormData(event.body || null, contentType, isBase64Encoded);
const multipartBody: any = {};
const copyIfPresent = (key: string) => {
if (fields[key] !== undefined) {
multipartBody[key] = fields[key];
}
};
['fullName', 'firstName', 'lastName', 'isdCode', 'mobileNumber', 'dateOfBirth', 'address1', 'address2', 'pinCode'].forEach(
copyIfPresent,
);
const parseNumberField = (key: string) => {
if (fields[key] !== undefined) {
const value = Number(fields[key]);
if (!Number.isNaN(value)) {
multipartBody[key] = value;
}
}
};
['countryXid', 'stateXid', 'cityXid'].forEach(parseNumberField);
const profileImageFile = files.find((f) => f.fieldName === 'profileImage');
if (profileImageFile) {
const uploadedUrl = await uploadProfileImageToS3(
profileImageFile.data,
profileImageFile.contentType,
profileImageFile.fileName,
userId,
);
multipartBody.profileImage = uploadedUrl;
} else if (fields.profileImage) {
multipartBody.profileImage = fields.profileImage;
}
body = multipartBody;
} else {
body = parseJsonBody(event);
}
const data = validateBody(body);
const name = normalizeNameFields(data);
const address = buildAddressInput(data);
@@ -223,6 +322,7 @@ export const handler = safeHandler(async (
isdCode: data.isdCode,
mobileNumber: data.mobileNumber,
dateOfBirth: data.dateOfBirth,
profileImage: data.profileImage,
});
await upsertAddressIfNeeded(tx, userId, address);
return getProfileSnapshot(tx, userId);
@@ -237,7 +337,7 @@ export const handler = safeHandler(async (
body: JSON.stringify({
success: true,
message: 'Profile updated successfully',
data: result,
data : null// no data payload per request
}),
};
});

View File

@@ -66,6 +66,8 @@ export const handler = safeHandler(async (
data: {
bucketCount: counts.bucketCount,
interestedCount: counts.interestedCount,
coverImage: counts.coverImage,
coverImagePresignedUrl: counts.coverImagePresignedUrl,
}
}),
};

View File

@@ -0,0 +1,46 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { safeHandler } from '../../../../common/utils/handlers/safeHandler';
import { prismaClient } from '../../../../common/database/prisma.lambda.service';
import ApiError from '../../../../common/utils/helper/ApiError';
import { UserService } from '../../services/user.service';
import { verifyUserToken } from '../../../../common/middlewares/jwt/authForUser';
const userService = new UserService(prismaClient);
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.');
}
// Verify token and get user info
const userInfo = await verifyUserToken(token);
const userId = Number(userInfo.id);
if (!userId || isNaN(userId)) {
throw new ApiError(400, 'Invalid user ID');
}
// Fetch user with their HostHeader stepper info
const result = await userService.getAllBucketActivities(
userId
);
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: true,
message: 'Data retrieved successfully',
data: result,
}),
};
});

View File

@@ -27,16 +27,16 @@ export const handler = safeHandler(async (
const longParam = event.queryStringParameters?.long ?? event.queryStringParameters?.lng ?? event.queryStringParameters?.longitude;
const radiusParam = event.queryStringParameters?.radiusKm ?? event.queryStringParameters?.radius;
if (!latParam || !longParam || !radiusParam) {
throw new ApiError(400, 'lat, long and radiusKm (in km) are required as query parameters');
}
const userLat = latParam ? Number(latParam) : undefined;
const userLong = longParam ? Number(longParam) : undefined;
const radiusKm = radiusParam ? Number(radiusParam) : 15; // default 15km
const userLat = Number(latParam);
const userLong = Number(longParam);
const radiusKm = Number(radiusParam);
if (Number.isNaN(userLat) || Number.isNaN(userLong) || Number.isNaN(radiusKm)) {
throw new ApiError(400, 'lat, long and radiusKm must be valid numbers');
if (
(userLat !== undefined && Number.isNaN(userLat)) ||
(userLong !== undefined && Number.isNaN(userLong)) ||
Number.isNaN(radiusKm)
) {
throw new ApiError(400, 'Invalid lat/long values');
}
const page = Number(event.queryStringParameters?.page ?? 1);

View File

@@ -26,19 +26,11 @@ export const handler = safeHandler(async (
}
// Extract query parameters for search
const activityTitle = event.queryStringParameters?.activityTitle?.trim();
const activityType = event.queryStringParameters?.activityType?.trim();
const checkInCity = event.queryStringParameters?.checkInCity?.trim();
// At least one search parameter should be provided
if (!activityTitle && !activityType && !checkInCity) {
throw new ApiError(400, 'At least one search parameter (activityTitle, activityType, or checkInCity) must be provided');
}
// Fetch activities based on search criteria
const result = await userService.searchActivities(
userId,
{ activityTitle, activityType, checkInCity }
activityType
);
return {

View File

@@ -9,9 +9,28 @@ import config from '../../../config/config';
const bucket = config.aws.bucketName;
const attachPresignedUrl = async (file: string | null | undefined) => {
if (!file) return null;
const key = file.startsWith('http')
? new URL(file).pathname.replace(/^\/+/, '')
: file;
return getPresignedUrl(bucket, key);
};
const attachMediaWithPresignedUrl = async (mediaArr: any[] = []) => {
return Promise.all(
mediaArr.map(async (m) => ({
...m,
presignedUrl: await attachPresignedUrl(m.mediaFileName),
})),
);
};
@Injectable()
export class FilteredLandingPageService {
constructor(private readonly prisma: PrismaClient) {}
constructor(private readonly prisma: PrismaClient) { }
normalizeName = (name: string): string => {
return name
@@ -21,16 +40,6 @@ export class FilteredLandingPageService {
.trim();
};
attachPresignedUrl = async (key: string | null): Promise<string | null> => {
if (!key) return null;
try {
return await getPresignedUrl(bucket, key);
} catch (error) {
console.error(`Failed to generate presigned URL for key: ${key}`, error);
return null;
}
};
findOrCreateLocation = async (
countryName: string,
stateName: string,
@@ -103,18 +112,18 @@ export class FilteredLandingPageService {
};
};
attachMediaWithPresignedUrl = async (mediaArr = []) => {
return (
await Promise.all(
mediaArr.map(async (m) => {
return {
...m,
presignedUrl: await this.attachPresignedUrl(m.mediaFileName),
};
}),
)
);
};
// attachMediaWithPresignedUrl = async (mediaArr = []) => {
// return (
// await Promise.all(
// mediaArr.map(async (m) => {
// return {
// ...m,
// presignedUrl: await this.attachPresignedUrl(m.mediaFileName),
// };
// }),
// )
// );
// };
calculateDistance = (
lat1: number | null,
@@ -136,7 +145,10 @@ export class FilteredLandingPageService {
Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
const distance = R * c;
// return distance rounded to 2 decimals
return Number(distance.toFixed(2));
};
async rankAndPaginateActivities(
@@ -247,12 +259,12 @@ export class FilteredLandingPageService {
energyLevel: activity.activityType.energyLevel
? {
...activity.activityType.energyLevel,
presignedUrl: await this.attachPresignedUrl(
activity.activityType.energyLevel.energyIcon,
presignedUrl: await attachPresignedUrl(
activity.activityType.energyLevel.energyIcon
),
}
: null,
media: await this.attachMediaWithPresignedUrl(activity.ActivitiesMedia),
media: await attachMediaWithPresignedUrl(activity.ActivitiesMedia),
})),
);
@@ -278,7 +290,7 @@ export class FilteredLandingPageService {
) {
const data = await this.prisma.$transaction(async (tx) => {
const userAddressDetails = await tx.userAddressDetails.findFirst({
where: { userXid: userId },
where: { userXid: userId, isActive: true },
select: {
id: true,
address1: true,
@@ -322,36 +334,8 @@ export class FilteredLandingPageService {
const effectiveCountryXid = effectiveLocation?.countryXid ?? null;
const effectiveStateXid = effectiveLocation?.stateXid ?? null;
const userInterests = await tx.userInterests.findMany({
where: { userXid: userId, isActive: true },
select: {
id: true,
interestXid: true,
interest: {
select: {
id: true,
interestName: true,
interestColor: true,
interestImage: true,
displayOrder: true,
},
},
},
});
if (!userInterests.length) {
return {
userAddressDetails,
interests: [],
activityTypes: [],
otherStatesActivities: null,
overSeasActivities: null,
};
}
// Get all activity types for user interests, filtered by selected activity types if provided
const activityTypeWhere: any = {
interestXid: { in: userInterests.map((ui) => ui.interestXid) },
isActive: true,
};
@@ -416,7 +400,7 @@ export class FilteredLandingPageService {
id: true,
schoolCompanyXid: true,
}
})
});
const otherConnectionUsers = await tx.connectDetails.findMany({
where: { userXid: { notIn: [userId] }, isActive: true, schoolCompanyXid: { in: userConnectionDetails.map((u) => u.schoolCompanyXid) } },
@@ -424,7 +408,7 @@ export class FilteredLandingPageService {
id: true,
userXid: true,
}
})
});
const connectionUserIds =
otherConnectionUsers.length > 0
@@ -466,7 +450,7 @@ export class FilteredLandingPageService {
return acc;
}, {} as any);
// Fetch activities for each activity type
// Fetch activities for each activity type with excluded activities filter
const activitiesByActivityType = await Promise.all(
activityTypesWithInterests.map(async (activityType) => {
const activities = await tx.activities.findMany({
@@ -554,12 +538,12 @@ export class FilteredLandingPageService {
energyLevel: activity.activityType.energyLevel
? {
...activity.activityType.energyLevel,
presignedUrl: await this.attachPresignedUrl(
activity.activityType.energyLevel.energyIcon,
presignedUrl: await attachPresignedUrl(
activity.activityType.energyLevel.energyIcon
),
}
: null,
media: await this.attachMediaWithPresignedUrl(activity.ActivitiesMedia),
media: await attachMediaWithPresignedUrl(activity.ActivitiesMedia),
};
}),
);
@@ -579,26 +563,29 @@ export class FilteredLandingPageService {
);
// Group by interests for the final structure
const interestsWithActivityTypes = await Promise.all(Object.values(activityTypesByInterest).map(
async (interestGroup: any) => ({
interestId: interestGroup.interest.id,
interestName: interestGroup.interest.interestName,
interestColor: interestGroup.interest.interestColor,
interestImage: interestGroup.interest.interestImage,
interestImagePresignedUrl: await this.attachPresignedUrl(interestGroup.interest.interestImage),
displayOrder: interestGroup.interest.displayOrder,
activityTypes: interestGroup.activityTypes.map((at: any) => {
const activityTypeData = activitiesByActivityType.find(
(adata) => adata.activityTypeId === at.activityTypeId
);
return {
...at,
activities: activityTypeData?.activities || [],
pagination: activityTypeData?.pagination || { page, limit, hasMore: false },
};
}),
}),
));
const interestsWithActivities = await Promise.all(
Object.values(activityTypesByInterest).map(async (interestGroup: any) => {
// collect all activities belonging to this interest
const activitiesForInterest = activitiesByActivityType
.filter(a => a.interestXid === interestGroup.interest.id)
.flatMap(a => a.activities);
return {
interestId: interestGroup.interest.id,
interestName: interestGroup.interest.interestName,
interestColor: interestGroup.interest.interestColor,
interestImage: interestGroup.interest.interestImage,
interestImagePresignedUrl: await attachPresignedUrl(
interestGroup.interest.interestImage
),
displayOrder: interestGroup.interest.displayOrder,
page,
limit,
hasMore: activitiesForInterest.length === limit,
activities: activitiesForInterest
};
})
);
// Most Hyped Activities with filtering
const mostHypedGrouped = await tx.userBucketInterested.groupBy({
@@ -606,6 +593,9 @@ export class FilteredLandingPageService {
where: {
isActive: true,
isBucket: false,
activityXid: {
notIn: allUserExcludedActivityIds.length ? allUserExcludedActivityIds : [-1],
},
},
_count: {
activityXid: true,
@@ -619,7 +609,7 @@ export class FilteredLandingPageService {
// Filter most hyped activities by activity type if provided
let filteredMostHypedActivityIds = mostHypedGrouped.map((a) => a.activityXid);
if (activityTypeXids && activityTypeXids.length > 0) {
const activitiesWithTypes = await tx.activities.findMany({
where: {
@@ -740,12 +730,12 @@ export class FilteredLandingPageService {
energyLevel: activity.activityType.energyLevel
? {
...activity.activityType.energyLevel,
presignedUrl: await this.attachPresignedUrl(
activity.activityType.energyLevel.energyIcon,
presignedUrl: await attachPresignedUrl(
activity.activityType.energyLevel.energyIcon
),
}
: null,
media: await this.attachMediaWithPresignedUrl(activity.ActivitiesMedia),
media: await attachMediaWithPresignedUrl(activity.ActivitiesMedia),
})),
);
@@ -886,9 +876,9 @@ export class FilteredLandingPageService {
activityId: activity!.id,
activityTitle: activity!.activityTitle,
coverImage: cover?.mediaFileName ?? null,
coverImagePresignedUrl: cover?.mediaFileName
? await this.attachPresignedUrl(cover.mediaFileName)
: null,
coverImagePresignedUrl: await attachPresignedUrl(
cover?.mediaFileName
),
};
}),
);
@@ -935,8 +925,7 @@ export class FilteredLandingPageService {
limit,
},
randomActivities,
interests: interestsWithActivityTypes,
activityTypes: activitiesByActivityType,
interests: interestsWithActivities,
otherStatesActivities: formattedOtherStatesActivities,
overSeasActivities: formattedOverSeasActivities,
newArrivalsActivities: formattedNewArrivalsActivities,

View File

@@ -420,6 +420,14 @@ export class UserService {
},
});
const totalActivityCount = await this.prisma.activities.count({
where: {
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
}
})
for (const interest of interests) {
if (interest.interestImage) {
const key = interest.interestImage.startsWith('http')
@@ -432,7 +440,7 @@ export class UserService {
}
}
return interests;
return { interests, totalActivityCount };
}
async getUserByMobileNumber(mobileNumber: string): Promise<User | null> {
@@ -762,6 +770,52 @@ export class UserService {
u => u.activityXid,
);
const latestUserActivity = await tx.userBucketInterested.findFirst({
where: {
userXid: userId,
isActive: true,
},
orderBy: {
createdAt: 'desc',
},
select: {
activityXid: true,
},
});
let latestCoverImage: string | null = null;
let latestCoverImagePresignedUrl: string | null = null;
if (latestUserActivity) {
const latestActivityImage = await tx.activities.findFirst({
where: {
id: latestUserActivity.activityXid,
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
},
select: {
ActivitiesMedia: {
where: {
isCoverImage: true,
isActive: true,
},
select: {
mediaFileName: true,
},
take: 1,
},
},
});
latestCoverImage =
latestActivityImage?.ActivitiesMedia?.[0]?.mediaFileName ?? null;
latestCoverImagePresignedUrl = latestCoverImage
? await attachPresignedUrl(latestCoverImage)
: null;
}
const userConnectionDetails = await tx.connectDetails.findMany({
where: { userXid: userId, isActive: true },
select: {
@@ -1265,6 +1319,9 @@ export class UserService {
loggedInNetworkCount: 0,
citiesInNetworkCount: 0,
rating: 0,
latestBucketInterestedCoverImage: latestCoverImage,
latestBucketInterestedCoverImagePresignedUrl:
latestCoverImagePresignedUrl,
interestedCount: userInterestedActivityIds.length,
bucketCount: userBucketActivityIds.length,
pagination: {
@@ -1460,6 +1517,17 @@ export class UserService {
activityTitle: true,
checkInLat: true,
checkInLong: true,
activityDurationMins: true,
sustainabilityScore: true,
ActivityVenues: {
select: {
ActivityPrices: {
select: {
sellPrice: true,
},
},
},
},
activityType: {
select: {
interestXid: true,
@@ -1473,8 +1541,14 @@ export class UserService {
},
});
const formattedOtherInterestActivities = await Promise.all(
otherInterestActivities.map(async (a) => ({
cheapestPrice:
a.ActivityVenues.flatMap(v => v.ActivityPrices)
.map(p => p.sellPrice)
.filter(Boolean)
.sort((a, b) => a - b)[0] ?? null,
interestXid: a.activityType.interestXid,
activityId: a.id,
connectionInterestedCount:
@@ -1486,6 +1560,8 @@ export class UserService {
a.checkInLat,
a.checkInLong
),
activityDurationMins: a.activityDurationMins,
sustainabilityScore: a.sustainabilityScore,
rating: 0,
energyLevel: {
...a.activityType.energyLevel,
@@ -1559,6 +1635,17 @@ export class UserService {
checkInLat: true,
checkInLong: true,
activityType: { select: { energyLevel: true } },
activityDurationMins: true,
sustainabilityScore: true,
ActivityVenues: {
select: {
ActivityPrices: {
select: {
sellPrice: true,
},
},
},
},
ActivitiesMedia: {
where: { isActive: true },
select: { id: true, mediaFileName: true, mediaType: true },
@@ -1571,6 +1658,12 @@ export class UserService {
const act = hypedActivities.find((a) => a.id === g.activityXid);
if (!act) return null;
return {
cheapestPrice: act.ActivityVenues.flatMap(v => v.ActivityPrices)
.map(p => p.sellPrice)
.filter(Boolean)
.sort((a, b) => a - b)[0] ?? null,
activityDurationMins: act.activityDurationMins,
sustainabilityScore: act.sustainabilityScore,
activityId: act.id,
activityTitle: act.activityTitle,
hypeCount: g._count.activityXid,
@@ -1619,6 +1712,17 @@ export class UserService {
id: true,
activityTitle: true,
activityType: { select: { energyLevel: true } },
activityDurationMins: true,
sustainabilityScore: true,
ActivityVenues: {
select: {
ActivityPrices: {
select: {
sellPrice: true,
},
},
},
},
ActivitiesMedia: {
where: { isActive: true },
select: { id: true, mediaFileName: true, mediaType: true },
@@ -1665,6 +1769,17 @@ export class UserService {
id: true,
activityTitle: true,
activityType: { select: { energyLevel: true } },
activityDurationMins: true,
sustainabilityScore: true,
ActivityVenues: {
select: {
ActivityPrices: {
select: {
sellPrice: true,
},
},
},
},
ActivitiesMedia: {
where: { isActive: true },
select: { id: true, mediaFileName: true, mediaType: true },
@@ -1679,6 +1794,17 @@ export class UserService {
id: true,
activityTitle: true,
activityType: { select: { energyLevel: true } },
activityDurationMins: true,
sustainabilityScore: true,
ActivityVenues: {
select: {
ActivityPrices: {
select: {
sellPrice: true,
},
},
},
},
ActivitiesMedia: {
where: { isActive: true },
select: { id: true, mediaFileName: true, mediaType: true },
@@ -1687,6 +1813,82 @@ export class UserService {
}),
]);
/* =====================================================
RANDOM ACTIVITIES (5 COVER IMAGES)
===================================================== */
const totalActiveCount = await tx.activities.count({
where: {
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
deletedAt: null,
id: {
notIn: safeExcludedIds,
},
...excludeUserInterestCondition,
},
});
let randomActivities: any[] = [];
if (totalActiveCount > 0) {
const takeCount = Math.min(5, totalActiveCount);
const randomOffsets = new Set<number>();
while (randomOffsets.size < takeCount) {
randomOffsets.add(Math.floor(Math.random() * totalActiveCount));
}
const randomFetched = await Promise.all(
Array.from(randomOffsets).map((offset) =>
tx.activities.findFirst({
skip: offset,
where: {
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
deletedAt: null,
id: {
notIn: safeExcludedIds,
},
...excludeUserInterestCondition,
},
select: {
id: true,
activityTitle: true,
ActivitiesMedia: {
where: { isActive: true, isCoverImage: true },
orderBy: { displayOrder: 'asc' },
take: 1,
select: {
mediaFileName: true,
},
},
},
}),
),
);
randomActivities = await Promise.all(
randomFetched
.filter(Boolean)
.map(async (activity) => {
const cover = activity!.ActivitiesMedia?.[0];
return {
activityId: activity!.id,
activityTitle: activity!.activityTitle,
coverImage: cover?.mediaFileName ?? null,
coverImagePresignedUrl: cover?.mediaFileName
? await attachPresignedUrl(cover.mediaFileName)
: null,
};
}),
);
}
/* =====================================================
7⃣ FINAL RESPONSE
===================================================== */
@@ -1695,6 +1897,7 @@ export class UserService {
interests: interestsWithActivities,
interestedCount: interestedActivityIds.length,
bucketCount: bucketActivityIds.length,
randomActivities,
mostHypedActivities: {
page,
@@ -1711,6 +1914,12 @@ export class UserService {
hasMore: skip + limit < newArrivalsCount,
activities: await Promise.all(
newArrivalsRaw.map(async (a) => ({
cheapestPrice: a.ActivityVenues.flatMap(v => v.ActivityPrices)
.map(p => p.sellPrice)
.filter(Boolean)
.sort((a, b) => a - b)[0] ?? null,
activityDurationMins: a.activityDurationMins,
sustainabilityScore: a.sustainabilityScore,
activityId: a.id,
activityTitle: a.activityTitle,
connectionInterestedCount:
@@ -1735,6 +1944,12 @@ export class UserService {
hasMore: skip + limit < otherStatesCount,
activities: await Promise.all(
otherStatesRaw.map(async (a) => ({
cheapestPrice: a.ActivityVenues.flatMap(v => v.ActivityPrices)
.map(p => p.sellPrice)
.filter(Boolean)
.sort((a, b) => a - b)[0] ?? null,
activityDurationMins: a.activityDurationMins,
sustainabilityScore: a.sustainabilityScore,
activityId: a.id,
activityTitle: a.activityTitle,
connectionInterestedCount:
@@ -1759,6 +1974,12 @@ export class UserService {
hasMore: skip + limit < overseasCount,
activities: await Promise.all(
overseasRaw.map(async (a) => ({
cheapestPrice: a.ActivityVenues.flatMap(v => v.ActivityPrices)
.map(p => p.sellPrice)
.filter(Boolean)
.sort((a, b) => a - b)[0] ?? null,
activityDurationMins: a.activityDurationMins,
sustainabilityScore: a.sustainabilityScore,
activityId: a.id,
activityTitle: a.activityTitle,
connectionInterestedCount:
@@ -2254,6 +2475,7 @@ export class UserService {
isActive: true,
user: {
isActive: true,
profileImage: { not: null },
},
},
select: {
@@ -2263,29 +2485,35 @@ export class UserService {
},
},
},
take: 5,
});
const randomFive = interestedUsers
.sort(() => Math.random() - 0.5)
.slice(0, 5);
const interestedUserImages: { profileImage: string }[] = [];
// const interestedUserImages: { profileImage: string }[] = [];
for (const item of randomFive) {
const profileImage = item.user.profileImage;
// for (const item of randomFive) {
// const profileImage = item.user.profileImage;
if (profileImage) {
const key = profileImage.startsWith('http')
? new URL(profileImage).pathname.replace(/^\/+/, '')
: profileImage;
// if (profileImage) {
// const key = profileImage.startsWith('http')
// ? new URL(profileImage).pathname.replace(/^\/+/, '')
// : profileImage;
const presignedUrl = await getPresignedUrl(bucket, key);
// const presignedUrl = await getPresignedUrl(bucket, key);
interestedUserImages.push({
profileImage: presignedUrl,
});
}
}
// interestedUserImages.push({
// profileImage: presignedUrl,
// });
// }
// }
const interestedUserImages = await Promise.all(
randomFive.map(async ({ user }) => ({
profileImage: await attachPresignedUrl(user.profileImage),
}))
);
const checkInLocation =
activity?.checkInCity?.cityName && activity?.checkInState?.stateName
@@ -2316,116 +2544,57 @@ export class UserService {
async searchActivities(
userId: number,
searchCriteria: {
activityTitle?: string;
activityType?: string;
checkInCity?: string;
},
activityType?: string
) {
const { activityTitle, activityType, checkInCity } = searchCriteria;
// Build the where clause dynamically
const where: any = {
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
};
// Add activityTitle filter if provided
if (activityTitle) {
where.activityTitle = {
contains: activityTitle,
if (activityType && activityType.trim().length > 0) {
where.activityTypeName = {
contains: activityType.trim(),
mode: 'insensitive',
};
}
// Add activityType filter if provided
if (activityType) {
where.activityType = {
activityTypeName: {
contains: activityType,
mode: 'insensitive',
},
};
}
// Add checkInCity filter if provided
if (checkInCity) {
where.checkInCity = {
cityName: {
contains: checkInCity,
mode: 'insensitive',
},
};
}
const activities = await this.prisma.activities.findMany({
const activityTypes = await this.prisma.activityTypes.findMany({
where,
select: {
id: true,
activityTitle: true,
activityDescription: true,
checkInAddress: true,
activityDurationMins: true,
sustainabilityScore: true,
activityRefNumber: true,
activityType: {
activityTypeName: true,
interests: {
select: {
activityTypeName: true,
energyLevel: {
select: {
energyLevelName: true,
energyColor: true,
energyIcon: true,
},
},
},
},
checkInCity: {
select: {
cityName: true,
},
},
ActivitiesMedia: {
where: { isActive: true },
select: {
id: true,
mediaFileName: true,
mediaType: true,
},
take: 1, // Get first media item
},
interestImage: true
}
}
},
take: 50, // Limit results to prevent too many
orderBy: {
activityTypeName: 'asc',
},
take: 20, // limit suggestions
});
// Get interested count for each activity
const activitiesWithCounts = await Promise.all(
activities.map(async (activity) => {
const interestedCount = await this.prisma.userBucketInterested.count({
where: {
activityXid: activity.id,
isActive: true,
},
});
const formattedResults = await Promise.all(
activityTypes.map(async (activity) => {
const image = activity.interests?.interestImage ?? null;
// Attach presigned URLs to media
const mediaWithUrls = await attachMediaWithPresignedUrl(
activity.ActivitiesMedia,
);
const presignedUrl = image
? await attachPresignedUrl(image)
: null;
return {
...activity,
ActivitiesMediaPresignedUrl: mediaWithUrls,
interestedCount,
rating: 0, // Placeholder
distance: 0, // Placeholder
id: activity.id,
activityTypeName: activity.activityTypeName,
interestImage: image,
interestImagePresignedUrl: presignedUrl,
};
}),
);
return activitiesWithCounts;
return formattedResults;
}
async getNearbyActivities(
@@ -2436,15 +2605,25 @@ export class UserService {
page: number,
limit: number,
) {
if (userLat === undefined || userLong === undefined || radiusKm === undefined) {
throw new ApiError(
400,
'Latitude, longitude and radius are required to find nearby activities',
);
}
// If lat/long not provided, fetch from user saved address
if (userLat === undefined || userLong === undefined) {
const userAddress = await this.prisma.userAddressDetails.findFirst({
where: { userXid: userId, isActive: true },
select: {
locationLat: true,
locationLong: true,
},
});
if (radiusKm <= 0) {
throw new ApiError(400, 'Radius must be greater than 0');
if (!userAddress?.locationLat || !userAddress?.locationLong) {
throw new ApiError(
400,
'User location not found. Please provide lat/long.',
);
}
userLat = userAddress.locationLat;
userLong = userAddress.locationLong;
}
const skip = (page - 1) * limit;
@@ -3346,14 +3525,84 @@ export class UserService {
}))
);
/* =====================================================
RANDOM ACTIVITIES FROM CONNECTION USERS (5 COVER IMAGES)
===================================================== */
const totalActiveCount = await tx.activities.count({
where: {
id: { in: connectionActivityIds },
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
activityTypeXid: { in: activityTypeIds },
deletedAt: null,
},
});
let randomActivities: any[] = [];
if (totalActiveCount > 0) {
const takeCount = Math.min(5, totalActiveCount);
const randomOffsets = new Set<number>();
while (randomOffsets.size < takeCount) {
randomOffsets.add(Math.floor(Math.random() * totalActiveCount));
}
const randomFetched = await Promise.all(
Array.from(randomOffsets).map((offset) =>
tx.activities.findFirst({
skip: offset,
where: {
id: { in: connectionActivityIds },
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
activityTypeXid: { in: activityTypeIds },
deletedAt: null,
},
select: {
id: true,
activityTitle: true,
ActivitiesMedia: {
where: { isActive: true, isCoverImage: true },
orderBy: { displayOrder: "asc" },
take: 1,
select: {
mediaFileName: true,
},
},
},
}),
),
);
randomActivities = await Promise.all(
randomFetched
.filter(Boolean)
.map(async (activity) => {
const cover = activity!.ActivitiesMedia?.[0];
return {
activityId: activity!.id,
activityTitle: activity!.activityTitle,
coverImage: cover?.mediaFileName ?? null,
coverImagePresignedUrl: cover?.mediaFileName
? await attachPresignedUrl(cover.mediaFileName)
: null,
};
}),
);
}
return {
experiencesLogged: 25,
citiesDiscovered: 10,
loggedInNetworkCount: 0,
citiesInNetworkCount: 0,
randomActivities,
interestedCount: userInterestedActivityIds.length,
bucketCount: userBucketActivityIds.length,
pagination: {
@@ -3905,6 +4154,30 @@ export class UserService {
},
});
const latestActivityImage = await this.prisma.activities.findFirst({
where: {
id: activityXid,
isActive: true,
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
},
select: {
ActivitiesMedia: {
where: {
isCoverImage: true
},
select: {
mediaFileName: true,
}
}
}
})
const coverImage = latestActivityImage?.ActivitiesMedia?.[0]?.mediaFileName ?? null;
// Generate presigned URL
const coverImagePresignedUrl = await attachPresignedUrl(coverImage);
// ✅ Get updated counts
const [bucketCount, interestedCount] = await Promise.all([
this.prisma.userBucketInterested.count({
@@ -3926,6 +4199,8 @@ export class UserService {
return {
bucketCount,
interestedCount,
coverImage,
coverImagePresignedUrl,
};
}
@@ -3981,4 +4256,70 @@ export class UserService {
interestedCount,
};
}
async getAllBucketActivities(userXid: number) {
const bucketActivities = await this.prisma.userBucketInterested.findMany({
where: {
userXid,
isBucket: true,
isActive: true,
},
select: {
id: true,
bucketTypeName: true,
activityXid: true,
Activities: {
select: {
activityTitle: true,
ActivitiesMedia: {
where: {
isCoverImage: true,
isActive: true,
},
select: {
mediaFileName: true,
},
},
},
},
},
});
const ready: any[] = [];
const planning: any[] = [];
const oneDay: any[] = [];
for (const item of bucketActivities) {
const media = item.Activities?.ActivitiesMedia?.[0]?.mediaFileName;
let presignedUrl = null;
if (media) {
presignedUrl = await attachPresignedUrl(media);
// your presigned url function
}
const activityData = {
id: item.id,
activityXid: item.activityXid,
bucketTypeName: item.bucketTypeName,
activityTitle: item.Activities?.activityTitle,
coverImage: presignedUrl,
};
if (item.bucketTypeName === 'Ready') {
ready.push(activityData);
} else if (item.bucketTypeName === 'Planning') {
planning.push(activityData);
} else if (item.bucketTypeName === 'One-day') {
oneDay.push(activityData);
}
}
return {
ready,
planning,
oneDay,
};
}
}