Compare commits
419 Commits
validation
...
paritosh-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d18a77ab5 | ||
|
|
e6ba52520d | ||
|
|
e164e1c25a | ||
|
|
ce9a9b6211 | ||
|
|
4e04781a06 | ||
|
|
e77d9e50c9 | ||
|
|
be9780f9ec | ||
|
|
3c56c45b01 | ||
|
|
d1c4ad76ba | ||
|
|
75025b62d9 | ||
|
|
04ae88b239 | ||
|
|
b18b6bc468 | ||
|
|
6f1504e93f | ||
|
|
1a6411acdc | ||
|
|
841539b8cc | ||
|
|
5fcff67916 | ||
|
|
acd31725ed | ||
|
|
0d96b1e67e | ||
|
|
f98354a1c8 | ||
|
|
66d65c3b84 | ||
|
|
eef9bbf368 | ||
|
|
2eac865c51 | ||
| f205dfedd6 | |||
| 22e2e8e1b7 | |||
|
|
ce1be2c94e | ||
|
|
d793852d63 | ||
|
|
9b95149469 | ||
|
|
1789084685 | ||
|
|
cf5bd02a04 | ||
| 0be293288d | |||
|
|
0ba056af98 | ||
| 9e8d9502ae | |||
| 94454a6f25 | |||
|
|
631ae79277 | ||
| 25b82bee31 | |||
|
|
dcb2259c7d | ||
| c5dcc5b1f0 | |||
| b47e6271a3 | |||
| 958a3e5cec | |||
| 181f32b2e7 | |||
| c5cad4fdce | |||
| daff265584 | |||
| 0f5061a129 | |||
| 5e87ab84d1 | |||
| 54a4f22d2f | |||
| bb87e0ac05 | |||
|
|
01569670b4 | ||
| 8fccc62f33 | |||
| fcdb813fb7 | |||
| 5239e04621 | |||
| fe0a2eb95b | |||
|
|
16d075b5f7 | ||
| 9df8c5443c | |||
| be0667d8e9 | |||
| a44321044f | |||
| fcac64e0a9 | |||
| 6ea2ebe5e1 | |||
| 388f3079a1 | |||
| 6a11c15f39 | |||
| ea461b6056 | |||
| 6703dc784d | |||
| e22c37bc65 | |||
| d32915c865 | |||
| 1c7ad52d0e | |||
| f1f1f199e8 | |||
| 1c32c18e03 | |||
| 0d3c71ab5a | |||
| 50f93bbeae | |||
| 3ce9d1d180 | |||
| cb819088a0 | |||
| 8f5f01c287 | |||
| e4a2a04045 | |||
| 50ce8e39c5 | |||
| 19e57f0e7f | |||
| ad5e343b66 | |||
| 8c3ece6ebd | |||
| 092f425bb3 | |||
| b1a3afd3a1 | |||
| 97f431260d | |||
| bf6d9ae00b | |||
| 518ec4eb21 | |||
| 95b061b400 | |||
| 92992797ab | |||
| c96e3b0c1a | |||
| f23b93801c | |||
| f1801a3210 | |||
| 2588ca4317 | |||
| e809ba4480 | |||
| 678be7c905 | |||
| 08b4231e5f | |||
|
|
4a61e2c412 | ||
| a3ab9db5a2 | |||
|
|
7167eae07e | ||
|
|
4f3d7fd737 | ||
|
|
199013b0f5 | ||
| 0b81dbf7b1 | |||
|
|
9722e1988c | ||
| cf2bbbf138 | |||
|
|
2ca785248f | ||
| 0aa2b9b53e | |||
| b5cdb20c4f | |||
| 00e07113e5 | |||
| c8f0f93792 | |||
| 87779664d1 | |||
| 5b31e5f2a9 | |||
| 2a073c44a2 | |||
|
|
220d309087 | ||
| f45c33ba83 | |||
| 22b3593150 | |||
| d186681ee4 | |||
| 8f428fc1cb | |||
| 0b503cf8bb | |||
| 7110d0462c | |||
| 96648fe37e | |||
|
|
2095f8e124 | ||
| 21c8799502 | |||
|
|
ad9e8e1a3f | ||
| b200e2cb94 | |||
| cae66237d2 | |||
| 25be8a5647 | |||
| 7a4aecdd45 | |||
| 5cced2981a | |||
|
|
b9fbab3717 | ||
|
|
90c897ad48 | ||
| 4a069cc67a | |||
|
|
5d046c4bcf | ||
| accfc4b769 | |||
| e149884f72 | |||
| a31ec97640 | |||
| 0c97412057 | |||
|
|
b4ff39c0d7 | ||
| bb5da7647b | |||
| 3f19bb4087 | |||
| be8b9cef7d | |||
|
|
77cef98091 | ||
| 97f9c2b26e | |||
|
|
b93cd6b32c | ||
|
|
51319a69fc | ||
| 5ad46309ef | |||
|
|
781212277a | ||
| 6b0ee461c5 | |||
| cc2fa3eb6b | |||
| fe6bb59cc7 | |||
| 3fa5b8a3dd | |||
| 414400f034 | |||
|
|
eaae2557c0 | ||
|
|
f1829a6d14 | ||
| 9d64759097 | |||
| 5d879542a1 | |||
| ea9938a736 | |||
|
|
84608cc025 | ||
|
|
9921edfea7 | ||
|
|
26a8e997f1 | ||
| b98b8cf864 | |||
| 85437ebc2e | |||
| 2b7b2b240f | |||
| 0242799eb5 | |||
|
|
c7408711a4 | ||
|
|
db2dc7532a | ||
| 485bdee063 | |||
| 0c9ff76fe4 | |||
| 0edcf3d515 | |||
| eb34edaedb | |||
| 41d33b72a3 | |||
|
|
f3076aaec3 | ||
|
|
e49d08fb14 | ||
|
|
23bbb39af3 | ||
|
|
c4fd797e31 | ||
|
|
3f96dd4ae1 | ||
|
|
89f1bf55bc | ||
| e736cdaa7b | |||
| 7703dba2d9 | |||
| a3e906a779 | |||
| 3145037238 | |||
|
|
ced8bdcbad | ||
| b22324539a | |||
| aa3e6fd3d1 | |||
|
|
5ec07d4480 | ||
|
|
1fbcf1dddc | ||
| c50c4b1c5a | |||
|
|
ca4def4695 | ||
| 4f8217c95a | |||
| 81994d97ff | |||
| 28b4145ce9 | |||
| e351dbf4b9 | |||
| d8475de70f | |||
|
|
2b0c1f4ae4 | ||
| 23932be637 | |||
| 84678bc00e | |||
|
|
a905cc7aee | ||
|
|
340d47a708 | ||
| b0eb2863cd | |||
|
|
b002077381 | ||
|
|
a43cc1e36a | ||
| 3e711cee18 | |||
| 4d3d95f906 | |||
| 6bac257a75 | |||
| af891834a7 | |||
| 77fee0cc04 | |||
|
|
95e1838303 | ||
|
|
f8b6f277a1 | ||
| 3f2e553947 | |||
| 7569b54d60 | |||
| 0a11c78351 | |||
| b4af010316 | |||
|
|
b35cb4e178 | ||
| bf9055c10a | |||
| d1e2c649c3 | |||
|
|
9c45d924e6 | ||
| 9846d40bfb | |||
| a0b015d900 | |||
|
|
ebeb4e5d06 | ||
| 036f7ab130 | |||
| e154be70ad | |||
|
|
62e7379306 | ||
|
|
68facd1146 | ||
|
|
1dcce8084c | ||
|
|
6e0d795348 | ||
| b1d0e0f52e | |||
|
|
76fdc42428 | ||
|
|
c216d128a6 | ||
| 5ccdcdedae | |||
| 73c528d1cc | |||
| 171a3aded6 | |||
| 42c1f2a268 | |||
| 00d53adf3d | |||
| 93fb58f4f4 | |||
|
|
1f53180b4e | ||
|
|
558cb214c0 | ||
| f414bc42f9 | |||
| e92eb230a0 | |||
|
|
5ca8ca266a | ||
| cb92fbe400 | |||
| d861e402e9 | |||
| 8ed0df7424 | |||
| 745e4fa73f | |||
|
|
46c8432582 | ||
|
|
9bbd8f0c9a | ||
| 07f0212b62 | |||
|
|
50d2242b55 | ||
| 3adbadc3ee | |||
|
|
3f92c6ca30 | ||
| ef7564f5a4 | |||
| 8be2eebaba | |||
| 1d90675d19 | |||
| 855cafb30f | |||
| 6bc68ddd04 | |||
|
|
003ce54465 | ||
|
|
4525e969e6 | ||
| 68f0dfe124 | |||
| 112fdab040 | |||
| 834d16a76e | |||
| f20e4191ee | |||
| 6d377296fc | |||
| 8534ac6c7d | |||
| 89ee19f35d | |||
|
|
b4db2a3bc2 | ||
| 5d93945729 | |||
| aa00e1585c | |||
| 9c10833856 | |||
|
|
905350406f | ||
|
|
bc0470cf52 | ||
|
|
39e03ce4c7 | ||
|
|
4953a179a6 | ||
|
|
82ba980b6f | ||
|
|
5ec819b881 | ||
| f7cc925e6f | |||
| 16c901b64b | |||
| e65ed8babc | |||
|
|
fa1359a1c9 | ||
|
|
2c965bfb92 | ||
| da4e2547ce | |||
| c6b6608ca6 | |||
| 4a6292d108 | |||
| 45aab89747 | |||
| 183614f45a | |||
|
|
3ddaad0a46 | ||
| e286cffa49 | |||
| fafb5d06a7 | |||
| 5482599e96 | |||
| e0086393b4 | |||
|
|
9161e224cc | ||
|
|
c2fa769220 | ||
|
|
aeea3d0ca8 | ||
|
|
c2d3ab9da2 | ||
|
|
401734096d | ||
|
|
ed3aaab961 | ||
| 8199ce327a | |||
| 5ce3dd3780 | |||
|
|
245af71705 | ||
| 2c81e949ca | |||
| 738b2c6608 | |||
|
|
999c5ff616 | ||
| 8dad4bfd2a | |||
| 65fc472902 | |||
|
|
6359d64b63 | ||
| b3e051bd63 | |||
| 80c552a3d5 | |||
|
|
e6d37e04f9 | ||
|
|
0c71ae3ccf | ||
| 716ce3363f | |||
|
|
ddc2e46dd2 | ||
|
|
927649b4f6 | ||
|
|
4f9955d9f4 | ||
| 745e827d22 | |||
| 8c86e72346 | |||
|
|
9b3fcd8cbe | ||
| d4b5153814 | |||
|
|
6440fc2440 | ||
|
|
95f823e749 | ||
| 60ee1f5f21 | |||
| bf16a31ae6 | |||
|
|
1540688b7d | ||
| cab4408dc8 | |||
| be65a6c021 | |||
|
|
f13e90ce39 | ||
|
|
5a223f126f | ||
|
|
2e4f318684 | ||
|
|
53785bd5f2 | ||
| c9b507f969 | |||
| 91871d1f44 | |||
| 05e48063c9 | |||
| a906dc5635 | |||
| 8ec8cf4854 | |||
| fab7642302 | |||
| 4d3796c5f3 | |||
| 2f3c531c56 | |||
| a2907929d4 | |||
|
|
ef2b23ef83 | ||
| 2767d29d79 | |||
| 46daec00ce | |||
| 43e494780d | |||
| 0da18b18f7 | |||
| b5304b3c26 | |||
| 82340c2918 | |||
| b8f5f92c98 | |||
| 3652d851f7 | |||
|
|
6166075967 | ||
|
|
b6cb5831c2 | ||
|
|
a39cc1c3c8 | ||
|
|
ae76618f7a | ||
| e957fc5c50 | |||
|
|
856db687c3 | ||
| a020a28993 | |||
| 1a520ae9e1 | |||
| d5d6951e64 | |||
| bbd55562af | |||
| 9abadba8f5 | |||
| 747566497c | |||
| 6c3e5ccd60 | |||
| ca9ba601ad | |||
| eab6565e12 | |||
| 6a84876518 | |||
| d8fb4b242d | |||
| 51b053310f | |||
| 06010ef6e8 | |||
| 7b9763c668 | |||
| 4c1a04d043 | |||
|
|
ecf45c3e7c | ||
|
|
20a931ab27 | ||
| 6a84fbc0c3 | |||
| 9fc8336bd9 | |||
| b049146664 | |||
| 963f84681c | |||
| ab9e02972e | |||
| 33b330a15b | |||
| d898dcd8ff | |||
|
|
ff18fcbf9f | ||
|
|
a872ed89e4 | ||
| 759eeb298c | |||
| 1b72e65a71 | |||
| 4a7e5fbb1e | |||
| ca5936d0db | |||
| c0d607a321 | |||
| 1d684b7de6 | |||
| 16b16ac7ca | |||
| 0e0c63e31a | |||
| 822b425c1d | |||
| 42e2d7a579 | |||
|
|
78f49b35dd | ||
|
|
930ae708a1 | ||
|
|
8fb3f16d18 | ||
| 3d6226ddac | |||
| 38c616a7af | |||
| c6ab3e57c0 | |||
|
|
776c03911e | ||
|
|
781058c443 | ||
| 3b723e5d1f | |||
|
|
56ebf44d37 | ||
| 4f8274adb9 | |||
| 39f182b8e9 | |||
| d9f7cd9a0f | |||
| 4772c320ba | |||
|
|
f19f5e46c4 | ||
|
|
156aed2429 | ||
|
|
fb77111e34 | ||
| 6b673a173d | |||
| 3c4b0db39f | |||
| e72c260b18 | |||
|
|
9a777eb3f9 | ||
|
|
c3f0a1d82a | ||
| 3f921febe0 | |||
|
|
4bc5eb8d4d | ||
| f3f0b2a81b | |||
|
|
6f0cdb4e0a | ||
| ce9c8174d8 | |||
| c72e757bf3 | |||
|
|
6aaf49bf72 | ||
|
|
1b31ca4a83 | ||
| 140f70615c | |||
| 32f4c7ce42 | |||
|
|
067d1a1f1b | ||
|
|
470298a3fb | ||
| 1d7d0749b3 | |||
|
|
d3fb1c85fb | ||
| e723e680ab | |||
|
|
0e50b8b187 | ||
|
|
bbe725dd9e | ||
| e5861654e9 |
44
.env.example
Normal file
44
.env.example
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Environment Configuration Template
|
||||||
|
# Copy this file to .env.dev, .env.test, or .env.uat and fill in the values
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_URL=
|
||||||
|
DB_USERNAME=
|
||||||
|
DB_PASSWORD=
|
||||||
|
DB_DATABASE_NAME=
|
||||||
|
DB_HOSTNAME=
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
# Email Bypass (set to true for dev/test, false for production-like environments)
|
||||||
|
BY_PASS_EMAIL=
|
||||||
|
BYPASS_OTP=
|
||||||
|
|
||||||
|
# Brevo Email Configuration
|
||||||
|
BREVO_EMAIL_API_KEY=
|
||||||
|
BREVO_API_BASEURL=https://api.brevo.com
|
||||||
|
BREVO_FROM_EMAIL=
|
||||||
|
BREVO_SMTP_HOST=smtp-relay.brevo.com
|
||||||
|
BREVO_SMTP_PORT=587
|
||||||
|
BREVO_SMTP_USER=
|
||||||
|
BREVO_SMTP_PASS=
|
||||||
|
|
||||||
|
# JWT Configuration
|
||||||
|
REFRESH_TOKEN_SECRET=
|
||||||
|
JWT_SECRET=
|
||||||
|
JWT_ACCESS_EXPIRATION_MINUTES=30
|
||||||
|
JWT_REFRESH_EXPIRATION_DAYS=7
|
||||||
|
JWT_RESET_PASSWORD_EXPIRATION_MINUTES=15
|
||||||
|
JWT_VERIFY_EMAIL_EXPIRATION_MINUTES=15
|
||||||
|
|
||||||
|
# Security
|
||||||
|
SALT_ROUNDS=
|
||||||
|
NODE_ENV=
|
||||||
|
|
||||||
|
# AWS S3
|
||||||
|
S3_BUCKET_NAME=
|
||||||
|
|
||||||
|
# Admin Configuration
|
||||||
|
MINGLAR_ADMIN_NAME=
|
||||||
|
MINGLAR_ADMIN_EMAIL=
|
||||||
|
AM_INVITATION_LINK=
|
||||||
|
HOST_LINK=
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -40,6 +40,9 @@ lerna-debug.log*
|
|||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
.env.local
|
.env.local
|
||||||
|
.env.dev
|
||||||
|
.env.test
|
||||||
|
.env.uat
|
||||||
|
|
||||||
# temp
|
# temp
|
||||||
.tmp
|
.tmp
|
||||||
|
|||||||
62
Dockerfile
62
Dockerfile
@@ -1,62 +0,0 @@
|
|||||||
# Multi-stage build for NestJS Serverless Application
|
|
||||||
FROM node:18-alpine AS builder
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy package files
|
|
||||||
COPY package*.json ./
|
|
||||||
COPY prisma ./prisma/
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN npm ci --only=production && npm cache clean --force
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Generate Prisma client
|
|
||||||
RUN npx prisma generate
|
|
||||||
|
|
||||||
# Build the application
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# Production stage
|
|
||||||
FROM node:18-alpine AS production
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install serverless framework globally
|
|
||||||
RUN npm install -g serverless
|
|
||||||
|
|
||||||
# Copy package files
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
# Install production dependencies
|
|
||||||
RUN npm ci --only=production && npm cache clean --force
|
|
||||||
|
|
||||||
# Copy built application
|
|
||||||
COPY --from=builder /app/dist ./dist
|
|
||||||
COPY --from=builder /app/prisma ./prisma
|
|
||||||
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
|
|
||||||
|
|
||||||
# Copy serverless configuration
|
|
||||||
COPY serverless*.yml ./
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN addgroup -g 1001 -S nodejs
|
|
||||||
RUN adduser -S nestjs -u 1001
|
|
||||||
|
|
||||||
# Change ownership
|
|
||||||
RUN chown -R nestjs:nodejs /app
|
|
||||||
USER nestjs
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
||||||
CMD curl -f http://localhost:3000/health || exit 1
|
|
||||||
|
|
||||||
# Start the application
|
|
||||||
CMD ["npm", "run", "start:prod"]
|
|
||||||
490
LAMBDA_OPTIMIZATION_GUIDE.md
Normal file
490
LAMBDA_OPTIMIZATION_GUIDE.md
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
# AWS Lambda Bundle Size Optimization Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide documents how to optimize AWS Lambda function bundle sizes when using:
|
||||||
|
- **Serverless Framework v4** (with built-in esbuild)
|
||||||
|
- **Prisma ORM** (with driver adapters)
|
||||||
|
- **NestJS** or any Node.js framework
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Lambda functions can become bloated (25+ MB) due to:
|
||||||
|
1. **Prisma binary engines** (~50MB uncompressed)
|
||||||
|
2. **AWS SDK v3** being bundled (~5-10MB)
|
||||||
|
3. **Dependencies copied to node_modules** instead of being bundled
|
||||||
|
|
||||||
|
## Solution Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ Lambda Function │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Your Code (bundled by esbuild) ~50-500 KB │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Prisma Layer (shared) ~15 MB │ │
|
||||||
|
│ │ - @prisma/client │ │
|
||||||
|
│ │ - @prisma/adapter-pg │ │
|
||||||
|
│ │ - .prisma/client (generated) │ │
|
||||||
|
│ │ - pg driver │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ AWS Lambda Runtime │ │
|
||||||
|
│ │ - AWS SDK v3 (built-in for Node.js 18+) │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step-by-Step Setup
|
||||||
|
|
||||||
|
### 1. Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
your-project/
|
||||||
|
├── serverless.yml
|
||||||
|
├── package.json
|
||||||
|
├── prisma/
|
||||||
|
│ └── schema.prisma
|
||||||
|
├── layers/
|
||||||
|
│ └── prisma/
|
||||||
|
│ └── nodejs/
|
||||||
|
│ └── package.json
|
||||||
|
├── src/
|
||||||
|
│ └── ... your code
|
||||||
|
└── build-prisma-layer.ps1 (or .sh for Linux/Mac)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Prisma Schema Configuration
|
||||||
|
|
||||||
|
**`prisma/schema.prisma`**
|
||||||
|
```prisma
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
// For Prisma 7+ with driver adapters, no binary targets needed
|
||||||
|
// The WASM-based query engine is used automatically
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: Prisma 7+ uses WASM-based query compiler instead of binary engines when using driver adapters, which is much smaller.
|
||||||
|
|
||||||
|
### 3. Layer Package.json
|
||||||
|
|
||||||
|
**`layers/prisma/nodejs/package.json`**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "prisma-layer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Lambda layer for Prisma with pg driver adapter",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "^7.0.0",
|
||||||
|
"@prisma/adapter-pg": "^7.0.0",
|
||||||
|
"pg": "^8.13.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Serverless Configuration
|
||||||
|
|
||||||
|
**`serverless.yml`**
|
||||||
|
```yaml
|
||||||
|
service: your-service-name
|
||||||
|
|
||||||
|
provider:
|
||||||
|
name: aws
|
||||||
|
runtime: nodejs22.x
|
||||||
|
region: your-region
|
||||||
|
memorySize: 512
|
||||||
|
# Apply Prisma layer to ALL functions
|
||||||
|
layers:
|
||||||
|
- !Ref PrismaLambdaLayer
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: ${env:DATABASE_URL}
|
||||||
|
# ... other env vars
|
||||||
|
|
||||||
|
# esbuild configuration (Serverless v4 built-in)
|
||||||
|
build:
|
||||||
|
esbuild:
|
||||||
|
bundle: true
|
||||||
|
minify: true
|
||||||
|
sourcemap: false
|
||||||
|
target: node20
|
||||||
|
platform: node
|
||||||
|
# Mark packages as external (not bundled into JS)
|
||||||
|
external:
|
||||||
|
- '@prisma/client'
|
||||||
|
- '@prisma/adapter-pg'
|
||||||
|
- '.prisma/client'
|
||||||
|
- '.prisma'
|
||||||
|
- 'pg'
|
||||||
|
- '@aws-sdk/*'
|
||||||
|
- '@smithy/*'
|
||||||
|
- '@aws-crypto/*'
|
||||||
|
# Exclude from npm install in zip (CRITICAL!)
|
||||||
|
exclude:
|
||||||
|
- 'aws-sdk'
|
||||||
|
- '@aws-sdk/*'
|
||||||
|
- '@smithy/*'
|
||||||
|
- '@aws-crypto/*'
|
||||||
|
- '@prisma/client'
|
||||||
|
- '@prisma/adapter-pg'
|
||||||
|
- '.prisma'
|
||||||
|
- '.prisma/client'
|
||||||
|
- 'pg'
|
||||||
|
- 'pg-*'
|
||||||
|
- 'postgres-*'
|
||||||
|
- 'pgpass'
|
||||||
|
- 'split2'
|
||||||
|
- 'xtend'
|
||||||
|
|
||||||
|
# Define the Prisma layer
|
||||||
|
layers:
|
||||||
|
prisma:
|
||||||
|
path: layers/prisma
|
||||||
|
name: ${self:service}-prisma-layer-${sls:stage}
|
||||||
|
description: Prisma client with pg driver adapter
|
||||||
|
compatibleRuntimes:
|
||||||
|
- nodejs22.x
|
||||||
|
retain: false
|
||||||
|
|
||||||
|
# Package configuration
|
||||||
|
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'
|
||||||
|
- '!.git/**'
|
||||||
|
|
||||||
|
functions:
|
||||||
|
myFunction:
|
||||||
|
handler: src/handlers/myHandler.handler
|
||||||
|
events:
|
||||||
|
- httpApi:
|
||||||
|
path: /my-endpoint
|
||||||
|
method: get
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- serverless-offline
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Build Script for Prisma Layer
|
||||||
|
|
||||||
|
**Windows (PowerShell) - `build-prisma-layer.ps1`**
|
||||||
|
```powershell
|
||||||
|
# Build Prisma Lambda Layer
|
||||||
|
$layerPath = "layers\prisma\nodejs"
|
||||||
|
|
||||||
|
Write-Host "Building Prisma Lambda Layer..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# 1. Clean existing node_modules in layer
|
||||||
|
Write-Host "Cleaning layer node_modules..."
|
||||||
|
if (Test-Path "$layerPath\node_modules") {
|
||||||
|
Remove-Item -Recurse -Force "$layerPath\node_modules"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Install dependencies in layer
|
||||||
|
Write-Host "Installing layer dependencies..."
|
||||||
|
Push-Location $layerPath
|
||||||
|
npm install --omit=dev
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
|
# 3. Generate Prisma client
|
||||||
|
Write-Host "Generating Prisma client..."
|
||||||
|
npx prisma generate
|
||||||
|
|
||||||
|
# 4. Copy .prisma/client to layer
|
||||||
|
Write-Host "Copying generated Prisma client to layer..."
|
||||||
|
$sourcePrisma = "node_modules\.prisma"
|
||||||
|
$destPrisma = "$layerPath\node_modules\.prisma"
|
||||||
|
|
||||||
|
if (Test-Path $sourcePrisma) {
|
||||||
|
if (Test-Path $destPrisma) {
|
||||||
|
Remove-Item -Recurse -Force $destPrisma
|
||||||
|
}
|
||||||
|
Copy-Item -Recurse $sourcePrisma $destPrisma
|
||||||
|
Write-Host "Copied .prisma/client successfully!" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: .prisma folder not found. Run 'npx prisma generate' first." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 5. Show layer size
|
||||||
|
$layerSize = (Get-ChildItem "$layerPath\node_modules" -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB
|
||||||
|
Write-Host "`nTotal layer size: $([math]::Round($layerSize, 2)) MB" -ForegroundColor Yellow
|
||||||
|
Write-Host "Prisma layer built successfully!" -ForegroundColor Green
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux/Mac (Bash) - `build-prisma-layer.sh`**
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
LAYER_PATH="layers/prisma/nodejs"
|
||||||
|
|
||||||
|
echo "Building Prisma Lambda Layer..."
|
||||||
|
|
||||||
|
# 1. Clean existing node_modules in layer
|
||||||
|
echo "Cleaning layer node_modules..."
|
||||||
|
rm -rf "$LAYER_PATH/node_modules"
|
||||||
|
|
||||||
|
# 2. Install dependencies in layer
|
||||||
|
echo "Installing layer dependencies..."
|
||||||
|
cd "$LAYER_PATH"
|
||||||
|
npm install --omit=dev
|
||||||
|
cd -
|
||||||
|
|
||||||
|
# 3. Generate Prisma client
|
||||||
|
echo "Generating Prisma client..."
|
||||||
|
npx prisma generate
|
||||||
|
|
||||||
|
# 4. Copy .prisma/client to layer
|
||||||
|
echo "Copying generated Prisma client to layer..."
|
||||||
|
if [ -d "node_modules/.prisma" ]; then
|
||||||
|
rm -rf "$LAYER_PATH/node_modules/.prisma"
|
||||||
|
cp -r "node_modules/.prisma" "$LAYER_PATH/node_modules/.prisma"
|
||||||
|
echo "Copied .prisma/client successfully!"
|
||||||
|
else
|
||||||
|
echo "ERROR: .prisma folder not found. Run 'npx prisma generate' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Show layer size
|
||||||
|
LAYER_SIZE=$(du -sm "$LAYER_PATH/node_modules" | cut -f1)
|
||||||
|
echo "Total layer size: ${LAYER_SIZE} MB"
|
||||||
|
echo "Prisma layer built successfully!"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Prisma Client Usage
|
||||||
|
|
||||||
|
**`src/common/database/prisma.client.ts`**
|
||||||
|
```typescript
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
|
import { Pool } from 'pg';
|
||||||
|
|
||||||
|
// Connection pool for serverless
|
||||||
|
const pool = new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
max: 5, // Limit connections in Lambda
|
||||||
|
});
|
||||||
|
|
||||||
|
const adapter = new PrismaPg(pool);
|
||||||
|
|
||||||
|
// Single instance for Lambda warm starts
|
||||||
|
let prisma: PrismaClient;
|
||||||
|
|
||||||
|
export function getPrismaClient(): PrismaClient {
|
||||||
|
if (!prisma) {
|
||||||
|
prisma = new PrismaClient({
|
||||||
|
adapter,
|
||||||
|
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return prisma;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { prisma };
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Build the Prisma layer (run after schema changes)
|
||||||
|
./build-prisma-layer.ps1 # Windows
|
||||||
|
# or
|
||||||
|
./build-prisma-layer.sh # Linux/Mac
|
||||||
|
|
||||||
|
# 2. Deploy
|
||||||
|
npx serverless deploy --stage=dev
|
||||||
|
|
||||||
|
# 3. Deploy single function (faster, uses existing layer)
|
||||||
|
npx serverless deploy function -f myFunction --stage=dev
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Configuration Explained
|
||||||
|
|
||||||
|
### esbuild `external` vs `exclude`
|
||||||
|
|
||||||
|
| Property | Purpose |
|
||||||
|
|----------|---------|
|
||||||
|
| `external` | Tells esbuild NOT to bundle these into the JS file. They become `require()` calls. |
|
||||||
|
| `exclude` | Tells Serverless NOT to `npm install` these packages into the function zip. |
|
||||||
|
|
||||||
|
**Both are required!**
|
||||||
|
- `external` alone = esbuild doesn't bundle, but Serverless still installs to node_modules
|
||||||
|
- `exclude` alone = Serverless doesn't install, but esbuild bundles the code
|
||||||
|
|
||||||
|
### Layer Reference
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# This creates a CloudFormation reference to the layer defined in the same stack
|
||||||
|
layers:
|
||||||
|
- !Ref PrismaLambdaLayer
|
||||||
|
```
|
||||||
|
|
||||||
|
The `PrismaLambdaLayer` name comes from the layer key (`prisma`) converted to PascalCase + `LambdaLayer`.
|
||||||
|
|
||||||
|
### Why Exclude pg-* packages?
|
||||||
|
|
||||||
|
When `pg` is external, its dependencies still get installed:
|
||||||
|
- `pg-connection-string`
|
||||||
|
- `pg-pool`
|
||||||
|
- `pg-protocol`
|
||||||
|
- `pg-types`
|
||||||
|
- etc.
|
||||||
|
|
||||||
|
These must all be in the `exclude` list to prevent duplication.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Expected Results
|
||||||
|
|
||||||
|
| Function Type | Before | After |
|
||||||
|
|---------------|--------|-------|
|
||||||
|
| Simple handlers | 25+ MB | **50-100 kB** |
|
||||||
|
| With validation (zod/yup) | 25+ MB | **300-500 kB** |
|
||||||
|
| With S3 uploads | 30+ MB | **1-2 MB** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### 1. "Cannot find module '@prisma/client'"
|
||||||
|
|
||||||
|
**Cause**: Layer doesn't have the generated `.prisma/client`
|
||||||
|
|
||||||
|
**Fix**: Run `build-prisma-layer.ps1` to regenerate the layer
|
||||||
|
|
||||||
|
### 2. Function size still large
|
||||||
|
|
||||||
|
**Debug**: Extract and inspect the zip:
|
||||||
|
```powershell
|
||||||
|
Expand-Archive ".serverless\build\your-function.zip" -DestinationPath "extracted"
|
||||||
|
Get-ChildItem "extracted\node_modules" -Directory
|
||||||
|
```
|
||||||
|
|
||||||
|
If you see `@prisma` or `pg` folders, the `exclude` config isn't working.
|
||||||
|
|
||||||
|
### 3. "Cannot resolve CloudFormation reference"
|
||||||
|
|
||||||
|
**Cause**: Using `${cf:...}` reference before first deploy
|
||||||
|
|
||||||
|
**Fix**: Use `!Ref PrismaLambdaLayer` instead (works on first deploy)
|
||||||
|
|
||||||
|
### 4. Cold starts still slow
|
||||||
|
|
||||||
|
Consider:
|
||||||
|
- **Provisioned Concurrency**: Pre-warm instances
|
||||||
|
- **Reduce memory**: Sometimes lower memory = same speed, lower cost
|
||||||
|
- **Connection pooling**: Use tools like PgBouncer for RDS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Additional Optimizations
|
||||||
|
|
||||||
|
### 1. Remove duplicate validation libraries
|
||||||
|
|
||||||
|
Pick ONE of: `zod`, `yup`, or `class-validator`. Don't use all three.
|
||||||
|
|
||||||
|
### 2. Tree-shake NestJS
|
||||||
|
|
||||||
|
If not using full NestJS, import only what you need:
|
||||||
|
```typescript
|
||||||
|
// Instead of
|
||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
|
||||||
|
// For Lambda handlers, you might not need NestJS at all
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Use AWS SDK v3 selectively
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
external:
|
||||||
|
- '@aws-sdk/*' # Exclude all
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in code:
|
||||||
|
```typescript
|
||||||
|
// AWS SDK v3 is available in Lambda runtime (Node.js 18+)
|
||||||
|
import { S3Client } from '@aws-sdk/client-s3';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Minimal serverless.yml for Prisma + Lambda optimization
|
||||||
|
build:
|
||||||
|
esbuild:
|
||||||
|
bundle: true
|
||||||
|
minify: true
|
||||||
|
external:
|
||||||
|
- '@prisma/client'
|
||||||
|
- '@prisma/adapter-pg'
|
||||||
|
- '.prisma/client'
|
||||||
|
- '.prisma'
|
||||||
|
- 'pg'
|
||||||
|
- '@aws-sdk/*'
|
||||||
|
exclude:
|
||||||
|
- '@prisma/client'
|
||||||
|
- '@prisma/adapter-pg'
|
||||||
|
- '.prisma'
|
||||||
|
- '.prisma/client'
|
||||||
|
- 'pg'
|
||||||
|
- 'pg-*'
|
||||||
|
- 'postgres-*'
|
||||||
|
- 'pgpass'
|
||||||
|
- 'split2'
|
||||||
|
- 'xtend'
|
||||||
|
- '@aws-sdk/*'
|
||||||
|
|
||||||
|
layers:
|
||||||
|
prisma:
|
||||||
|
path: layers/prisma
|
||||||
|
name: ${self:service}-prisma-${sls:stage}
|
||||||
|
compatibleRuntimes:
|
||||||
|
- nodejs22.x
|
||||||
|
|
||||||
|
provider:
|
||||||
|
layers:
|
||||||
|
- !Ref PrismaLambdaLayer
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version Compatibility
|
||||||
|
|
||||||
|
| Tool | Tested Version |
|
||||||
|
|------|----------------|
|
||||||
|
| Serverless Framework | v4.x |
|
||||||
|
| Prisma | v7.x |
|
||||||
|
| Node.js | 20.x, 22.x |
|
||||||
|
| AWS Lambda Runtime | nodejs20.x, nodejs22.x |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last updated: December 2025*
|
||||||
51
build-prisma-layer.ps1
Normal file
51
build-prisma-layer.ps1
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Build Prisma Lambda Layer
|
||||||
|
# Run this script before deploying to ensure the layer has the generated client
|
||||||
|
|
||||||
|
$layerPath = "layers\prisma\nodejs"
|
||||||
|
|
||||||
|
Write-Host "Building Prisma Lambda Layer..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# 1. Clean existing node_modules in layer
|
||||||
|
Write-Host "Cleaning layer node_modules..."
|
||||||
|
if (Test-Path "$layerPath\node_modules") {
|
||||||
|
Remove-Item -Recurse -Force "$layerPath\node_modules"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Install dependencies in layer
|
||||||
|
Write-Host "Installing layer dependencies..."
|
||||||
|
Push-Location $layerPath
|
||||||
|
npm install --omit=dev
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
|
# 3. Generate Prisma client into the layer
|
||||||
|
Write-Host "Generating Prisma client into layer..."
|
||||||
|
# Set the output directory for Prisma client
|
||||||
|
$env:PRISMA_GENERATE_DATAPROXY = "false"
|
||||||
|
|
||||||
|
# Generate client - this creates .prisma/client
|
||||||
|
npx prisma generate
|
||||||
|
|
||||||
|
# 4. Copy .prisma/client to layer
|
||||||
|
Write-Host "Copying generated Prisma client to layer..."
|
||||||
|
$sourcePrisma = "node_modules\.prisma"
|
||||||
|
$destPrisma = "$layerPath\node_modules\.prisma"
|
||||||
|
|
||||||
|
if (Test-Path $sourcePrisma) {
|
||||||
|
if (Test-Path $destPrisma) {
|
||||||
|
Remove-Item -Recurse -Force $destPrisma
|
||||||
|
}
|
||||||
|
Copy-Item -Recurse $sourcePrisma $destPrisma
|
||||||
|
Write-Host "Copied .prisma/client successfully!" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: .prisma folder not found. Run 'npx prisma generate' first." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 5. Show layer size
|
||||||
|
Write-Host "`nLayer contents:"
|
||||||
|
Get-ChildItem "$layerPath\node_modules" -Directory | Select-Object Name
|
||||||
|
$layerSize = (Get-ChildItem "$layerPath\node_modules" -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB
|
||||||
|
Write-Host "`nTotal layer size: $([math]::Round($layerSize, 2)) MB" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
Write-Host "`nPrisma layer built successfully!" -ForegroundColor Green
|
||||||
|
Write-Host "Run 'serverless deploy' to deploy with the updated layer."
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
# PostgreSQL Database
|
|
||||||
postgres:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
container_name: nestjs-postgres
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: nestjs_user_crud
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
|
||||||
networks:
|
|
||||||
- nestjs-network
|
|
||||||
|
|
||||||
# Redis for caching (optional)
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
container_name: nestjs-redis
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
volumes:
|
|
||||||
- redis_data:/data
|
|
||||||
networks:
|
|
||||||
- nestjs-network
|
|
||||||
|
|
||||||
# NestJS Application
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
target: production
|
|
||||||
container_name: nestjs-app
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
environment:
|
|
||||||
NODE_ENV: development
|
|
||||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/nestjs_user_crud?schema=public
|
|
||||||
JWT_SECRET: docker-jwt-secret-key
|
|
||||||
JWT_EXPIRES_IN: 7d
|
|
||||||
API_PREFIX: api/v1
|
|
||||||
API_VERSION: 1.0.0
|
|
||||||
THROTTLE_TTL: 60
|
|
||||||
THROTTLE_LIMIT: 10
|
|
||||||
CORS_ORIGIN: http://localhost:3000
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
- redis
|
|
||||||
networks:
|
|
||||||
- nestjs-network
|
|
||||||
volumes:
|
|
||||||
- ./src:/app/src
|
|
||||||
- ./prisma:/app/prisma
|
|
||||||
|
|
||||||
# Prisma Studio
|
|
||||||
prisma-studio:
|
|
||||||
image: node:18-alpine
|
|
||||||
container_name: nestjs-prisma-studio
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "5555:5555"
|
|
||||||
environment:
|
|
||||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/nestjs_user_crud?schema=public
|
|
||||||
working_dir: /app
|
|
||||||
volumes:
|
|
||||||
- .:/app
|
|
||||||
command: sh -c "npm install && npx prisma generate && npx prisma studio --hostname 0.0.0.0"
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
networks:
|
|
||||||
- nestjs-network
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres_data:
|
|
||||||
redis_data:
|
|
||||||
|
|
||||||
networks:
|
|
||||||
nestjs-network:
|
|
||||||
driver: bridge
|
|
||||||
19
init.sql
19
init.sql
@@ -1,19 +0,0 @@
|
|||||||
-- Initialize database for NestJS Serverless Application
|
|
||||||
-- This file is executed when the PostgreSQL container starts
|
|
||||||
|
|
||||||
-- Create database if it doesn't exist
|
|
||||||
SELECT 'CREATE DATABASE nestjs_user_crud'
|
|
||||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'nestjs_user_crud')\gexec
|
|
||||||
|
|
||||||
-- Connect to the database
|
|
||||||
\c nestjs_user_crud;
|
|
||||||
|
|
||||||
-- Create extensions if needed
|
|
||||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
||||||
|
|
||||||
-- Set timezone
|
|
||||||
SET timezone = 'UTC';
|
|
||||||
|
|
||||||
-- Create a user for the application (optional)
|
|
||||||
-- CREATE USER nestjs_user WITH PASSWORD 'nestjs_password';
|
|
||||||
-- GRANT ALL PRIVILEGES ON DATABASE nestjs_user_crud TO nestjs_user;
|
|
||||||
53
insertCities.js
Normal file
53
insertCities.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
async function insertCities() {
|
||||||
|
try {
|
||||||
|
const statesFolder = path.join(process.cwd(), 'states-cities');
|
||||||
|
const files = fs.readdirSync(statesFolder);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (!file.endsWith('.json')) continue;
|
||||||
|
|
||||||
|
const stateName = file.replace('.json', '');
|
||||||
|
|
||||||
|
const state = await prisma.states.findFirst({
|
||||||
|
where: { stateName },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
console.log(`❌ State not found: ${stateName}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(statesFolder, file);
|
||||||
|
const citiesData = JSON.parse(
|
||||||
|
fs.readFileSync(filePath, 'utf-8')
|
||||||
|
);
|
||||||
|
|
||||||
|
await prisma.cities.createMany({
|
||||||
|
data: citiesData.map((city) => ({
|
||||||
|
stateXid: state.id,
|
||||||
|
cityName:
|
||||||
|
typeof city === 'string'
|
||||||
|
? city.trim()
|
||||||
|
: city.cityName.trim(),
|
||||||
|
})),
|
||||||
|
skipDuplicates: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✅ ${stateName} cities inserted`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🎉 All cities inserted successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error inserting cities:', error);
|
||||||
|
} finally {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertCities();
|
||||||
217
layers/prisma/nodejs/package-lock.json
generated
Normal file
217
layers/prisma/nodejs/package-lock.json
generated
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
{
|
||||||
|
"name": "prisma-layer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "prisma-layer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/adapter-pg": "^7.0.1",
|
||||||
|
"@prisma/client": "^7.0.1",
|
||||||
|
"pg": "^8.13.0",
|
||||||
|
"zod": "^4.1.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/adapter-pg": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-euIdQ13cRB2wZ3jPsnDnFhINquo1PYFPCg6yVL8b2rp3EdinQHsX9EDdCtRr489D5uhphcRk463OdQAFlsCr0w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/driver-adapter-utils": "7.2.0",
|
||||||
|
"pg": "^8.16.3",
|
||||||
|
"postgres-array": "3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/client": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-JdLF8lWZ+LjKGKpBqyAlenxd/kXjd1Abf/xK+6vUA7R7L2Suo6AFTHFRpPSdAKCan9wzdFApsUpSa/F6+t1AtA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client-runtime-utils": "7.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19 || ^22.12 || >=24.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prisma": "*",
|
||||||
|
"typescript": ">=5.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"prisma": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/client-runtime-utils": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-dn7oB53v0tqkB0wBdMuTNFNPdEbfICEUe82Tn9FoKAhJCUkDH+fmyEp0ClciGh+9Hp2Tuu2K52kth2MTLstvmA=="
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/debug": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw=="
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/driver-adapter-utils": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-gzrUcbI9VmHS24Uf+0+7DNzdIw7keglJsD5m/MHxQOU68OhGVzlphQRobLiDMn8CHNA2XN8uugwKjudVtnfMVQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "7.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg": {
|
||||||
|
"version": "8.16.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||||
|
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-connection-string": "^2.9.1",
|
||||||
|
"pg-pool": "^3.10.1",
|
||||||
|
"pg-protocol": "^1.10.3",
|
||||||
|
"pg-types": "2.2.0",
|
||||||
|
"pgpass": "1.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"pg-cloudflare": "^1.2.7"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg-native": ">=3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"pg-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-cloudflare": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/pg-connection-string": {
|
||||||
|
"version": "2.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
||||||
|
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="
|
||||||
|
},
|
||||||
|
"node_modules/pg-int8": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-pool": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-protocol": {
|
||||||
|
"version": "1.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
|
||||||
|
"integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="
|
||||||
|
},
|
||||||
|
"node_modules/pg-types": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-int8": "1.0.1",
|
||||||
|
"postgres-array": "~2.0.0",
|
||||||
|
"postgres-bytea": "~1.0.0",
|
||||||
|
"postgres-date": "~1.0.4",
|
||||||
|
"postgres-interval": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-types/node_modules/postgres-array": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pgpass": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||||
|
"dependencies": {
|
||||||
|
"split2": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-array": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-bytea": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-date": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-interval": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
layers/prisma/nodejs/package.json
Normal file
11
layers/prisma/nodejs/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "prisma-layer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Lambda layer for Prisma 7 with pg driver adapter and zod",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "^7.0.1",
|
||||||
|
"@prisma/adapter-pg": "^7.0.1",
|
||||||
|
"pg": "^8.13.0",
|
||||||
|
"zod": "^4.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
543
package-lock.json
generated
543
package-lock.json
generated
@@ -13,7 +13,7 @@
|
|||||||
"@aws-crypto/sha256-browser": "^5.2.0",
|
"@aws-crypto/sha256-browser": "^5.2.0",
|
||||||
"@aws-crypto/sha256-js": "^5.2.0",
|
"@aws-crypto/sha256-js": "^5.2.0",
|
||||||
"@aws-sdk/client-s3": "^3.928.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",
|
"@aws/lambda-invoke-store": "^0.2.1",
|
||||||
"@nestjs/common": "^10.3.0",
|
"@nestjs/common": "^10.3.0",
|
||||||
"@nestjs/config": "^3.1.1",
|
"@nestjs/config": "^3.1.1",
|
||||||
@@ -31,22 +31,31 @@
|
|||||||
"@types/http-status": "^1.1.2",
|
"@types/http-status": "^1.1.2",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"aws-lambda": "^1.0.7",
|
"aws-lambda": "^1.0.7",
|
||||||
|
"aws-sdk": "^2.1692.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"dayjs": "^1.11.19",
|
||||||
|
"docx": "^9.6.0",
|
||||||
|
"docxtemplater": "^3.68.3",
|
||||||
"fast-xml-parser": "^5.3.1",
|
"fast-xml-parser": "^5.3.1",
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
"helmet": "^7.1.0",
|
"helmet": "^7.1.0",
|
||||||
"http-status": "^2.1.0",
|
"http-status": "^2.1.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
|
"number-to-words": "^1.2.4",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"pdf-lib": "^1.17.1",
|
||||||
|
"pizzip": "^3.2.0",
|
||||||
"prisma": "^7.0.1",
|
"prisma": "^7.0.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"serverless": "4.17.0",
|
"serverless": "4.24.0",
|
||||||
"swagger-ui-express": "^5.0.0",
|
"swagger-ui-express": "^5.0.0",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
@@ -73,6 +82,7 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"serverless-esbuild": "^1.55.1",
|
"serverless-esbuild": "^1.55.1",
|
||||||
"serverless-offline": "^14.4.0",
|
"serverless-offline": "^14.4.0",
|
||||||
|
"serverless-plugin-split-stacks": "^1.14.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^6.3.4",
|
"supertest": "^6.3.4",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
@@ -4650,6 +4660,24 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@@ -5751,6 +5779,13 @@
|
|||||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
|
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@tsconfig/node10": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
||||||
@@ -6546,6 +6581,15 @@
|
|||||||
"@xtuc/long": "4.2.2"
|
"@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": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
@@ -6624,6 +6668,16 @@
|
|||||||
"node": ">=0.4.0"
|
"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": {
|
"node_modules/aggregate-error": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||||
@@ -6964,6 +7018,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/async": {
|
||||||
"version": "3.2.6",
|
"version": "3.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||||
@@ -6992,6 +7059,13 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/aws-lambda": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/aws-lambda/-/aws-lambda-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/aws-lambda/-/aws-lambda-1.0.7.tgz",
|
||||||
@@ -7257,6 +7331,16 @@
|
|||||||
"baseline-browser-mapping": "dist/cli.js"
|
"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": {
|
"node_modules/bcrypt": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz",
|
||||||
@@ -8386,7 +8470,6 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cors": {
|
"node_modules/cors": {
|
||||||
@@ -8539,6 +8622,12 @@
|
|||||||
"url": "https://github.com/sponsors/kossnocorp"
|
"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": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
@@ -8633,6 +8722,21 @@
|
|||||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@@ -8763,6 +8867,50 @@
|
|||||||
"node": ">=6.0.0"
|
"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": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.4.5",
|
"version": "16.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
||||||
@@ -9031,6 +9179,39 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/eslint": {
|
||||||
"version": "8.57.1",
|
"version": "8.57.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
|
||||||
@@ -10022,6 +10203,12 @@
|
|||||||
"node": ">= 0.8"
|
"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": {
|
"node_modules/fs-constants": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
"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"
|
"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": {
|
"node_modules/giget": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
|
||||||
@@ -10437,6 +10649,16 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
@@ -10499,6 +10721,20 @@
|
|||||||
"node": ">= 0.8"
|
"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": {
|
"node_modules/http-status": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-status/-/http-status-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-status/-/http-status-2.1.0.tgz",
|
||||||
@@ -10514,6 +10750,20 @@
|
|||||||
"integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==",
|
"integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/human-signals": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||||
@@ -10556,7 +10806,6 @@
|
|||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
@@ -10661,6 +10910,16 @@
|
|||||||
"node": ">=12.0.0"
|
"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": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
@@ -11986,7 +12245,6 @@
|
|||||||
"version": "3.10.1",
|
"version": "3.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
"dev": true,
|
|
||||||
"license": "(MIT OR GPL-3.0-or-later)",
|
"license": "(MIT OR GPL-3.0-or-later)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lie": "~3.3.0",
|
"lie": "~3.3.0",
|
||||||
@@ -11999,7 +12257,6 @@
|
|||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
@@ -12015,14 +12272,12 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/jszip/node_modules/string_decoder": {
|
"node_modules/jszip/node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
@@ -12149,7 +12404,6 @@
|
|||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"immediate": "~3.0.5"
|
"immediate": "~3.0.5"
|
||||||
@@ -12548,6 +12802,12 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/minimatch": {
|
||||||
"version": "9.0.3",
|
"version": "9.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||||
@@ -12698,6 +12958,24 @@
|
|||||||
"node": ">=12"
|
"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": {
|
"node_modules/natural-compare": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||||
@@ -12722,6 +13000,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/nock": {
|
||||||
"version": "13.5.6",
|
"version": "13.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
|
||||||
@@ -12873,6 +13161,12 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/nypm": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz",
|
||||||
@@ -13118,6 +13412,40 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/package-json-from-dist": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
"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",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
"dev": true,
|
|
||||||
"license": "(MIT AND Zlib)"
|
"license": "(MIT AND Zlib)"
|
||||||
},
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
@@ -13219,6 +13546,16 @@
|
|||||||
"node": ">= 0.4.0"
|
"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": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
@@ -13293,6 +13630,21 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/pathe": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
"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": {
|
"node_modules/perfect-debounce": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
|
||||||
@@ -13438,6 +13808,21 @@
|
|||||||
"node": ">= 6"
|
"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": {
|
"node_modules/pkg-dir": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
"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": {
|
"node_modules/process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/prompts": {
|
"node_modules/prompts": {
|
||||||
@@ -13756,6 +14149,36 @@
|
|||||||
"node": ">= 0.10"
|
"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": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
@@ -14467,12 +14890,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serverless": {
|
"node_modules/serverless": {
|
||||||
"version": "4.17.0",
|
"version": "4.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/serverless/-/serverless-4.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/serverless/-/serverless-4.24.0.tgz",
|
||||||
"integrity": "sha512-hoZmipwyN/h7y9HwkWGlJ0YT06RFq7WNOD7fFEiPfnSnnUMVTzeNHq2BRrUlpHhf5s9srCHDc2wx5I06acfq1Q==",
|
"integrity": "sha512-bgxFQ6QyOGJC9IZjZIXo4m6bdWMl9I7HNZ4jrmwSpdePdsRd46igGRpSnhdYFOc71GNplhSOeoCibL94yCHfrg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.8.3",
|
"axios": "^1.12.1",
|
||||||
"axios-proxy-builder": "^0.1.2",
|
"axios-proxy-builder": "^0.1.2",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"xml2js": "0.6.2"
|
"xml2js": "0.6.2"
|
||||||
@@ -14756,6 +15179,23 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/serverless/node_modules/rimraf": {
|
||||||
"version": "5.0.10",
|
"version": "5.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
|
||||||
@@ -14792,7 +15232,6 @@
|
|||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
@@ -14923,6 +15362,47 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/sorted-array-functions": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
|
||||||
@@ -15504,6 +15984,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
"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": {
|
"node_modules/xml2js": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||||
|
|||||||
15
package.json
15
package.json
@@ -30,7 +30,7 @@
|
|||||||
"@aws-crypto/sha256-browser": "^5.2.0",
|
"@aws-crypto/sha256-browser": "^5.2.0",
|
||||||
"@aws-crypto/sha256-js": "^5.2.0",
|
"@aws-crypto/sha256-js": "^5.2.0",
|
||||||
"@aws-sdk/client-s3": "^3.928.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",
|
"@aws/lambda-invoke-store": "^0.2.1",
|
||||||
"@nestjs/common": "^10.3.0",
|
"@nestjs/common": "^10.3.0",
|
||||||
"@nestjs/config": "^3.1.1",
|
"@nestjs/config": "^3.1.1",
|
||||||
@@ -48,22 +48,32 @@
|
|||||||
"@types/http-status": "^1.1.2",
|
"@types/http-status": "^1.1.2",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"aws-lambda": "^1.0.7",
|
"aws-lambda": "^1.0.7",
|
||||||
|
"aws-sdk": "^2.1692.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"dayjs": "^1.11.19",
|
||||||
|
"docx": "^9.6.0",
|
||||||
|
"docxtemplater": "^3.68.3",
|
||||||
"fast-xml-parser": "^5.3.1",
|
"fast-xml-parser": "^5.3.1",
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
"helmet": "^7.1.0",
|
"helmet": "^7.1.0",
|
||||||
"http-status": "^2.1.0",
|
"http-status": "^2.1.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
|
"number-to-words": "^1.2.4",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"pdf-lib": "^1.17.1",
|
||||||
|
"pizzip": "^3.2.0",
|
||||||
"prisma": "^7.0.1",
|
"prisma": "^7.0.1",
|
||||||
|
"razorpay": "^2.9.6",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"serverless": "4.17.0",
|
"serverless": "4.24.0",
|
||||||
"swagger-ui-express": "^5.0.0",
|
"swagger-ui-express": "^5.0.0",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
@@ -90,6 +100,7 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"serverless-esbuild": "^1.55.1",
|
"serverless-esbuild": "^1.55.1",
|
||||||
"serverless-offline": "^14.4.0",
|
"serverless-offline": "^14.4.0",
|
||||||
|
"serverless-plugin-split-stacks": "^1.14.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^6.3.4",
|
"supertest": "^6.3.4",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
|
|||||||
64
prisma/citiesSeeder.ts
Normal file
64
prisma/citiesSeeder.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
|
||||||
|
export async function seedCities(prisma: PrismaClient) {
|
||||||
|
|
||||||
|
const statesFolder = path.join(process.cwd(), 'states-cities')
|
||||||
|
const files = fs.readdirSync(statesFolder)
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (!file.endsWith('.json')) continue
|
||||||
|
|
||||||
|
const stateName = file.replace('.json', '')
|
||||||
|
|
||||||
|
const state = await prisma.states.findFirst({
|
||||||
|
where: {
|
||||||
|
stateName: {
|
||||||
|
equals: stateName,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
console.log(`❌ State not found: ${stateName}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(statesFolder, file)
|
||||||
|
const rawData = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
|
||||||
|
|
||||||
|
if (!rawData.districts) {
|
||||||
|
console.log(`❌ Invalid structure in ${file}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const allVillages: string[] = []
|
||||||
|
|
||||||
|
for (const district of rawData.districts) {
|
||||||
|
for (const sub of district.subDistricts || []) {
|
||||||
|
for (const village of sub.villages || []) {
|
||||||
|
if (village && village.trim()) {
|
||||||
|
allVillages.push(village.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`📦 Total villages found in ${stateName}:`, allVillages.length)
|
||||||
|
|
||||||
|
const result = await prisma.cities.createMany({
|
||||||
|
data: allVillages.map((village) => ({
|
||||||
|
stateXid: state.id,
|
||||||
|
cityName: village,
|
||||||
|
})),
|
||||||
|
skipDuplicates: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`✅ ${stateName} inserted: ${result.count}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🎉 All states processed successfully!')
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
// prisma.ts
|
// prisma.ts
|
||||||
import { PrismaClient } from '@prisma/client';
|
// Re-export from the main singleton for consistency
|
||||||
|
import { prisma } from '../src/common/database/prisma.client';
|
||||||
|
|
||||||
// The DATABASE_URL environment variable will be automatically used
|
export { prisma };
|
||||||
export const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
process.on('SIGINT', async () => {
|
process.on('SIGINT', async () => {
|
||||||
await prisma.$disconnect();
|
await prisma.$disconnect();
|
||||||
|
|||||||
1097
prisma/schema.prisma
1097
prisma/schema.prisma
File diff suppressed because it is too large
Load Diff
2625
prisma/seed.ts
2625
prisma/seed.ts
File diff suppressed because it is too large
Load Diff
13
serverless.host.yml
Normal file
13
serverless.host.yml
Normal 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)}
|
||||||
|
|
||||||
13
serverless.minglaradmin.yml
Normal file
13
serverless.minglaradmin.yml
Normal 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)}
|
||||||
|
|
||||||
12
serverless.operator.yml
Normal file
12
serverless.operator.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
service: minglar-operator
|
||||||
|
|
||||||
|
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/operator.yml)}
|
||||||
13
serverless.prepopulate.yml
Normal file
13
serverless.prepopulate.yml
Normal 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)}
|
||||||
|
|
||||||
32
serverless.prisma-layer.yml
Normal file
32
serverless.prisma-layer.yml
Normal 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
13
serverless.user.yml
Normal 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)}
|
||||||
|
|
||||||
107
serverless.yml
107
serverless.yml
@@ -1,16 +1,42 @@
|
|||||||
service: minglarDev
|
# Legacy monolith config. For new deployments use serverless.*.yml files.
|
||||||
|
service: minglar
|
||||||
|
|
||||||
|
|
||||||
|
useDotenv: true
|
||||||
|
|
||||||
|
params:
|
||||||
|
dev:
|
||||||
|
stage: dev
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
uat:
|
||||||
|
stage: uat
|
||||||
|
|
||||||
provider:
|
provider:
|
||||||
name: aws
|
name: aws
|
||||||
runtime: nodejs22.x
|
runtime: nodejs22.x
|
||||||
region: ap-south-1
|
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
|
versionFunctions: false
|
||||||
memorySize: 512
|
memorySize: 512
|
||||||
|
# Apply Prisma layer to all functions
|
||||||
|
# Reference the layer defined in this stack using CloudFormation Ref
|
||||||
|
layers:
|
||||||
|
# Use the exported stack output so deploy function works (expects a string ARN)
|
||||||
|
# For offline/local, fall back to an empty string so the CF lookup is optional.
|
||||||
|
- ${cf:${self:service}-${sls:stage}.PrismaLambdaLayerQualifiedArn, ''}
|
||||||
apiGateway:
|
apiGateway:
|
||||||
binaryMediaTypes:
|
binaryMediaTypes:
|
||||||
- '*/*'
|
- '*/*'
|
||||||
minimumCompressionSize: 1024
|
minimumCompressionSize: 1024
|
||||||
|
websocketsApiName: minglar-ws-${sls:stage}
|
||||||
|
websocketsApiRouteSelectionExpression: $request.body.action
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: ${env:DATABASE_URL}
|
DATABASE_URL: ${env:DATABASE_URL}
|
||||||
DB_USERNAME: ${env:DB_USERNAME}
|
DB_USERNAME: ${env:DB_USERNAME}
|
||||||
@@ -38,7 +64,13 @@ provider:
|
|||||||
S3_BUCKET_NAME: ${env:S3_BUCKET_NAME}
|
S3_BUCKET_NAME: ${env:S3_BUCKET_NAME}
|
||||||
MINGLAR_ADMIN_NAME: ${env:MINGLAR_ADMIN_NAME}
|
MINGLAR_ADMIN_NAME: ${env:MINGLAR_ADMIN_NAME}
|
||||||
MINGLAR_ADMIN_EMAIL: ${env:MINGLAR_ADMIN_EMAIL}
|
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}
|
||||||
|
RAZORPAY_KEY_ID: ${env:RAZORPAY_KEY_ID}
|
||||||
|
RAZORPAY_KEY_SECRET: ${env:RAZORPAY_KEY_SECRET}
|
||||||
|
RAZORPAY_WEBHOOK_SECRET: ${env:RAZORPAY_WEBHOOK_SECRET}
|
||||||
|
|
||||||
iam:
|
iam:
|
||||||
role:
|
role:
|
||||||
statements:
|
statements:
|
||||||
@@ -51,27 +83,64 @@ provider:
|
|||||||
Resource:
|
Resource:
|
||||||
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}'
|
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}'
|
||||||
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}/*'
|
- 'arn:aws:s3:::${env:S3_BUCKET_NAME}/*'
|
||||||
|
- Effect: Allow
|
||||||
custom:
|
Action:
|
||||||
|
- execute-api:ManageConnections
|
||||||
|
Resource:
|
||||||
|
- 'arn:aws:execute-api:${self:provider.region}:*:*/*/@connections/*'
|
||||||
|
|
||||||
|
build:
|
||||||
esbuild:
|
esbuild:
|
||||||
bundle: true
|
bundle: true
|
||||||
minify: true
|
minify: true
|
||||||
sourcemap: false
|
sourcemap: false
|
||||||
target: node20
|
target: node20
|
||||||
platform: node
|
platform: node
|
||||||
concurrency: 5
|
# Mark as external so they're not bundled into the JS
|
||||||
external:
|
external:
|
||||||
- '@prisma/client'
|
- '@prisma/client'
|
||||||
|
- '.prisma/client'
|
||||||
- '.prisma'
|
- '.prisma'
|
||||||
|
- '@prisma/adapter-pg'
|
||||||
|
- 'pg'
|
||||||
|
- 'zod'
|
||||||
|
- '@aws-sdk/*'
|
||||||
|
- '@smithy/*'
|
||||||
|
- '@aws-crypto/*'
|
||||||
|
# Exclude prevents npm install of these packages in the zip
|
||||||
exclude:
|
exclude:
|
||||||
- 'aws-sdk'
|
- 'aws-sdk'
|
||||||
serverless-offline:
|
- '@aws-sdk/*'
|
||||||
reloadHandler: true
|
- '@smithy/*'
|
||||||
|
- '@aws-crypto/*'
|
||||||
|
- '@prisma/adapter-pg'
|
||||||
|
- '@prisma/client'
|
||||||
|
- '.prisma'
|
||||||
|
- '.prisma/client'
|
||||||
|
- 'pg'
|
||||||
|
- 'zod'
|
||||||
|
- 'pg-*'
|
||||||
|
- 'postgres-*'
|
||||||
|
- 'pgpass'
|
||||||
|
- 'split2'
|
||||||
|
- 'xtend'
|
||||||
|
|
||||||
|
# Define layers
|
||||||
|
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
|
||||||
|
|
||||||
package:
|
package:
|
||||||
individually: true
|
individually: true
|
||||||
patterns:
|
patterns:
|
||||||
- '!node_modules/**'
|
- '!node_modules/**'
|
||||||
|
- '!node_modules/@prisma/**'
|
||||||
|
- '!node_modules/.prisma/**'
|
||||||
- '!**/*.test.js'
|
- '!**/*.test.js'
|
||||||
- '!**/*.spec.js'
|
- '!**/*.spec.js'
|
||||||
- '!**/test/**'
|
- '!**/test/**'
|
||||||
@@ -82,12 +151,26 @@ package:
|
|||||||
- '!*.config.js'
|
- '!*.config.js'
|
||||||
- '!.git/**'
|
- '!.git/**'
|
||||||
- '!.github/**'
|
- '!.github/**'
|
||||||
|
|
||||||
# Import function definitions from separate files organized by module
|
# Import function definitions from separate files organized by module
|
||||||
functions:
|
functions:
|
||||||
- ${file(./serverless/functions/host.yml)}
|
- ${file(./serverless/functions/host.yml)}
|
||||||
|
- ${file(./serverless/functions/operator.yml)}
|
||||||
- ${file(./serverless/functions/minglaradmin.yml)}
|
- ${file(./serverless/functions/minglaradmin.yml)}
|
||||||
- ${file(./serverless/functions/prepopulate.yml)}
|
- ${file(./serverless/functions/prepopulate.yml)}
|
||||||
|
- ${file(./serverless/functions/user.yml)}
|
||||||
|
- ${file(./serverless/functions/websocket.yml)}
|
||||||
|
|
||||||
plugins:
|
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
|
||||||
|
|||||||
@@ -1,764 +0,0 @@
|
|||||||
service: minglarDev
|
|
||||||
|
|
||||||
provider:
|
|
||||||
name: aws
|
|
||||||
runtime: nodejs22.x
|
|
||||||
region: ap-south-1
|
|
||||||
versionFunctions: false
|
|
||||||
memorySize: 512 # Default memory for all functions (can be overridden per function)
|
|
||||||
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}
|
|
||||||
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}
|
|
||||||
|
|
||||||
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}/*'
|
|
||||||
|
|
||||||
custom:
|
|
||||||
esbuild:
|
|
||||||
bundle: true
|
|
||||||
minify: true
|
|
||||||
sourcemap: false
|
|
||||||
target: node20
|
|
||||||
platform: node
|
|
||||||
concurrency: 5
|
|
||||||
external:
|
|
||||||
- '@prisma/client'
|
|
||||||
- '.prisma'
|
|
||||||
exclude:
|
|
||||||
- 'aws-sdk'
|
|
||||||
|
|
||||||
package:
|
|
||||||
individually: true
|
|
||||||
patterns:
|
|
||||||
- '!node_modules/**'
|
|
||||||
- '!**/*.test.js'
|
|
||||||
- '!**/*.spec.js'
|
|
||||||
- '!**/test/**'
|
|
||||||
- '!**/__tests__/**'
|
|
||||||
- '!package-lock.json'
|
|
||||||
- '!yarn.lock'
|
|
||||||
- '!README.md'
|
|
||||||
- '!*.config.js'
|
|
||||||
- '!.git/**'
|
|
||||||
- '!.github/**'
|
|
||||||
|
|
||||||
functions:
|
|
||||||
getHosts:
|
|
||||||
handler: src/modules/host/handlers/host.handler
|
|
||||||
memorySize: 384 # Lower memory for simple GET operations
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/host.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host
|
|
||||||
method: get
|
|
||||||
|
|
||||||
verifyOtp:
|
|
||||||
handler: src/modules/host/handlers/verifyOtp.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/verifyOtp.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/verify-otp
|
|
||||||
method: post
|
|
||||||
|
|
||||||
loginForHost:
|
|
||||||
handler: src/modules/host/handlers/loginForHost.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/loginForHost.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/login
|
|
||||||
method: post
|
|
||||||
|
|
||||||
registrationOfHost:
|
|
||||||
handler: src/modules/host/handlers/registration.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/registration.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/registration
|
|
||||||
method: post
|
|
||||||
|
|
||||||
createPasswordForHost:
|
|
||||||
handler: src/modules/host/handlers/createPassword.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/createPassword.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/create-password
|
|
||||||
method: post
|
|
||||||
|
|
||||||
addPaymentDetailsForHost:
|
|
||||||
handler: src/modules/host/handlers/addPaymentDetails.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/addPaymentDetails.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/add-payment-details
|
|
||||||
method: post
|
|
||||||
|
|
||||||
addActivity:
|
|
||||||
handler: src/modules/host/handlers/addActivity.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/addActivity.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/add-activity
|
|
||||||
method: post
|
|
||||||
|
|
||||||
getHostById:
|
|
||||||
handler: src/modules/host/handlers/getbyidhandler.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/getbyidhandler.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/getById
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getPQQQuestionDetailsById:
|
|
||||||
handler: src/modules/host/handlers/getByIdPQQ.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/getByIdPQQ.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/get-pqq-question-details
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getLatestPQQQuestionDetails:
|
|
||||||
handler: src/modules/host/handlers/getLatestQuestionDetailsPQQ.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/getLatestQuestionDetailsPQQ.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/get-latest-pqq-question-details
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getActivityTypes:
|
|
||||||
handler: src/modules/host/handlers/getActivity.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/getActivity.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/get-activity
|
|
||||||
method: get
|
|
||||||
|
|
||||||
acceptMinglarAgreement:
|
|
||||||
handler: src/modules/host/handlers/acceptAgreement.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/acceptAgreement.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/accept-agreement
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
getStepperInfo:
|
|
||||||
handler: src/modules/host/handlers/getStepper.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/getStepper.*'
|
|
||||||
- 'src/common/utils/handlers/safeHandler.*'
|
|
||||||
- 'src/common/database/**'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /stepper
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getSuggestion:
|
|
||||||
handler: src/modules/minglaradmin/handlers/getSuggestion.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/get-suggestion
|
|
||||||
method: get
|
|
||||||
|
|
||||||
minglarRegistration:
|
|
||||||
handler: src/modules/minglaradmin/handlers/registration.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/registration
|
|
||||||
method: post
|
|
||||||
|
|
||||||
minglarLoginForAdmin:
|
|
||||||
handler: src/modules/minglaradmin/handlers/loginForMinglar.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/login
|
|
||||||
method: post
|
|
||||||
|
|
||||||
minglarCreatePassword:
|
|
||||||
handler: src/modules/minglaradmin/handlers/createPassword.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/create-password
|
|
||||||
method: post
|
|
||||||
|
|
||||||
# Functions using AWS SDK - KEEP AS IS with higher memory
|
|
||||||
updateMinglarProfile:
|
|
||||||
handler: src/modules/minglaradmin/handlers/updateProfile.handler
|
|
||||||
memorySize: 512 # Higher memory for AWS SDK operations
|
|
||||||
timeout: 30
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/handlers/updateProfile.*'
|
|
||||||
- 'src/modules/minglaradmin/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
- 'node_modules/@aws-sdk/**'
|
|
||||||
- 'node_modules/@smithy/**'
|
|
||||||
- 'node_modules/tslib/**'
|
|
||||||
- 'node_modules/fast-xml-parser/**'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/update-profile
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
prepopulateTeammate:
|
|
||||||
handler: src/modules/minglaradmin/handlers/prepopulateTeammate.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/prepopulate-Roles
|
|
||||||
method: get
|
|
||||||
|
|
||||||
inviteTeammate:
|
|
||||||
handler: src/modules/minglaradmin/handlers/inviteTeammate.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/invite-teammate
|
|
||||||
method: post
|
|
||||||
|
|
||||||
getAllHostApplication:
|
|
||||||
handler: src/modules/minglaradmin/handlers/getAllHostApplication.handler
|
|
||||||
memorySize: 512 # Higher memory for data-intensive operations
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/get-all-host-applications-am
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getAllOnboardingHostApplications:
|
|
||||||
handler: src/modules/minglaradmin/handlers/getAllOnboardingHosts.handler
|
|
||||||
memorySize: 512 # Higher memory for data-intensive operations
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/get-all-host-applications-admin
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getAllOnboardingHostApplications_New:
|
|
||||||
handler: src/modules/minglaradmin/handlers/getOnboardingNewApplications.handler
|
|
||||||
memorySize: 512 # Higher memory for data-intensive operations
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/get-all-host-applications-admin-new
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getAllInvitationDetails:
|
|
||||||
handler: src/modules/minglaradmin/handlers/getAllInvitationDetails.handler
|
|
||||||
memorySize: 512
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/get-all-invitation-details
|
|
||||||
method: get
|
|
||||||
|
|
||||||
addSuggestion:
|
|
||||||
handler: src/modules/minglaradmin/handlers/addSuggestion.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/add-suggestion
|
|
||||||
method: post
|
|
||||||
|
|
||||||
getAllCoadminAndAMDetails:
|
|
||||||
handler: src/modules/minglaradmin/handlers/getAllCoadminAndAM.handler
|
|
||||||
memorySize: 512
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/get-all-coadmin-and-am-details
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getAllInvitedCoadminAndAMDetails:
|
|
||||||
handler: src/modules/minglaradmin/handlers/getAllInvitedCoadminAndAM.handler
|
|
||||||
memorySize: 512
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/get-all-invited-coadmin-and-am
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getAllBankAndCurrencyDetails:
|
|
||||||
handler: src/modules/prepopulate/handlers/getAllBankDetails.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /prepopulate/get-all-bank-currency-details
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getCityByState:
|
|
||||||
handler: src/modules/prepopulate/handlers/getCityByState.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/prepopulate/handlers/getCityByState.*'
|
|
||||||
- 'src/modules/prepopulate/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /prepopulate/get-city-by-state
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getBranchByBankXid:
|
|
||||||
handler: src/modules/prepopulate/handlers/getBranchByBank.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/prepopulate/handlers/getBranchByBank.*'
|
|
||||||
- 'src/modules/prepopulate/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /prepopulate/get-branch-by-bank
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getAllDocumentCountryStateCityDetails:
|
|
||||||
handler: src/modules/prepopulate/handlers/getAllDocTypeWithCountryState.handler
|
|
||||||
memorySize: 512
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /prepopulate/get-all-doc-country
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getAllPqqQuesAns:
|
|
||||||
handler: src/modules/prepopulate/handlers/getAllPQQQuesWithAns.handler
|
|
||||||
memorySize: 512
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /prepopulate/get-all-pqq-ques-ans
|
|
||||||
method: get
|
|
||||||
|
|
||||||
getFrequenciesOfActivity:
|
|
||||||
handler: src/modules/prepopulate/handlers/getAllFrequencies.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /prepopulate/get-all-Frequencies
|
|
||||||
method: get
|
|
||||||
|
|
||||||
assignAMToHost:
|
|
||||||
handler: src/modules/minglaradmin/handlers/assignAM.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/assign-am-to-host
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
editAgreementDetails:
|
|
||||||
handler: src/modules/minglaradmin/handlers/editAgreementDetails.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/edit-agreement-details
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
acceptHostApplication:
|
|
||||||
handler: src/modules/minglaradmin/handlers/acceptHostApplication.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/accept-host-application
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
acceptHostApplicationMinglar:
|
|
||||||
handler: src/modules/minglaradmin/handlers/acceptHostAppMinglar.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/accept-host-application-minglar
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
rejectHostApplication:
|
|
||||||
handler: src/modules/minglaradmin/handlers/rejectHostApplication.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/reject-host-application
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
rejectHostApplicationAM:
|
|
||||||
handler: src/modules/minglaradmin/handlers/rejectHostApplicationAM.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglaradmin/reject-host-application-am
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
# Functions using AWS SDK and S3 - KEEP AS IS with higher memory
|
|
||||||
addCompanyDetails:
|
|
||||||
handler: src/modules/host/handlers/addCompanyDetails.handler
|
|
||||||
memorySize: 512
|
|
||||||
timeout: 30
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/addCompanyDetails.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
# Only include specific AWS SDK modules needed for S3
|
|
||||||
- 'node_modules/@aws-sdk/client-s3/**'
|
|
||||||
- 'node_modules/@aws-sdk/s3-request-presigner/**'
|
|
||||||
- 'node_modules/@aws-sdk/types/**'
|
|
||||||
- 'node_modules/@aws-sdk/middleware-logger/**'
|
|
||||||
- 'node_modules/@aws-sdk/util-utf8-node/**'
|
|
||||||
- 'node_modules/@aws-sdk/util-utf8-browser/**'
|
|
||||||
- 'node_modules/@smithy/**'
|
|
||||||
- 'node_modules/tslib/**'
|
|
||||||
# Remove these large/unnecessary packages:
|
|
||||||
- 'node_modules/fast-xml-parser/**' # Remove if not used
|
|
||||||
- 'node_modules/lambda-multipart-parser/**' # You're using busboy directly
|
|
||||||
- 'node_modules/busboy/**'
|
|
||||||
# Remove these AWS utility packages (included in main SDK):
|
|
||||||
- 'node_modules/@aws-crypto/**'
|
|
||||||
# - 'node_modules/uuid/**' # AWS SDK includes its own
|
|
||||||
# - 'node_modules/@aws/util-uri-escape/**'
|
|
||||||
# - 'node_modules/@aws/util-middleware/**'
|
|
||||||
- 'node_modules/@aws/smithy-client/**'
|
|
||||||
# - 'node_modules/@aws/lambda-invoke-store/**'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/add-company-details
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
submitPqqAnswer:
|
|
||||||
handler: src/modules/host/handlers/submitPqqAns.handler
|
|
||||||
memorySize: 512
|
|
||||||
timeout: 30
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/submitPqqAns.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
- 'node_modules/@aws-sdk/**'
|
|
||||||
- 'node_modules/@smithy/**'
|
|
||||||
- 'node_modules/tslib/**'
|
|
||||||
- 'node_modules/fast-xml-parser/**'
|
|
||||||
- 'node_modules/lambda-multipart-parser/**'
|
|
||||||
- 'node_modules/busboy/**'
|
|
||||||
- 'node_modules/@aws-crypto/**'
|
|
||||||
- 'node_modules/uuid/**'
|
|
||||||
- 'node_modules/@aws/util-uri-escape/**'
|
|
||||||
- 'node_modules/@aws/util-middleware/**'
|
|
||||||
- 'node_modules/@aws/smithy-client/**'
|
|
||||||
- 'node_modules/@aws/lambda-invoke-store/**'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/submit-pqq-ans
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
submitFinalPqqAnswer:
|
|
||||||
handler: src/modules/host/handlers/getPQQScore.handler
|
|
||||||
memorySize: 512
|
|
||||||
timeout: 30
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/host/handlers/getPQQScore.*'
|
|
||||||
- 'src/modules/host/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
- 'node_modules/@aws-sdk/**'
|
|
||||||
- 'node_modules/@smithy/**'
|
|
||||||
- 'node_modules/tslib/**'
|
|
||||||
- 'node_modules/fast-xml-parser/**'
|
|
||||||
- 'node_modules/lambda-multipart-parser/**'
|
|
||||||
- 'node_modules/busboy/**'
|
|
||||||
- 'node_modules/@aws-crypto/**'
|
|
||||||
- 'node_modules/uuid/**'
|
|
||||||
- 'node_modules/@aws/util-uri-escape/**'
|
|
||||||
- 'node_modules/@aws/util-middleware/**'
|
|
||||||
- 'node_modules/@aws/smithy-client/**'
|
|
||||||
- 'node_modules/@aws/lambda-invoke-store/**'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /host/submit-final-pqq-ans
|
|
||||||
method: patch
|
|
||||||
|
|
||||||
addPQQSuggestion:
|
|
||||||
handler: src/modules/minglar/handlers/addPQQSuggestion.handler
|
|
||||||
memorySize: 384
|
|
||||||
package:
|
|
||||||
patterns:
|
|
||||||
- 'src/modules/minglaradmin/handlers/addPQQSuggestion.*'
|
|
||||||
- 'src/modules/minglaradmin/services/**'
|
|
||||||
- 'src/common/**'
|
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/**'
|
|
||||||
events:
|
|
||||||
- httpApi:
|
|
||||||
path: /minglar/add-Pqq-suggestion
|
|
||||||
method: post
|
|
||||||
138
serverless/common.yml
Normal file
138
serverless/common.yml
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
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}
|
||||||
|
AM_INTERFACE_LINK: ${env:AM_INTERFACE_LINK}
|
||||||
|
HOST_LINK: ${env:HOST_LINK}
|
||||||
|
HOST_LINK_PQ: ${env:HOST_LINK_PQ}
|
||||||
|
RAZORPAY_KEY_ID: ${env:RAZORPAY_KEY_ID}
|
||||||
|
RAZORPAY_KEY_SECRET: ${env:RAZORPAY_KEY_SECRET}
|
||||||
|
RAZORPAY_WEBHOOK_SECRET: ${env:RAZORPAY_WEBHOOK_SECRET}
|
||||||
|
|
||||||
|
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
|
||||||
@@ -14,7 +14,7 @@ getHosts:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host
|
path: /
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
verifyOTP:
|
verifyOTP:
|
||||||
@@ -30,7 +30,7 @@ verifyOTP:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Host_Admin/onboarding/verify-otp
|
path: /Host_Admin/onboarding/verify-otp
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
login:
|
login:
|
||||||
@@ -46,7 +46,7 @@ login:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Host_Admin/onboarding/login
|
path: /Host_Admin/onboarding/login
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
signUp:
|
signUp:
|
||||||
@@ -62,7 +62,7 @@ signUp:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Host_Admin/onboarding/registration
|
path: /Host_Admin/onboarding/registration
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
createPassword:
|
createPassword:
|
||||||
@@ -78,7 +78,7 @@ createPassword:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Host_Admin/onboarding/create-password
|
path: /Host_Admin/onboarding/create-password
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
updateBankDetails:
|
updateBankDetails:
|
||||||
@@ -94,7 +94,7 @@ updateBankDetails:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Host_Admin/onboarding/add-payment-details
|
path: /Host_Admin/onboarding/add-payment-details
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
saveActivity_ForPQQ:
|
saveActivity_ForPQQ:
|
||||||
@@ -110,7 +110,7 @@ saveActivity_ForPQQ:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/add-activity
|
path: /Activity_Hub/OnBoarding/add-activity
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
getHostById:
|
getHostById:
|
||||||
@@ -126,7 +126,7 @@ getHostById:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/getById
|
path: /getById
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getPQQ_ByQuestionId:
|
getPQQ_ByQuestionId:
|
||||||
@@ -142,7 +142,7 @@ getPQQ_ByQuestionId:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/get-pqq-question-details
|
path: /Activity_Hub/OnBoarding/get-pqq-question-details
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getPQQ_LastUpdatedQuestion:
|
getPQQ_LastUpdatedQuestion:
|
||||||
@@ -158,10 +158,10 @@ getPQQ_LastUpdatedQuestion:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/get-latest-pqq-question-details
|
path: /Activity_Hub/OnBoarding/get-latest-pqq-question-details
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllActivityType:
|
prePopulateNewActivity:
|
||||||
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/getAllActivityType.handler
|
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/getAllActivityType.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
package:
|
package:
|
||||||
@@ -174,9 +174,26 @@ getAllActivityType:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/get-activity-type
|
path: /Activity_Hub/OnBoarding/prepopulate-new-activity
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
|
createNewActivity:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/CreateNewActivity.handler
|
||||||
|
memorySize: 1024
|
||||||
|
timeout: 30
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/CreateNewActivity.*'
|
||||||
|
- '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: /Activity_Hub/OnBoarding/create-new-activity
|
||||||
|
method: patch
|
||||||
|
|
||||||
showSuggestion:
|
showSuggestion:
|
||||||
handler: src/modules/host/handlers/Host_Admin/onboarding/showSuggestion.handler
|
handler: src/modules/host/handlers/Host_Admin/onboarding/showSuggestion.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
@@ -190,7 +207,23 @@ showSuggestion:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/get-suggestion
|
path: /get-suggestion
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getAllActivitySuggestion:
|
||||||
|
handler: src/modules/host/handlers/Host_Admin/onboarding/getAllActvitySuggestion.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Host_Admin/onboarding/getAllActvitySuggestion.handler.*'
|
||||||
|
- '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: /get-Activity-suggestion
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllHostActivity:
|
getAllHostActivity:
|
||||||
@@ -206,7 +239,7 @@ getAllHostActivity:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/get-all-host-activity
|
path: /Activity_Hub/OnBoarding/get-all-host-activity
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
acceptAggrement:
|
acceptAggrement:
|
||||||
@@ -222,9 +255,25 @@ acceptAggrement:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Host_Admin/onboarding/accept-agreement
|
path: /Host_Admin/onboarding/accept-agreement
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
|
getLatestAgreement:
|
||||||
|
handler: src/modules/host/handlers/Host_Admin/onboarding/getLatestAgreement.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Host_Admin/onboarding/getLatestAgreement.*'
|
||||||
|
- '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_Admin/onboarding/get-latest-agreement
|
||||||
|
method: get
|
||||||
|
|
||||||
getStepperInfo:
|
getStepperInfo:
|
||||||
handler: src/modules/host/handlers/getStepper.handler
|
handler: src/modules/host/handlers/getStepper.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
@@ -243,6 +292,118 @@ getStepperInfo:
|
|||||||
path: /stepper
|
path: /stepper
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
|
updateHostProfile:
|
||||||
|
handler: src/modules/host/handlers/updateHostProfile.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/updateHostProfile.*'
|
||||||
|
- '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: /profile
|
||||||
|
method: patch
|
||||||
|
|
||||||
|
inviteHostMember:
|
||||||
|
handler: src/modules/host/handlers/settings/inviteMember.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/settings/**'
|
||||||
|
- '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: /settings/invite-member
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getAllInvitedCoadminAndOperator:
|
||||||
|
handler: src/modules/host/handlers/settings/getAllInvitedCoadminAndOperator.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/settings/**'
|
||||||
|
- '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: /settings/invited-coadmin-operators
|
||||||
|
method: get
|
||||||
|
|
||||||
|
saveRolePermissions:
|
||||||
|
handler: src/modules/host/handlers/settings/saveRolePermissions.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/settings/**'
|
||||||
|
- '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: /settings/save-role-permissions
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getPermissionMasters:
|
||||||
|
handler: src/modules/host/handlers/settings/getPermissionMasters.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/settings/**'
|
||||||
|
- '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: /settings/permission-masters
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getHostMemberRoles:
|
||||||
|
handler: src/modules/host/handlers/settings/getMemberRoles.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/settings/**'
|
||||||
|
- '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: /settings/member-roles
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getMemberPermissions:
|
||||||
|
handler: src/modules/host/handlers/settings/getMemberPermissions.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/settings/**'
|
||||||
|
- '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: /settings/member-permissions/{memberUserXid}
|
||||||
|
method: get
|
||||||
|
|
||||||
# Functions with S3/AWS SDK dependencies
|
# Functions with S3/AWS SDK dependencies
|
||||||
submitCompanyDetails:
|
submitCompanyDetails:
|
||||||
handler: src/modules/host/handlers/Host_Admin/onboarding/submitCompanyDetails.handler
|
handler: src/modules/host/handlers/Host_Admin/onboarding/submitCompanyDetails.handler
|
||||||
@@ -253,31 +414,9 @@ submitCompanyDetails:
|
|||||||
- 'src/modules/host/handlers/addCompanyDetails.*'
|
- 'src/modules/host/handlers/addCompanyDetails.*'
|
||||||
- 'src/modules/host/services/**'
|
- 'src/modules/host/services/**'
|
||||||
- 'src/common/**'
|
- 'src/common/**'
|
||||||
- 'node_modules/@prisma/client/**'
|
|
||||||
- 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
|
||||||
# Only include specific AWS SDK modules needed for S3
|
|
||||||
- 'node_modules/@aws-sdk/client-s3/**'
|
|
||||||
- 'node_modules/@aws-sdk/s3-request-presigner/**'
|
|
||||||
- 'node_modules/@aws-sdk/types/**'
|
|
||||||
- 'node_modules/@aws-sdk/middleware-logger/**'
|
|
||||||
- 'node_modules/@aws-sdk/util-utf8-node/**'
|
|
||||||
- 'node_modules/@aws-sdk/util-utf8-browser/**'
|
|
||||||
- 'node_modules/@smithy/**'
|
|
||||||
- 'node_modules/tslib/**'
|
|
||||||
# Remove these large/unnecessary packages:
|
|
||||||
- 'node_modules/fast-xml-parser/**' # Remove if not used
|
|
||||||
- 'node_modules/lambda-multipart-parser/**' # You're using busboy directly
|
|
||||||
- 'node_modules/busboy/**'
|
|
||||||
# Remove these AWS utility packages (included in main SDK):
|
|
||||||
- 'node_modules/@aws-crypto/**'
|
|
||||||
# - 'node_modules/uuid/**' # AWS SDK includes its own
|
|
||||||
# - 'node_modules/@aws/util-uri-escape/**'
|
|
||||||
# - 'node_modules/@aws/util-middleware/**'
|
|
||||||
- 'node_modules/@aws/smithy-client/**'
|
|
||||||
# - 'node_modules/@aws/lambda-invoke-store/**'
|
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Host_Admin/onboarding/add-company-details
|
path: /Host_Admin/onboarding/add-company-details
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
submitPQQ_Answer:
|
submitPQQ_Answer:
|
||||||
@@ -291,27 +430,9 @@ submitPQQ_Answer:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern2}
|
- ${file(./serverless/patterns/base.yml):pattern2}
|
||||||
- ${file(./serverless/patterns/base.yml):pattern3}
|
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
- 'node_modules/@aws-sdk/client-s3/**'
|
|
||||||
- 'node_modules/@aws-sdk/s3-request-presigner/**'
|
|
||||||
- 'node_modules/@aws-sdk/types/**'
|
|
||||||
- 'node_modules/@aws-sdk/middleware-logger/**'
|
|
||||||
- 'node_modules/@aws-sdk/util-utf8-node/**'
|
|
||||||
- 'node_modules/@aws-sdk/util-utf8-browser/**'
|
|
||||||
- 'node_modules/@smithy/**'
|
|
||||||
- 'node_modules/tslib/**'
|
|
||||||
# Remove these large/unnecessary packages:
|
|
||||||
- 'node_modules/fast-xml-parser/**' # Remove if not used
|
|
||||||
- 'node_modules/lambda-multipart-parser/**' # You're using busboy directly
|
|
||||||
- 'node_modules/busboy/**'
|
|
||||||
# Remove these AWS utility packages (included in main SDK):
|
|
||||||
- 'node_modules/@aws-crypto/**'
|
|
||||||
# - 'node_modules/uuid/**' # AWS SDK includes its own
|
|
||||||
# - 'node_modules/@aws/util-uri-escape/**'
|
|
||||||
# - 'node_modules/@aws/util-middleware/**'
|
|
||||||
- 'node_modules/@aws/smithy-client/**'
|
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/submit-pqq-answer
|
path: /Activity_Hub/OnBoarding/submit-pqq-answer
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
updatePQQ_LastAnswer:
|
updatePQQ_LastAnswer:
|
||||||
@@ -327,9 +448,25 @@ updatePQQ_LastAnswer:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/submit-final-pqq-answer
|
path: /Activity_Hub/OnBoarding/submit-final-pqq-answer
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
|
submitPQQForReview:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQQForReview.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQQForReview.*'
|
||||||
|
- '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: /Activity_Hub/OnBoarding/submit-pqq-for-review
|
||||||
|
method: patch
|
||||||
|
|
||||||
getAllPQQwithSubmittedAns:
|
getAllPQQwithSubmittedAns:
|
||||||
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/getAllPQQwithSubmittedAns.handler
|
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/getAllPQQwithSubmittedAns.handler
|
||||||
memorySize: 512
|
memorySize: 512
|
||||||
@@ -342,7 +479,22 @@ getAllPQQwithSubmittedAns:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- 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:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/getAllDetailsOfActivityAndVenue.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/**'
|
||||||
|
- ${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/get-all-details-activity-venue/{activityXid}
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
updateSuggestionAsReviewed:
|
updateSuggestionAsReviewed:
|
||||||
@@ -357,7 +509,7 @@ updateSuggestionAsReviewed:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /host/Activity_Hub/OnBoarding/update-suggestion-reviewed
|
path: /Activity_Hub/OnBoarding/update-suggestion-reviewed
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
resendOTPmail:
|
resendOTPmail:
|
||||||
@@ -374,3 +526,155 @@ resendOTPmail:
|
|||||||
- httpApi:
|
- httpApi:
|
||||||
path: /resend-otp
|
path: /resend-otp
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
|
mediaUploadTos3:
|
||||||
|
handler: src/modules/host/handlers/mediaUploadToS3.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/mediaUploadToS3/**'
|
||||||
|
- ${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: /media/upload/activity/{activityXid}
|
||||||
|
method: post
|
||||||
|
|
||||||
|
venueMediaUploadTos3:
|
||||||
|
handler: src/modules/host/handlers/mediaUploadForVenueToS3.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/mediaUploadForVenueToS3/**'
|
||||||
|
- ${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: /media/upload/venue/activity/{activityXid}
|
||||||
|
method: post
|
||||||
|
|
||||||
|
mediaDeleteFroms3:
|
||||||
|
handler: src/modules/host/handlers/mediaDeleteFromS3.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/mediaDeleteFromS3/**'
|
||||||
|
- ${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: /media/delete
|
||||||
|
method: delete
|
||||||
|
|
||||||
|
createSchedulingForAct:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/Scheduling/createSchedulingOfAct.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/Scheduling/**'
|
||||||
|
- ${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: /scheduling/create
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getActivitiesByStatus:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/Scheduling/getSchedulingOfAct.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/Scheduling/getSchedulingOfAct.*'
|
||||||
|
- '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: /scheduling/get-all-activities
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getVenueDurationByAct:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/Scheduling/getVenueDurationByAct.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/Scheduling/getVenueDurationByAct.*'
|
||||||
|
- '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: /scheduling/get-venue-duration/{activityXid}
|
||||||
|
method: get
|
||||||
|
|
||||||
|
cancelSlotForActivity:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/Scheduling/cancelSlot.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/Scheduling/**'
|
||||||
|
- ${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: /scheduling/cancel-slot
|
||||||
|
method: post
|
||||||
|
|
||||||
|
openCanceledSlotForActivity:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/Scheduling/openCanceledSlot.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/Scheduling/**'
|
||||||
|
- ${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: /scheduling/open-canceled-slot
|
||||||
|
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
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
# Minglar Admin Module Functions
|
# Minglar Admin Module Functions
|
||||||
# Admin dashboard and management endpoints
|
# Admin dashboard and management endpoints
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
minglarRegistration:
|
minglarRegistration:
|
||||||
handler: src/modules/minglaradmin/handlers/registration.handler
|
handler: src/modules/minglaradmin/handlers/registration.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
@@ -15,7 +13,7 @@ minglarRegistration:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/registration
|
path: /registration
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
minglarLoginForAdmin:
|
minglarLoginForAdmin:
|
||||||
@@ -30,7 +28,7 @@ minglarLoginForAdmin:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/login
|
path: /login
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
minglarCreatePassword:
|
minglarCreatePassword:
|
||||||
@@ -45,7 +43,7 @@ minglarCreatePassword:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/create-password
|
path: /create-password
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
updateMinglarProfile:
|
updateMinglarProfile:
|
||||||
@@ -60,25 +58,9 @@ updateMinglarProfile:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern2}
|
- ${file(./serverless/patterns/base.yml):pattern2}
|
||||||
- ${file(./serverless/patterns/base.yml):pattern3}
|
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern1}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern2}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern3}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern4}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern5}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern6}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern7}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern8}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern9}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern10}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern11}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern12}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern13}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern14}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern15}
|
|
||||||
- ${file(./serverless/patterns/aws-s3.yml):pattern16}
|
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/update-profile
|
path: /update-profile
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
prepopulateRole:
|
prepopulateRole:
|
||||||
@@ -93,10 +75,9 @@ prepopulateRole:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/prepopulate-Roles
|
path: /prepopulate-Roles
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
|
|
||||||
getHostDetailsById:
|
getHostDetailsById:
|
||||||
handler: src/modules/minglaradmin/handlers/hosthub/hosts/getByIdHostDetails.handler
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/getByIdHostDetails.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
@@ -109,7 +90,7 @@ getHostDetailsById:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/hosts/get-host-details/{host_xid}
|
path: /hosthub/hosts/get-host-details/{host_xid}
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
inviteTeammate:
|
inviteTeammate:
|
||||||
@@ -124,7 +105,7 @@ inviteTeammate:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/settings/teammates/invite-teammate
|
path: /settings/teammates/invite-teammate
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
getAllHostApplication:
|
getAllHostApplication:
|
||||||
@@ -140,7 +121,7 @@ getAllHostApplication:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/hosts/get-all-host-applications-am
|
path: /hosthub/hosts/get-all-host-applications-am
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllHostActivityForAdmin:
|
getAllHostActivityForAdmin:
|
||||||
@@ -156,7 +137,7 @@ getAllHostActivityForAdmin:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/get-all-activity-of-host/{id}
|
path: /get-all-activity-of-host/{id}
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllOnboardingHostApplications:
|
getAllOnboardingHostApplications:
|
||||||
@@ -172,7 +153,7 @@ getAllOnboardingHostApplications:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/onboarding/get-all-host-applications-admin
|
path: /hosthub/onboarding/get-all-host-applications-admin
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllOnboardingHostApplications_New:
|
getAllOnboardingHostApplications_New:
|
||||||
@@ -188,7 +169,7 @@ getAllOnboardingHostApplications_New:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/onboarding/get-all-host-applications-admin-new
|
path: /hosthub/onboarding/get-all-host-applications-admin-new
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllInvitationDetails:
|
getAllInvitationDetails:
|
||||||
@@ -203,7 +184,7 @@ getAllInvitationDetails:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/settings/teammates/get-all-invitation-details
|
path: /settings/teammates/get-all-invitation-details
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
addSuggestion:
|
addSuggestion:
|
||||||
@@ -219,7 +200,7 @@ addSuggestion:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/hosts/add-suggestion
|
path: /hosthub/hosts/add-suggestion
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
getAllCoadminAndAMDetails:
|
getAllCoadminAndAMDetails:
|
||||||
@@ -234,7 +215,7 @@ getAllCoadminAndAMDetails:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/settings/teammates/get-all-coadmin-am
|
path: /settings/teammates/get-all-coadmin-am
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllInvitedCoadminAndAMDetails:
|
getAllInvitedCoadminAndAMDetails:
|
||||||
@@ -249,7 +230,7 @@ getAllInvitedCoadminAndAMDetails:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/settings/teammates/get-all-invited-coadmin-am
|
path: /settings/teammates/get-all-invited-coadmin-am
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAmDetailsbyId:
|
getAmDetailsbyId:
|
||||||
@@ -264,7 +245,7 @@ getAmDetailsbyId:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/settings/teammates/get-am-details-by-id/{amXid}
|
path: /settings/teammates/get-am-details-by-id/{amXid}
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
assignAMToHost:
|
assignAMToHost:
|
||||||
@@ -280,11 +261,11 @@ assignAMToHost:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/onboarding/assign-am
|
path: /hosthub/onboarding/assign-am
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
editAgreementDetails:
|
editAgreementDetailsAndAccept:
|
||||||
handler: src/modules/minglaradmin/handlers/hosthub/onboarding/editAgreementDetails.handler
|
handler: src/modules/minglaradmin/handlers/hosthub/onboarding/editAgreementDetailsAndAccept.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
package:
|
package:
|
||||||
patterns:
|
patterns:
|
||||||
@@ -296,9 +277,24 @@ editAgreementDetails:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/onboarding/edit-agreement
|
path: /hosthub/onboarding/edit-agreement-accept-host
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
|
getAllPqqQuesAnsForAM:
|
||||||
|
handler: src/modules/host/handlers/Activity_Hub/OnBoarding/getAllPQQwithSubmittedAns.handler
|
||||||
|
memorySize: 512
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/Activity_Hub/OnBoarding/getAllPQQwithSubmittedAns**'
|
||||||
|
- ${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: /hosthub/onboarding/get-all-pqq-ques-ans-for-am
|
||||||
|
method: get
|
||||||
|
|
||||||
acceptHostApplication:
|
acceptHostApplication:
|
||||||
handler: src/modules/minglaradmin/handlers/hosthub/hosts/acceptHostApplication.handler
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/acceptHostApplication.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
@@ -312,7 +308,7 @@ acceptHostApplication:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/hosts/accept-host-application
|
path: /hosthub/hosts/accept-host-application
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
RejectPQQByAM:
|
RejectPQQByAM:
|
||||||
@@ -328,15 +324,15 @@ RejectPQQByAM:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/hosts/reject-pqq-by-am
|
path: /hosthub/hosts/reject-pq-by-am
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
acceptHostApplicationMinglar:
|
rejectActivityDetailsApplicationByAM:
|
||||||
handler: src/modules/minglaradmin/handlers/hosthub/onboarding/acceptHostAppMinglar.handler
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/rejectActivityApplicationByAM.handler
|
||||||
memorySize: 384
|
memorySize: 384
|
||||||
package:
|
package:
|
||||||
patterns:
|
patterns:
|
||||||
- 'src/modules/minglaradmin/handlers/hosthub/onboarding/**'
|
- 'src/modules/minglaradmin/handlers/hosthub/hosts/rejectActivityApplicationByAM**'
|
||||||
- 'src/modules/minglaradmin/services/**'
|
- 'src/modules/minglaradmin/services/**'
|
||||||
- ${file(./serverless/patterns/base.yml):pattern1}
|
- ${file(./serverless/patterns/base.yml):pattern1}
|
||||||
- ${file(./serverless/patterns/base.yml):pattern2}
|
- ${file(./serverless/patterns/base.yml):pattern2}
|
||||||
@@ -344,7 +340,39 @@ acceptHostApplicationMinglar:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/onboarding/accept-host-application-minglar
|
path: /hosthub/hosts/reject-activity-application-by-am
|
||||||
|
method: patch
|
||||||
|
|
||||||
|
acceptPQByAM:
|
||||||
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/acceptPQByAM.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/minglaradmin/handlers/hosthub/hosts/acceptPQByAM**'
|
||||||
|
- 'src/modules/minglaradmin/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: /hosthub/hosts/accept-pq-by-am
|
||||||
|
method: patch
|
||||||
|
|
||||||
|
acceptActivityDetailsApplicationByAM:
|
||||||
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/acceptActivityApplicationByAM.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/minglaradmin/handlers/hosthub/hosts/acceptActivityApplicationByAM**'
|
||||||
|
- 'src/modules/minglaradmin/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: /hosthub/hosts/accept-activity-application-by-am
|
||||||
method: patch
|
method: patch
|
||||||
|
|
||||||
rejectHostApplication:
|
rejectHostApplication:
|
||||||
@@ -360,8 +388,8 @@ rejectHostApplication:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/onboarding/reject-host-application
|
path: /hosthub/onboarding/reject-host-application
|
||||||
method: post
|
method: patch
|
||||||
|
|
||||||
rejectHostApplicationAM:
|
rejectHostApplicationAM:
|
||||||
handler: src/modules/minglaradmin/handlers/hosthub/hosts/rejectHostApplicationAM.handler
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/rejectHostApplicationAM.handler
|
||||||
@@ -376,8 +404,8 @@ rejectHostApplicationAM:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/hosts/reject-host-application-am
|
path: /hosthub/hosts/reject-host-application-am
|
||||||
method: post
|
method: patch
|
||||||
|
|
||||||
addPQQSuggestion:
|
addPQQSuggestion:
|
||||||
handler: src/modules/minglaradmin/handlers/hosthub/hosts/addPQQSuggestion.handler
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/addPQQSuggestion.handler
|
||||||
@@ -392,5 +420,53 @@ addPQQSuggestion:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /minglaradmin/hosthub/hosts/add-Pqq-suggestion
|
path: /hosthub/hosts/add-Pqq-suggestion
|
||||||
method: post
|
method: post
|
||||||
|
|
||||||
|
addActivitySuggestion:
|
||||||
|
handler: src/modules/minglaradmin/handlers/hosthub/hosts/addActivtiySuggestion.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/minglaradmin/handlers/hosthub/hosts/**'
|
||||||
|
- 'src/modules/minglaradmin/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: /hosthub/hosts/add-Activity-suggestion
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getAllPQPDetailsForAM:
|
||||||
|
handler: src/modules/minglaradmin/handlers/hosthub/pqp/getAllPQPDetailsForAM.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/minglaradmin/handlers/hosthub/pqp/getAllPQPDetailsForAM**'
|
||||||
|
- 'src/modules/minglaradmin/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: /hosthub/pqp/pqp-details-for-am/{activityXid}
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getSuggestionsForAM:
|
||||||
|
handler: src/modules/minglaradmin/handlers/hosthub/onboarding/showSuggestionToAM.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/minglaradmin/handlers/hosthub/onboarding/showSuggestionToAM**'
|
||||||
|
- 'src/modules/minglaradmin/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: /hosthub/onboarding/show-suggestion-to-am/{hostXid}
|
||||||
|
method: get
|
||||||
|
|||||||
198
serverless/functions/operator.yml
Normal file
198
serverless/functions/operator.yml
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
# Operator Module Functions
|
||||||
|
|
||||||
|
operatorSignUp:
|
||||||
|
handler: src/modules/host/handlers/operator/signUp.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorAuth.service.ts'
|
||||||
|
- 'src/modules/host/services/token.service.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /signup
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorVerifyOtp:
|
||||||
|
handler: src/modules/host/handlers/operator/verifyOtp.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorAuth.service.ts'
|
||||||
|
- 'src/modules/host/services/token.service.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /verify-otp
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorCreatePassword:
|
||||||
|
handler: src/modules/host/handlers/operator/createPassword.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorAuth.service.ts'
|
||||||
|
- 'src/modules/host/services/token.service.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /create-password
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorLogin:
|
||||||
|
handler: src/modules/host/handlers/operator/login.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorAuth.service.ts'
|
||||||
|
- 'src/modules/host/services/token.service.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /login
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorVerifyPassword:
|
||||||
|
handler: src/modules/host/handlers/operator/verifyPassword.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorAuth.service.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /verify-password
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorGetActivitiesByDate:
|
||||||
|
handler: src/modules/host/handlers/operator/getActivitiesByDate.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorActivity.service.ts'
|
||||||
|
- 'src/modules/host/dto/operator.activity.dto.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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-by-date
|
||||||
|
method: get
|
||||||
|
|
||||||
|
operatorGetReservationByCheckInCode:
|
||||||
|
handler: src/modules/host/handlers/operator/getReservationByCheckInCode.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorActivity.service.ts'
|
||||||
|
- 'src/modules/host/dto/operator.activity.dto.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /reservation-by-checkin-code
|
||||||
|
method: get
|
||||||
|
|
||||||
|
operatorSendOtpCheckIn:
|
||||||
|
handler: src/modules/host/handlers/operator/sendOtpCheckIn.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorActivity.service.ts'
|
||||||
|
- 'src/modules/host/dto/operator.activity.dto.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /send-otp-checkin
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorSendOtpCheckout:
|
||||||
|
handler: src/modules/host/handlers/operator/sendOtpCheckout.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorActivity.service.ts'
|
||||||
|
- 'src/modules/host/dto/operator.activity.dto.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /send-otp-checkout
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorVerifyOtpCheckIn:
|
||||||
|
handler: src/modules/host/handlers/operator/verifyOtpCheckIn.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorActivity.service.ts'
|
||||||
|
- 'src/modules/host/dto/operator.activity.dto.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /verify-otp-checkin
|
||||||
|
method: post
|
||||||
|
|
||||||
|
operatorVerifyOtpCheckout:
|
||||||
|
handler: src/modules/host/handlers/operator/verifyOtpCheckout.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/host/handlers/operator/**'
|
||||||
|
- 'src/modules/host/services/operatorActivity.service.ts'
|
||||||
|
- 'src/modules/host/dto/operator.activity.dto.ts'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${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: /verify-otp-checkout
|
||||||
|
method: post
|
||||||
@@ -13,7 +13,7 @@ getAllBankAndCurrencyDetails:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /prepopulate/get-all-bank-currency-details
|
path: /get-all-bank-currency-details
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getCityByState:
|
getCityByState:
|
||||||
@@ -29,7 +29,7 @@ getCityByState:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /prepopulate/get-city-by-state
|
path: /get-city-by-state
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getBranchByBankXid:
|
getBranchByBankXid:
|
||||||
@@ -45,7 +45,7 @@ getBranchByBankXid:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /prepopulate/get-branch-by-bank
|
path: /get-branch-by-bank
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllDocumentCountryStateCityDetails:
|
getAllDocumentCountryStateCityDetails:
|
||||||
@@ -60,7 +60,7 @@ getAllDocumentCountryStateCityDetails:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /prepopulate/get-all-doc-country
|
path: /get-all-doc-country
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getAllPqqQuesAns:
|
getAllPqqQuesAns:
|
||||||
@@ -75,7 +75,7 @@ getAllPqqQuesAns:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /prepopulate/get-all-pqq-ques-ans
|
path: /get-all-pqq-ques-ans
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
getFrequenciesOfActivity:
|
getFrequenciesOfActivity:
|
||||||
@@ -90,5 +90,20 @@ getFrequenciesOfActivity:
|
|||||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
events:
|
events:
|
||||||
- httpApi:
|
- httpApi:
|
||||||
path: /prepopulate/get-all-Frequencies
|
path: /get-all-Frequencies
|
||||||
method: get
|
method: get
|
||||||
|
|
||||||
|
getAddActivityPrePopulate:
|
||||||
|
handler: src/modules/prepopulate/handlers/getAddActivityPrePopulate.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/prepopulate/**'
|
||||||
|
- ${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: /get-add-activity-prepopulate
|
||||||
|
method: get
|
||||||
|
|||||||
589
serverless/functions/user.yml
Normal file
589
serverless/functions/user.yml
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
# Prepopulate Module Functions
|
||||||
|
# Reference data and lookup endpoints
|
||||||
|
|
||||||
|
registerUser:
|
||||||
|
handler: src/modules/user/handlers/authentication/registration.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /register
|
||||||
|
method: post
|
||||||
|
|
||||||
|
submitPersonalInfo:
|
||||||
|
handler: src/modules/user/handlers/authentication/submitPersonalInfo.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /submit-personal-info
|
||||||
|
method: post
|
||||||
|
|
||||||
|
verifyOtpForUser:
|
||||||
|
handler: src/modules/user/handlers/authentication/verifyOtpForUser.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /verify-otp
|
||||||
|
method: post
|
||||||
|
|
||||||
|
generateAccessFromRefreshToken:
|
||||||
|
handler: src/modules/user/handlers/authentication/generateRefereshToAccess.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /generate-access-from-refresh
|
||||||
|
method: post
|
||||||
|
|
||||||
|
|
||||||
|
setPasscodeForMobile:
|
||||||
|
handler: src/modules/user/handlers/authentication/setPasscodeForMobile.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /set-passcode
|
||||||
|
method: post
|
||||||
|
|
||||||
|
|
||||||
|
verifyPasscode:
|
||||||
|
handler: src/modules/user/handlers/authentication/verifyPasscode.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /verify-passcode
|
||||||
|
method: post
|
||||||
|
|
||||||
|
setUserInterest:
|
||||||
|
handler: src/modules/user/handlers/authentication/SetuserInterest.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /set-interests
|
||||||
|
method: post
|
||||||
|
|
||||||
|
setUserLocationss:
|
||||||
|
handler: src/modules/user/handlers/authentication/SetLocationofUser.handler
|
||||||
|
memorySize: 384
|
||||||
|
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: /set-location-user
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getLandingPageDetails:
|
||||||
|
handler: src/modules/user/handlers/activities/landingPageAllDetails.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/activities/**'
|
||||||
|
- ${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-landing-page-details
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getSurpriseMePageDetails:
|
||||||
|
handler: src/modules/user/handlers/activities/surpriseMePage.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/activities/**'
|
||||||
|
- ${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-surprise-me-page-details
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getActivityDetailsById:
|
||||||
|
handler: src/modules/user/handlers/activities/getByIdActivityDetails.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/activities/**'
|
||||||
|
- ${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-activity-details-by-id/{activity_xid}
|
||||||
|
method: get
|
||||||
|
|
||||||
|
checkAvailabilityDetails:
|
||||||
|
handler: src/modules/user/handlers/activities/checkAvailabilityDetails.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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/check-availability/{activity_xid}
|
||||||
|
method: get
|
||||||
|
|
||||||
|
searchActivities:
|
||||||
|
handler: src/modules/user/handlers/activities/getSpecificSearchApi.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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/specific-search
|
||||||
|
method: get
|
||||||
|
|
||||||
|
searchSchoolsAndCompanies:
|
||||||
|
handler: src/modules/user/handlers/connections/getSchoolCompanyName.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/connections/**'
|
||||||
|
- ${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: /connections/search-schools-companies
|
||||||
|
method: get
|
||||||
|
|
||||||
|
searchCities:
|
||||||
|
handler: src/modules/user/handlers/connections/searchCities.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/connections/**'
|
||||||
|
- ${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: /connections/search-cities
|
||||||
|
method: get
|
||||||
|
|
||||||
|
addSchoolCompanyDetail:
|
||||||
|
handler: src/modules/user/handlers/connections/addSchoolCompanyDetail.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/connections/**'
|
||||||
|
- ${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: /connections/add-school-company
|
||||||
|
method: post
|
||||||
|
|
||||||
|
removeConnectionDetails:
|
||||||
|
handler: src/modules/user/handlers/connections/removeConnectionDetails.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/connections/**'
|
||||||
|
- ${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: /connections/remove-connection-details
|
||||||
|
method: delete
|
||||||
|
|
||||||
|
getAllConnectionOfUser:
|
||||||
|
handler: src/modules/user/handlers/connections/getAllConnectionDetailsOfUser.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/connections/**'
|
||||||
|
- ${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: /connections/get-all-connections-details
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getActivityFromConnectionsInterest:
|
||||||
|
handler: src/modules/user/handlers/connections/getActivityFromConnectionsInterest.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/connections/**'
|
||||||
|
- ${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: /connections/get-activity-from-connections-interest
|
||||||
|
method: get
|
||||||
|
|
||||||
|
searchConnectionPeople:
|
||||||
|
handler: src/modules/user/handlers/connections/searchConnectionPeople.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/connections/**'
|
||||||
|
- ${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: /connections/search-connection-people
|
||||||
|
method: get
|
||||||
|
|
||||||
|
viewMoreActivitiesByInterest:
|
||||||
|
handler: src/modules/user/handlers/activities/viewMoreActivities.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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/view-more-activities
|
||||||
|
method: get
|
||||||
|
|
||||||
|
viewMoreActivitiesUpperSection:
|
||||||
|
handler: src/modules/user/handlers/activities/viewMoreUpperSection.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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/view-more-activities-upper-section
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getRandomActiveActivity:
|
||||||
|
handler: src/modules/user/handlers/activities/getRandomActiveActivity.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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-random-active-activity
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getNearbyActivities:
|
||||||
|
handler: src/modules/user/handlers/activities/getNearbyActivities.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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-nearby-activities
|
||||||
|
method: get
|
||||||
|
|
||||||
|
addActivityToBucketInterested:
|
||||||
|
handler: src/modules/user/handlers/activities/addToBucketInterested.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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/add-to-bucket-interested
|
||||||
|
method: post
|
||||||
|
|
||||||
|
removeActivityFromBucketInterested:
|
||||||
|
handler: src/modules/user/handlers/activities/removeFromBucketInterested.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/user/handlers/activities/**'
|
||||||
|
- ${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/remove-from-bucket-interested
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getFilteredLandingPageAllDetails:
|
||||||
|
handler: src/modules/user/handlers/activities/filteredLandingPageAllDetails.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-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
|
||||||
|
|
||||||
|
getUserItineraryDetails:
|
||||||
|
handler: src/modules/user/handlers/itinerary/getUserItineraryDetails.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: /itinerary/get-user-itinerary-details
|
||||||
|
method: get
|
||||||
|
|
||||||
|
getItineraryCheckoutDetails:
|
||||||
|
handler: src/modules/user/handlers/itinerary/getItineraryCheckoutDetails.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: /itinerary/get-itinerary-checkout-details
|
||||||
|
method: get
|
||||||
|
|
||||||
|
saveUserItinerary:
|
||||||
|
handler: src/modules/user/handlers/itinerary/saveUserItinerary.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: /itinerary/save-user-itinerary
|
||||||
|
method: post
|
||||||
|
|
||||||
|
saveItineraryActivitySelections:
|
||||||
|
handler: src/modules/user/handlers/itinerary/saveItineraryActivitySelections.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: /itinerary/save-itinerary-activity-selections
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getAllUserSavedItineraries:
|
||||||
|
handler: src/modules/user/handlers/itinerary/getAllUserSavedItineraries.handler
|
||||||
|
memorySize: 1024
|
||||||
|
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: /itinerary/get-all-user-saved-itineraries
|
||||||
|
method: get
|
||||||
|
|
||||||
|
cancelUserItinerary:
|
||||||
|
handler: src/modules/user/handlers/itinerary/cancelUserItinerary.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: /itinerary/cancel-itinerary
|
||||||
|
method: post
|
||||||
|
|
||||||
|
afterBookingFromCalendar:
|
||||||
|
handler: src/modules/user/handlers/itinerary/afterBookingFromCalendar.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: /itinerary/after-booking-from-calendar
|
||||||
|
method: post
|
||||||
|
|
||||||
|
createRazorpayOrder:
|
||||||
|
handler: src/modules/user/handlers/payment/createOrder.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: /payment/create-order
|
||||||
|
method: post
|
||||||
|
|
||||||
|
verifyRazorpayPayment:
|
||||||
|
handler: src/modules/user/handlers/payment/verifyPayment.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: /payment/verify-payment
|
||||||
|
method: post
|
||||||
|
|
||||||
|
razorpayWebhook:
|
||||||
|
handler: src/modules/user/handlers/payment/razorpayWebhook.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: /payment/webhook/razorpay
|
||||||
|
method: post
|
||||||
|
|
||||||
|
getMatchingBucketInterestedActivities:
|
||||||
|
handler: src/modules/user/handlers/itinerary/getMatchingBucketInterestedActivities.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: /itinerary/get-matching-bucket-interested-activities
|
||||||
|
method: post
|
||||||
64
serverless/functions/websocket.yml
Normal file
64
serverless/functions/websocket.yml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
websocketConnect:
|
||||||
|
handler: src/modules/websocket/handlers/connect.handler
|
||||||
|
memorySize: 256
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/websocket/**'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
|
events:
|
||||||
|
- websocket:
|
||||||
|
route: $connect
|
||||||
|
|
||||||
|
websocketDisconnect:
|
||||||
|
handler: src/modules/websocket/handlers/disconnect.handler
|
||||||
|
memorySize: 256
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/websocket/**'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
|
events:
|
||||||
|
- websocket:
|
||||||
|
route: $disconnect
|
||||||
|
|
||||||
|
websocketDefault:
|
||||||
|
handler: src/modules/websocket/handlers/default.handler
|
||||||
|
memorySize: 256
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/websocket/**'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
|
events:
|
||||||
|
- websocket:
|
||||||
|
route: $default
|
||||||
|
|
||||||
|
websocketSendMessage:
|
||||||
|
handler: src/modules/websocket/handlers/sendMessage.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/websocket/**'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
|
events:
|
||||||
|
- websocket:
|
||||||
|
route: sendMessage
|
||||||
|
|
||||||
|
websocketGetMessages:
|
||||||
|
handler: src/modules/websocket/handlers/getMessages.handler
|
||||||
|
memorySize: 384
|
||||||
|
package:
|
||||||
|
patterns:
|
||||||
|
- 'src/modules/websocket/**'
|
||||||
|
- 'src/common/**'
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||||
|
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||||
|
events:
|
||||||
|
- websocket:
|
||||||
|
route: getMessages
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
# Base packaging patterns shared across all functions
|
# Base packaging patterns shared across all functions
|
||||||
pattern1: 'src/common/**'
|
pattern1: 'src/common/**'
|
||||||
pattern2: 'common/**'
|
pattern2: 'common/**'
|
||||||
pattern3: 'node_modules/@prisma/client/**'
|
# REMOVED: Prisma is now provided by Lambda layer - DO NOT include in function packages
|
||||||
pattern4: 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
# pattern3: 'node_modules/@prisma/client/**'
|
||||||
|
# pattern4: 'node_modules/.prisma/client/libquery_engine-rhel-openssl-3.0.x.so.node'
|
||||||
|
pattern3: '!node_modules/@prisma/**'
|
||||||
|
pattern4: '!node_modules/.prisma/**'
|
||||||
pattern5: '!node_modules/.prisma/client/libquery_engine*'
|
pattern5: '!node_modules/.prisma/client/libquery_engine*'
|
||||||
@@ -1,11 +1,29 @@
|
|||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import { PrismaPg } from '@prisma/adapter-pg';
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
|
|
||||||
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
// Singleton pattern for Prisma client - prevents "Too many database connections" error
|
||||||
|
const globalForPrisma = globalThis as unknown as {
|
||||||
|
prisma: PrismaClient | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export const prisma = new PrismaClient({
|
function createPrismaClient() {
|
||||||
adapter,
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
log: process.env.NODE_ENV === 'dev' ? ['query', 'info', 'warn', 'error'] : ['error'],
|
|
||||||
});
|
return new PrismaClient({
|
||||||
|
adapter,
|
||||||
|
log: process.env.NODE_ENV === 'dev' ? ['query', 'info', 'warn', 'error'] : ['error'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const prisma = globalForPrisma.prisma ?? createPrismaClient();
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
globalForPrisma.prisma = prisma;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For serverless environments, always cache the client
|
||||||
|
if (process.env.IS_OFFLINE || process.env.AWS_LAMBDA_FUNCTION_NAME) {
|
||||||
|
globalForPrisma.prisma = prisma;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
5
src/common/database/prisma.lambda.service.ts
Normal file
5
src/common/database/prisma.lambda.service.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Re-export the singleton prisma client for Lambda handlers
|
||||||
|
// This ensures all Lambda functions use the same cached connection
|
||||||
|
import { prisma } from './prisma.client';
|
||||||
|
|
||||||
|
export const prismaClient = prisma;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Global, Module } from '@nestjs/common';
|
import { Global, Module } from '@nestjs/common';
|
||||||
import { PrismaService } from './prisma.service';
|
import { PrismaService } from './prisma.service'; // correct export location
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { Injectable, OnModuleInit, OnModuleDestroy, INestApplication } from '@nestjs/common';
|
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import { PrismaPg } from '@prisma/adapter-pg';
|
import { prisma } from './prisma.client';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||||
constructor() {
|
constructor() {
|
||||||
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
super();
|
||||||
super({
|
// Use the singleton instance
|
||||||
adapter,
|
Object.assign(this, prisma);
|
||||||
log: process.env.NODE_ENV === 'dev' ? ['query', 'info', 'warn', 'error'] : ['error'],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onModuleInit() {
|
async onModuleInit() {
|
||||||
@@ -19,10 +17,4 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul
|
|||||||
async onModuleDestroy() {
|
async onModuleDestroy() {
|
||||||
await this.$disconnect();
|
await this.$disconnect();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
async enableShutdownHooks(app: INestApplication) {
|
|
||||||
process.on('beforeExit', async () => {
|
|
||||||
await app.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
78
src/common/middlewares/jwt/authForAny.ts
Normal file
78
src/common/middlewares/jwt/authForAny.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import httpStatus from 'http-status';
|
||||||
|
import ApiError from '../../utils/helper/ApiError';
|
||||||
|
import config from '../../../config/config';
|
||||||
|
import { ROLE } from '@/common/utils/constants/common.constant';
|
||||||
|
import { prisma } from '../../database/prisma.client';
|
||||||
|
|
||||||
|
interface DecodedToken {
|
||||||
|
id?: number;
|
||||||
|
sub?: string | number;
|
||||||
|
role?: string;
|
||||||
|
iat: number;
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyAnyToken(
|
||||||
|
token: string
|
||||||
|
): Promise<{ id: number; roleXid: 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
include: { role: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: { userXid: userId },
|
||||||
|
orderBy: { id: 'desc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (latestToken?.isBlackListed === true) {
|
||||||
|
throw new ApiError(401, 'This session is expired. Please login.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.isActive === false) {
|
||||||
|
throw new ApiError(
|
||||||
|
httpStatus.FORBIDDEN,
|
||||||
|
'Your account is deactivated by admin.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.roleXid !== ROLE.USER && user.roleXid !== ROLE.HOST) {
|
||||||
|
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: user.id, roleXid: user.roleXid || 0, 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.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import httpStatus from 'http-status';
|
|
||||||
import { Request, Response, NextFunction } from 'express';
|
|
||||||
import ApiError from '../../utils/helper/ApiError';
|
|
||||||
import config from '../../../config/config';
|
|
||||||
import { ROLE } from '@/common/utils/constants/common.constant';
|
import { ROLE } from '@/common/utils/constants/common.constant';
|
||||||
|
import { NextFunction, Request, Response } from 'express';
|
||||||
|
import httpStatus from 'http-status';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import config from '../../../config/config';
|
||||||
import { prisma } from '../../database/prisma.client';
|
import { prisma } from '../../database/prisma.client';
|
||||||
|
import ApiError from '../../utils/helper/ApiError';
|
||||||
|
|
||||||
interface DecodedToken {
|
interface DecodedToken {
|
||||||
id?: number;
|
id?: number;
|
||||||
@@ -29,6 +29,68 @@ declare module 'express-serve-static-core' {
|
|||||||
* Core authentication function - verifies JWT and validates Host user
|
* Core authentication function - verifies JWT and validates Host user
|
||||||
* Can be used by both Express middleware and Lambda handlers
|
* Can be used by both Express middleware and Lambda handlers
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Verifies JWT and validates Operator user (role_xid = 5)
|
||||||
|
*/
|
||||||
|
export async function verifyOperatorToken(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 (Operator user only)
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
include: { role: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: {
|
||||||
|
userXid: userId
|
||||||
|
},
|
||||||
|
orderBy: { id: 'desc' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (latestToken?.isBlackListed == true) {
|
||||||
|
throw new ApiError(401, "This session is expired. Please login.")
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Operator role (role_xid = 5)
|
||||||
|
if (user.roleXid !== ROLE.OPERATOR) {
|
||||||
|
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: user.id, role: user.role?.roleName };
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof jwt.TokenExpiredError) {
|
||||||
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'Your session has expired. Please log in again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof ApiError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ApiError(httpStatus.FORBIDDEN, 'Invalid or expired authentication token.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function verifyHostToken(token: string): Promise<{ id: number; role?: string }> {
|
export async function verifyHostToken(token: string): Promise<{ id: number; role?: string }> {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
|
||||||
@@ -49,6 +111,17 @@ export async function verifyHostToken(token: string): Promise<{ id: number; role
|
|||||||
include: { role: true },
|
include: { role: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: {
|
||||||
|
userXid: userId
|
||||||
|
},
|
||||||
|
orderBy: { id: 'desc' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (latestToken.isBlackListed == true) {
|
||||||
|
throw new ApiError(401, "This session is expired. Please login.")
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||||
}
|
}
|
||||||
@@ -89,7 +162,7 @@ const verifyCallback = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const userInfo = await verifyHostToken(token);
|
const userInfo = await verifyHostToken(token);
|
||||||
|
|
||||||
// Attach user to request
|
// Attach user to request
|
||||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||||
|
|
||||||
@@ -104,12 +177,12 @@ const verifyCallback = async (
|
|||||||
*/
|
*/
|
||||||
const authForHost =
|
const authForHost =
|
||||||
() =>
|
() =>
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
verifyCallback(req, resolve, reject);
|
verifyCallback(req, resolve, reject);
|
||||||
})
|
})
|
||||||
.then(() => next())
|
.then(() => next())
|
||||||
.catch((err) => next(err));
|
.catch((err) => next(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default authForHost;
|
export default authForHost;
|
||||||
|
|||||||
@@ -49,6 +49,17 @@ export async function verifyMinglarAdminToken(token: string): Promise<{ id: numb
|
|||||||
include: { role: true },
|
include: { role: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: {
|
||||||
|
userXid: userId
|
||||||
|
},
|
||||||
|
orderBy: { id: 'desc' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (latestToken.isBlackListed == true) {
|
||||||
|
throw new ApiError(401, "This session is expired. Please login.")
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||||
}
|
}
|
||||||
@@ -62,7 +73,7 @@ export async function verifyMinglarAdminToken(token: string): Promise<{ id: numb
|
|||||||
if (![ROLE.MINGLAR_ADMIN, ROLE.CO_ADMIN, ROLE.ACCOUNT_MANAGER].includes(user.roleXid)) {
|
if (![ROLE.MINGLAR_ADMIN, ROLE.CO_ADMIN, ROLE.ACCOUNT_MANAGER].includes(user.roleXid)) {
|
||||||
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return { id: user.id, role: user.role?.roleName };
|
return { id: user.id, role: user.role?.roleName };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -90,7 +101,7 @@ const verifyCallback = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const userInfo = await verifyMinglarAdminToken(token);
|
const userInfo = await verifyMinglarAdminToken(token);
|
||||||
|
|
||||||
// Attach user to request
|
// Attach user to request
|
||||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||||
|
|
||||||
@@ -105,12 +116,12 @@ const verifyCallback = async (
|
|||||||
*/
|
*/
|
||||||
const authForHost =
|
const authForHost =
|
||||||
() =>
|
() =>
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
verifyCallback(req, resolve, reject);
|
verifyCallback(req, resolve, reject);
|
||||||
})
|
})
|
||||||
.then(() => next())
|
.then(() => next())
|
||||||
.catch((err) => next(err));
|
.catch((err) => next(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default authForHost;
|
export default authForHost;
|
||||||
|
|||||||
@@ -51,6 +51,17 @@ export async function verifyMinglarAdminHostToken(token: string): Promise<{ id:
|
|||||||
include: { role: true },
|
include: { role: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: {
|
||||||
|
userXid: userId
|
||||||
|
},
|
||||||
|
orderBy: { id: 'desc' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (latestToken.isBlackListed == true) {
|
||||||
|
throw new ApiError(401, "This session is expired. Please login.")
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,17 @@ export async function verifyOnlyMinglarAdminToken(token: string): Promise<{ id:
|
|||||||
include: { role: true },
|
include: { role: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: {
|
||||||
|
userXid: userId
|
||||||
|
},
|
||||||
|
orderBy: { id: 'desc' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (latestToken.isBlackListed == true) {
|
||||||
|
throw new ApiError(401, "This session is expired. Please login.")
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||||
}
|
}
|
||||||
@@ -62,7 +73,7 @@ export async function verifyOnlyMinglarAdminToken(token: string): Promise<{ id:
|
|||||||
if (user.roleXid !== ROLE.MINGLAR_ADMIN) {
|
if (user.roleXid !== ROLE.MINGLAR_ADMIN) {
|
||||||
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return { id: user.id, role: user.role?.roleName };
|
return { id: user.id, role: user.role?.roleName };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -90,7 +101,7 @@ const verifyCallback = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const userInfo = await verifyOnlyMinglarAdminToken(token);
|
const userInfo = await verifyOnlyMinglarAdminToken(token);
|
||||||
|
|
||||||
// Attach user to request
|
// Attach user to request
|
||||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||||
|
|
||||||
@@ -105,12 +116,12 @@ const verifyCallback = async (
|
|||||||
*/
|
*/
|
||||||
const authForHost =
|
const authForHost =
|
||||||
() =>
|
() =>
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
verifyCallback(req, resolve, reject);
|
verifyCallback(req, resolve, reject);
|
||||||
})
|
})
|
||||||
.then(() => next())
|
.then(() => next())
|
||||||
.catch((err) => next(err));
|
.catch((err) => next(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default authForHost;
|
export default authForHost;
|
||||||
|
|||||||
112
src/common/middlewares/jwt/authForOperator.ts
Normal file
112
src/common/middlewares/jwt/authForOperator.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import httpStatus from 'http-status';
|
||||||
|
import { NextFunction, Request, Response } from 'express';
|
||||||
|
import { prisma } from '../../database/prisma.client';
|
||||||
|
import ApiError from '../../utils/helper/ApiError';
|
||||||
|
import config from '../../../config/config';
|
||||||
|
import { ROLE } from '../../utils/constants/common.constant';
|
||||||
|
|
||||||
|
interface DecodedToken {
|
||||||
|
id?: number;
|
||||||
|
sub?: string | number;
|
||||||
|
role?: string;
|
||||||
|
iat: number;
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserPayload {
|
||||||
|
id: string;
|
||||||
|
role?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'express-serve-static-core' {
|
||||||
|
interface Request {
|
||||||
|
user?: UserPayload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyOperatorToken(
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
include: { role: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: { userXid: userId },
|
||||||
|
orderBy: { id: 'desc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (latestToken?.isBlackListed === true) {
|
||||||
|
throw new ApiError(401, 'This session is expired. Please login.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.isActive === false) {
|
||||||
|
throw new ApiError(httpStatus.FORBIDDEN, 'Your account is deactivated by admin.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.roleXid !== ROLE.OPERATOR) {
|
||||||
|
throw new ApiError(httpStatus.FORBIDDEN, 'Access denied.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: user.id, role: user.role?.roleName };
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof jwt.TokenExpiredError) {
|
||||||
|
throw new ApiError(
|
||||||
|
httpStatus.UNAUTHORIZED,
|
||||||
|
'Your session has expired. Please log in again.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof ApiError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ApiError(httpStatus.FORBIDDEN, 'Invalid or expired authentication token.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 verifyOperatorToken(token);
|
||||||
|
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
return reject(error as Error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const authForOperator =
|
||||||
|
() =>
|
||||||
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
verifyCallback(req, resolve, reject);
|
||||||
|
})
|
||||||
|
.then(() => next())
|
||||||
|
.catch((err) => next(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default authForOperator;
|
||||||
@@ -50,6 +50,17 @@ export async function verifyUserToken(token: string): Promise<{ id: number; role
|
|||||||
include: { role: true },
|
include: { role: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const latestToken = await prisma.token.findFirst({
|
||||||
|
where: {
|
||||||
|
userXid: userId
|
||||||
|
},
|
||||||
|
orderBy: { id: 'desc' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (latestToken.isBlackListed == true) {
|
||||||
|
throw new ApiError(401, "This session is expired. Please login.")
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
throw new ApiError(httpStatus.UNAUTHORIZED, 'User not found');
|
||||||
}
|
}
|
||||||
@@ -90,7 +101,7 @@ const verifyCallback = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const userInfo = await verifyUserToken(token);
|
const userInfo = await verifyUserToken(token);
|
||||||
|
|
||||||
// Attach user to request
|
// Attach user to request
|
||||||
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
req.user = { id: userInfo.id.toString(), role: userInfo.role };
|
||||||
|
|
||||||
@@ -105,12 +116,12 @@ const verifyCallback = async (
|
|||||||
*/
|
*/
|
||||||
const authForHost =
|
const authForHost =
|
||||||
() =>
|
() =>
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
verifyCallback(req, resolve, reject);
|
verifyCallback(req, resolve, reject);
|
||||||
})
|
})
|
||||||
.then(() => next())
|
.then(() => next())
|
||||||
.catch((err) => next(err));
|
.catch((err) => next(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default authForHost;
|
export default authForHost;
|
||||||
|
|||||||
150
src/common/services/chat.service.ts
Normal file
150
src/common/services/chat.service.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import ApiError from '../utils/helper/ApiError';
|
||||||
|
|
||||||
|
interface SendMessageInput {
|
||||||
|
activityXid: number;
|
||||||
|
senderXid: number;
|
||||||
|
receiverXid: number;
|
||||||
|
message: string;
|
||||||
|
status?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetMessagesInput {
|
||||||
|
activityXid: number;
|
||||||
|
userXid: number;
|
||||||
|
otherUserXid: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChatService {
|
||||||
|
constructor(private prisma: PrismaClient) {}
|
||||||
|
|
||||||
|
private async getHostUserIdForActivity(activityXid: number): Promise<number> {
|
||||||
|
const activity = await this.prisma.activities.findUnique({
|
||||||
|
where: { id: activityXid },
|
||||||
|
select: { host: { select: { userXid: true } } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!activity) {
|
||||||
|
throw new ApiError(404, 'Activity not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostUserXid = activity.host?.userXid;
|
||||||
|
|
||||||
|
if (!hostUserXid) {
|
||||||
|
throw new ApiError(400, 'Host user not found for activity');
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostUserXid;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMessage(input: SendMessageInput) {
|
||||||
|
if (!input.activityXid || isNaN(input.activityXid)) {
|
||||||
|
throw new ApiError(400, 'Valid activityXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input.senderXid || isNaN(input.senderXid)) {
|
||||||
|
throw new ApiError(400, 'Valid senderXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input.receiverXid || isNaN(input.receiverXid)) {
|
||||||
|
throw new ApiError(400, 'Valid receiverXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.senderXid === input.receiverXid) {
|
||||||
|
throw new ApiError(400, 'Sender and receiver cannot be the same');
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = input.message?.trim();
|
||||||
|
if (!message) {
|
||||||
|
throw new ApiError(400, 'Message is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostUserXid = await this.getHostUserIdForActivity(input.activityXid);
|
||||||
|
|
||||||
|
if (input.senderXid !== hostUserXid && input.receiverXid !== hostUserXid) {
|
||||||
|
throw new ApiError(
|
||||||
|
400,
|
||||||
|
'Sender or receiver must be the host for this activity'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const receiverExists = await this.prisma.user.findUnique({
|
||||||
|
where: { id: input.receiverXid },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!receiverExists) {
|
||||||
|
throw new ApiError(404, 'Receiver not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.prisma.activityMessages.create({
|
||||||
|
data: {
|
||||||
|
activityXid: input.activityXid,
|
||||||
|
senderXid: input.senderXid,
|
||||||
|
receivedXid: input.receiverXid,
|
||||||
|
message,
|
||||||
|
status: input.status?.trim() || 'unread',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMessages(input: GetMessagesInput) {
|
||||||
|
if (!input.activityXid || isNaN(input.activityXid)) {
|
||||||
|
throw new ApiError(400, 'Valid activityXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input.userXid || isNaN(input.userXid)) {
|
||||||
|
throw new ApiError(400, 'Valid userXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input.otherUserXid || isNaN(input.otherUserXid)) {
|
||||||
|
throw new ApiError(400, 'Valid otherUserXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.userXid === input.otherUserXid) {
|
||||||
|
throw new ApiError(400, 'Invalid otherUserXid');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostUserXid = await this.getHostUserIdForActivity(input.activityXid);
|
||||||
|
|
||||||
|
if (input.userXid !== hostUserXid && input.otherUserXid !== hostUserXid) {
|
||||||
|
throw new ApiError(
|
||||||
|
400,
|
||||||
|
'Conversation must include the host for this activity'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const limit = Math.min(Math.max(input.limit || 50, 1), 200);
|
||||||
|
|
||||||
|
const messages = await this.prisma.activityMessages.findMany({
|
||||||
|
where: {
|
||||||
|
activityXid: input.activityXid,
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
senderXid: input.userXid,
|
||||||
|
receivedXid: input.otherUserXid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
senderXid: input.otherUserXid,
|
||||||
|
receivedXid: input.userXid,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'asc' },
|
||||||
|
take: limit,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.prisma.activityMessages.updateMany({
|
||||||
|
where: {
|
||||||
|
activityXid: input.activityXid,
|
||||||
|
senderXid: input.otherUserXid,
|
||||||
|
receivedXid: input.userXid,
|
||||||
|
status: 'unread',
|
||||||
|
},
|
||||||
|
data: { status: 'read' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/common/services/websocket.service.ts
Normal file
58
src/common/services/websocket.service.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
export class WebSocketService {
|
||||||
|
constructor(private prisma: PrismaClient) {}
|
||||||
|
|
||||||
|
async connect(params: {
|
||||||
|
connectionId: string;
|
||||||
|
userXid: number;
|
||||||
|
activityXid?: number | null;
|
||||||
|
}) {
|
||||||
|
const { connectionId, userXid, activityXid } = params;
|
||||||
|
return this.prisma.chatConnections.upsert({
|
||||||
|
where: { connectionId },
|
||||||
|
create: {
|
||||||
|
connectionId,
|
||||||
|
userXid,
|
||||||
|
activityXid: activityXid ?? null,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
userXid,
|
||||||
|
activityXid: activityXid ?? null,
|
||||||
|
isActive: true,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect(connectionId: string) {
|
||||||
|
return this.prisma.chatConnections.updateMany({
|
||||||
|
where: { connectionId },
|
||||||
|
data: {
|
||||||
|
isActive: false,
|
||||||
|
deletedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConnectionById(connectionId: string) {
|
||||||
|
return this.prisma.chatConnections.findFirst({
|
||||||
|
where: { connectionId, isActive: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConnectionsForUser(params: {
|
||||||
|
userXid: number;
|
||||||
|
activityXid?: number | null;
|
||||||
|
}) {
|
||||||
|
const { userXid, activityXid } = params;
|
||||||
|
return this.prisma.chatConnections.findMany({
|
||||||
|
where: {
|
||||||
|
userXid,
|
||||||
|
isActive: true,
|
||||||
|
...(activityXid ? { activityXid } : {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
357
src/common/utils/constants/agreementTemplate.ts
Normal file
357
src/common/utils/constants/agreementTemplate.ts
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
export const AGREEMENT_TEMPLATE = `
|
||||||
|
MINGLAR HOST AGREEMENT
|
||||||
|
Effective Date: [EFFECTIVE_DATE]
|
||||||
|
|
||||||
|
BETWEEN
|
||||||
|
|
||||||
|
Minglar India Private Limited, a company incorporated under the Companies Act, 2013, having its registered office at 602, Aaradhya Avenue X Eve, Naidu Colony, Pant Nagar, Ghatkopar (East), Mumbai 400075 (hereinafter referred to as “Minglar India Private Limited”, which expression shall include its successors and permitted assigns);
|
||||||
|
|
||||||
|
AND
|
||||||
|
|
||||||
|
[HOST_LEGAL_NAME], a [COMPANY_TYPE], having its principal place of business at [FULL_ADDRESS] (hereinafter referred to as “Host”, which expression shall include its owners, partners, directors, employees, representatives, and permitted assigns).
|
||||||
|
|
||||||
|
Minglar India Private Limited and Host are individually referred to as a “Party” and collectively as the “Parties”.
|
||||||
|
|
||||||
|
1. PURPOSE AND RELATIONSHIP
|
||||||
|
|
||||||
|
1.1 Platform Overview
|
||||||
|
Minglar India Private Limited operates a curated digital marketplace under the brand “Minglar” that enables users (“Users”) to discover, review, and book experiential activities, events, workshops, tours, and related services (“Activities”) offered by independent Hosts.
|
||||||
|
|
||||||
|
1.2 Engagement
|
||||||
|
The Host desires to list and provide its Activities on the Minglar platform subject to the terms and standards set forth in this Agreement.
|
||||||
|
|
||||||
|
1.3 Independent Contractor
|
||||||
|
Nothing in this Agreement shall be construed as creating a partnership under the Indian Partnership Act, 1932, joint venture, agency, employment, franchise, or profit-sharing arrangement. The Host operates strictly as an independent contractor and shall have full operational control over execution of its Activities.
|
||||||
|
|
||||||
|
2. TERM
|
||||||
|
|
||||||
|
2.1 Duration
|
||||||
|
This Agreement shall remain valid for a fixed term of [DURATION_TEXT] and shall automatically expire on [EXPIRY_DATE], unless terminated earlier in accordance with Section 16.
|
||||||
|
|
||||||
|
2.2 Renewal
|
||||||
|
Renewal shall be subject to mutual agreement. Commission adjustments, if any, shall apply only at renewal due to inflation or additional expenses incurred by Minglar India Private Limited for platform upgrades or new features.
|
||||||
|
|
||||||
|
3. HOST RESPONSIBILITIES
|
||||||
|
|
||||||
|
3.1 Legal Compliance and Document Submission
|
||||||
|
3.1.1 Compliance
|
||||||
|
The Host shall obtain, maintain, and keep valid at all times all licenses, permits, approvals, registrations, certifications, insurance policies, and governmental permissions required under applicable laws for the lawful conduct of the Activities.
|
||||||
|
|
||||||
|
3.1.2 Document Upload
|
||||||
|
The Host shall, at the time of onboarding on the Minglar platform, upload true, complete, and legible copies of licenses, permits, registrations, tax certificates (including GST registration, where applicable), identity proof, business registration documents, insurance certificates, and any other documents reasonably required by Minglar India Private Limited for verification purposes.
|
||||||
|
|
||||||
|
3.1.3 Accuracy and Updates
|
||||||
|
The Host represents and warrants that all documents submitted are authentic, valid, and up to date. The Host shall promptly upload updated copies whenever any document expires, is renewed, modified, or replaced. Failure to provide valid documentation may result in suspension, delisting, or termination of this Agreement at the sole discretion of Minglar India Private Limited.
|
||||||
|
|
||||||
|
3.1.4 Verification
|
||||||
|
Minglar India Private Limited shall have the right to verify such documents and request additional documentation if required for regulatory, compliance, safety, or audit purposes.
|
||||||
|
|
||||||
|
3.2 Accurate Listing and Host Anonymity
|
||||||
|
3.2.1 Activity Details
|
||||||
|
The Host shall provide complete, accurate, and up-to-date descriptions for each Activity, including inclusions, exclusions, duration, safety requirements, pricing, and applicable tax percentages at the time of onboarding. The Host shall ensure that all information remains current throughout the term of this Agreement.
|
||||||
|
|
||||||
|
3.2.2 Host Content
|
||||||
|
Hosts retain ownership of original content they upload (such as activity descriptions, images, and videos).
|
||||||
|
However, by uploading content, the Host grants Minglar a:
|
||||||
|
- Worldwide
|
||||||
|
- Non-exclusive
|
||||||
|
- Royalty-free
|
||||||
|
- Transferable
|
||||||
|
- Sub-licensable
|
||||||
|
license to use, reproduce, modify, adapt, publish, translate, distribute and display such content for purposes of operating, marketing and promoting the platform.
|
||||||
|
This license continues for as long as the content remains on the platform.
|
||||||
|
|
||||||
|
3.2.3 Unique Activity Name
|
||||||
|
The Host shall provide a unique name for each Activity during onboarding. The Host’s actual company name, personal name, or brand shall not be visible to Users on the activity card or in any public-facing listing.
|
||||||
|
|
||||||
|
3.2.4 Prohibition on Branding and Contact
|
||||||
|
The Host shall not display, embed, or otherwise reveal its contact information, company name, logo, website, email, or other identifiable details in any photos, videos, descriptions, chat messages, or other content shared with Users via the Minglar platform.
|
||||||
|
|
||||||
|
3.2.5 Breach and Suspension
|
||||||
|
Any attempt to circumvent these provisions or display unauthorized branding or contact details shall be considered a material breach of this Agreement. Minglar India Private Limited reserves the right to suspend or delist the Activity immediately and take other remedial actions as necessary.
|
||||||
|
|
||||||
|
3.3 Taxes
|
||||||
|
3.3.1 Tax Responsibility
|
||||||
|
The total Activity price listed shall be inclusive of all applicable taxes. Minglar India Private Limited shall collect such taxes from Users and transfer them to the Host. The Host shall be solely responsible for depositing and complying with tax obligations before local authorities.
|
||||||
|
|
||||||
|
4. SAFETY AND OPERATIONAL STANDARDS
|
||||||
|
|
||||||
|
4.1 General Duty of Care
|
||||||
|
The Host shall conduct all Activities in a safe, hygienic, and controlled manner ensuring the well-being of Users at all times.
|
||||||
|
|
||||||
|
4.2 Risk Management
|
||||||
|
The Host shall conduct risk assessments, maintain standard operating procedures, provide safety briefings, and ensure trained and competent personnel supervise Activities.
|
||||||
|
|
||||||
|
4.3 Transportation
|
||||||
|
If the Host provides pick-up, drop-off, or in-Activity transportation, such transportation shall be safe, clean, reasonably comfortable, and legally compliant. The Host shall remain responsible for User safety during transport.
|
||||||
|
|
||||||
|
4.4 SOS Emergency Protocol
|
||||||
|
During execution of Activities, Minglar’s SOS feature shall be active. If activated by a User, the Host Operator shall receive immediate notification and live location details. The Host Operator shall immediately contact and reach the User. The emergency shall be cleared only after ensuring the User is safe. The Host Operator shall be the first point of contact for all emergencies.
|
||||||
|
|
||||||
|
4.5 Equipment Standards
|
||||||
|
The Host shall ensure that all equipment, tools, and materials used for conducting any Activity are:
|
||||||
|
1. Maintained in accordance with the manufacturer’s recommendations and operational guidelines.
|
||||||
|
2. Tested regularly to confirm they are safe and functional before each Activity.
|
||||||
|
3. Kept clean, hygienic, and in good working order at all times.
|
||||||
|
4. Adequate for the number of Users participating, ensuring no overuse or overcrowding that may compromise safety.
|
||||||
|
The Host shall be solely responsible for any incidents, accidents, or injuries caused due to faulty, poorly maintained, unhygienic, or unsafe equipment. Failure to comply may result in suspension, delisting, or immediate termination of this Agreement under Section 16.
|
||||||
|
|
||||||
|
5. QUALITY AND PUNCTUALITY
|
||||||
|
|
||||||
|
5.1 Quality Standards
|
||||||
|
The Host shall maintain consistently high service standards and continuously strive to improve user experience toward achieving five-star ratings.
|
||||||
|
|
||||||
|
5.2 Timeliness
|
||||||
|
The Host shall ensure timely check-in, commencement, and completion of Activities. Delays impacting Users’ onward travel or other bookings shall constitute service failure.
|
||||||
|
|
||||||
|
6. INSURANCE
|
||||||
|
|
||||||
|
The Host shall obtain, maintain, and keep valid insurance coverage appropriate for the risk associated with the respective Activities. Coverage shall be sufficient to protect Users, the Host, and Minglar India Private Limited against claims arising from accidents, injuries, fatalities, or property damage.
|
||||||
|
|
||||||
|
6.1 Risk-Based Coverage
|
||||||
|
The Host shall maintain Public Liability Insurance appropriate to the risk and scale of the respective Activities.
|
||||||
|
|
||||||
|
6.2 Coverage Amount
|
||||||
|
For high-risk or multi-participant Activities, coverage is recommended up to INR 5 Crores or an amount appropriate to cover potential claims arising from injury, death, or property damage.
|
||||||
|
|
||||||
|
6.3 Additional Insured
|
||||||
|
Policies shall name Minglar India Private Limited and Minglar Group Pte Ltd, Singapore as Additional Insured.
|
||||||
|
|
||||||
|
6.4 Proof
|
||||||
|
Valid insurance certificates must be submitted prior to onboarding and maintained throughout the Agreement term.
|
||||||
|
|
||||||
|
7. USER WAIVERS
|
||||||
|
The Host shall ensure that Users acknowledge and accept all standard waivers, terms, and risk disclosures prior to participation in any Activity, as provided by the Minglar platform.
|
||||||
|
|
||||||
|
8. INDEMNITY
|
||||||
|
The Host shall indemnify, defend, and hold harmless Minglar India Private Limited, its affiliates, employees, directors, and representatives from any claims, losses, damages, liabilities, fines, or expenses arising directly or indirectly from the Host’s failure to comply with laws, negligence, or breach of this Agreement.
|
||||||
|
|
||||||
|
9. LIMITATION OF LIABILITY
|
||||||
|
The total aggregate liability of Minglar India Private Limited arising out of or in connection with any claim relating to a specific Activity shall not exceed the total commission earned by Minglar India Private Limited from that particular Activity (or substantially similar activity category) conducted by the Host during the three (3) months immediately preceding the date of the claim.
|
||||||
|
If the Host lists multiple different Activities, the liability cap applies only to the commission earned from the specific Activity giving rise to the claim and does not include commission earned from other unrelated Activities.
|
||||||
|
Under no circumstances shall Minglar India Private Limited be liable for indirect, incidental, consequential, punitive, or special damages, including loss of profits, goodwill, reputation, or business opportunity.
|
||||||
|
|
||||||
|
10. PAYMENT AND COMMISSION
|
||||||
|
|
||||||
|
10.1 Commission
|
||||||
|
10.1.1 Standard Commission
|
||||||
|
A [COMMISSION_TEXT] commission shall be charged by Minglar India Private Limited on the Activity revenue (after deduction of applicable taxes).
|
||||||
|
|
||||||
|
10.1.2 Fixed During Term
|
||||||
|
The term of this agreement is [DURATION_TEXT]. This commission shall remain unchanged during the term of this Agreement.
|
||||||
|
|
||||||
|
10.1.3 Renewal Adjustment
|
||||||
|
At the time of renewal, Minglar India Private Limited reserves the right to adjust commission rates due to inflation or platform upgrades.
|
||||||
|
|
||||||
|
10.2 Host Payout
|
||||||
|
10.2.1 Timing
|
||||||
|
Minglar India Private Limited shall remit payments to the Host 24 hours after check-in completion or no-show.
|
||||||
|
|
||||||
|
10.2.2 Monthly Commission Invoice
|
||||||
|
Minglar India Private Limited shall issue a monthly invoice to the Host detailing the commission earned. GST shall be charged extra.
|
||||||
|
|
||||||
|
10.2.3 Banking and Gateway Charges
|
||||||
|
All banking, payment gateway, and transaction fees arising from normal bookings shall be borne by the Host.
|
||||||
|
|
||||||
|
11. BOOKING CANCELLATION
|
||||||
|
|
||||||
|
11.1 Host-Related Cancellation
|
||||||
|
If an Activity is cancelled due to host health, extreme weather, natural disasters, government restrictions, venue issues, equipment failure, team/partner unavailability, or other uncontrollable circumstances, the Host shall pay the applicable cancellation fee charged by the payment gateway. No payment will be remitted to the Host, and 100% of the booking amount including taxes shall be refunded to Users.
|
||||||
|
|
||||||
|
11.2 User-Initiated Cancellation
|
||||||
|
If Users cancel within the permitted period, the platform fee shall be deducted and the remaining amount refunded to Users. The Host will not receive payment for such cancellations.
|
||||||
|
|
||||||
|
12. PARTICIPATION OF MINGLAR ACCOUNT MANAGER FOR AUDIT
|
||||||
|
In case of low performance, low bookings, or safety/quality issues reported by Users that remain unresolved for more than one week, Minglar India Private Limited may send an Account Manager to audit the Activity at the Host’s location.
|
||||||
|
The date and slot for such an audit shall be coordinated by the Account Manager and the Host.
|
||||||
|
The participation of the Account Manager is free of charge. If the audit requires overnight stay or the Activity duration exceeds one day, the Host shall provide accommodation.
|
||||||
|
Transportation costs for the Account Manager shall be shared 50% by the Host.
|
||||||
|
Minglar India Private Limited reserves the right to send an Account Manager once per year or sooner in case of low performance, safety concerns, or user complaints with less than 2-star ratings.
|
||||||
|
|
||||||
|
13. DATA PROTECTION, PRIVACY & INFORMATION SECURITY
|
||||||
|
|
||||||
|
13.1 Compliance with Applicable Laws
|
||||||
|
The Parties acknowledge that Minglar Group Pte. Ltd. (Singapore) and/or Minglar India Private Limited (India) collects and processes personal data in compliance with applicable laws, including:
|
||||||
|
- The Personal Data Protection Act 2012 (“PDPA”); and
|
||||||
|
- The Digital Personal Data Protection Act 2023 (“DPDP Act”).
|
||||||
|
Each Party agrees to comply with all applicable privacy and data protection laws in the jurisdiction where the Activity is conducted.
|
||||||
|
|
||||||
|
13.2 Roles of the Parties
|
||||||
|
a) Minglar shall act as the Data Fiduciary / Data Controller for personal data collected via the Minglar platform.
|
||||||
|
b) The Host shall act as a Data Processor / Authorized Data Recipient when processing User personal data solely for the purpose of conducting booked Activities.
|
||||||
|
The Host shall not independently determine the purpose or means of processing User personal data without prior written authorization from Minglar.
|
||||||
|
|
||||||
|
PART A – USER PERSONAL DATA
|
||||||
|
|
||||||
|
13.3 Categories of User Data Collected
|
||||||
|
The Host acknowledges that Minglar may collect and process the following categories of User personal data through the platform:
|
||||||
|
a) Full name
|
||||||
|
b) Date of birth
|
||||||
|
c) Mobile number and email address
|
||||||
|
d) Gender
|
||||||
|
e) Height and weight (where Activity eligibility or safety restrictions apply)
|
||||||
|
f) School and college information
|
||||||
|
g) Profile photograph
|
||||||
|
h) Home location and/or real-time GPS location
|
||||||
|
i) Check-in and check-out data
|
||||||
|
j) SOS or emergency alerts
|
||||||
|
k) Payment method details and transaction information
|
||||||
|
l) Spending preferences or monthly budget indicators
|
||||||
|
m) Biometric verification data (including facial verification, where applicable)
|
||||||
|
n) Medical conditions or health disclosures (Phase 2 implementation)
|
||||||
|
o) Attendance logs and activity participation records
|
||||||
|
|
||||||
|
13.4 Sensitive Personal Data
|
||||||
|
Certain categories of data may constitute sensitive personal data, including:
|
||||||
|
- Biometric data
|
||||||
|
- Medical or health information
|
||||||
|
- Real-time location data
|
||||||
|
- Financial/payment information
|
||||||
|
- Profile photographs capable of biometric identification
|
||||||
|
Such data shall:
|
||||||
|
a) Be processed only where necessary for safety, verification, compliance, or Activity execution;
|
||||||
|
b) Be accessed strictly on a need-to-know basis;
|
||||||
|
c) Be protected using enhanced security measures;
|
||||||
|
d) Not be downloaded, stored externally, or retained by the Host beyond the Activity duration unless legally required.
|
||||||
|
The Host shall not independently collect additional medical, biometric, or financial data outside the Minglar platform without prior written approval.
|
||||||
|
|
||||||
|
13.5 Confidentiality and Use Restrictions
|
||||||
|
The Host shall treat all User and platform data as strictly confidential, including attendance logs, SOS alerts, emergency records, and location data collected during Activities.
|
||||||
|
User data shall:
|
||||||
|
a) Be used solely for Activity execution and safety;
|
||||||
|
b) Not be shared with third parties without prior written consent from Minglar India Private Limited;
|
||||||
|
c) Not be used for independent marketing, profiling, or database creation;
|
||||||
|
d) Not be retained after completion of the Activity unless legally required.
|
||||||
|
The Host shall not contact Users outside the Minglar platform unless expressly authorized.
|
||||||
|
|
||||||
|
13.6 Security Measures
|
||||||
|
The Host shall implement reasonable technical and organizational measures to protect personal data from unauthorized access, alteration, misuse, or disclosure, including:
|
||||||
|
- Secure password-protected systems;
|
||||||
|
- Restricted employee access;
|
||||||
|
- Confidentiality undertakings from employees and operators;
|
||||||
|
- Secure handling of operator devices used for check-in/check-out;
|
||||||
|
- Immediate reporting of lost or compromised devices.
|
||||||
|
|
||||||
|
13.7 Data Breach Notification
|
||||||
|
Any actual or suspected breach involving personal data — including medical, biometric, financial, or location data — shall be reported to Minglar India Private Limited immediately and in any event within twenty-four (24) hours of discovery.
|
||||||
|
The Host shall fully cooperate in investigation, mitigation, and regulatory reporting obligations.
|
||||||
|
|
||||||
|
13.8 Retention and Deletion
|
||||||
|
The Host shall not retain personal data beyond the period necessary for conducting the Activity unless required by law.
|
||||||
|
Upon termination of this Agreement, the Host shall delete all User data in its possession and confirm deletion upon request.
|
||||||
|
|
||||||
|
13.9 User Responsibility for Medical Disclosures
|
||||||
|
Users remain responsible for the accuracy and completeness of any medical or health information voluntarily disclosed.
|
||||||
|
The Host may rely on such disclosures in good faith for safety and eligibility determinations.
|
||||||
|
Minglar shall not be liable for losses arising from incomplete or inaccurate medical information provided by Users.
|
||||||
|
|
||||||
|
PART B – HOST PERSONAL DATA
|
||||||
|
|
||||||
|
13.10 Collection of Host Personal Data
|
||||||
|
The Host acknowledges that Minglar may collect and process personal data relating to the Host and its directors, partners, employees, and authorized representatives for:
|
||||||
|
- KYC verification and onboarding;
|
||||||
|
- Regulatory compliance;
|
||||||
|
- Risk assessment and fraud prevention;
|
||||||
|
- Payment processing and settlement;
|
||||||
|
- Audit and safety review;
|
||||||
|
- Enforcement of this Agreement.
|
||||||
|
|
||||||
|
13.11 Sharing and Cross-Border Transfers
|
||||||
|
Host personal data may be shared:
|
||||||
|
- Within Minglar Group entities;
|
||||||
|
- With banks, payment processors, verification agencies, insurers, auditors, and professional advisors;
|
||||||
|
- With regulatory or governmental authorities where legally required.
|
||||||
|
The Host acknowledges that personal data may be transferred between Singapore, India, and other jurisdictions where Minglar operates, subject to compliance with applicable cross-border transfer laws.
|
||||||
|
|
||||||
|
PART C – ACTIVITY LOCATION & OPERATIONAL DATA
|
||||||
|
|
||||||
|
13.12 Collection of Activity Location and Operational Data
|
||||||
|
The Host agrees that Minglar may collect and process operational data relating to Activities, including:
|
||||||
|
- Venue address;
|
||||||
|
- Check-in and check-out locations;
|
||||||
|
- GPS coordinates;
|
||||||
|
- Pick-up and drop-off locations;
|
||||||
|
- Route and logistical details;
|
||||||
|
- Activity schedules.
|
||||||
|
Such data may include precise geo-location information.
|
||||||
|
|
||||||
|
13.13 Use and Display of Location Data
|
||||||
|
The Host expressly consents to Minglar:
|
||||||
|
- Displaying Activity venue, pick-up, and drop-off details on the platform;
|
||||||
|
- Using location data for navigation, safety monitoring, and emergency coordination;
|
||||||
|
- Using such data for fraud prevention and verification.
|
||||||
|
The Host warrants that all location data provided is accurate and lawful.
|
||||||
|
Minglar shall not be liable for losses arising from inaccurate or misleading information provided by the Host.
|
||||||
|
|
||||||
|
PART D – ANALYTICS & PLATFORM OPTIMIZATION
|
||||||
|
|
||||||
|
13.14 Anonymized and Aggregated Data
|
||||||
|
Minglar may use anonymized, aggregated, or de-identified data derived from User or Host activity for:
|
||||||
|
- Platform improvement;
|
||||||
|
- Artificial intelligence systems;
|
||||||
|
- Recommendation engines;
|
||||||
|
- Safety analytics;
|
||||||
|
- Market research;
|
||||||
|
- Business strategy and investor reporting.
|
||||||
|
Such data shall not identify any individual User or Host.
|
||||||
|
All analytics models, algorithms, insights, and derived data shall remain the exclusive intellectual property of Minglar.
|
||||||
|
|
||||||
|
PART E – LIABILITY & SURVIVAL
|
||||||
|
|
||||||
|
13.15 Indemnity
|
||||||
|
The Host shall indemnify and hold harmless Minglar Group Pte. Ltd. and Minglar India Private Limited against any losses, penalties, regulatory actions, claims, damages, or costs arising from:
|
||||||
|
- Unauthorized use of personal data;
|
||||||
|
- Data breaches attributable to the Host;
|
||||||
|
- Non-compliance with applicable data protection laws;
|
||||||
|
- Inaccurate operational or location data provided by the Host.
|
||||||
|
|
||||||
|
13.16 Survival
|
||||||
|
This Section 13 shall survive termination or expiry of this Agreement.
|
||||||
|
|
||||||
|
14. CONFIDENTIALITY
|
||||||
|
The Host shall maintain strict confidentiality regarding all platform operations, processes, user data, and commercial information. These obligations survive five (5) years post-termination. Exceptions only apply if required by law.
|
||||||
|
|
||||||
|
15. NON-EXCLUSIVITY
|
||||||
|
This Agreement does not restrict the Host from offering Activities on other platforms. Minglar India Private Limited does not claim exclusivity. The Host shall not interfere with other platform operations or solicit Users to bypass Minglar.
|
||||||
|
|
||||||
|
16. TERMINATION
|
||||||
|
16.1 Immediate Termination
|
||||||
|
Minglar India Private Limited may terminate immediately in case of breach, misrepresentation, failure to maintain safety or quality standards, or violation of this Agreement.
|
||||||
|
16.2 Termination with Notice
|
||||||
|
Either Party may terminate this Agreement by providing 30 days’ written notice.
|
||||||
|
16.3 Post-Termination Obligations
|
||||||
|
Upon termination, the Host must execute all bookings already made by Users. Minglar India Private Limited will block further bookings for the Host’s Activities.
|
||||||
|
16.4 Commission Adjustment at Renewal
|
||||||
|
Any change in commission shall only occur at renewal of this Agreement. No adjustments shall be made during the active term.
|
||||||
|
16.5 Non-Exclusivity
|
||||||
|
Termination or continuation of this Agreement does not prevent the Host from offering Activities elsewhere.
|
||||||
|
|
||||||
|
17. GOVERNING LAW
|
||||||
|
This Agreement shall be governed by the laws of India. Courts at the registered office jurisdiction of Minglar India Private Limited shall have exclusive jurisdiction.
|
||||||
|
|
||||||
|
18. ENTIRE AGREEMENT
|
||||||
|
This Agreement constitutes the complete understanding between the Parties and supersedes all prior communications.
|
||||||
|
|
||||||
|
19. HOST ACKNOWLEDGMENT & ELECTRONIC CONSENT
|
||||||
|
19.1 Electronic Acceptance
|
||||||
|
By clicking the “I Agree” button, the Host acknowledges they have read, understood, and accepted the terms of this Agreement and the Minglar Privacy Policy.
|
||||||
|
19.2 Binding Agreement
|
||||||
|
Electronic acceptance constitutes a legally binding contract, equivalent to a wet signature.
|
||||||
|
19.3 Host Obligations
|
||||||
|
- Provide accurate and lawful data
|
||||||
|
- Protect User data and follow emergency protocols
|
||||||
|
- Notify Minglar of breaches or unauthorized access
|
||||||
|
- Comply with applicable laws
|
||||||
|
19.4 Electronic Records
|
||||||
|
All electronic records and communications are valid and enforceable.
|
||||||
|
19.5 Updates
|
||||||
|
Continued use after updates constitutes acceptance of amended terms.
|
||||||
|
|
||||||
|
Signed Electronically by:
|
||||||
|
Minglar India Private Limited
|
||||||
|
Authorized Signatory
|
||||||
|
|
||||||
|
Host:
|
||||||
|
[HOST_LEGAL_NAME]
|
||||||
|
Date: [ACCEPT_DATE]
|
||||||
|
`;
|
||||||
@@ -21,4 +21,9 @@ export const USER_STATUS = {
|
|||||||
ACTIVE: "Active",
|
ACTIVE: "Active",
|
||||||
DE_ACTIVATED: "De-activated",
|
DE_ACTIVATED: "De-activated",
|
||||||
REJECTED: "Rejected"
|
REJECTED: "Rejected"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RESTRICTION_NAME = {
|
||||||
|
ABOVE: "Above",
|
||||||
|
BELOW: "Below",
|
||||||
}
|
}
|
||||||
@@ -1,72 +1,111 @@
|
|||||||
export const HOST_STATUS_INTERNAL = {
|
export const HOST_STATUS_INTERNAL = {
|
||||||
HOST_SUBMITTED: "Host Submitted",
|
HOST_SUBMITTED: 'Host Submitted',
|
||||||
HOST_TO_UPDATE: "Host To Update",
|
HOST_TO_UPDATE: 'Host To Update',
|
||||||
REJECTED: "Rejected",
|
REJECTED: 'Rejected',
|
||||||
APPROVED: "Approved",
|
APPROVED: 'Approved',
|
||||||
DRAFT: "Draft",
|
DRAFT: 'Draft',
|
||||||
}
|
};
|
||||||
|
|
||||||
export const HOST_STATUS_DISPLAY = {
|
export const HOST_STATUS_DISPLAY = {
|
||||||
DRAFT: "Draft",
|
DRAFT: 'Draft',
|
||||||
UNDER_REVIEW: "Under Review",
|
UNDER_REVIEW: 'Under Review',
|
||||||
ENHANCING: "Enhancing",
|
ENHANCING: 'Enhancing',
|
||||||
REJECTED: "Rejected",
|
REJECTED: 'Rejected',
|
||||||
APPROVED: "Approved",
|
APPROVED: 'Approved',
|
||||||
}
|
};
|
||||||
|
|
||||||
export const STEPPER = {
|
export const STEPPER = {
|
||||||
NOT_SUBMITTED: 1,
|
NOT_SUBMITTED: 1,
|
||||||
UNDER_REVIEW: 2,
|
UNDER_REVIEW: 2,
|
||||||
COMPANY_DETAILS_APPROVED: 3,
|
COMPANY_DETAILS_APPROVED: 3,
|
||||||
BANK_DETAILS_UPDATED: 4,
|
BANK_DETAILS_UPDATED: 4,
|
||||||
AGREEMENT_ACCEPTED: 5,
|
AGREEMENT_ACCEPTED: 5,
|
||||||
REJECTED: 6
|
REJECTED: 6,
|
||||||
}
|
};
|
||||||
|
|
||||||
export const LAST_QUESTION_ID = {
|
|
||||||
Q_ID: 55
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ACTIVITY_INTERNAL_STATUS = {
|
export const ACTIVITY_INTERNAL_STATUS = {
|
||||||
DRAFT_PQ: 'Draft - PQ',
|
DRAFT_PQ: 'Draft - PQ',
|
||||||
APPROVED: 'Approved',
|
APPROVED: 'Approved',
|
||||||
REJECTED: 'Rejected',
|
REJECTED: 'Rejected',
|
||||||
DRAFT: 'Draft',
|
DRAFT: 'Draft',
|
||||||
UNDER_REVIEW: 'Under-Review',
|
UNDER_REVIEW: 'Under-Review',
|
||||||
PQQ_FAILED: 'PQQ Failed',
|
PQ_FAILED: 'PQ Failed',
|
||||||
PQQ_TO_UPDATE: 'PQ To Update',
|
PQ_TO_UPDATE: 'PQ To Update',
|
||||||
PQQ_SUBMITTED: 'PQ Submitted'
|
PQ_SUBMITTED: 'PQ Submitted',
|
||||||
}
|
PQ_APPROVED: 'PQ Approved',
|
||||||
|
|
||||||
|
ACTIVITY_DRAFT: 'Draft - Activity',
|
||||||
|
ACTIVITY_SUBMITTED: 'Activity Submitted',
|
||||||
|
ACTIVITY_TO_REVIEW: 'Activity To Review',
|
||||||
|
ACTIVITY_REJECTED: 'Activity Rejected',
|
||||||
|
ACTIVITY_APPROVED: 'Activity Approved',
|
||||||
|
ACTIVITY_LISTED: 'Activity Listed',
|
||||||
|
ACTIVITY_UNLISTED: 'Activity UnListed ',
|
||||||
|
ACTIVITY_NOT_LISTED: 'Activity Not Listed',
|
||||||
|
};
|
||||||
|
|
||||||
export const ACTIVITY_DISPLAY_STATUS = {
|
export const ACTIVITY_DISPLAY_STATUS = {
|
||||||
DRAFT_PQ: 'Draft - PQ',
|
DRAFT_PQ: 'Draft',
|
||||||
APPROVED: 'Approved',
|
APPROVED: 'Approved',
|
||||||
REJECTED: 'Rejected',
|
REJECTED: 'Rejected',
|
||||||
DRAFT: 'Draft',
|
DRAFT: 'Draft',
|
||||||
UNDER_REVIEW: 'Under-Review',
|
UNDER_REVIEW: 'Under-Review',
|
||||||
PQQ_FAILED: 'PQQ Failed',
|
PQ_FAILED: 'PQ Failed',
|
||||||
ENHANCING: 'Enchancing',
|
ENHANCING: 'Enhancing',
|
||||||
PQ_IN_REVIEW: 'PQ In Review'
|
PQ_IN_REVIEW: 'PQ In Review',
|
||||||
}
|
PQ_APPROVED: 'PQ Approved',
|
||||||
|
|
||||||
|
ACTIVITY_DRAFT: 'Draft',
|
||||||
|
ACTIVITY_IN_REVIEW: 'In Review',
|
||||||
|
ACTIVITY_TO_REVIEW: 'Re-submitted',
|
||||||
|
NOT_LISTED: 'Not Listed',
|
||||||
|
ACTIVITY_LISTED: 'Listed',
|
||||||
|
ACTIVITY_UNLISTED: 'Un Listed',
|
||||||
|
};
|
||||||
|
|
||||||
export const ACTIVITY_AM_INTERNAL_STATUS = {
|
export const ACTIVITY_AM_INTERNAL_STATUS = {
|
||||||
DRAFT_PQ: 'Draft - PQ',
|
DRAFT_PQ: 'Draft - PQ',
|
||||||
APPROVED: 'Approved',
|
APPROVED: 'Approved',
|
||||||
REJECTED: 'Rejected',
|
REJECTED: 'Rejected',
|
||||||
DRAFT: 'Draft',
|
DRAFT: 'Draft',
|
||||||
UNDER_REVIEW: 'Under-Review',
|
UNDER_REVIEW: 'Under-Review',
|
||||||
PQQ_FAILED: 'PQQ Failed',
|
PQ_FAILED: 'PQ Failed',
|
||||||
PQQ_REJECTED: 'PQ Rejected',
|
PQ_REJECTED: 'PQ Rejected',
|
||||||
PQQ_TO_REVIEW: 'PQ To Review'
|
PQ_TO_REVIEW: 'PQ To Review',
|
||||||
}
|
PQ_APPROVED: 'PQ Approved',
|
||||||
|
|
||||||
|
ACTIVITY_DRAFT: 'Draft - Activity',
|
||||||
|
ACTIVITY_TO_REVIEW: 'Activity To Review',
|
||||||
|
ACTIVITY_REJECTED: 'Activity Rejected',
|
||||||
|
ACTIVITY_APPROVED: 'Activity Approved',
|
||||||
|
ACTIVITY_LISTED: 'Activity Listed',
|
||||||
|
ACTIVITY_SUBMITED: 'Activity Submitted',
|
||||||
|
};
|
||||||
|
|
||||||
export const ACTIVITY_AM_DISPLAY_STATUS = {
|
export const ACTIVITY_AM_DISPLAY_STATUS = {
|
||||||
DRAFT_PQ: 'Draft - PQ',
|
DRAFT_PQ: 'Draft',
|
||||||
APPROVED: 'Approved',
|
APPROVED: 'Approved',
|
||||||
REJECTED: 'Rejected',
|
REJECTED: 'Rejected',
|
||||||
DRAFT: 'Draft',
|
DRAFT: 'Draft',
|
||||||
UNDER_REVIEW: 'Under-Review',
|
UNDER_REVIEW: 'Under-Review',
|
||||||
PQQ_FAILED: 'PQQ Failed',
|
PQ_FAILED: 'PQ Failed',
|
||||||
ENHANCING: 'Enchancing',
|
ENHANCING: 'Enhancing',
|
||||||
NEW: 'New'
|
NEW: 'New',
|
||||||
}
|
PQ_APPROVED: 'PQ Approved',
|
||||||
|
REVISED: 'Revised',
|
||||||
|
|
||||||
|
ACTIVITY_DRAFT: 'Draft',
|
||||||
|
ACTIVITY_NEW: 'New',
|
||||||
|
ACTIVITY_TO_REVIEW: 'Activity To Review',
|
||||||
|
ACTIVITY_ENHANCING: 'Enhancing',
|
||||||
|
NOT_LISTED: 'Not Listed',
|
||||||
|
ACTIVITY_LISTED: 'Listed',
|
||||||
|
ACTIVITY_REVISED: 'Activity Revised'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SCHEDULING_TYPE = {
|
||||||
|
ONCE: 'ONCE',
|
||||||
|
WEEKLY: 'WEEKLY',
|
||||||
|
MONTHLY: 'MONTHLY',
|
||||||
|
CUSTOM: 'CUSTOM',
|
||||||
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const MINGLAR_STATUS_DISPLAY = {
|
|||||||
ENHANCING: 'Enhancing',
|
ENHANCING: 'Enhancing',
|
||||||
APPROVED: 'Approved',
|
APPROVED: 'Approved',
|
||||||
REJECTED: 'Rejected',
|
REJECTED: 'Rejected',
|
||||||
|
RE_SUBMITTED: 'Re-submitted',
|
||||||
DRAFT: 'Draft'
|
DRAFT: 'Draft'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,23 +26,38 @@ export const MINGLAR_INVITATION_STATUS = {
|
|||||||
INVITED: 'Invited',
|
INVITED: 'Invited',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HOST_SUGGESTION_TITLES = {
|
export const ACTIVITY_TRACK_TYPE = {
|
||||||
COMPANY_DETAILS: 'Complete Details',
|
PQ: 'PQ',
|
||||||
COMPANY_DOCUMENTATION: 'Company documentataion',
|
ACTIVITY: 'Activity'
|
||||||
COMPANY_SOCIAL_PROOF: 'Social Proof',
|
}
|
||||||
ACTIVITY_INFORMATION: 'Activity Information',
|
|
||||||
ACTIVITY_LOCATION: 'Activity Location',
|
export const ACTIVITY_TRACK_STATUS = {
|
||||||
PICKUP_DROP_LOCATION: 'Pickup-Drop Location',
|
REJECTED_BY_AM: 'Rejected By AM',
|
||||||
NUMBER_OF_PEOPLE: 'Number of People',
|
ACCEPTED_BY_AM: 'Accepted By AM',
|
||||||
INCLUSION: 'Inclusion',
|
ENHANCING: 'Enhancing',
|
||||||
TAX_SETUP: 'Tax Setup',
|
PQ_SUBMITTED: 'PQ Submitted',
|
||||||
ENERGY_LEVEL: 'Energy Level',
|
UNDER_REVIEW:'Under Review',
|
||||||
ELIGIBILITY_CRITERIA: 'Eligibility Criteria',
|
SUBMITTED:'Activity Submitted',
|
||||||
AMENITIES: 'Amenities',
|
DRAFT:'Activity Draft'
|
||||||
EXLUSIVE_NOTES: 'Exclusive Notes',
|
}
|
||||||
CANCELLATION_POLICY: 'Cancellation Policy',
|
|
||||||
DOs_AND_DONTs: 'Do’s and Dont’s',
|
// export const HOST_SUGGESTION_TITLES = {
|
||||||
TIPS_FOR_USERS: 'Tips for Users',
|
// COMPANY_DETAILS: 'Complete Details',
|
||||||
SUSTAINABILITY: 'Sustainability',
|
// COMPANY_DOCUMENTATION: 'Company documentataion',
|
||||||
TERMS_AND_CONDITION_FOR_USER: 'Terms and Conditions for User'
|
// COMPANY_SOCIAL_PROOF: 'Social Proof',
|
||||||
};
|
// ACTIVITY_INFORMATION: 'Activity Information',
|
||||||
|
// ACTIVITY_LOCATION: 'Activity Location',
|
||||||
|
// PICKUP_DROP_LOCATION: 'Pickup-Drop Location',
|
||||||
|
// NUMBER_OF_PEOPLE: 'Number of People',
|
||||||
|
// INCLUSION: 'Inclusion',
|
||||||
|
// TAX_SETUP: 'Tax Setup',
|
||||||
|
// ENERGY_LEVEL: 'Energy Level',
|
||||||
|
// ELIGIBILITY_CRITERIA: 'Eligibility Criteria',
|
||||||
|
// AMENITIES: 'Amenities',
|
||||||
|
// EXLUSIVE_NOTES: 'Exclusive Notes',
|
||||||
|
// CANCELLATION_POLICY: 'Cancellation Policy',
|
||||||
|
// DOs_AND_DONTs: 'Do’s and Dont’s',
|
||||||
|
// TIPS_FOR_USERS: 'Tips for Users',
|
||||||
|
// SUSTAINABILITY: 'Sustainability',
|
||||||
|
// TERMS_AND_CONDITION_FOR_USER: 'Terms and Conditions for User'
|
||||||
|
// };
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ export interface OtpResult {
|
|||||||
export async function resendOtpHelper(
|
export async function resendOtpHelper(
|
||||||
prisma: any,
|
prisma: any,
|
||||||
userId: number,
|
userId: number,
|
||||||
email: string,
|
emailPurpose: string,
|
||||||
emailPurpose: "Register" | "Login" | "ForgotPassword",
|
|
||||||
otpLength: 4 | 6 = 4,
|
otpLength: 4 | 6 = 4,
|
||||||
expiryMinutes: number = 5
|
expiryMinutes: number = 5
|
||||||
): Promise<OtpResult> {
|
): Promise<OtpResult> {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export async function generateOtpHelper(
|
|||||||
prisma: any, // ⭐ Inject prisma
|
prisma: any, // ⭐ Inject prisma
|
||||||
userId: number,
|
userId: number,
|
||||||
email: string,
|
email: string,
|
||||||
emailPurpose: "Register" | "Login" | "ForgotPassword",
|
emailPurpose: string,
|
||||||
otpLength: 4 | 6 = 4,
|
otpLength: 4 | 6 = 4,
|
||||||
expiryMinutes: number = 5
|
expiryMinutes: number = 5
|
||||||
): Promise<OtpResult> {
|
): Promise<OtpResult> {
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
/**
|
|
||||||
* Host Activity Validation Schemas
|
|
||||||
* Production-ready Zod validations for host activity management
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { idSchema, optionalIdSchema, searchQuerySchema, paginationSchema } from '../validation.utils';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// CREATE ACTIVITY (FOR PQQ)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create activity schema
|
|
||||||
*/
|
|
||||||
export const createActivitySchema = z.object({
|
|
||||||
activityTypeXid: idSchema.describe('Activity type ID'),
|
|
||||||
frequenciesXid: optionalIdSchema.describe('Frequency ID'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type CreateActivityInput = z.infer<typeof createActivitySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET ACTIVITY TYPE
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all activity types query params
|
|
||||||
*/
|
|
||||||
export const getActivityTypeQuerySchema = z.object({
|
|
||||||
interestXid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Interest ID must be an integer')
|
|
||||||
.positive('Interest ID must be positive')
|
|
||||||
.optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetActivityTypeQuery = z.infer<typeof getActivityTypeQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET PQQ BY QUESTION ID
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get PQQ by question ID query params
|
|
||||||
*/
|
|
||||||
export const getPqqByQuestionIdQuerySchema = z.object({
|
|
||||||
question_xid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Question ID must be an integer')
|
|
||||||
.positive('Question ID must be positive'),
|
|
||||||
activity_xid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Activity ID must be an integer')
|
|
||||||
.positive('Activity ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetPqqByQuestionIdQuery = z.infer<typeof getPqqByQuestionIdQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// SUBMIT PQQ ANSWER
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit PQQ answer schema
|
|
||||||
*/
|
|
||||||
export const submitPqqAnswerSchema = z.object({
|
|
||||||
activityXid: idSchema.describe('Activity ID'),
|
|
||||||
questionXid: idSchema.describe('Question ID'),
|
|
||||||
answerXid: idSchema.describe('Answer ID'),
|
|
||||||
// For file uploads, these are handled separately
|
|
||||||
documentPath: z.string().max(500).optional(),
|
|
||||||
remarks: z.string().max(500, 'Remarks cannot exceed 500 characters').optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type SubmitPqqAnswerInput = z.infer<typeof submitPqqAnswerSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// UPDATE SUGGESTION AS REVIEWED
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update suggestion as reviewed schema
|
|
||||||
*/
|
|
||||||
export const updateSuggestionReviewedSchema = z.object({
|
|
||||||
activityPqqHeaderXid: idSchema.describe('Activity PQQ Header ID'),
|
|
||||||
activityPQQSuggestionId: idSchema.optional().describe('Activity PQQ Suggestion ID'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type UpdateSuggestionReviewedInput = z.infer<typeof updateSuggestionReviewedSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET ALL HOST ACTIVITY
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all host activities query params
|
|
||||||
*/
|
|
||||||
export const getAllHostActivityQuerySchema = z.object({
|
|
||||||
hostXid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Host ID must be an integer')
|
|
||||||
.positive('Host ID must be positive')
|
|
||||||
.optional(),
|
|
||||||
status: z
|
|
||||||
.enum(['pending', 'approved', 'rejected', 'draft'])
|
|
||||||
.optional(),
|
|
||||||
...paginationSchema.shape,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetAllHostActivityQuery = z.infer<typeof getAllHostActivityQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET LATEST QUESTION (activity_xid query param)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get latest PQQ question query params
|
|
||||||
*/
|
|
||||||
export const getLatestPqqQuestionQuerySchema = z.object({
|
|
||||||
activity_xid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Activity ID must be an integer')
|
|
||||||
.positive('Activity ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetLatestPqqQuestionQuery = z.infer<typeof getLatestPqqQuestionQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// SEARCH QUERY (optional)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional search query params
|
|
||||||
*/
|
|
||||||
export const optionalSearchQuerySchema = z.object({
|
|
||||||
search: searchQuerySchema,
|
|
||||||
q: searchQuerySchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type OptionalSearchQuery = z.infer<typeof optionalSearchQuerySchema>;
|
|
||||||
@@ -1,27 +1,35 @@
|
|||||||
/**
|
// validations/hostBankDetails.validation.ts
|
||||||
* Host Bank Details Validation Schema
|
import { z } from "zod";
|
||||||
* Production-ready Zod validation for payment/bank details
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { idSchema, ifscCodeSchema, accountNumberSchema } from '../validation.utils';
|
|
||||||
|
|
||||||
export const hostBankDetailsSchema = z.object({
|
export const hostBankDetailsSchema = z.object({
|
||||||
accountNumber: accountNumberSchema,
|
accountNumber: z
|
||||||
|
.string()
|
||||||
|
.nonempty("Account number is required"),
|
||||||
|
|
||||||
accountHolderName: z
|
accountHolderName: z
|
||||||
.string()
|
.string()
|
||||||
.min(2, 'Account holder name must be at least 2 characters')
|
.nonempty("Account holder name is required")
|
||||||
.max(100, 'Account holder name cannot exceed 100 characters'),
|
.min(2, { message: "Account holder name must be at least 2 characters" }),
|
||||||
|
|
||||||
ifscCode: ifscCodeSchema,
|
bankXid: z
|
||||||
|
.number()
|
||||||
|
.int({ message: "Bank ID must be an integer" })
|
||||||
|
.positive({ message: "Bank ID must be a positive number" }),
|
||||||
|
|
||||||
bankXid: idSchema.describe('Bank ID'),
|
hostXid: z
|
||||||
|
.number()
|
||||||
|
.int({ message: "Host ID must be an integer" })
|
||||||
|
.positive({ message: "Host ID must be a positive number" }),
|
||||||
|
|
||||||
hostXid: idSchema.describe('Host ID'),
|
bankBranchXid: z
|
||||||
|
.number()
|
||||||
|
.int({ message: "Bank branch ID must be an integer" })
|
||||||
|
.positive({ message: "Bank branch ID must be a positive number" }),
|
||||||
|
|
||||||
bankBranchXid: idSchema.describe('Bank branch ID'),
|
currencyXid: z
|
||||||
|
.number()
|
||||||
currencyXid: idSchema.describe('Currency ID'),
|
.int({ message: "Currency ID must be an integer" })
|
||||||
|
.positive({ message: "Currency ID must be a positive number" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type HostBankDetailsInput = z.infer<typeof hostBankDetailsSchema>;
|
export type HostBankDetailsSchema = z.infer<typeof hostBankDetailsSchema>;
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
/**
|
|
||||||
* Host Onboarding Validation Schemas
|
|
||||||
* Production-ready Zod validations for host registration and authentication
|
|
||||||
* Compatible with Zod v4
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
import {
|
|
||||||
emailSchema,
|
|
||||||
simplePasswordSchema,
|
|
||||||
otpSchema,
|
|
||||||
nameSchema,
|
|
||||||
optionalNameSchema,
|
|
||||||
mobileNumberSchema,
|
|
||||||
isdCodeSchema,
|
|
||||||
} from '../validation.utils';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// SIGNUP / REGISTRATION
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Host registration/signup schema
|
|
||||||
*/
|
|
||||||
export const hostSignUpSchema = z.object({
|
|
||||||
email: emailSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type HostSignUpInput = z.infer<typeof hostSignUpSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// OTP VERIFICATION
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OTP verification schema
|
|
||||||
*/
|
|
||||||
export const verifyOtpSchema = z.object({
|
|
||||||
otp: otpSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type VerifyOtpInput = z.infer<typeof verifyOtpSchema>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OTP verification with email schema (for verifyOTP handler)
|
|
||||||
*/
|
|
||||||
export const verifyOtpWithEmailSchema = z.object({
|
|
||||||
email: emailSchema,
|
|
||||||
otp: otpSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type VerifyOtpWithEmailInput = z.infer<typeof verifyOtpWithEmailSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// LOGIN
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Host login schema
|
|
||||||
*/
|
|
||||||
export const hostLoginSchema = z.object({
|
|
||||||
emailAddress: emailSchema,
|
|
||||||
userPassword: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Password is required'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type HostLoginInput = z.infer<typeof hostLoginSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// CREATE PASSWORD
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create password schema with confirmation matching
|
|
||||||
*/
|
|
||||||
export const createPasswordSchema = z
|
|
||||||
.object({
|
|
||||||
password: simplePasswordSchema,
|
|
||||||
confirmPassword: z.string().min(1, 'Confirm password is required'),
|
|
||||||
})
|
|
||||||
.refine((data) => data.password === data.confirmPassword, {
|
|
||||||
message: 'Password and confirm password do not match',
|
|
||||||
path: ['confirmPassword'],
|
|
||||||
});
|
|
||||||
|
|
||||||
export type CreatePasswordInput = z.infer<typeof createPasswordSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// RESEND OTP
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resend OTP schema
|
|
||||||
*/
|
|
||||||
export const resendOtpSchema = z.object({
|
|
||||||
email: emailSchema,
|
|
||||||
purpose: z
|
|
||||||
.enum(['Register', 'Login', 'ForgotPassword'])
|
|
||||||
.optional()
|
|
||||||
.default('Register'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type ResendOtpInput = z.infer<typeof resendOtpSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// UPDATE PROFILE
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Host profile update schema
|
|
||||||
*/
|
|
||||||
export const updateHostProfileSchema = z.object({
|
|
||||||
firstName: optionalNameSchema,
|
|
||||||
lastName: optionalNameSchema,
|
|
||||||
mobileNumber: mobileNumberSchema,
|
|
||||||
isdCode: isdCodeSchema,
|
|
||||||
dateOfBirth: z
|
|
||||||
.string()
|
|
||||||
.refine((val) => !val || !isNaN(Date.parse(val)), 'Invalid date format')
|
|
||||||
.optional(),
|
|
||||||
profileImage: z.string().max(500, 'Profile image path cannot exceed 500 characters').optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type UpdateHostProfileInput = z.infer<typeof updateHostProfileSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// ACCEPT AGREEMENT
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accept agreement schema (just confirmation)
|
|
||||||
*/
|
|
||||||
export const acceptAgreementSchema = z.object({
|
|
||||||
agreementAccepted: z.literal(true, {
|
|
||||||
message: 'Agreement must be accepted',
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type AcceptAgreementInput = z.infer<typeof acceptAgreementSchema>;
|
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { SCHEDULING_TYPE } from '../../constants/host.constant';
|
||||||
|
|
||||||
|
const WeekdayEnum = z.enum([
|
||||||
|
'MONDAY',
|
||||||
|
'TUESDAY',
|
||||||
|
'WEDNESDAY',
|
||||||
|
'THURSDAY',
|
||||||
|
'FRIDAY',
|
||||||
|
'SATURDAY',
|
||||||
|
'SUNDAY',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const scheduleActivity = z.object({
|
||||||
|
activityXid: z.number(),
|
||||||
|
listNow: z.boolean(),
|
||||||
|
|
||||||
|
scheduleType: z.enum([
|
||||||
|
SCHEDULING_TYPE.ONCE,
|
||||||
|
SCHEDULING_TYPE.WEEKLY,
|
||||||
|
SCHEDULING_TYPE.MONTHLY,
|
||||||
|
SCHEDULING_TYPE.CUSTOM,
|
||||||
|
]),
|
||||||
|
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.string(),
|
||||||
|
endDate: z.string().nullable().optional(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
rules: z.object({
|
||||||
|
weekdays: z.array(WeekdayEnum).optional(),
|
||||||
|
monthDates: z.array(z.number()).optional(),
|
||||||
|
customDates: z.array(z.string()).optional(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
venues: z.array(
|
||||||
|
z.object({
|
||||||
|
venueXid: z.number(),
|
||||||
|
slots: z.array(
|
||||||
|
z.object({
|
||||||
|
startTime: z.string(),
|
||||||
|
endTime: z.string(),
|
||||||
|
weekDay: WeekdayEnum.nullable().optional(),
|
||||||
|
dayOfMonth: z.number().nullable().optional(),
|
||||||
|
occurrenceDate: z.string().nullable().optional(),
|
||||||
|
maxCapacity: z.number(),
|
||||||
|
})
|
||||||
|
).optional().default([]),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
|
||||||
|
earlyCheckInMins: z.number().optional(),
|
||||||
|
bookingCutOffMins: z.number().optional(),
|
||||||
|
isLateCheckingAllowed: z.boolean().optional(),
|
||||||
|
isInstantBooking: z.boolean().optional(),
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
if (data.scheduleType === 'WEEKLY' && !data.rules.weekdays?.length) {
|
||||||
|
ctx.addIssue({ path: ['rules', 'weekdays'], message: 'Weekdays required for WEEKLY schedule', code: 'custom' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.scheduleType === 'MONTHLY' && !data.rules.monthDates?.length) {
|
||||||
|
ctx.addIssue({ path: ['rules', 'monthDates'], message: 'Month dates required for MONTHLY schedule', code: 'custom' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(data.scheduleType === 'CUSTOM' || data.scheduleType === 'ONCE') &&
|
||||||
|
!data.rules.customDates?.length
|
||||||
|
) {
|
||||||
|
ctx.addIssue({ path: ['rules', 'customDates'], message: 'Custom dates required', code: 'custom' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ScheduleActivityDTO = z.infer<typeof scheduleActivity>;
|
||||||
|
|
||||||
|
|
||||||
@@ -2,31 +2,29 @@ import { z } from "zod";
|
|||||||
|
|
||||||
export const parentCompanySchema = z.object({
|
export const parentCompanySchema = z.object({
|
||||||
companyName: z.string()
|
companyName: z.string()
|
||||||
.min(1, "Parent company name is required")
|
.max(100, "Parent company name cannot exceed 100 characters")
|
||||||
.max(100, "Parent company name cannot exceed 100 characters"),
|
.optional(),
|
||||||
|
|
||||||
address1: z.string()
|
address1: z.string()
|
||||||
.min(1, "Address1 is required")
|
.max(150, "Address1 cannot exceed 150 characters")
|
||||||
.max(150, "Address1 cannot exceed 150 characters"),
|
.optional(),
|
||||||
|
|
||||||
address2: z.string()
|
address2: z.string()
|
||||||
.max(150, "Address2 cannot exceed 150 characters")
|
.max(150, "Address2 cannot exceed 150 characters")
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|
||||||
cityXid: z.number().min(1, "City is required"),
|
cityXid: z.number().optional(),
|
||||||
stateXid: z.number().min(1, "State is required"),
|
stateXid: z.number().optional(),
|
||||||
countryXid: z.number().min(1, "Country is required"),
|
countryXid: z.number().optional(),
|
||||||
|
|
||||||
pinCode: z.string()
|
pinCode: z.string()
|
||||||
.min(4, "Pincode/Zipcode is required")
|
.max(30, "Pincode cannot exceed 30 characters")
|
||||||
.max(30, "Pincode cannot exceed 30 characters"),
|
.optional(),
|
||||||
|
|
||||||
logoPath: z.string()
|
logoPath: z.string()
|
||||||
.max(400, "Logo path cannot exceed 400 characters")
|
.max(400, "Logo path cannot exceed 400 characters")
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|
||||||
isSubsidairy: z.boolean().optional(),
|
|
||||||
|
|
||||||
registrationNumber: z.string()
|
registrationNumber: z.string()
|
||||||
.max(30, "Registration number cannot exceed 30 characters")
|
.max(30, "Registration number cannot exceed 30 characters")
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -45,15 +43,27 @@ export const parentCompanySchema = z.object({
|
|||||||
message: "Formation date must be a valid date",
|
message: "Formation date must be a valid date",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
companyType: z.string()
|
companyTypeXid: z.number()
|
||||||
.min(1, "Company type is required")
|
.optional(),
|
||||||
.max(30, "Company type cannot exceed 30 characters"),
|
|
||||||
|
firstName: z.string()
|
||||||
|
.max(50, "First name cannot exceed 50 characters")
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
lastName: z.string()
|
||||||
|
.max(50, "Last name cannot exceed 50 characters")
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
mobileNumber: z.string()
|
||||||
|
.max(15, "Mobile number cannot exceed 15 characters")
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
websiteUrl: z.string().nullable().optional(),
|
||||||
|
instagramUrl: z.string().nullable().optional(),
|
||||||
|
facebookUrl: z.string().nullable().optional(),
|
||||||
|
linkedinUrl: z.string().nullable().optional(),
|
||||||
|
twitterUrl: z.string().nullable().optional(),
|
||||||
|
|
||||||
websiteUrl: z.string().url().max(80, "Website URL cannot exceed 80 characters").optional(),
|
|
||||||
instagramUrl: z.string().url().max(80, "Instagram URL cannot exceed 80 characters").optional(),
|
|
||||||
facebookUrl: z.string().url().max(80, "Facebook URL cannot exceed 80 characters").optional(),
|
|
||||||
linkedinUrl: z.string().url().max(80, "LinkedIn URL cannot exceed 80 characters").optional(),
|
|
||||||
twitterUrl: z.string().url().max(80, "Twitter URL cannot exceed 80 characters").optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -106,15 +116,20 @@ export const hostCompanyDetailsSchema = z.object({
|
|||||||
message: "Formation date must be a valid date",
|
message: "Formation date must be a valid date",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
companyType: z.string()
|
companyTypeXid: z.number()
|
||||||
.min(1, "Company type is required")
|
.int("Company type must be a valid integer")
|
||||||
.max(30, "Company type cannot exceed 30 characters"),
|
.min(1, "Company type is required"),
|
||||||
|
|
||||||
|
|
||||||
|
referencedBy: z.string()
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
websiteUrl: z.string().nullable().optional(),
|
||||||
|
instagramUrl: z.string().nullable().optional(),
|
||||||
|
facebookUrl: z.string().nullable().optional(),
|
||||||
|
linkedinUrl: z.string().nullable().optional(),
|
||||||
|
twitterUrl: z.string().nullable().optional(),
|
||||||
|
|
||||||
websiteUrl: z.string().url().max(50, "Website URL cannot exceed 50 characters").optional(),
|
|
||||||
instagramUrl: z.string().url().max(80, "Instagram URL cannot exceed 80 characters").optional(),
|
|
||||||
facebookUrl: z.string().url().max(80, "Facebook URL cannot exceed 80 characters").optional(),
|
|
||||||
linkedinUrl: z.string().url().max(80, "LinkedIn URL cannot exceed 80 characters").optional(),
|
|
||||||
twitterUrl: z.string().url().max(80, "Twitter URL cannot exceed 80 characters").optional(),
|
|
||||||
|
|
||||||
parentCompany: parentCompanySchema.optional(),
|
parentCompany: parentCompanySchema.optional(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Host Module Validation Schemas Index
|
|
||||||
* Export all host-related validation schemas
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Authentication & Onboarding
|
|
||||||
export * from './auth.validation';
|
|
||||||
export * from './login.validation';
|
|
||||||
export * from './addPaymentDetails.validation';
|
|
||||||
export * from './hostCompanyDetails.validation';
|
|
||||||
|
|
||||||
// Activity Management
|
|
||||||
export * from './activity.validation';
|
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
/**
|
// validations/hostBankDetails.validation.ts
|
||||||
* Host Login Validation Schema
|
import { z } from "zod";
|
||||||
* Production-ready Zod validation for host login
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { emailSchema } from '../validation.utils';
|
|
||||||
|
|
||||||
export const loginForHostSchema = z.object({
|
export const loginForHostSchema = z.object({
|
||||||
emailAddress: emailSchema,
|
|
||||||
|
|
||||||
|
emailAddress : z
|
||||||
|
.string()
|
||||||
|
.nonempty("Email is required"),
|
||||||
|
|
||||||
|
userPassword : z
|
||||||
|
.string()
|
||||||
|
.nonempty("Password is required")
|
||||||
|
.min(8, { message: "Password must be at least 8 characters" }),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
userPassword: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Password is required'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type LoginForHostInput = z.infer<typeof loginForHostSchema>;
|
export type loginForHostSchema = z.infer<typeof loginForHostSchema>;
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* Validation Module Index
|
|
||||||
* Central export for all validation schemas and utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Validation Utilities
|
|
||||||
export * from './validation.utils';
|
|
||||||
|
|
||||||
// Host Module Validations
|
|
||||||
export * as hostValidation from './host';
|
|
||||||
|
|
||||||
// Minglar Admin Module Validations
|
|
||||||
export * as minglarValidation from './minglaradmin';
|
|
||||||
|
|
||||||
// Prepopulate Module Validations
|
|
||||||
export * as prepopulateValidation from './prepopulate';
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/**
|
|
||||||
* Minglar Admin Authentication Validation Schemas
|
|
||||||
* Production-ready Zod validations for admin authentication
|
|
||||||
* Compatible with Zod v4
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { emailSchema, simplePasswordSchema, otpSchema } from '../validation.utils';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// REGISTRATION
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minglar admin registration schema
|
|
||||||
*/
|
|
||||||
export const minglarRegistrationSchema = z.object({
|
|
||||||
email: emailSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type MinglarRegistrationInput = z.infer<typeof minglarRegistrationSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// LOGIN
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minglar admin login schema
|
|
||||||
*/
|
|
||||||
export const minglarLoginSchema = z.object({
|
|
||||||
emailAddress: emailSchema,
|
|
||||||
userPassword: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Password is required'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type MinglarLoginInput = z.infer<typeof minglarLoginSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// CREATE PASSWORD
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create password schema with confirmation
|
|
||||||
*/
|
|
||||||
export const minglarCreatePasswordSchema = z
|
|
||||||
.object({
|
|
||||||
password: simplePasswordSchema,
|
|
||||||
confirmPassword: z.string().min(1, 'Confirm password is required'),
|
|
||||||
})
|
|
||||||
.refine((data) => data.password === data.confirmPassword, {
|
|
||||||
message: 'Password and confirm password do not match',
|
|
||||||
path: ['confirmPassword'],
|
|
||||||
});
|
|
||||||
|
|
||||||
export type MinglarCreatePasswordInput = z.infer<typeof minglarCreatePasswordSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// VERIFY OTP
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OTP verification schema
|
|
||||||
*/
|
|
||||||
export const minglarVerifyOtpSchema = z.object({
|
|
||||||
otp: otpSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type MinglarVerifyOtpInput = z.infer<typeof minglarVerifyOtpSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// UPDATE PROFILE
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Admin profile update schema
|
|
||||||
*/
|
|
||||||
export const minglarUpdateProfileSchema = z.object({
|
|
||||||
firstName: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'First name is required')
|
|
||||||
.max(50, 'First name cannot exceed 50 characters')
|
|
||||||
.optional(),
|
|
||||||
lastName: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Last name is required')
|
|
||||||
.max(50, 'Last name cannot exceed 50 characters')
|
|
||||||
.optional(),
|
|
||||||
mobileNumber: z
|
|
||||||
.string()
|
|
||||||
.max(15, 'Mobile number cannot exceed 15 digits')
|
|
||||||
.regex(/^[0-9]*$/, 'Mobile number must contain only digits')
|
|
||||||
.optional(),
|
|
||||||
isdCode: z
|
|
||||||
.string()
|
|
||||||
.max(6, 'ISD code cannot exceed 6 characters')
|
|
||||||
.optional(),
|
|
||||||
dateOfBirth: z
|
|
||||||
.string()
|
|
||||||
.refine((val) => !val || !isNaN(Date.parse(val)), 'Invalid date format')
|
|
||||||
.optional(),
|
|
||||||
profileImage: z
|
|
||||||
.string()
|
|
||||||
.max(500, 'Profile image path cannot exceed 500 characters')
|
|
||||||
.optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type MinglarUpdateProfileInput = z.infer<typeof minglarUpdateProfileSchema>;
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
/**
|
|
||||||
* Minglar Admin Host Hub Validation Schemas
|
|
||||||
* Production-ready Zod validations for host management by admins
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { idSchema, searchQuerySchema, paginationSchema } from '../validation.utils';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET ALL HOST APPLICATIONS
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query params for getting all host applications
|
|
||||||
*/
|
|
||||||
export const getAllHostApplicationsQuerySchema = z.object({
|
|
||||||
search: searchQuerySchema,
|
|
||||||
userStatus: z
|
|
||||||
.string()
|
|
||||||
.max(20, 'Status cannot exceed 20 characters')
|
|
||||||
.optional(),
|
|
||||||
roleFilter: z
|
|
||||||
.string()
|
|
||||||
.max(30, 'Role filter cannot exceed 30 characters')
|
|
||||||
.optional(),
|
|
||||||
...paginationSchema.shape,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetAllHostApplicationsQuery = z.infer<typeof getAllHostApplicationsQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// ASSIGN AM TO HOST
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign account manager to host schema
|
|
||||||
*/
|
|
||||||
export const assignAmToHostSchema = z.object({
|
|
||||||
hostXid: idSchema.describe('Host ID'),
|
|
||||||
accountManagerXid: idSchema.describe('Account Manager ID'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type AssignAmToHostInput = z.infer<typeof assignAmToHostSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// UPDATE HOST STATUS
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update host status schema
|
|
||||||
*/
|
|
||||||
export const updateHostStatusSchema = z.object({
|
|
||||||
hostXid: idSchema.describe('Host ID'),
|
|
||||||
status: z
|
|
||||||
.enum(['approved', 'rejected', 'pending', 'resubmit'])
|
|
||||||
.describe('New host status'),
|
|
||||||
remarks: z
|
|
||||||
.string()
|
|
||||||
.max(500, 'Remarks cannot exceed 500 characters')
|
|
||||||
.optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type UpdateHostStatusInput = z.infer<typeof updateHostStatusSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET HOST BY ID
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get host by ID query params
|
|
||||||
*/
|
|
||||||
export const getHostByIdQuerySchema = z.object({
|
|
||||||
hostXid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Host ID must be an integer')
|
|
||||||
.positive('Host ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetHostByIdQuery = z.infer<typeof getHostByIdQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// ADD HOST SUGGESTION
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add suggestion to host schema
|
|
||||||
*/
|
|
||||||
export const addHostSuggestionSchema = z.object({
|
|
||||||
hostXid: idSchema.describe('Host ID'),
|
|
||||||
title: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Title is required')
|
|
||||||
.max(100, 'Title cannot exceed 100 characters'),
|
|
||||||
comments: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Comments are required')
|
|
||||||
.max(500, 'Comments cannot exceed 500 characters'),
|
|
||||||
isParent: z.boolean().optional().default(false),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type AddHostSuggestionInput = z.infer<typeof addHostSuggestionSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// AGREEMENT SETTINGS
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update agreement settings schema
|
|
||||||
*/
|
|
||||||
export const updateAgreementSettingsSchema = z.object({
|
|
||||||
hostXid: idSchema.describe('Host ID'),
|
|
||||||
agreementStartDate: z
|
|
||||||
.string()
|
|
||||||
.refine((val) => !isNaN(Date.parse(val)), 'Invalid date format'),
|
|
||||||
durationNumber: z
|
|
||||||
.number()
|
|
||||||
.int('Duration must be an integer')
|
|
||||||
.positive('Duration must be positive'),
|
|
||||||
durationFrequency: z.enum(['days', 'months', 'years']),
|
|
||||||
isCommisionBase: z.boolean(),
|
|
||||||
commisionPer: z
|
|
||||||
.number()
|
|
||||||
.min(0, 'Commission percentage must be at least 0')
|
|
||||||
.max(100, 'Commission percentage cannot exceed 100')
|
|
||||||
.optional(),
|
|
||||||
amountPerBooking: z
|
|
||||||
.number()
|
|
||||||
.int('Amount must be an integer')
|
|
||||||
.positive('Amount must be positive')
|
|
||||||
.optional(),
|
|
||||||
payoutDurationNum: z
|
|
||||||
.number()
|
|
||||||
.int('Payout duration must be an integer')
|
|
||||||
.positive('Payout duration must be positive')
|
|
||||||
.optional(),
|
|
||||||
payoutDurationFrequency: z.enum(['days', 'months', 'years']).optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type UpdateAgreementSettingsInput = z.infer<typeof updateAgreementSettingsSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// ACCEPT/REJECT HOST APPLICATION (by hostXid)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Host application action schema (accept/reject by AM or Admin)
|
|
||||||
*/
|
|
||||||
export const hostApplicationActionSchema = z.object({
|
|
||||||
hostXid: idSchema.describe('Host ID'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type HostApplicationActionInput = z.infer<typeof hostApplicationActionSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// ADD PQQ SUGGESTION
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add PQQ suggestion schema
|
|
||||||
*/
|
|
||||||
export const addPqqSuggestionSchema = z.object({
|
|
||||||
title: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Title is required')
|
|
||||||
.max(100, 'Title cannot exceed 100 characters'),
|
|
||||||
comments: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Comments are required')
|
|
||||||
.max(500, 'Comments cannot exceed 500 characters'),
|
|
||||||
activity_pqq_header_xid: idSchema.describe('Activity PQQ Header ID'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type AddPqqSuggestionInput = z.infer<typeof addPqqSuggestionSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET HOST BY ID (path param)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get host by ID from path params
|
|
||||||
*/
|
|
||||||
export const getHostByIdPathSchema = z.object({
|
|
||||||
host_xid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Host ID must be an integer')
|
|
||||||
.positive('Host ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetHostByIdPathInput = z.infer<typeof getHostByIdPathSchema>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get host by ID from path params (alternative with 'id')
|
|
||||||
*/
|
|
||||||
export const getHostByIdAltPathSchema = z.object({
|
|
||||||
id: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Host ID must be an integer')
|
|
||||||
.positive('Host ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetHostByIdAltPathInput = z.infer<typeof getHostByIdAltPathSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// REJECT PQQ BY AM
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reject PQQ by AM schema
|
|
||||||
*/
|
|
||||||
export const rejectPqqByAmSchema = z.object({
|
|
||||||
activityId: idSchema.describe('Activity ID'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type RejectPqqByAmInput = z.infer<typeof rejectPqqByAmSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// EDIT AGREEMENT DETAILS
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit agreement details schema
|
|
||||||
*/
|
|
||||||
export const editAgreementDetailsSchema = z.object({
|
|
||||||
host_xid: idSchema.describe('Host ID'),
|
|
||||||
agreementStartDate: z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Agreement start date is required')
|
|
||||||
.refine((val) => !isNaN(Date.parse(val)), 'Invalid date format'),
|
|
||||||
duration: z
|
|
||||||
.number()
|
|
||||||
.int('Duration must be an integer')
|
|
||||||
.positive('Duration must be positive'),
|
|
||||||
isCommisionBase: z.boolean(),
|
|
||||||
commisionPer: z
|
|
||||||
.number()
|
|
||||||
.min(0, 'Commission percentage must be at least 0')
|
|
||||||
.max(100, 'Commission percentage cannot exceed 100')
|
|
||||||
.optional(),
|
|
||||||
amountPerBooking: z
|
|
||||||
.number()
|
|
||||||
.int('Amount must be an integer')
|
|
||||||
.positive('Amount must be positive')
|
|
||||||
.optional(),
|
|
||||||
durationFrequency: z.string().min(1, 'Duration frequency is required'),
|
|
||||||
payoutDurationNum: z
|
|
||||||
.number()
|
|
||||||
.int('Payout duration must be an integer')
|
|
||||||
.positive('Payout duration must be positive')
|
|
||||||
.optional(),
|
|
||||||
payoutDurationFrequency: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type EditAgreementDetailsInput = z.infer<typeof editAgreementDetailsSchema>;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Minglar Admin Module Validation Schemas Index
|
|
||||||
* Export all minglar admin-related validation schemas
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Authentication
|
|
||||||
export * from './auth.validation';
|
|
||||||
|
|
||||||
// Teammate Management
|
|
||||||
export * from './teammate.validation';
|
|
||||||
|
|
||||||
// Host Hub Management
|
|
||||||
export * from './hosthub.validation';
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/**
|
|
||||||
* Minglar Admin Teammate Management Validation Schemas
|
|
||||||
* Production-ready Zod validations for teammate/invite management
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { emailSchema, idSchema, searchQuerySchema, paginationSchema } from '../validation.utils';
|
|
||||||
import { ROLE } from '../../constants/common.constant';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// INVITE TEAMMATE
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invite teammate schema
|
|
||||||
*/
|
|
||||||
export const inviteTeammateSchema = z.object({
|
|
||||||
emailAddress: emailSchema,
|
|
||||||
|
|
||||||
roleXid: z
|
|
||||||
.number()
|
|
||||||
.int('Role ID must be an integer')
|
|
||||||
.refine(
|
|
||||||
(val) => [ROLE.CO_ADMIN, ROLE.ACCOUNT_MANAGER].includes(val),
|
|
||||||
'Invalid role. Only Co-Admin and Account Manager roles can be assigned'
|
|
||||||
),
|
|
||||||
|
|
||||||
isFixedSalary: z.boolean(),
|
|
||||||
|
|
||||||
perValue: z
|
|
||||||
.number()
|
|
||||||
.positive('Per value must be greater than 0')
|
|
||||||
.optional(),
|
|
||||||
}).refine(
|
|
||||||
(data) => {
|
|
||||||
// If Account Manager and not fixed salary, perValue is required
|
|
||||||
if (data.roleXid === ROLE.ACCOUNT_MANAGER && !data.isFixedSalary) {
|
|
||||||
return data.perValue !== undefined && data.perValue > 0;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: 'Per value is required for commission-based Account Managers',
|
|
||||||
path: ['perValue'],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export type InviteTeammateInput = z.infer<typeof inviteTeammateSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET ALL COADMIN AND AM (with search)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query params for getting co-admins and account managers
|
|
||||||
*/
|
|
||||||
export const getAllCoadminAndAMQuerySchema = z.object({
|
|
||||||
search: searchQuerySchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetAllCoadminAndAMQuery = z.infer<typeof getAllCoadminAndAMQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET ALL INVITED COADMIN AND AM (with search)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query params for getting invited co-admins and account managers
|
|
||||||
*/
|
|
||||||
export const getAllInvitedCoadminAndAMQuerySchema = z.object({
|
|
||||||
search: searchQuerySchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetAllInvitedCoadminAndAMQuery = z.infer<typeof getAllInvitedCoadminAndAMQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET INVITATION DETAILS (with search)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query params for getting invitation details
|
|
||||||
*/
|
|
||||||
export const getInvitationDetailsQuerySchema = z.object({
|
|
||||||
search: searchQuerySchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetInvitationDetailsQuery = z.infer<typeof getInvitationDetailsQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// UPDATE TEAMMATE STATUS
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update teammate status schema
|
|
||||||
*/
|
|
||||||
export const updateTeammateStatusSchema = z.object({
|
|
||||||
userId: idSchema.describe('User ID'),
|
|
||||||
status: z.enum(['active', 'inactive', 'suspended']),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type UpdateTeammateStatusInput = z.infer<typeof updateTeammateStatusSchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// ASSIGN REVENUE
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign revenue to teammate schema
|
|
||||||
*/
|
|
||||||
export const assignRevenueSchema = z.object({
|
|
||||||
userId: idSchema.describe('User ID'),
|
|
||||||
isFixedSalary: z.boolean(),
|
|
||||||
perValue: z
|
|
||||||
.number()
|
|
||||||
.positive('Per value must be greater than 0'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type AssignRevenueInput = z.infer<typeof assignRevenueSchema>;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* Prepopulate Module Validation Schemas Index
|
|
||||||
* Export all prepopulate-related validation schemas
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './prepopulate.validation';
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* Prepopulate Module Validation Schemas
|
|
||||||
* Production-ready Zod validations for prepopulate endpoints
|
|
||||||
*/
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET BANK BRANCHES BY BANK ID
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get bank branches by bank ID query params
|
|
||||||
*/
|
|
||||||
export const getBankBranchesByBankIdQuerySchema = z.object({
|
|
||||||
bankXid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Bank ID must be an integer')
|
|
||||||
.positive('Bank ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetBankBranchesByBankIdQuery = z.infer<typeof getBankBranchesByBankIdQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET CITY BY STATE ID
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get city by state ID query params
|
|
||||||
*/
|
|
||||||
export const getCityByStateIdQuerySchema = z.object({
|
|
||||||
stateXid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('State ID must be an integer')
|
|
||||||
.positive('State ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetCityByStateIdQuery = z.infer<typeof getCityByStateIdQuerySchema>;
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// GET AM DETAIL BY ID (path param)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get AM detail by ID path params
|
|
||||||
*/
|
|
||||||
export const getAmDetailByIdPathSchema = z.object({
|
|
||||||
amXid: z.coerce
|
|
||||||
.number()
|
|
||||||
.int('Account Manager ID must be an integer')
|
|
||||||
.positive('Account Manager ID must be positive'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type GetAmDetailByIdPath = z.infer<typeof getAmDetailByIdPathSchema>;
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// validations/hostBankDetails.validation.ts
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const userPersonalInfoSchema = z.object({
|
||||||
|
firstName: z
|
||||||
|
.string()
|
||||||
|
.nonempty("First name is required"),
|
||||||
|
|
||||||
|
lastName: z
|
||||||
|
.string()
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
genderName: z
|
||||||
|
.string()
|
||||||
|
.nonempty("Gender is required"),
|
||||||
|
|
||||||
|
dateOfBirth: z
|
||||||
|
.string()
|
||||||
|
.nonempty("Date of birth is required")
|
||||||
|
.refine(val => !isNaN(Date.parse(val)), {
|
||||||
|
message: "Date of birth must be a valid ISO date (YYYY-MM-DD)",
|
||||||
|
}),
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export type UserPersonalInfoSchema = z.infer<typeof userPersonalInfoSchema>;
|
||||||
@@ -1,358 +0,0 @@
|
|||||||
/**
|
|
||||||
* Production-Ready Validation Utilities
|
|
||||||
* Centralized validation helpers for Zod schemas
|
|
||||||
* Compatible with Zod v4
|
|
||||||
*/
|
|
||||||
import { z, ZodError, ZodSchema } from 'zod';
|
|
||||||
import ApiError from '../helper/ApiError';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats Zod validation errors into user-friendly messages
|
|
||||||
*/
|
|
||||||
export function formatZodErrors(error: ZodError): string {
|
|
||||||
return error.issues
|
|
||||||
.map((issue) => {
|
|
||||||
const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';
|
|
||||||
return `${path}${issue.message}`;
|
|
||||||
})
|
|
||||||
.join('; ');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates data against a Zod schema and throws ApiError on failure
|
|
||||||
* @param schema - Zod schema to validate against
|
|
||||||
* @param data - Data to validate
|
|
||||||
* @param errorCode - HTTP status code for validation errors (default: 400)
|
|
||||||
* @returns Validated and typed data
|
|
||||||
*/
|
|
||||||
export function validateSchema<T>(
|
|
||||||
schema: ZodSchema<T>,
|
|
||||||
data: unknown,
|
|
||||||
errorCode: number = 400
|
|
||||||
): T {
|
|
||||||
const result = schema.safeParse(data);
|
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
const errorMessage = formatZodErrors(result.error);
|
|
||||||
throw new ApiError(errorCode, `Validation failed: ${errorMessage}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Safe validation that returns result object instead of throwing
|
|
||||||
*/
|
|
||||||
export function safeValidate<T>(
|
|
||||||
schema: ZodSchema<T>,
|
|
||||||
data: unknown
|
|
||||||
): { success: true; data: T } | { success: false; errors: string } {
|
|
||||||
const result = schema.safeParse(data);
|
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
errors: formatZodErrors(result.error),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: result.data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses JSON body from API Gateway event safely
|
|
||||||
*/
|
|
||||||
export function parseBody<T>(
|
|
||||||
body: string | null,
|
|
||||||
schema: ZodSchema<T>
|
|
||||||
): T {
|
|
||||||
if (!body) {
|
|
||||||
throw new ApiError(400, 'Request body is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsedBody: unknown;
|
|
||||||
try {
|
|
||||||
parsedBody = JSON.parse(body);
|
|
||||||
} catch {
|
|
||||||
throw new ApiError(400, 'Invalid JSON in request body');
|
|
||||||
}
|
|
||||||
|
|
||||||
return validateSchema(schema, parsedBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses query string parameters with validation
|
|
||||||
*/
|
|
||||||
export function parseQueryParams<T>(
|
|
||||||
params: Record<string, string | undefined> | null,
|
|
||||||
schema: ZodSchema<T>
|
|
||||||
): T {
|
|
||||||
return validateSchema(schema, params || {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// COMMON REUSABLE FIELD SCHEMAS (Zod v4 compatible)
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Email validation with proper format
|
|
||||||
*/
|
|
||||||
export const emailSchema = z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Email is required')
|
|
||||||
.email('Invalid email format')
|
|
||||||
.max(150, 'Email cannot exceed 150 characters')
|
|
||||||
.transform((val) => val.toLowerCase().trim());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Password validation with security requirements
|
|
||||||
*/
|
|
||||||
export const passwordSchema = z
|
|
||||||
.string()
|
|
||||||
.min(8, 'Password must be at least 8 characters')
|
|
||||||
.max(255, 'Password cannot exceed 255 characters')
|
|
||||||
.regex(
|
|
||||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/,
|
|
||||||
'Password must contain at least one uppercase letter, one lowercase letter, one number and one special character'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple password (for cases where strong password isn't required)
|
|
||||||
*/
|
|
||||||
export const simplePasswordSchema = z
|
|
||||||
.string()
|
|
||||||
.min(8, 'Password must be at least 8 characters')
|
|
||||||
.max(255, 'Password cannot exceed 255 characters');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mobile number validation (international format)
|
|
||||||
*/
|
|
||||||
export const mobileNumberSchema = z
|
|
||||||
.string()
|
|
||||||
.min(7, 'Mobile number must be at least 7 digits')
|
|
||||||
.max(15, 'Mobile number cannot exceed 15 digits')
|
|
||||||
.regex(/^[0-9]+$/, 'Mobile number must contain only digits')
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ISD Code validation
|
|
||||||
*/
|
|
||||||
export const isdCodeSchema = z
|
|
||||||
.string()
|
|
||||||
.max(6, 'ISD code cannot exceed 6 characters')
|
|
||||||
.regex(/^\+?[0-9]+$/, 'Invalid ISD code format')
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name validation (first name, last name, etc.)
|
|
||||||
*/
|
|
||||||
export const nameSchema = z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Name is required')
|
|
||||||
.max(50, 'Name cannot exceed 50 characters')
|
|
||||||
.regex(/^[a-zA-Z\s'-]+$/, 'Name can only contain letters, spaces, hyphens and apostrophes');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional name schema
|
|
||||||
*/
|
|
||||||
export const optionalNameSchema = nameSchema.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Positive integer ID validation
|
|
||||||
*/
|
|
||||||
export const idSchema = z
|
|
||||||
.number()
|
|
||||||
.int('ID must be an integer')
|
|
||||||
.positive('ID must be a positive number');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional positive integer ID
|
|
||||||
*/
|
|
||||||
export const optionalIdSchema = z
|
|
||||||
.number()
|
|
||||||
.int('ID must be an integer')
|
|
||||||
.positive('ID must be a positive number')
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Coerce string to number for query params
|
|
||||||
*/
|
|
||||||
export const numericStringSchema = z
|
|
||||||
.string()
|
|
||||||
.transform((val) => parseInt(val, 10))
|
|
||||||
.pipe(z.number().int().positive());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional numeric string
|
|
||||||
*/
|
|
||||||
export const optionalNumericStringSchema = z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.transform((val) => (val ? parseInt(val, 10) : undefined))
|
|
||||||
.pipe(z.number().int().positive().optional());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address line validation
|
|
||||||
*/
|
|
||||||
export const addressLine1Schema = z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Address is required')
|
|
||||||
.max(150, 'Address cannot exceed 150 characters');
|
|
||||||
|
|
||||||
export const addressLine2Schema = z
|
|
||||||
.string()
|
|
||||||
.max(150, 'Address cannot exceed 150 characters')
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pin/Zip code validation
|
|
||||||
*/
|
|
||||||
export const pinCodeSchema = z
|
|
||||||
.string()
|
|
||||||
.min(4, 'Pin code must be at least 4 characters')
|
|
||||||
.max(30, 'Pin code cannot exceed 30 characters');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL validation
|
|
||||||
*/
|
|
||||||
export const urlSchema = z
|
|
||||||
.string()
|
|
||||||
.url('Invalid URL format')
|
|
||||||
.max(500, 'URL cannot exceed 500 characters')
|
|
||||||
.optional()
|
|
||||||
.or(z.literal(''));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Social media URL (with empty string handling)
|
|
||||||
*/
|
|
||||||
export const socialUrlSchema = z
|
|
||||||
.string()
|
|
||||||
.max(80, 'URL cannot exceed 80 characters')
|
|
||||||
.refine(
|
|
||||||
(val) => !val || val === '' || z.string().url().safeParse(val).success,
|
|
||||||
'Invalid URL format'
|
|
||||||
)
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Date string validation
|
|
||||||
*/
|
|
||||||
export const dateStringSchema = z
|
|
||||||
.string()
|
|
||||||
.refine((val) => !isNaN(Date.parse(val)), 'Invalid date format')
|
|
||||||
.transform((val) => new Date(val));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional date string
|
|
||||||
*/
|
|
||||||
export const optionalDateStringSchema = z
|
|
||||||
.string()
|
|
||||||
.refine((val) => !val || !isNaN(Date.parse(val)), 'Invalid date format')
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boolean schema with string coercion (for query params)
|
|
||||||
*/
|
|
||||||
export const booleanStringSchema = z
|
|
||||||
.string()
|
|
||||||
.transform((val) => val === 'true' || val === '1')
|
|
||||||
.pipe(z.boolean());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OTP validation (6 digits)
|
|
||||||
*/
|
|
||||||
export const otpSchema = z
|
|
||||||
.string()
|
|
||||||
.length(6, 'OTP must be exactly 6 digits')
|
|
||||||
.regex(/^[0-9]+$/, 'OTP must contain only digits');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search query validation
|
|
||||||
*/
|
|
||||||
export const searchQuerySchema = z
|
|
||||||
.string()
|
|
||||||
.max(100, 'Search query cannot exceed 100 characters')
|
|
||||||
.transform((val) => val.trim())
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pagination parameters
|
|
||||||
*/
|
|
||||||
export const paginationSchema = z.object({
|
|
||||||
page: z.coerce.number().int().positive().default(1),
|
|
||||||
limit: z.coerce.number().int().positive().max(100).default(10),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* File path validation for S3
|
|
||||||
*/
|
|
||||||
export const filePathSchema = z
|
|
||||||
.string()
|
|
||||||
.max(500, 'File path cannot exceed 500 characters')
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registration/Reference number validation
|
|
||||||
*/
|
|
||||||
export const registrationNumberSchema = z
|
|
||||||
.string()
|
|
||||||
.max(30, 'Registration number cannot exceed 30 characters')
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PAN number validation (India)
|
|
||||||
*/
|
|
||||||
export const panNumberSchema = z
|
|
||||||
.string()
|
|
||||||
.max(30, 'PAN number cannot exceed 30 characters')
|
|
||||||
.regex(/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/, 'Invalid PAN number format')
|
|
||||||
.optional()
|
|
||||||
.or(z.literal(''));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GST number validation (India)
|
|
||||||
*/
|
|
||||||
export const gstNumberSchema = z
|
|
||||||
.string()
|
|
||||||
.max(30, 'GST number cannot exceed 30 characters')
|
|
||||||
.regex(
|
|
||||||
/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/,
|
|
||||||
'Invalid GST number format'
|
|
||||||
)
|
|
||||||
.optional()
|
|
||||||
.or(z.literal(''));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IFSC code validation (India)
|
|
||||||
*/
|
|
||||||
export const ifscCodeSchema = z
|
|
||||||
.string()
|
|
||||||
.min(1, 'IFSC code is required')
|
|
||||||
.regex(/^[A-Z]{4}0[A-Z0-9]{6}$/, 'Invalid IFSC code format');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bank account number validation
|
|
||||||
*/
|
|
||||||
export const accountNumberSchema = z
|
|
||||||
.string()
|
|
||||||
.min(9, 'Account number must be at least 9 digits')
|
|
||||||
.max(18, 'Account number cannot exceed 18 digits')
|
|
||||||
.regex(/^[0-9]+$/, 'Account number must contain only digits');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Company name validation
|
|
||||||
*/
|
|
||||||
export const companyNameSchema = z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Company name is required')
|
|
||||||
.max(100, 'Company name cannot exceed 100 characters');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Token validation
|
|
||||||
*/
|
|
||||||
export const tokenSchema = z
|
|
||||||
.string()
|
|
||||||
.min(1, 'Authentication token is required');
|
|
||||||
@@ -81,6 +81,14 @@ const envVarsSchema = yup
|
|||||||
DB_PORT: yup.number().default(3306).required('DB Port is required'),
|
DB_PORT: yup.number().default(3306).required('DB Port is required'),
|
||||||
//OTP Bypass
|
//OTP Bypass
|
||||||
BYPASS_OTP: yup.boolean().default(false).required('Bypass OTP is required'),
|
BYPASS_OTP: yup.boolean().default(false).required('Bypass OTP is required'),
|
||||||
|
// Email links
|
||||||
|
AM_INVITATION_LINK: yup.string().required('Link to send in AM invitation mail is required'),
|
||||||
|
AM_INTERFACE_LINK:yup.string().required('Link to am interface is required'),
|
||||||
|
HOST_LINK: yup.string().required('Link to host panel is required'),
|
||||||
|
HOST_LINK_PQ: yup.string().required('Link to host panel pqp is required'),
|
||||||
|
RAZORPAY_KEY_SECRET: yup.string().required('Razorpay key secret is required'),
|
||||||
|
RAZORPAY_KEY_ID: yup.string().required('Razorpay key id is required'),
|
||||||
|
RAZORPAY_WEBHOOK_SECRET: yup.string().required('Razorpay webhook secret is required'),
|
||||||
})
|
})
|
||||||
.noUnknown(true);
|
.noUnknown(true);
|
||||||
|
|
||||||
@@ -158,6 +166,14 @@ function getConfig() {
|
|||||||
//Minglar admin
|
//Minglar admin
|
||||||
MinglarAdminEmail: envVars.MINGLAR_ADMIN_EMAIL,
|
MinglarAdminEmail: envVars.MINGLAR_ADMIN_EMAIL,
|
||||||
MinglarAdminName: envVars.MINGLAR_ADMIN_NAME,
|
MinglarAdminName: envVars.MINGLAR_ADMIN_NAME,
|
||||||
|
AM_INVITATION_LINK: envVars.AM_INVITATION_LINK,
|
||||||
|
HOST_LINK: envVars.HOST_LINK,
|
||||||
|
HOST_LINK_PQ: envVars.HOST_LINK_PQ,
|
||||||
|
RAZORPAY_KEY_ID: envVars.RAZORPAY_KEY_ID,
|
||||||
|
RAZORPAY_KEY_SECRET: envVars.RAZORPAY_KEY_SECRET,
|
||||||
|
RAZORPAY_WEBHOOK_SECRET: envVars.RAZORPAY_WEBHOOK_SECRET,
|
||||||
|
AM_INTERFACE_LINK: envVars.AM_INTERFACE_LINK,
|
||||||
|
|
||||||
// oneSignal: {
|
// oneSignal: {
|
||||||
// appID: envVars.ONESIGNAL_APPID,
|
// appID: envVars.ONESIGNAL_APPID,
|
||||||
// restApiKey: envVars.ONESIGNAL_REST_APIKEY,
|
// restApiKey: envVars.ONESIGNAL_REST_APIKEY,
|
||||||
|
|||||||
176
src/modules/host/dto/createActivity.schema.ts
Normal file
176
src/modules/host/dto/createActivity.schema.ts
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/* ================= MEDIA ================= */
|
||||||
|
export const MediaDto = z.object({
|
||||||
|
mediaType: z.string().optional(),
|
||||||
|
mediaFileName: z.string(),
|
||||||
|
isCoverImage: z.boolean().optional().default(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= PRICE ================= */
|
||||||
|
export const PriceDto = z.object({
|
||||||
|
noOfSession: z.number().int().optional().default(1),
|
||||||
|
isPackage: z.boolean().optional().default(false),
|
||||||
|
sessionValidity: z.number().int().optional().default(0),
|
||||||
|
sessionValidityFrequency: z.string().optional().default('Days'),
|
||||||
|
basePrice: z.number().int().optional().default(0),
|
||||||
|
sellPrice: z.number().int(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= VENUE ================= */
|
||||||
|
export const VenueDto = z.object({
|
||||||
|
venueName: z.string(),
|
||||||
|
venueLabel: z.string(),
|
||||||
|
venueCapacity: z.number().int().optional().default(0),
|
||||||
|
availableSeats: z.number().int().optional().default(0),
|
||||||
|
isMinPeopleReqMandatory: z.boolean().optional().default(false),
|
||||||
|
minPeopleRequired: z.number().int().nullable().optional(),
|
||||||
|
minReqfullfilledBeforeMins: z.number().int().nullable().optional(),
|
||||||
|
venueDescription: z.string().optional(),
|
||||||
|
media: z.array(MediaDto).optional().default([]),
|
||||||
|
prices: z.array(PriceDto).optional().default([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= PICKUP / DROP ================= */
|
||||||
|
export const PickupDetailDto = z.object({
|
||||||
|
isPickUp: z.boolean().optional().default(false),
|
||||||
|
locationLat: z.number().nullable().optional(),
|
||||||
|
locationLong: z.number().nullable().optional(),
|
||||||
|
locationAddress: z.string().nullable().optional(),
|
||||||
|
transportTotalPrice: z.number().int().min(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PickupTransportDto = z.object({
|
||||||
|
transportModeXid: z.number().int(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= EQUIPMENT ================= */
|
||||||
|
export const EquipmentDto = z.object({
|
||||||
|
equipmentName: z.string(),
|
||||||
|
isEquipmentChargeable: z.boolean().optional(),
|
||||||
|
equipmentBasePrice: z.number().int().optional().default(0),
|
||||||
|
equipmentTotalPrice: z.number().int().optional().default(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= NAVIGATION MODE ================= */
|
||||||
|
export const NavigationModeDto = z.object({
|
||||||
|
navigationModeName: z.string().optional(),
|
||||||
|
isChargeable: z.boolean().optional(),
|
||||||
|
totalPrice: z.number().int().optional().default(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= ELIGIBILITY ================= */
|
||||||
|
export const EligibilityDto = z.object({
|
||||||
|
isAgeRestriction: z.boolean().optional().default(false),
|
||||||
|
ageRestrictionName: z.string().nullable().optional(),
|
||||||
|
ageEntered: z.number().int().nullable().optional(),
|
||||||
|
ageIn: z.string().nullable().optional(),
|
||||||
|
minAge: z.number().int().nullable().optional(),
|
||||||
|
maxAge: z.number().int().nullable().optional(),
|
||||||
|
|
||||||
|
isWeightRestriction: z.boolean().optional().default(false),
|
||||||
|
weightRestrictionName: z.string().nullable().optional(),
|
||||||
|
weightEntered: z.number().int().nullable().optional(),
|
||||||
|
weightIn: z.string().nullable().optional(),
|
||||||
|
minWeight: z.number().int().nullable().optional(),
|
||||||
|
maxWeight: z.number().int().nullable().optional(),
|
||||||
|
|
||||||
|
isHeightRestriction: z.boolean().optional().default(false),
|
||||||
|
heightRestrictionName: z.string().nullable().optional(),
|
||||||
|
heightEntered: z.number().int().nullable().optional(),
|
||||||
|
heightIn: z.string().nullable().optional(),
|
||||||
|
minHeight: z.number().int().nullable().optional(),
|
||||||
|
maxHeight: z.number().int().nullable().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= OTHER DETAILS ================= */
|
||||||
|
export const OtherDetailsDto = z.object({
|
||||||
|
exclusiveNotes: z.string().optional(),
|
||||||
|
safetyInstruction: z.string().optional(),
|
||||||
|
dosNotes: z.string().optional(),
|
||||||
|
dontsNotes: z.string().optional(),
|
||||||
|
tipsNotes: z.string().optional(),
|
||||||
|
termsAndCondition: z.string().optional(),
|
||||||
|
cancellations: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ================= CREATE ACTIVITY ================= */
|
||||||
|
export const CreateActivityDto = z.object({
|
||||||
|
activityXid: z.number().int(),
|
||||||
|
|
||||||
|
activityTypeXid: z.number().int().optional(),
|
||||||
|
frequenciesXid: z.number().int().nullable().optional(),
|
||||||
|
activityTitle: z.string().optional(),
|
||||||
|
activityDescription: z.string().optional(),
|
||||||
|
|
||||||
|
checkInLat: z.number().nullable().optional(),
|
||||||
|
checkInLong: z.number().nullable().optional(),
|
||||||
|
checkInAddress: z.string().nullable().optional(),
|
||||||
|
isCheckOutSame: z.boolean().optional().default(true),
|
||||||
|
checkOutLat: z.number().nullable().optional(),
|
||||||
|
checkOutLong: z.number().nullable().optional(),
|
||||||
|
checkOutAddress: z.string().nullable().optional(),
|
||||||
|
checkInStateName: z.string().nullable().optional(),
|
||||||
|
checkInCityName: z.string().nullable().optional(),
|
||||||
|
checkInCountryName: z.string().nullable().optional(),
|
||||||
|
checkOutStateName: z.string().nullable().optional(),
|
||||||
|
checkOutCityName: z.string().nullable().optional(),
|
||||||
|
checkOutCountryName: z.string().nullable().optional(),
|
||||||
|
|
||||||
|
energyLevelXid: z.number().int().nullable().optional(),
|
||||||
|
durationDays: z.number().int().optional(),
|
||||||
|
durationHours: z.number().int().optional(),
|
||||||
|
durationMins: z.number().int().optional(),
|
||||||
|
|
||||||
|
foodAvailable: z.boolean().nullable().optional(),
|
||||||
|
foodIsChargeable: z.boolean().optional().default(false),
|
||||||
|
alcoholAvailable: z.boolean().nullable().optional(),
|
||||||
|
|
||||||
|
trainerAvailable: z.boolean().nullable().optional(),
|
||||||
|
trainerIsChargeable: z.boolean().optional().default(false),
|
||||||
|
|
||||||
|
pickUpDropAvailable: z.boolean().nullable().optional(),
|
||||||
|
pickUpDropIsChargeable: z.boolean().optional().default(false),
|
||||||
|
|
||||||
|
inActivityAvailable: z.boolean().nullable().optional(),
|
||||||
|
inActivityIsChargeable: z.boolean().optional().default(false),
|
||||||
|
|
||||||
|
equipmentAvailable: z.boolean().nullable().optional(),
|
||||||
|
equipmentIsChargeable: z.boolean().optional().default(false),
|
||||||
|
|
||||||
|
cancellationAvailable: z.boolean().nullable().optional(),
|
||||||
|
cancellationAllowedBeforeMins: z.number().int().nullable().optional(),
|
||||||
|
|
||||||
|
currencyXid: z.number().int().nullable().optional(),
|
||||||
|
sustainabilityScore: z.number().int().nullable().optional(),
|
||||||
|
safetyScore: z.number().int().nullable().optional(),
|
||||||
|
isInstantBooking: z.boolean().optional().default(false),
|
||||||
|
|
||||||
|
taxXids: z.array(z.number().int()).optional().default([]),
|
||||||
|
|
||||||
|
media: z.array(MediaDto).optional().default([]),
|
||||||
|
venues: z.array(VenueDto).optional().default([]),
|
||||||
|
|
||||||
|
foodTypeIds: z.array(z.number().int()).optional().default([]),
|
||||||
|
cuisineIds: z.array(z.number().int()).optional().default([]),
|
||||||
|
pickupTransports: z.array(PickupTransportDto).optional().default([]),
|
||||||
|
pickupDetails: z.array(PickupDetailDto).optional().default([]),
|
||||||
|
|
||||||
|
navigationModes: z
|
||||||
|
.array(NavigationModeDto)
|
||||||
|
.optional()
|
||||||
|
.default([]),
|
||||||
|
|
||||||
|
equipments: z.array(EquipmentDto).optional().default([]),
|
||||||
|
amenitiesIds: z.array(z.number().int()).optional().default([]),
|
||||||
|
|
||||||
|
foodTotalAmount: z.number().int().optional().default(0),
|
||||||
|
|
||||||
|
eligibility: EligibilityDto.optional(),
|
||||||
|
otherDetails: OtherDetailsDto.optional(),
|
||||||
|
|
||||||
|
allowedEntryTypes: z.array(z.number().int()).optional().default([]),
|
||||||
|
trainerTotalAmount: z.number().int().optional().default(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreateActivityInput = z.infer<typeof CreateActivityDto>;
|
||||||
39
src/modules/host/dto/createSchedulingOfAct.dto.ts
Normal file
39
src/modules/host/dto/createSchedulingOfAct.dto.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
export interface ScheduleSlotDTO {
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
weekDay?: 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY' | null;
|
||||||
|
dayOfMonth?: number | null; // 1–31
|
||||||
|
occurrenceDate?: string | null;
|
||||||
|
maxCapacity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScheduleVenueDTO {
|
||||||
|
venueXid: number;
|
||||||
|
slots: ScheduleSlotDTO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// export interface ScheduleActivityDTO {
|
||||||
|
// activityXid: number;
|
||||||
|
// scheduleType: 'ONCE' | 'WEEKLY' | 'MONTHLY' | 'CUSTOM';
|
||||||
|
|
||||||
|
// dateRange: {
|
||||||
|
// startDate: string;
|
||||||
|
// endDate?: string | null;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// rules: {
|
||||||
|
// weekdays?: (
|
||||||
|
// 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' |
|
||||||
|
// 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY'
|
||||||
|
// )[];
|
||||||
|
// monthDates?: number[];
|
||||||
|
// customDates?: string[];
|
||||||
|
// };
|
||||||
|
|
||||||
|
// venues: ScheduleVenueDTO[];
|
||||||
|
|
||||||
|
// earlyCheckInMins?: number;
|
||||||
|
// bookingCutOffMins?: number;
|
||||||
|
// isLateCheckingAllowed?: boolean;
|
||||||
|
// isInstantBooking?: boolean;
|
||||||
|
// }
|
||||||
@@ -90,5 +90,6 @@ export class AddPaymentDetailsDTO {
|
|||||||
this.accountHolderName = accountHolderName;
|
this.accountHolderName = accountHolderName;
|
||||||
this.ifscCode = ifscCode;
|
this.ifscCode = ifscCode;
|
||||||
this.hostXid = hostXid;
|
this.hostXid = hostXid;
|
||||||
|
this.currencyXid = currencyXid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
113
src/modules/host/dto/operator.activity.dto.ts
Normal file
113
src/modules/host/dto/operator.activity.dto.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
export class GetActivitiesByDateRequestDTO {
|
||||||
|
activityDate?: string; // ISO date format: YYYY-MM-DD (optional, defaults to today)
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetReservationByCheckInCodeRequestDTO {
|
||||||
|
checkInCode!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationVerificationOtpRequestDTO {
|
||||||
|
checkInCode!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationVerifyOtpRequestDTO {
|
||||||
|
checkInCode!: string;
|
||||||
|
otp!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DateBreakdownDTO {
|
||||||
|
date: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActivitySummaryDTO {
|
||||||
|
activityName: string;
|
||||||
|
activityImage: string | null;
|
||||||
|
activityImagePreSignedUrl: string | null;
|
||||||
|
count: number;
|
||||||
|
dateBreakdown: DateBreakdownDTO[]; // Booking count for each scheduled date
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetActivitiesByDateResponseDTO {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data: {
|
||||||
|
date: string;
|
||||||
|
activities: ActivitySummaryDTO[];
|
||||||
|
totalCount: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationPersonalDetailsDTO {
|
||||||
|
fullName: string;
|
||||||
|
firstName: string | null;
|
||||||
|
lastName: string | null;
|
||||||
|
role: string | null;
|
||||||
|
mobileNumber: string | null;
|
||||||
|
profileImage: string | null;
|
||||||
|
profileImagePreSignedUrl: string | null;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationBookingInformationDTO {
|
||||||
|
activityName: string | null;
|
||||||
|
slot: string | null;
|
||||||
|
startTime: string | null;
|
||||||
|
endTime: string | null;
|
||||||
|
track: string | null;
|
||||||
|
trackLabel: string | null;
|
||||||
|
date: string | null;
|
||||||
|
dateLabel: string | null;
|
||||||
|
bookedOn: string | null;
|
||||||
|
bookedOnLabel: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationBookingIncludedDTO {
|
||||||
|
food: string;
|
||||||
|
selectedFoodTypes: string[];
|
||||||
|
equipment: string;
|
||||||
|
selectedEquipments: string[];
|
||||||
|
trainerOrGuide: string;
|
||||||
|
pickupLocation: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationByCheckInCodeDTO {
|
||||||
|
itineraryHeaderXid: number;
|
||||||
|
itineraryActivityXid: number;
|
||||||
|
bookingId: string | null;
|
||||||
|
checkInCode: string | null;
|
||||||
|
reservationStatus: string | null;
|
||||||
|
personalDetails: OperatorReservationPersonalDetailsDTO;
|
||||||
|
bookingInformation: OperatorReservationBookingInformationDTO;
|
||||||
|
bookingIncluded: OperatorReservationBookingIncludedDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetReservationByCheckInCodeResponseDTO {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data: OperatorReservationByCheckInCodeDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationVerificationOtpResponseDTO {
|
||||||
|
itineraryActivityXid: number;
|
||||||
|
itineraryMemberXid: number;
|
||||||
|
activityName: string | null;
|
||||||
|
checkInCode: string;
|
||||||
|
verificationType: 'check-in' | 'checkout';
|
||||||
|
requestedChannel: 'email' | 'mobile';
|
||||||
|
deliveryChannel: 'email';
|
||||||
|
destination: string;
|
||||||
|
reservationStatus: string | null;
|
||||||
|
expiresInMinutes: number;
|
||||||
|
deliveryNote: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OperatorReservationVerifyOtpResponseDTO {
|
||||||
|
itineraryActivityXid: number;
|
||||||
|
itineraryMemberXid: number;
|
||||||
|
activityName: string | null;
|
||||||
|
checkInCode: string;
|
||||||
|
verificationType: 'check-in' | 'checkout';
|
||||||
|
verified: boolean;
|
||||||
|
reservationStatus: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
||||||
|
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 {
|
||||||
|
CreateActivityDto,
|
||||||
|
CreateActivityInput,
|
||||||
|
} from '../../../dto/createActivity.schema';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
|
const hostService = new HostService(prismaClient);
|
||||||
|
|
||||||
|
export const handler = safeHandler(
|
||||||
|
async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
||||||
|
|
||||||
|
/* 1️⃣ AUTH */
|
||||||
|
const token =
|
||||||
|
event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) {
|
||||||
|
throw new ApiError(401, 'Missing auth token');
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo = await verifyHostToken(token);
|
||||||
|
|
||||||
|
/* 2️⃣ PARSE JSON BODY */
|
||||||
|
if (!event.body) {
|
||||||
|
throw new ApiError(400, 'Request body is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: any;
|
||||||
|
try {
|
||||||
|
body = JSON.parse(event.body);
|
||||||
|
} catch {
|
||||||
|
throw new ApiError(400, 'Invalid JSON body');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
activity,
|
||||||
|
media = [],
|
||||||
|
isDraft = false,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!activity) {
|
||||||
|
throw new ApiError(400, 'activity payload is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3️⃣ NORMALIZE ACTIVITY ID */
|
||||||
|
if (activity.activityXid) {
|
||||||
|
activity.activityXid = Number(activity.activityXid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4️⃣ ATTACH ACTIVITY MEDIA (S3 URLs) */
|
||||||
|
if (!Array.isArray(media)) {
|
||||||
|
throw new ApiError(400, 'media must be an array');
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.media = media.map((m: any) => ({
|
||||||
|
mediaType: m.mediaType ?? 'image',
|
||||||
|
mediaFileName: m.mediaFileName,
|
||||||
|
isCoverImage: m.isCoverImage ?? false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* 4.1️⃣ ATTACH SAFETY INSTRUCTIONS (string only) */
|
||||||
|
if (activity.safetyInstruction !== undefined && activity.safetyInstruction !== null) {
|
||||||
|
if (typeof activity.safetyInstruction !== 'string') {
|
||||||
|
throw new ApiError(400, 'safetyInstruction must be a string');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4.2️⃣ ATTACH CANCELLATIONS (string only) */
|
||||||
|
if (activity.cancellations !== undefined && activity.cancellations !== null) {
|
||||||
|
if (typeof activity.cancellations !== 'string') {
|
||||||
|
throw new ApiError(400, 'cancellations must be a string');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 5️⃣ VALIDATION */
|
||||||
|
let parsedDto: CreateActivityInput;
|
||||||
|
|
||||||
|
if (!isDraft) {
|
||||||
|
const parsed = CreateActivityDto.safeParse(activity);
|
||||||
|
if (!parsed.success) {
|
||||||
|
throw new ApiError(
|
||||||
|
400,
|
||||||
|
parsed.error.issues.map((i) => i.message).join(', ')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
parsedDto = parsed.data;
|
||||||
|
} else {
|
||||||
|
parsedDto = activity as CreateActivityInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 6️⃣ SAVE TO DB */
|
||||||
|
const result = await hostService.createOrUpdateActivity(
|
||||||
|
userInfo.id,
|
||||||
|
parsedDto,
|
||||||
|
isDraft
|
||||||
|
);
|
||||||
|
|
||||||
|
/* 7️⃣ RESPONSE */
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: isDraft
|
||||||
|
? 'Activity saved as draft successfully'
|
||||||
|
: 'Activity submitted successfully',
|
||||||
|
data: result,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
|
import {
|
||||||
|
APIGatewayProxyEvent,
|
||||||
|
APIGatewayProxyResult,
|
||||||
|
Context,
|
||||||
|
} from 'aws-lambda';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
|
const hostService = new HostService(prismaClient);
|
||||||
|
|
||||||
|
export const handler = safeHandler(
|
||||||
|
async (
|
||||||
|
event: APIGatewayProxyEvent,
|
||||||
|
context?: Context,
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
|
// Verify authentication token
|
||||||
|
const token =
|
||||||
|
event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) {
|
||||||
|
throw new ApiError(
|
||||||
|
401,
|
||||||
|
'This is a protected route. Please provide a valid token.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify token and get user info
|
||||||
|
const userInfo = await verifyHostToken(token);
|
||||||
|
|
||||||
|
let body: any = {};
|
||||||
|
try {
|
||||||
|
body = event.body ? JSON.parse(event.body) : {};
|
||||||
|
} catch (err) {
|
||||||
|
throw new ApiError(400, 'Invalid JSON in request body');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { activityTypeXid, frequenciesXid } = body;
|
||||||
|
|
||||||
|
if (!activityTypeXid || !frequenciesXid) {
|
||||||
|
throw new ApiError(400, 'activityType and frequency ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all host applications from service based on user role
|
||||||
|
const createdData = await hostService.createActivityAndAllQuestionsEntry(userInfo.id, activityTypeXid, frequenciesXid);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Activity created successfully',
|
||||||
|
data: createdData
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { PrePopulateService } from '../../../../prepopulate/services/prepopulate.service';
|
import { PrePopulateService } from '../../../../prepopulate/services/prepopulate.service';
|
||||||
import { parseQueryParams } from '../../../../../common/utils/validation/validation.utils';
|
|
||||||
import { optionalSearchQuerySchema } from '../../../../../common/utils/validation/host/activity.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
const prePopulateService = new PrePopulateService(prismaClient);
|
||||||
const prePopulateService = new PrePopulateService(prismaService);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all activity types with interest handler
|
* Add suggestion handler for host applications
|
||||||
|
* Allows Minglar Admin, Co_Admin, and Account Manager to add suggestions
|
||||||
|
* Types: Setup Profile, Review Account, Add Payment Details, Agreement
|
||||||
*/
|
*/
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
@@ -26,13 +25,13 @@ export const handler = safeHandler(async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify token and get user info
|
// Verify token and get user info
|
||||||
const userInfo = await verifyHostToken(token);
|
await verifyHostToken(token);
|
||||||
|
|
||||||
// Parse and validate query params using Zod
|
|
||||||
const { search, q } = parseQueryParams(event.queryStringParameters, optionalSearchQuerySchema);
|
|
||||||
const searchTerm = search || q || undefined;
|
|
||||||
|
|
||||||
const data = await hostService.getAllActivityTypesWithInterest(searchTerm);
|
// Read optional search query (supports ?search= or ?q=)
|
||||||
|
const search = event.queryStringParameters?.search || event.queryStringParameters?.q || undefined;
|
||||||
|
|
||||||
|
const data = await hostService.getAllActivityTypesWithInterest(search);
|
||||||
const frequencies = await prePopulateService.getAllFrequencies();
|
const frequencies = await prePopulateService.getAllFrequencies();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { verifyMinglarAdminHostToken } from '../../../../../common/middlewares/jwt/authForMinglarAdminHost';
|
||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
|
const hostService = new HostService(prismaClient);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add suggestion handler for host applications
|
||||||
|
* Allows Minglar Admin, Co_Admin, and Account Manager to add suggestions
|
||||||
|
* Types: Setup Profile, Review Account, Add Payment Details, Agreement
|
||||||
|
*/
|
||||||
|
export const handler = safeHandler(async (
|
||||||
|
event: APIGatewayProxyEvent,
|
||||||
|
context?: Context
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
|
// Verify authentication token
|
||||||
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) {
|
||||||
|
throw new ApiError(401, 'This is a protected route. Please provide a valid token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify token and get user info
|
||||||
|
await verifyMinglarAdminHostToken(token);
|
||||||
|
|
||||||
|
const activityXid = event.pathParameters?.activityXid
|
||||||
|
if (!activityXid) {
|
||||||
|
throw new ApiError(400, 'activityXid is required in path parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await hostService.getAllDetailsOfActivityAndVenue(Number(activityXid));
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Data retrieved successfully',
|
||||||
|
data,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { parseQueryParams } from '../../../../../common/utils/validation/validation.utils';
|
import { paginationService } from '../../../../../common/utils/pagination/pagination.service';
|
||||||
import { optionalSearchQuerySchema } from '../../../../../common/utils/validation/host/activity.validation';
|
|
||||||
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all host activities handler
|
* Add suggestion handler for host applications
|
||||||
|
* Allows Minglar Admin, Co_Admin, and Account Manager to add suggestions
|
||||||
|
* Types: Setup Profile, Review Account, Add Payment Details, Agreement
|
||||||
*/
|
*/
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
@@ -28,11 +28,18 @@ export const handler = safeHandler(async (
|
|||||||
// Verify token and get user info
|
// Verify token and get user info
|
||||||
const userInfo = await verifyHostToken(token);
|
const userInfo = await verifyHostToken(token);
|
||||||
|
|
||||||
// Parse and validate query params using Zod
|
// Get pagination params from event
|
||||||
const { search, q } = parseQueryParams(event.queryStringParameters, optionalSearchQuerySchema);
|
const paginationParams = paginationService.getPaginationFromEvent(event);
|
||||||
const searchTerm = search || q || undefined;
|
const paginationOptions = paginationService.parsePaginationParams(paginationParams);
|
||||||
|
|
||||||
const data = await hostService.getAllHostActivity(searchTerm, Number(userInfo.id));
|
// Read optional search query (supports ?search= or ?q=)
|
||||||
|
const search = event.queryStringParameters?.search || event.queryStringParameters?.q || undefined;
|
||||||
|
|
||||||
|
const result = await hostService.getAllHostActivity(
|
||||||
|
search ? String(search) : undefined,
|
||||||
|
Number(userInfo.id),
|
||||||
|
paginationOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -44,8 +51,7 @@ export const handler = safeHandler(async (
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Data retrieved successfully',
|
message: 'Data retrieved successfully',
|
||||||
data,
|
...result,
|
||||||
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { verifyOnlyMinglarAdminToken } from '@/common/middlewares/jwt/authForOnlyMinglarAdmin';
|
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { PrePopulateService } from '../../../../prepopulate/services/prepopulate.service';
|
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { verifyMinglarAdminHostToken } from '@/common/middlewares/jwt/authForMinglarAdmin&Host';
|
import { verifyMinglarAdminHostToken } from '../../../../../common/middlewares/jwt/authForMinglarAdminHost';
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
@@ -23,7 +20,15 @@ export const handler = safeHandler(async (
|
|||||||
// Authenticate user using the shared authForHost function
|
// Authenticate user using the shared authForHost function
|
||||||
await verifyMinglarAdminHostToken(token);
|
await verifyMinglarAdminHostToken(token);
|
||||||
|
|
||||||
const result = await hostService.getAllPQQQuesAndSubmittedAns();
|
const activity_xid = event.queryStringParameters?.activity_xid
|
||||||
|
? Number(event.queryStringParameters.activity_xid)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (!activity_xid) {
|
||||||
|
throw new ApiError(409, "Activity ID is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await hostService.getAllPQQQuesAndSubmittedAns(Number(activity_xid));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
|||||||
@@ -1,55 +1,43 @@
|
|||||||
import config from '@/config/config';
|
import config from '../../../../../config/config';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
||||||
import AWS from 'aws-sdk';
|
import AWS from 'aws-sdk';
|
||||||
import Busboy from 'busboy';
|
import Busboy from 'busboy';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { LAST_QUESTION_ID } from '@/common/utils/constants/host.constant';
|
import { sendPQPEmailToAM } from '../../../services/sendHostResubmitEmailToAM.service';
|
||||||
|
|
||||||
const prisma = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const pqqService = new HostService(prisma);
|
|
||||||
|
|
||||||
const s3 = new AWS.S3({ region: config.aws.region });
|
const s3 = new AWS.S3({ region: config.aws.region });
|
||||||
|
|
||||||
// Function to extract S3 key from URL
|
// Extract S3 key from URL
|
||||||
function getS3KeyFromUrl(url: string): string {
|
function getS3KeyFromUrl(url: string): string {
|
||||||
const bucketBaseUrl = `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/`;
|
const bucketBaseUrl = `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/`;
|
||||||
return url.replace(bucketBaseUrl, '');
|
return url.replace(bucketBaseUrl, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to delete file from S3
|
// Delete file from S3
|
||||||
async function deleteFromS3(s3Key: string): Promise<void> {
|
async function deleteFromS3(s3Key: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await s3.deleteObject({
|
await s3.deleteObject({
|
||||||
Bucket: config.aws.bucketName,
|
Bucket: config.aws.bucketName,
|
||||||
Key: s3Key,
|
Key: s3Key,
|
||||||
}).promise();
|
}).promise();
|
||||||
console.log(`✅ File deleted from S3: ${s3Key}`);
|
console.log(`✅ Deleted from S3: ${s3Key}`);
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error(`❌ Error deleting file from S3: ${s3Key}`, error);
|
console.error(`❌ Failed to delete from S3: ${s3Key}`, err);
|
||||||
// Don't throw error here, continue with upload
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, prefix: string, existingUrl?: string): Promise<string> {
|
// Upload new file
|
||||||
let s3Key: string;
|
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, prefix: string): Promise<string> {
|
||||||
|
const uniqueKey = `${crypto.randomUUID()}_${originalName}`;
|
||||||
|
const s3Key = `${prefix}/${uniqueKey}`;
|
||||||
|
|
||||||
// If existing URL provided, use the same S3 key to replace the file
|
|
||||||
if (existingUrl) {
|
|
||||||
s3Key = getS3KeyFromUrl(existingUrl);
|
|
||||||
// Delete existing file first
|
|
||||||
await deleteFromS3(s3Key);
|
|
||||||
} else {
|
|
||||||
// Generate new unique key for new file
|
|
||||||
const uniqueKey = `${crypto.randomUUID()}_${originalName}`;
|
|
||||||
s3Key = `${prefix}/${uniqueKey}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload new file (replaces existing if same key)
|
|
||||||
await s3.upload({
|
await s3.upload({
|
||||||
Bucket: config.aws.bucketName,
|
Bucket: config.aws.bucketName,
|
||||||
Key: s3Key,
|
Key: s3Key,
|
||||||
@@ -58,253 +46,169 @@ async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string
|
|||||||
ACL: 'private'
|
ACL: 'private'
|
||||||
}).promise();
|
}).promise();
|
||||||
|
|
||||||
console.log(`✅ File uploaded to S3: ${s3Key}`);
|
console.log(`✅ Uploaded to S3: ${s3Key}`);
|
||||||
return `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${s3Key}`;
|
return `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${s3Key}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
||||||
try {
|
try {
|
||||||
// 1) Auth
|
// AUTH
|
||||||
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
if (!token) throw new ApiError(401, 'Missing token.');
|
if (!token) throw new ApiError(401, 'Missing token');
|
||||||
|
|
||||||
const user = await verifyHostToken(token);
|
const user = await verifyHostToken(token);
|
||||||
|
|
||||||
// 2) Content-Type check
|
// Content-Type
|
||||||
const contentType = event.headers["content-type"] || event.headers["Content-Type"];
|
const contentType = event.headers["content-type"] || event.headers["Content-Type"];
|
||||||
if (!contentType?.startsWith("multipart/form-data"))
|
if (!contentType?.startsWith("multipart/form-data"))
|
||||||
throw new ApiError(400, "Content-Type must be multipart/form-data");
|
throw new ApiError(400, "Content-Type must be multipart/form-data");
|
||||||
|
|
||||||
if (!event.isBase64Encoded)
|
if (!event.isBase64Encoded) throw new ApiError(400, "Body must be base64 encoded");
|
||||||
throw new ApiError(400, "Body must be base64 encoded");
|
|
||||||
|
|
||||||
const bodyBuffer = Buffer.from(event.body!, "base64");
|
const bodyBuffer = Buffer.from(event.body!, "base64");
|
||||||
|
|
||||||
const fields: any = {};
|
const fields: any = {};
|
||||||
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
|
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
|
||||||
|
|
||||||
// 3) Parse multipart data
|
// Parse multipart
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const bb = Busboy({ headers: { 'content-type': contentType } });
|
const bb = Busboy({ headers: { 'content-type': contentType } });
|
||||||
|
|
||||||
bb.on('file', (fieldname, file, info) => {
|
bb.on('file', (fieldname, file, info) => {
|
||||||
const { filename, mimeType } = info;
|
const { filename, mimeType } = info;
|
||||||
|
if (!filename) return file.resume();
|
||||||
// Skip if no filename (empty file field)
|
|
||||||
if (!filename) {
|
|
||||||
file.resume();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const chunks: Buffer[] = [];
|
const chunks: Buffer[] = [];
|
||||||
let totalSize = 0;
|
let size = 0;
|
||||||
const MAX_SIZE = 5 * 1024 * 1024; // 5 MB
|
|
||||||
|
file.on('data', chunk => {
|
||||||
|
size += chunk.length;
|
||||||
|
if (size > 5 * 1024 * 1024)
|
||||||
|
return reject(new ApiError(400, `File ${filename} exceeds 5MB limit`));
|
||||||
|
|
||||||
file.on('data', (chunk) => {
|
|
||||||
totalSize += chunk.length;
|
|
||||||
if (totalSize > MAX_SIZE) {
|
|
||||||
file.resume();
|
|
||||||
return reject(new ApiError(400, `File ${filename} exceeds 5MB limit.`));
|
|
||||||
}
|
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
file.on('end', () => {
|
file.on('end', () => {
|
||||||
// Only add file if we have data
|
|
||||||
if (chunks.length > 0) {
|
if (chunks.length > 0) {
|
||||||
files.push({
|
files.push({
|
||||||
buffer: Buffer.concat(chunks),
|
buffer: Buffer.concat(chunks),
|
||||||
mimeType,
|
mimeType,
|
||||||
fileName: filename,
|
fileName: filename,
|
||||||
fieldName: fieldname,
|
fieldName: fieldname
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
file.on('error', (err) => {
|
|
||||||
reject(new ApiError(400, `File upload error: ${err.message}`));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bb.on('field', (fieldname, val) => {
|
bb.on('field', (fieldname, val) => {
|
||||||
// Handle empty or null values
|
try { fields[fieldname] = JSON.parse(val); }
|
||||||
if (val === '' || val === 'null' || val === 'undefined') {
|
catch { fields[fieldname] = val; }
|
||||||
fields[fieldname] = null;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
fields[fieldname] = JSON.parse(val);
|
|
||||||
} catch {
|
|
||||||
fields[fieldname] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
bb.on('close', () => {
|
|
||||||
console.log("✅ Busboy parsing completed");
|
|
||||||
console.log("📌 Fields:", fields);
|
|
||||||
console.log("📁 Files:", files.length);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
bb.on('error', (err) => {
|
|
||||||
console.error("❌ Busboy error:", err);
|
|
||||||
reject(new ApiError(400, `Multipart parsing error: ${err.message}`));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bb.on('close', resolve);
|
||||||
|
bb.on('error', err => reject(new ApiError(400, err.message)));
|
||||||
bb.end(bodyBuffer);
|
bb.end(bodyBuffer);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4) Extract required fields - only activityXid, pqqQuestionXid, pqqAnswerXid are required
|
// Required fields
|
||||||
const activityXid = Number(fields.activityXid);
|
const activityXid = Number(fields.activityXid);
|
||||||
const pqqQuestionXid = Number(fields.pqqQuestionXid);
|
const pqqQuestionXid = Number(fields.pqqQuestionXid);
|
||||||
const pqqAnswerXid = Number(fields.pqqAnswerXid);
|
const pqqAnswerXid = Number(fields.pqqAnswerXid);
|
||||||
|
|
||||||
// Comments and files are optional
|
|
||||||
const comments = fields.comments || null;
|
const comments = fields.comments || null;
|
||||||
|
|
||||||
// Validate required fields
|
if (!activityXid || !pqqQuestionXid || !pqqAnswerXid)
|
||||||
if (!activityXid || isNaN(activityXid)) throw new ApiError(400, "Valid activityXid is required");
|
throw new ApiError(400, "Missing required fields");
|
||||||
if (!pqqQuestionXid || isNaN(pqqQuestionXid)) throw new ApiError(400, "Valid pqqQuestionXid is required");
|
|
||||||
if (pqqQuestionXid !== LAST_QUESTION_ID.Q_ID) throw new ApiError(400, "Wrong question id.")
|
|
||||||
if (!pqqAnswerXid || isNaN(pqqAnswerXid)) throw new ApiError(400, "Valid pqqAnswerXid is required");
|
|
||||||
|
|
||||||
// console.log(`📝 Processing - Activity: ${activityXid}, Question: ${pqqQuestionXid}, Answer: ${pqqAnswerXid}`);
|
|
||||||
// console.log(`💬 Comments: ${comments ? 'Provided' : 'Not provided'}`);
|
|
||||||
// console.log(`📎 Files: ${files.length}`);
|
|
||||||
|
|
||||||
// 5) UPSERT: Check if header already exists for this combination
|
|
||||||
const existingHeader = await pqqService.findHeaderByCompositeKey(
|
|
||||||
activityXid,
|
|
||||||
pqqQuestionXid,
|
|
||||||
pqqAnswerXid
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// UPSERT header
|
||||||
|
const existingHeader = await hostService.findHeaderByCompositeKey(activityXid, pqqQuestionXid);
|
||||||
let header;
|
let header;
|
||||||
|
|
||||||
if (existingHeader) {
|
if (existingHeader) {
|
||||||
console.log("🔄 Updating existing PQQ header");
|
header = await hostService.updateHeader(existingHeader.id, pqqAnswerXid, comments);
|
||||||
// Update existing header (comments can be null)
|
|
||||||
header = await pqqService.updateHeader(
|
|
||||||
existingHeader.id,
|
|
||||||
comments
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
console.log("🆕 Creating new PQQ header");
|
header = await hostService.createHeader(activityXid, pqqQuestionXid, pqqAnswerXid, comments);
|
||||||
// Create new header (comments can be null)
|
|
||||||
header = await pqqService.createHeader(
|
|
||||||
activityXid,
|
|
||||||
pqqQuestionXid,
|
|
||||||
pqqAnswerXid,
|
|
||||||
comments
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// Calculate score after answer submission
|
|
||||||
const score = await pqqService.calculatePqqScoreForUser(activityXid);
|
|
||||||
|
|
||||||
|
// SCORE
|
||||||
|
const score = await hostService.calculatePqqScoreForUser(activityXid);
|
||||||
|
|
||||||
// 6) Get existing supporting files for this header
|
// Existing supporting files
|
||||||
const existingSupportingFiles = await pqqService.getSupportingFilesByHeaderId(header.id);
|
const existingSupportingFiles = await hostService.getSupportingFilesByHeaderId(header.id);
|
||||||
console.log(`📁 Found ${existingSupportingFiles.length} existing supporting files`);
|
|
||||||
|
|
||||||
// 7) Handle file UPSERT - only if files are provided
|
// Read deletedFiles from frontend
|
||||||
const uploadedFiles: any[] = [];
|
const deletedFiles = Array.isArray(fields.deletedFiles) ? fields.deletedFiles : [];
|
||||||
|
|
||||||
|
const deleteResults = [];
|
||||||
|
const addResults = [];
|
||||||
|
|
||||||
|
// DELETE explicitly requested files (Case 3)
|
||||||
|
if (deletedFiles.length > 0) {
|
||||||
|
for (const del of deletedFiles) {
|
||||||
|
const id = Number(del.id);
|
||||||
|
const record = existingSupportingFiles.find(f => f.id === id);
|
||||||
|
if (!record) continue;
|
||||||
|
|
||||||
|
// Delete from S3
|
||||||
|
if (record.mediaFileName) {
|
||||||
|
const key = getS3KeyFromUrl(record.mediaFileName);
|
||||||
|
await deleteFromS3(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete from DB
|
||||||
|
await hostService.deleteSupportingFile(record.id);
|
||||||
|
deleteResults.push({ id: record.id, deleted: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADD new uploaded files (Case 1 + Case 3 new files)
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
console.log("📤 Processing file uploads...");
|
for (const file of files) {
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
const file = files[i];
|
|
||||||
const existingFile = existingSupportingFiles[i] || null;
|
|
||||||
|
|
||||||
const url = await uploadToS3(
|
const url = await uploadToS3(
|
||||||
file.buffer,
|
file.buffer,
|
||||||
file.mimeType,
|
file.mimeType,
|
||||||
file.fileName,
|
file.fileName,
|
||||||
`ActivityOnboarding/supportings/${activityXid}`,
|
`ActivityOnboarding/supportings/${activityXid}`
|
||||||
existingFile ? existingFile.mediaFileName : undefined
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let supporting;
|
const newRec = await hostService.addSupportingFile(header.id, file.mimeType, url);
|
||||||
if (existingFile) {
|
addResults.push(newRec);
|
||||||
// Update existing supporting file record
|
|
||||||
supporting = await pqqService.updateSupportingFile(
|
|
||||||
existingFile.id,
|
|
||||||
file.mimeType,
|
|
||||||
url
|
|
||||||
);
|
|
||||||
console.log(`🔄 Updated supporting file: ${existingFile.id}`);
|
|
||||||
} else {
|
|
||||||
// Create new supporting file record
|
|
||||||
supporting = await pqqService.addSupportingFile(
|
|
||||||
header.id,
|
|
||||||
file.mimeType,
|
|
||||||
url
|
|
||||||
);
|
|
||||||
console.log(`🆕 Created new supporting file: ${supporting.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadedFiles.push(supporting);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8) Delete any remaining existing files that weren't replaced
|
|
||||||
if (existingSupportingFiles.length > files.length) {
|
|
||||||
const filesToDelete = existingSupportingFiles.slice(files.length);
|
|
||||||
console.log(`🗑️ Deleting ${filesToDelete.length} unused supporting files`);
|
|
||||||
|
|
||||||
for (const fileToDelete of filesToDelete) {
|
|
||||||
await pqqService.deleteSupportingFile(fileToDelete.id);
|
|
||||||
// Also delete from S3
|
|
||||||
const s3Key = getS3KeyFromUrl(fileToDelete.mediaFileName);
|
|
||||||
await deleteFromS3(s3Key);
|
|
||||||
console.log(`🗑️ Deleted supporting file: ${fileToDelete.id}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("📭 No files provided in request");
|
|
||||||
|
|
||||||
// If no files provided but existing files exist, delete them (cleanup)
|
|
||||||
if (existingSupportingFiles.length > 0) {
|
|
||||||
console.log(`🗑️ No new files provided, deleting ${existingSupportingFiles.length} existing files`);
|
|
||||||
for (const fileToDelete of existingSupportingFiles) {
|
|
||||||
await pqqService.deleteSupportingFile(fileToDelete.id);
|
|
||||||
const s3Key = getS3KeyFromUrl(fileToDelete.mediaFileName);
|
|
||||||
await deleteFromS3(s3Key);
|
|
||||||
console.log(`🗑️ Deleted supporting file: ${fileToDelete.id}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9) Prepare response
|
const getAllUpdatedQuestionResponse = await hostService.getAllPQUpdatedResponse(activityXid)
|
||||||
const responseMessage = existingHeader ? "PQQ answer updated successfully" : "PQQ answer submitted successfully";
|
|
||||||
|
const details = await hostService.getSuggestionDetails(user.id);
|
||||||
|
|
||||||
|
await sendPQPEmailToAM(
|
||||||
|
details.hostDetails.accountManager.emailAddress,
|
||||||
|
details.hostDetails.accountManager.firstName,
|
||||||
|
details.hostDetails.companyName,
|
||||||
|
details.hostDetails.user.userRefNumber,
|
||||||
|
)
|
||||||
|
|
||||||
|
// CASE 2 — NO deletion & NO new files => DO NOTHING to existing files
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
headers: {
|
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" },
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Access-Control-Allow-Origin": "*"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
message: responseMessage,
|
message: existingHeader ? "PQQ answer updated successfully" : "PQQ answer submitted successfully",
|
||||||
data: {
|
data: {
|
||||||
headerId: header.id,
|
headerId: header.id,
|
||||||
activityXid,
|
activityXid,
|
||||||
pqqQuestionXid,
|
pqqQuestionXid,
|
||||||
pqqAnswerXid,
|
pqqAnswerXid,
|
||||||
comments: comments,
|
comments,
|
||||||
score,
|
score,
|
||||||
files: {
|
getAllUpdatedQuestionResponse
|
||||||
uploaded: uploadedFiles,
|
|
||||||
total: uploadedFiles.length
|
|
||||||
},
|
|
||||||
operation: existingHeader ? 'updated' : 'created',
|
|
||||||
fileOperation: files.length > 0 ?
|
|
||||||
(existingSupportingFiles.length > 0 ? 'replaced' : 'added') :
|
|
||||||
(existingSupportingFiles.length > 0 ? 'removed' : 'unchanged')
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (err: any) {
|
||||||
console.error("❌ Error in submitPqqAnswer:", error);
|
console.error("❌ Error:", err);
|
||||||
throw error;
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
|
import { verifyMinglarAdminHostToken } from '../../../../../common/middlewares/jwt/authForMinglarAdminHost';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { verifyMinglarAdminHostToken } from '@/common/middlewares/jwt/authForMinglarAdmin&Host';
|
|
||||||
import { parseQueryParams } from '../../../../../common/utils/validation/validation.utils';
|
|
||||||
import { getPqqByQuestionIdQuerySchema } from '../../../../../common/utils/validation/host/activity.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
context?: Context
|
context?: Context
|
||||||
@@ -23,11 +20,12 @@ export const handler = safeHandler(async (
|
|||||||
const userInfo = await verifyMinglarAdminHostToken(token);
|
const userInfo = await verifyMinglarAdminHostToken(token);
|
||||||
const userId = Number(userInfo.id);
|
const userId = Number(userInfo.id);
|
||||||
|
|
||||||
// Parse and validate query params using Zod
|
const question_xid = Number(event.queryStringParameters?.question_xid);
|
||||||
const { question_xid, activity_xid } = parseQueryParams(
|
const activity_xid = Number(event.queryStringParameters?.activity_xid);
|
||||||
event.queryStringParameters,
|
|
||||||
getPqqByQuestionIdQuerySchema
|
if (!question_xid || !activity_xid) {
|
||||||
);
|
throw new ApiError(400, "Question and activity xid are required.")
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch user with their HostHeader stepper info
|
// Fetch user with their HostHeader stepper info
|
||||||
const pqqQuestionDetails = await hostService.getPQQQuestionDetail(question_xid, activity_xid);
|
const pqqQuestionDetails = await hostService.getPQQQuestionDetail(question_xid, activity_xid);
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { parseQueryParams } from '../../../../../common/utils/validation/validation.utils';
|
|
||||||
import { getLatestPqqQuestionQuerySchema } from '../../../../../common/utils/validation/host/activity.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
context?: Context
|
context?: Context
|
||||||
@@ -23,20 +20,26 @@ export const handler = safeHandler(async (
|
|||||||
const userInfo = await verifyHostToken(token);
|
const userInfo = await verifyHostToken(token);
|
||||||
const userId = Number(userInfo.id);
|
const userId = Number(userInfo.id);
|
||||||
|
|
||||||
// Parse and validate query params using Zod
|
const activity_xid = event.queryStringParameters?.activity_xid
|
||||||
const { activity_xid } = parseQueryParams(
|
? Number(event.queryStringParameters.activity_xid)
|
||||||
event.queryStringParameters,
|
: null;
|
||||||
getLatestPqqQuestionQuerySchema
|
|
||||||
);
|
// Validate it
|
||||||
|
if (!activity_xid || isNaN(activity_xid)) {
|
||||||
|
throw new ApiError(400, "Activity id is required and must be a number.");
|
||||||
|
}
|
||||||
|
let result = null;
|
||||||
|
|
||||||
// Fetch user with their HostHeader stepper info
|
// Fetch user with their HostHeader stepper info
|
||||||
const pqqQuestionDetails = await hostService.getLatestQuestionDetailsPQQ(activity_xid);
|
const pqqQuestionDetails = await hostService.getLatestQuestionDetailsPQQ(activity_xid);
|
||||||
|
|
||||||
const result = {
|
if (pqqQuestionDetails) {
|
||||||
pqqQuestionXid: pqqQuestionDetails.pqqQuestionXid,
|
result = {
|
||||||
pqqAnswerXid: pqqQuestionDetails.pqqAnswerXid,
|
pqqQuestionXid: pqqQuestionDetails.pqqQuestionXid,
|
||||||
pqqSubCategoryXid: pqqQuestionDetails.pqqQuestions.pqqSubCategoryXid,
|
pqqAnswerXid: pqqQuestionDetails.pqqAnswerXid || null,
|
||||||
categoryXid: pqqQuestionDetails.pqqQuestions.pqqSubCategories.categoryXid
|
pqqSubCategoryXid: pqqQuestionDetails.pqqQuestions.pqqSubCategoryXid || null,
|
||||||
|
categoryXid: pqqQuestionDetails.pqqQuestions.pqqSubCategories.categoryXid || null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,43 +1,50 @@
|
|||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
// import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
// import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
// import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
// import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
// import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
// import { HostService } from '../../../services/host.service';
|
||||||
import { parseBody } from '../../../../../common/utils/validation/validation.utils';
|
|
||||||
import { createActivitySchema } from '../../../../../common/utils/validation/host/activity.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
// const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
export const handler = safeHandler(async (
|
// export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
// event: APIGatewayProxyEvent,
|
||||||
context?: Context
|
// context?: Context
|
||||||
): Promise<APIGatewayProxyResult> => {
|
// ): Promise<APIGatewayProxyResult> => {
|
||||||
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
// const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
if (!token) throw new ApiError(401, 'This is a protected route. Please provide a valid token.');
|
// if (!token) throw new ApiError(401, 'This is a protected route. Please provide a valid token.');
|
||||||
|
|
||||||
const userInfo = await verifyHostToken(token);
|
// const userInfo = await verifyHostToken(token);
|
||||||
|
|
||||||
// Parse and validate request body using Zod
|
// let body: any = {};
|
||||||
const { activityTypeXid, frequenciesXid } = parseBody(event.body, createActivitySchema);
|
// try {
|
||||||
|
// body = event.body ? JSON.parse(event.body) : {};
|
||||||
|
// } catch (err) {
|
||||||
|
// throw new ApiError(400, 'Invalid JSON in request body');
|
||||||
|
// }
|
||||||
|
|
||||||
await hostService.createActivity(
|
// const { activityTypeXid, frequenciesXid } = body;
|
||||||
userInfo.id,
|
|
||||||
activityTypeXid,
|
|
||||||
frequenciesXid,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
// if (!activityTypeXid) {
|
||||||
statusCode: 201,
|
// throw new ApiError(400, 'activityTypeXid is required');
|
||||||
headers: {
|
// }
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Access-Control-Allow-Origin': '*',
|
// await hostService.createActivity(
|
||||||
},
|
// userInfo.id,
|
||||||
body: JSON.stringify({
|
// Number(activityTypeXid),
|
||||||
success: true,
|
// frequenciesXid ? Number(frequenciesXid) : undefined,
|
||||||
message: 'Activity created successfully',
|
// );
|
||||||
data: null,
|
|
||||||
}),
|
// return {
|
||||||
};
|
// statusCode: 201,
|
||||||
});
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
// 'Access-Control-Allow-Origin': '*',
|
||||||
|
// },
|
||||||
|
// body: JSON.stringify({
|
||||||
|
// success: true,
|
||||||
|
// message: 'Activity created successfully',
|
||||||
|
// data: null,
|
||||||
|
// }),
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|||||||
@@ -0,0 +1,319 @@
|
|||||||
|
import config from '../../../../../config/config';
|
||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
||||||
|
import AWS from 'aws-sdk';
|
||||||
|
import Busboy from 'busboy';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
|
||||||
|
const hostService = new HostService(prismaClient);
|
||||||
|
|
||||||
|
const s3 = new AWS.S3({ region: config.aws.region });
|
||||||
|
|
||||||
|
function parseMultipartFieldValue(val: string) {
|
||||||
|
if (val === '' || val === 'null' || val === 'undefined') return null;
|
||||||
|
|
||||||
|
const cleaned = val.trim();
|
||||||
|
const looksLikeJson =
|
||||||
|
(cleaned.startsWith('{') && cleaned.endsWith('}')) ||
|
||||||
|
(cleaned.startsWith('[') && cleaned.endsWith(']')) ||
|
||||||
|
(cleaned.startsWith('"') && cleaned.endsWith('"'));
|
||||||
|
|
||||||
|
if (!looksLikeJson) return val;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(cleaned);
|
||||||
|
} catch {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeComments(comments: unknown): string | null {
|
||||||
|
if (comments === null || comments === undefined || comments === '') return null;
|
||||||
|
|
||||||
|
const value = String(comments).trim();
|
||||||
|
if (!value) return null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(value.startsWith('"') && value.endsWith('"')) ||
|
||||||
|
(value.startsWith("'") && value.endsWith("'"))
|
||||||
|
) {
|
||||||
|
return value.slice(1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to extract S3 key from URL
|
||||||
|
function getS3KeyFromUrl(url: string): string {
|
||||||
|
const bucketBaseUrl = `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/`;
|
||||||
|
return url.replace(bucketBaseUrl, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to delete file from S3
|
||||||
|
async function deleteFromS3(s3Key: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await s3.deleteObject({
|
||||||
|
Bucket: config.aws.bucketName,
|
||||||
|
Key: s3Key,
|
||||||
|
}).promise();
|
||||||
|
console.log(`✅ File deleted from S3: ${s3Key}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Error deleting file from S3: ${s3Key}`, error);
|
||||||
|
// continue — we don't want S3 deletion failure to crash the whole request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, prefix: string, existingUrl?: string): Promise<string> {
|
||||||
|
// We intentionally do NOT reuse old key. If existingUrl is provided we delete old file and create a new random key.
|
||||||
|
if (existingUrl) {
|
||||||
|
try {
|
||||||
|
const oldKey = getS3KeyFromUrl(existingUrl);
|
||||||
|
await deleteFromS3(oldKey);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Warning deleting existingUrl before upload', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueKey = `${crypto.randomUUID()}_${originalName}`;
|
||||||
|
const s3Key = `${prefix}/${uniqueKey}`;
|
||||||
|
|
||||||
|
await s3.upload({
|
||||||
|
Bucket: config.aws.bucketName,
|
||||||
|
Key: s3Key,
|
||||||
|
Body: buffer,
|
||||||
|
ContentType: mimeType,
|
||||||
|
ACL: 'private'
|
||||||
|
}).promise();
|
||||||
|
|
||||||
|
console.log(`✅ File uploaded to S3: ${s3Key}`);
|
||||||
|
return `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${s3Key}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
||||||
|
try {
|
||||||
|
// 1) Auth
|
||||||
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) throw new ApiError(401, 'Missing token.');
|
||||||
|
const user = await verifyHostToken(token);
|
||||||
|
|
||||||
|
// 2) Content-Type check
|
||||||
|
const contentType = event.headers["content-type"] || event.headers["Content-Type"];
|
||||||
|
if (!contentType?.includes("multipart/form-data"))
|
||||||
|
throw new ApiError(400, "Content-Type must be multipart/form-data");
|
||||||
|
|
||||||
|
// 3) Body decoding
|
||||||
|
const bodyBuffer = event.isBase64Encoded
|
||||||
|
? Buffer.from(event.body!, "base64")
|
||||||
|
: Buffer.from(event.body!, "binary");
|
||||||
|
|
||||||
|
const fields: any = {};
|
||||||
|
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
|
||||||
|
|
||||||
|
// 4) Parse multipart data
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
const bb = Busboy({ headers: { 'content-type': contentType } });
|
||||||
|
|
||||||
|
bb.on('file', (fieldname, file, info) => {
|
||||||
|
const { filename, mimeType } = info;
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
file.resume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
let size = 0;
|
||||||
|
const MAX_SIZE = 5 * 1024 * 1024;
|
||||||
|
|
||||||
|
file.on("data", (chunk) => {
|
||||||
|
size += chunk.length;
|
||||||
|
if (size > MAX_SIZE) {
|
||||||
|
file.destroy(new Error(`File ${filename} exceeds 5MB limit.`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chunks.push(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
file.on("end", () => {
|
||||||
|
if (chunks.length > 0) {
|
||||||
|
files.push({
|
||||||
|
buffer: Buffer.concat(chunks),
|
||||||
|
mimeType,
|
||||||
|
fileName: filename,
|
||||||
|
fieldName: fieldname,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
file.on("error", (err) =>
|
||||||
|
reject(new ApiError(400, `File upload error: ${err.message}`))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
bb.on("field", (fieldname, val) => {
|
||||||
|
console.log(`FIELD RAW: ${fieldname} =`, val);
|
||||||
|
fields[fieldname] = parseMultipartFieldValue(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
bb.on("close", () => resolve());
|
||||||
|
bb.on("error", (err) =>
|
||||||
|
reject(new ApiError(400, `Multipart parsing error: ${err.message}`))
|
||||||
|
);
|
||||||
|
|
||||||
|
// IMPORTANT FIX for HTTP API
|
||||||
|
bb.write(bodyBuffer);
|
||||||
|
bb.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5) Extract required fields
|
||||||
|
const activityXid = Number(fields.activityXid);
|
||||||
|
const pqqQuestionXid = Number(fields.pqqQuestionXid);
|
||||||
|
const pqqAnswerXid = Number(fields.pqqAnswerXid);
|
||||||
|
const comments = normalizeComments(fields.comments);
|
||||||
|
|
||||||
|
if (!activityXid || isNaN(activityXid)) throw new ApiError(400, "Please provide a valid activity");
|
||||||
|
if (!pqqQuestionXid || isNaN(pqqQuestionXid)) throw new ApiError(400, "Please select a valid question");
|
||||||
|
if (!pqqAnswerXid || isNaN(pqqAnswerXid)) throw new ApiError(400, "Please select a valid answer");
|
||||||
|
|
||||||
|
// 6) UPSERT header
|
||||||
|
const existingHeader = await hostService.findHeaderByCompositeKey(
|
||||||
|
activityXid,
|
||||||
|
pqqQuestionXid,
|
||||||
|
);
|
||||||
|
|
||||||
|
let header;
|
||||||
|
if (existingHeader) {
|
||||||
|
console.log("🔄 Updating existing PQQ header");
|
||||||
|
header = await hostService.updateHeader(
|
||||||
|
existingHeader.id,
|
||||||
|
pqqAnswerXid,
|
||||||
|
comments
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log("🆕 Creating new PQQ header");
|
||||||
|
header = await hostService.createHeader(
|
||||||
|
activityXid,
|
||||||
|
pqqQuestionXid,
|
||||||
|
pqqAnswerXid,
|
||||||
|
comments
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7) Get existing supporting files
|
||||||
|
const existingSupportingFiles = await hostService.getSupportingFilesByHeaderId(header.id);
|
||||||
|
console.log(`📁 Found ${existingSupportingFiles.length} existing supporting files`);
|
||||||
|
|
||||||
|
// 8) Parse incoming control fields
|
||||||
|
// fields.deletedFiles should be array like [{ id: number, url: string }, ...] or null
|
||||||
|
const deletedFiles: Array<{ id: number; url?: string }> = Array.isArray(fields.deletedFiles) ? fields.deletedFiles : [];
|
||||||
|
// fields.existingFiles can be an array of urls; we accept it but do not require it
|
||||||
|
const existingFilesFromFront: string[] = Array.isArray(fields.existingFiles) ? fields.existingFiles : [];
|
||||||
|
|
||||||
|
// Prepare response trackers
|
||||||
|
const deletedResults: Array<{ id: number; success: boolean; reason?: string }> = [];
|
||||||
|
const addedResults: Array<any> = [];
|
||||||
|
|
||||||
|
// 9) Handle explicit deletions (ONLY delete ids provided in deletedFiles)
|
||||||
|
if (deletedFiles.length > 0) {
|
||||||
|
console.log(`🗑️ Processing ${deletedFiles.length} explicit deletions`);
|
||||||
|
// Build a map of existing supporting files by id for quick lookup
|
||||||
|
const existingById = new Map<number, any>();
|
||||||
|
for (const f of existingSupportingFiles) {
|
||||||
|
existingById.set(f.id, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const del of deletedFiles) {
|
||||||
|
const id = Number(del.id);
|
||||||
|
if (!id || !existingById.has(id)) {
|
||||||
|
deletedResults.push({ id, success: false, reason: 'Not found or invalid id' });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = existingById.get(id);
|
||||||
|
try {
|
||||||
|
// delete from s3
|
||||||
|
if (record.mediaFileName) {
|
||||||
|
const s3Key = getS3KeyFromUrl(record.mediaFileName);
|
||||||
|
await deleteFromS3(s3Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete DB record
|
||||||
|
await hostService.deleteSupportingFile(record.id);
|
||||||
|
|
||||||
|
deletedResults.push({ id: record.id, success: true });
|
||||||
|
console.log(`🗑️ Deleted supporting file record ${record.id}`);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(`❌ Failed to delete supporting file id ${id}`, err);
|
||||||
|
deletedResults.push({ id, success: false, reason: err.message || 'delete failed' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ No explicit deletions requested (deletedFiles empty)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10) Handle new uploaded files (these are ALWAYS added as new rows)
|
||||||
|
if (files.length > 0) {
|
||||||
|
console.log(`📤 Processing ${files.length} uploaded new file(s)`);
|
||||||
|
for (const file of files) {
|
||||||
|
try {
|
||||||
|
const url = await uploadToS3(
|
||||||
|
file.buffer,
|
||||||
|
file.mimeType,
|
||||||
|
file.fileName,
|
||||||
|
`ActivityOnboarding/supportings/${activityXid}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// create DB record
|
||||||
|
const supporting = await hostService.addSupportingFile(
|
||||||
|
header.id,
|
||||||
|
file.mimeType,
|
||||||
|
url
|
||||||
|
);
|
||||||
|
|
||||||
|
addedResults.push(supporting);
|
||||||
|
console.log(`🆕 Created new supporting file record: ${supporting.id}`);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('❌ Error uploading/creating supporting file', err);
|
||||||
|
// push failure result but continue processing other files
|
||||||
|
addedResults.push({ success: false, reason: err.message || 'upload/create failed' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('📭 No new files uploaded in request');
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: We DO NOT delete or modify existing supporting files that were not listed in deletedFiles.
|
||||||
|
// This satisfies your Case 2: "if no files are provided, do not touch existing supporting files".
|
||||||
|
|
||||||
|
const allPQPQuestionAnswerResponse = await hostService.getAllPQUpdatedResponse(activityXid)
|
||||||
|
|
||||||
|
// 11) Compose response
|
||||||
|
const responseMessage = existingHeader ? "PQQ answer updated successfully" : "PQQ answer submitted successfully";
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: responseMessage,
|
||||||
|
data: {
|
||||||
|
responseOfUpdatedData: allPQPQuestionAnswerResponse,
|
||||||
|
operation: existingHeader ? 'updated' : 'created',
|
||||||
|
// summary label for UI convenience:
|
||||||
|
fileOperation: (deletedResults.length > 0 || addedResults.length > 0) ? 'modified' : 'unchanged'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("❌ Error in submitPqqAnswer:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
|
const pqqService = new HostService(prismaClient);
|
||||||
|
|
||||||
|
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
||||||
|
try {
|
||||||
|
// 1) Auth
|
||||||
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) throw new ApiError(401, 'Missing token.');
|
||||||
|
const user = await verifyHostToken(token);
|
||||||
|
|
||||||
|
const activity_xid = event.queryStringParameters?.activity_xid
|
||||||
|
? Number(event.queryStringParameters.activity_xid)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
await pqqService.submitpqqforreview(Number(activity_xid), Number(user.id))
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: "Your PQQ has been submitted for review.",
|
||||||
|
data: null
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("❌ Error in submitPqqAnswer:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
import config from '@/config/config';
|
import config from '../../../../../config/config';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
||||||
import AWS from 'aws-sdk';
|
import AWS from 'aws-sdk';
|
||||||
import Busboy from 'busboy';
|
import Busboy from 'busboy';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
const prisma = new PrismaService();
|
const pqqService = new HostService(prismaClient);
|
||||||
const pqqService = new HostService(prisma);
|
|
||||||
|
|
||||||
const s3 = new AWS.S3({ region: config.aws.region });
|
const s3 = new AWS.S3({ region: config.aws.region });
|
||||||
|
|
||||||
@@ -30,34 +29,24 @@ async function deleteFromS3(s3Key: string): Promise<void> {
|
|||||||
console.log(`✅ File deleted from S3: ${s3Key}`);
|
console.log(`✅ File deleted from S3: ${s3Key}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`❌ Error deleting file from S3: ${s3Key}`, error);
|
console.error(`❌ Error deleting file from S3: ${s3Key}`, error);
|
||||||
// Don't throw error here, continue with upload
|
// continue — we don't want S3 deletion failure to crash the whole request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, prefix: string, existingUrl?: string): Promise<string> {
|
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, prefix: string, existingUrl?: string): Promise<string> {
|
||||||
let s3Key: string;
|
// We intentionally do NOT reuse old key. If existingUrl is provided we delete old file and create a new random key.
|
||||||
|
|
||||||
// If existing URL provided, use the same S3 key to replace the file
|
|
||||||
// if (existingUrl) {
|
|
||||||
// s3Key = getS3KeyFromUrl(existingUrl);
|
|
||||||
// // Delete existing file first
|
|
||||||
// await deleteFromS3(s3Key);
|
|
||||||
// } else {
|
|
||||||
// // Generate new unique key for new file
|
|
||||||
// const uniqueKey = `${crypto.randomUUID()}_${originalName}`;
|
|
||||||
// s3Key = `${prefix}/${uniqueKey}`;
|
|
||||||
// }
|
|
||||||
if (existingUrl) {
|
if (existingUrl) {
|
||||||
// Delete old file, but DO NOT reuse its name
|
try {
|
||||||
const oldKey = getS3KeyFromUrl(existingUrl);
|
const oldKey = getS3KeyFromUrl(existingUrl);
|
||||||
await deleteFromS3(oldKey);
|
await deleteFromS3(oldKey);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Warning deleting existingUrl before upload', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new key always
|
|
||||||
const uniqueKey = `${crypto.randomUUID()}_${originalName}`;
|
const uniqueKey = `${crypto.randomUUID()}_${originalName}`;
|
||||||
s3Key = `${prefix}/${uniqueKey}`;
|
const s3Key = `${prefix}/${uniqueKey}`;
|
||||||
|
|
||||||
// Upload new file (replaces existing if same key)
|
|
||||||
await s3.upload({
|
await s3.upload({
|
||||||
Bucket: config.aws.bucketName,
|
Bucket: config.aws.bucketName,
|
||||||
Key: s3Key,
|
Key: s3Key,
|
||||||
@@ -82,7 +71,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
if (!contentType?.includes("multipart/form-data"))
|
if (!contentType?.includes("multipart/form-data"))
|
||||||
throw new ApiError(400, "Content-Type must be multipart/form-data");
|
throw new ApiError(400, "Content-Type must be multipart/form-data");
|
||||||
|
|
||||||
// 3) Body decoding (FIXED – same as addCompanyDetails)
|
// 3) Body decoding
|
||||||
const bodyBuffer = event.isBase64Encoded
|
const bodyBuffer = event.isBase64Encoded
|
||||||
? Buffer.from(event.body!, "base64")
|
? Buffer.from(event.body!, "base64")
|
||||||
: Buffer.from(event.body!, "binary");
|
: Buffer.from(event.body!, "binary");
|
||||||
@@ -90,7 +79,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
const fields: any = {};
|
const fields: any = {};
|
||||||
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
|
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
|
||||||
|
|
||||||
// 4) Parse multipart data (FIXED – using bb.write + bb.end exactly like working lambda)
|
// 4) Parse multipart data
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const bb = Busboy({ headers: { 'content-type': contentType } });
|
const bb = Busboy({ headers: { 'content-type': contentType } });
|
||||||
|
|
||||||
@@ -152,41 +141,32 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
bb.end();
|
bb.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4) Extract required fields - only activityXid, pqqQuestionXid, pqqAnswerXid are required
|
// 5) Extract required fields
|
||||||
const activityXid = Number(fields.activityXid);
|
const activityXid = Number(fields.activityXid);
|
||||||
const pqqQuestionXid = Number(fields.pqqQuestionXid);
|
const pqqQuestionXid = Number(fields.pqqQuestionXid);
|
||||||
const pqqAnswerXid = Number(fields.pqqAnswerXid);
|
const pqqAnswerXid = Number(fields.pqqAnswerXid);
|
||||||
|
|
||||||
// Comments and files are optional
|
|
||||||
const comments = fields.comments || null;
|
const comments = fields.comments || null;
|
||||||
|
|
||||||
// Validate required fields
|
if (!activityXid || isNaN(activityXid)) throw new ApiError(400, "Please provide a valid activity");
|
||||||
if (!activityXid || isNaN(activityXid)) throw new ApiError(400, "Valid activityXid is required");
|
if (!pqqQuestionXid || isNaN(pqqQuestionXid)) throw new ApiError(400, "Please select a valid question");
|
||||||
if (!pqqQuestionXid || isNaN(pqqQuestionXid)) throw new ApiError(400, "Valid pqqQuestionXid is required");
|
if (!pqqAnswerXid || isNaN(pqqAnswerXid)) throw new ApiError(400, "Please select a valid answer");
|
||||||
if (!pqqAnswerXid || isNaN(pqqAnswerXid)) throw new ApiError(400, "Valid pqqAnswerXid is required");
|
|
||||||
|
|
||||||
// console.log(`📝 Processing - Activity: ${activityXid}, Question: ${pqqQuestionXid}, Answer: ${pqqAnswerXid}`);
|
// 6) UPSERT header
|
||||||
// console.log(`💬 Comments: ${comments ? 'Provided' : 'Not provided'}`);
|
|
||||||
// console.log(`📎 Files: ${files.length}`);
|
|
||||||
|
|
||||||
// 5) UPSERT: Check if header already exists for this combination
|
|
||||||
const existingHeader = await pqqService.findHeaderByCompositeKey(
|
const existingHeader = await pqqService.findHeaderByCompositeKey(
|
||||||
activityXid,
|
activityXid,
|
||||||
pqqQuestionXid,
|
pqqQuestionXid,
|
||||||
pqqAnswerXid
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let header;
|
let header;
|
||||||
if (existingHeader) {
|
if (existingHeader) {
|
||||||
console.log("🔄 Updating existing PQQ header");
|
console.log("🔄 Updating existing PQQ header");
|
||||||
// Update existing header (comments can be null)
|
|
||||||
header = await pqqService.updateHeader(
|
header = await pqqService.updateHeader(
|
||||||
existingHeader.id,
|
existingHeader.id,
|
||||||
|
pqqAnswerXid,
|
||||||
comments
|
comments
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log("🆕 Creating new PQQ header");
|
console.log("🆕 Creating new PQQ header");
|
||||||
// Create new header (comments can be null)
|
|
||||||
header = await pqqService.createHeader(
|
header = await pqqService.createHeader(
|
||||||
activityXid,
|
activityXid,
|
||||||
pqqQuestionXid,
|
pqqQuestionXid,
|
||||||
@@ -195,79 +175,93 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6) Get existing supporting files for this header
|
// 7) Get existing supporting files
|
||||||
const existingSupportingFiles = await pqqService.getSupportingFilesByHeaderId(header.id);
|
const existingSupportingFiles = await pqqService.getSupportingFilesByHeaderId(header.id);
|
||||||
console.log(`📁 Found ${existingSupportingFiles.length} existing supporting files`);
|
console.log(`📁 Found ${existingSupportingFiles.length} existing supporting files`);
|
||||||
|
|
||||||
// 7) Handle file UPSERT - only if files are provided
|
// 8) Parse incoming control fields
|
||||||
const uploadedFiles: any[] = [];
|
// fields.deletedFiles should be array like [{ id: number, url: string }, ...] or null
|
||||||
|
const deletedFiles: Array<{ id: number; url?: string }> = Array.isArray(fields.deletedFiles) ? fields.deletedFiles : [];
|
||||||
|
// fields.existingFiles can be an array of urls; we accept it but do not require it
|
||||||
|
const existingFilesFromFront: string[] = Array.isArray(fields.existingFiles) ? fields.existingFiles : [];
|
||||||
|
|
||||||
|
// Prepare response trackers
|
||||||
|
const deletedResults: Array<{ id: number; success: boolean; reason?: string }> = [];
|
||||||
|
const addedResults: Array<any> = [];
|
||||||
|
|
||||||
|
// 9) Handle explicit deletions (ONLY delete ids provided in deletedFiles)
|
||||||
|
if (deletedFiles.length > 0) {
|
||||||
|
console.log(`🗑️ Processing ${deletedFiles.length} explicit deletions`);
|
||||||
|
// Build a map of existing supporting files by id for quick lookup
|
||||||
|
const existingById = new Map<number, any>();
|
||||||
|
for (const f of existingSupportingFiles) {
|
||||||
|
existingById.set(f.id, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const del of deletedFiles) {
|
||||||
|
const id = Number(del.id);
|
||||||
|
if (!id || !existingById.has(id)) {
|
||||||
|
deletedResults.push({ id, success: false, reason: 'Not found or invalid id' });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = existingById.get(id);
|
||||||
|
try {
|
||||||
|
// delete from s3
|
||||||
|
if (record.mediaFileName) {
|
||||||
|
const s3Key = getS3KeyFromUrl(record.mediaFileName);
|
||||||
|
await deleteFromS3(s3Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete DB record
|
||||||
|
await pqqService.deleteSupportingFile(record.id);
|
||||||
|
|
||||||
|
deletedResults.push({ id: record.id, success: true });
|
||||||
|
console.log(`🗑️ Deleted supporting file record ${record.id}`);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(`❌ Failed to delete supporting file id ${id}`, err);
|
||||||
|
deletedResults.push({ id, success: false, reason: err.message || 'delete failed' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ No explicit deletions requested (deletedFiles empty)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10) Handle new uploaded files (these are ALWAYS added as new rows)
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
console.log("📤 Processing file uploads...");
|
console.log(`📤 Processing ${files.length} uploaded new file(s)`);
|
||||||
|
for (const file of files) {
|
||||||
for (let i = 0; i < files.length; i++) {
|
try {
|
||||||
const file = files[i];
|
const url = await uploadToS3(
|
||||||
const existingFile = existingSupportingFiles[i] || null;
|
file.buffer,
|
||||||
|
|
||||||
const url = await uploadToS3(
|
|
||||||
file.buffer,
|
|
||||||
file.mimeType,
|
|
||||||
file.fileName,
|
|
||||||
`ActivityOnboarding/supportings/${activityXid}`,
|
|
||||||
existingFile ? existingFile.mediaFileName : undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
let supporting;
|
|
||||||
if (existingFile) {
|
|
||||||
// Update existing supporting file record
|
|
||||||
supporting = await pqqService.updateSupportingFile(
|
|
||||||
existingFile.id,
|
|
||||||
file.mimeType,
|
file.mimeType,
|
||||||
url
|
file.fileName,
|
||||||
|
`ActivityOnboarding/supportings/${activityXid}`
|
||||||
);
|
);
|
||||||
console.log(`🔄 Updated supporting file: ${existingFile.id}`);
|
|
||||||
} else {
|
// create DB record
|
||||||
// Create new supporting file record
|
const supporting = await pqqService.addSupportingFile(
|
||||||
supporting = await pqqService.addSupportingFile(
|
|
||||||
header.id,
|
header.id,
|
||||||
file.mimeType,
|
file.mimeType,
|
||||||
url
|
url
|
||||||
);
|
);
|
||||||
console.log(`🆕 Created new supporting file: ${supporting.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadedFiles.push(supporting);
|
addedResults.push(supporting);
|
||||||
}
|
console.log(`🆕 Created new supporting file record: ${supporting.id}`);
|
||||||
|
} catch (err: any) {
|
||||||
// 8) Delete any remaining existing files that weren't replaced
|
console.error('❌ Error uploading/creating supporting file', err);
|
||||||
if (existingSupportingFiles.length > files.length) {
|
// push failure result but continue processing other files
|
||||||
const filesToDelete = existingSupportingFiles.slice(files.length);
|
addedResults.push({ success: false, reason: err.message || 'upload/create failed' });
|
||||||
console.log(`🗑️ Deleting ${filesToDelete.length} unused supporting files`);
|
|
||||||
|
|
||||||
for (const fileToDelete of filesToDelete) {
|
|
||||||
await pqqService.deleteSupportingFile(fileToDelete.id);
|
|
||||||
// Also delete from S3
|
|
||||||
const s3Key = getS3KeyFromUrl(fileToDelete.mediaFileName);
|
|
||||||
await deleteFromS3(s3Key);
|
|
||||||
console.log(`🗑️ Deleted supporting file: ${fileToDelete.id}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("📭 No files provided in request");
|
console.log('📭 No new files uploaded in request');
|
||||||
|
|
||||||
// If no files provided but existing files exist, delete them (cleanup)
|
|
||||||
if (existingSupportingFiles.length > 0) {
|
|
||||||
console.log(`🗑️ No new files provided, deleting ${existingSupportingFiles.length} existing files`);
|
|
||||||
for (const fileToDelete of existingSupportingFiles) {
|
|
||||||
await pqqService.deleteSupportingFile(fileToDelete.id);
|
|
||||||
const s3Key = getS3KeyFromUrl(fileToDelete.mediaFileName);
|
|
||||||
await deleteFromS3(s3Key);
|
|
||||||
console.log(`🗑️ Deleted supporting file: ${fileToDelete.id}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9) Prepare response
|
// NOTE: We DO NOT delete or modify existing supporting files that were not listed in deletedFiles.
|
||||||
|
// This satisfies your Case 2: "if no files are provided, do not touch existing supporting files".
|
||||||
|
|
||||||
|
// 11) Compose response
|
||||||
const responseMessage = existingHeader ? "PQQ answer updated successfully" : "PQQ answer submitted successfully";
|
const responseMessage = existingHeader ? "PQQ answer updated successfully" : "PQQ answer submitted successfully";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -284,15 +278,15 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
activityXid,
|
activityXid,
|
||||||
pqqQuestionXid,
|
pqqQuestionXid,
|
||||||
pqqAnswerXid,
|
pqqAnswerXid,
|
||||||
comments: comments,
|
comments,
|
||||||
files: {
|
files: {
|
||||||
uploaded: uploadedFiles,
|
added: addedResults,
|
||||||
total: uploadedFiles.length
|
deleted: deletedResults,
|
||||||
|
existingKeptCount: (existingSupportingFiles.length - deletedResults.filter(d => d.success).length)
|
||||||
},
|
},
|
||||||
operation: existingHeader ? 'updated' : 'created',
|
operation: existingHeader ? 'updated' : 'created',
|
||||||
fileOperation: files.length > 0 ?
|
// summary label for UI convenience:
|
||||||
(existingSupportingFiles.length > 0 ? 'replaced' : 'added') :
|
fileOperation: (deletedResults.length > 0 || addedResults.length > 0) ? 'modified' : 'unchanged'
|
||||||
(existingSupportingFiles.length > 0 ? 'removed' : 'unchanged')
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@@ -301,4 +295,4 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
console.error("❌ Error in submitPqqAnswer:", error);
|
console.error("❌ Error in submitPqqAnswer:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { parseBody } from '../../../../../common/utils/validation/validation.utils';
|
|
||||||
import { updateSuggestionReviewedSchema } from '../../../../../common/utils/validation/host/activity.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
@@ -19,8 +16,18 @@ export const handler = safeHandler(async (
|
|||||||
|
|
||||||
const userInfo = await verifyHostToken(token);
|
const userInfo = await verifyHostToken(token);
|
||||||
|
|
||||||
// Parse and validate body using Zod
|
let body: any = {};
|
||||||
const { activityPqqHeaderXid, activityPQQSuggestionId } = parseBody(event.body, updateSuggestionReviewedSchema);
|
try {
|
||||||
|
body = event.body ? JSON.parse(event.body) : {};
|
||||||
|
} catch (err) {
|
||||||
|
throw new ApiError(400, 'Invalid JSON in request body');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { activityPqqHeaderXid, activityPQQSuggestionId } = body;
|
||||||
|
|
||||||
|
if (!activityPqqHeaderXid) {
|
||||||
|
throw new ApiError(400, 'activityPqqHeaderXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
await hostService.markPQQSuggestionReviewed(
|
await hostService.markPQQSuggestionReviewed(
|
||||||
userInfo.id,
|
userInfo.id,
|
||||||
|
|||||||
100
src/modules/host/handlers/Activity_Hub/Scheduling/cancelSlot.ts
Normal file
100
src/modules/host/handlers/Activity_Hub/Scheduling/cancelSlot.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
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 { SchedulingService } from '../../../services/activityScheduling.service';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
|
const schedulingService = new SchedulingService(prismaClient);
|
||||||
|
const hostService = new HostService(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.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate user using the shared authForHost function
|
||||||
|
const userInfo = await verifyHostToken(token);
|
||||||
|
const hostId = userInfo.id;
|
||||||
|
|
||||||
|
if (Number.isNaN(hostId)) {
|
||||||
|
throw new ApiError(400, 'Host id must be a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = await hostService.getHostIdByUserXid(hostId);
|
||||||
|
if (!host) {
|
||||||
|
throw new ApiError(404, 'Host not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: {
|
||||||
|
activityXid: number;
|
||||||
|
venueXid: number;
|
||||||
|
cancellations: {
|
||||||
|
scheduleHeaderXid: number;
|
||||||
|
occurenceDate: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
cancellationReason: string
|
||||||
|
}[]
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
body = event.body ? JSON.parse(event.body) : {};
|
||||||
|
} catch {
|
||||||
|
throw new ApiError(400, 'Invalid JSON payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!body.activityXid || !body.venueXid || !Array.isArray(body.cancellations) || body.cancellations.length === 0) {
|
||||||
|
throw new ApiError(400, 'Missing required fields');
|
||||||
|
}
|
||||||
|
|
||||||
|
const activity = await schedulingService.getActivityByXid(body.activityXid);
|
||||||
|
if (!activity) {
|
||||||
|
throw new ApiError(404, "Activity not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const venueExists = await schedulingService.getVenueFromVenueXid(
|
||||||
|
body.venueXid,
|
||||||
|
body.activityXid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!venueExists) {
|
||||||
|
throw new ApiError(
|
||||||
|
404,
|
||||||
|
`Venue not found for this activity`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await schedulingService.cancelMultipleSlotsForActivity(
|
||||||
|
body.cancellations.map((item: any) => ({
|
||||||
|
scheduleHeaderXid: Number(item.scheduleHeaderXid),
|
||||||
|
occurenceDate: item.occurenceDate,
|
||||||
|
startTime: item.startTime,
|
||||||
|
endTime: item.endTime,
|
||||||
|
cancellationReason: item.cancellationReason
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const result = await schedulingService.getVenueDurationByAct(Number(body.activityXid), Number(hostId));
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Slot blocked successfully',
|
||||||
|
data: result
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { SchedulingService } from '../../../services/activityScheduling.service';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
|
import { scheduleActivity } from '../../../../../common/utils/validation/host/createSchedulingOfAct.validation';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const schedulingService = new SchedulingService(prismaClient);
|
||||||
|
const hostService = new HostService(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.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate user using the shared authForHost function
|
||||||
|
const userInfo = await verifyHostToken(token);
|
||||||
|
const hostId = userInfo.id;
|
||||||
|
|
||||||
|
if (Number.isNaN(hostId)) {
|
||||||
|
throw new ApiError(400, 'Host id must be a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = await hostService.getHostIdByUserXid(hostId);
|
||||||
|
if (!host) {
|
||||||
|
throw new ApiError(404, 'Host not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: unknown;
|
||||||
|
try {
|
||||||
|
body = event.body ? JSON.parse(event.body) : {};
|
||||||
|
} catch {
|
||||||
|
throw new ApiError(400, 'Invalid JSON payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Validate payload using Zod
|
||||||
|
|
||||||
|
const parsed = scheduleActivity.safeParse(body);
|
||||||
|
|
||||||
|
if (!parsed.success) {
|
||||||
|
const msg = parsed.error.issues
|
||||||
|
.map(e => e.message)
|
||||||
|
.join(', ');
|
||||||
|
throw new ApiError(400, `Validation failed: ${msg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const activity = await schedulingService.getActivityByXid(parsed.data.activityXid);
|
||||||
|
if (!activity) {
|
||||||
|
throw new ApiError(404, "Activity not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.data.venues && parsed.data.venues.length > 0) {
|
||||||
|
for (const venue of parsed.data.venues) {
|
||||||
|
const venueExists = await schedulingService.getVenueFromVenueXid(
|
||||||
|
venue.venueXid,
|
||||||
|
parsed.data.activityXid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!venueExists) {
|
||||||
|
throw new ApiError(
|
||||||
|
404,
|
||||||
|
`Venue with xid ${venue.venueXid} not found for this activity`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await schedulingService.addSchedulingForActivity(parsed.data);
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Scheduling details updated successfully',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { verifyMinglarAdminHostToken } from '../../../../../common/middlewares/jwt/authForMinglarAdminHost';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { SchedulingService } from '../../../services/activityScheduling.service';
|
||||||
|
import { ACTIVITY_INTERNAL_STATUS } from '../../../../../common/utils/constants/host.constant';
|
||||||
|
|
||||||
|
const schedulingService = new SchedulingService(prismaClient);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /activities
|
||||||
|
* Query Parameters:
|
||||||
|
* - status: Listed | Unlisted | Not_Listed (optional - if not provided, returns all)
|
||||||
|
* - hostId: ID of host (required from token)
|
||||||
|
*
|
||||||
|
* Returns activities based on status filter
|
||||||
|
*/
|
||||||
|
export const handler = safeHandler(async (
|
||||||
|
event: APIGatewayProxyEvent,
|
||||||
|
context?: Context
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
|
// Get and verify token
|
||||||
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) {
|
||||||
|
throw new ApiError(400, 'This is a protected route. Please provide a valid token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo = await verifyMinglarAdminHostToken(token);
|
||||||
|
const userId = Number(userInfo.id);
|
||||||
|
|
||||||
|
// Get status filter from query parameters
|
||||||
|
const status = event.queryStringParameters?.status as string | undefined;
|
||||||
|
|
||||||
|
const hostId = await schedulingService.getHostIdByUserId(userId);
|
||||||
|
|
||||||
|
// Validate status if provided
|
||||||
|
const validStatuses = [ACTIVITY_INTERNAL_STATUS.ACTIVITY_APPROVED, ACTIVITY_INTERNAL_STATUS.ACTIVITY_UNLISTED, ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED];
|
||||||
|
if (status && !validStatuses.includes(status)) {
|
||||||
|
throw new ApiError(400, `Invalid status. Must be one of: ${validStatuses.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get activities from service
|
||||||
|
const activities = await schedulingService.getActivitiesByStatus(hostId, status);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Activities retrieved successfully',
|
||||||
|
data: {
|
||||||
|
total: activities.length,
|
||||||
|
activities: activities,
|
||||||
|
filter: {
|
||||||
|
status: status || 'All',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { SchedulingService } from '../../../services/activityScheduling.service';
|
||||||
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
|
|
||||||
|
const schedulingService = new SchedulingService(prismaClient);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /activities
|
||||||
|
* Query Parameters:
|
||||||
|
* - status: Listed | Unlisted | Not_Listed (optional - if not provided, returns all)
|
||||||
|
* - hostId: ID of host (required from token)
|
||||||
|
*
|
||||||
|
* Returns activities based on status filter
|
||||||
|
*/
|
||||||
|
export const handler = safeHandler(async (
|
||||||
|
event: APIGatewayProxyEvent,
|
||||||
|
context?: Context
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
|
// Get and verify token
|
||||||
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) {
|
||||||
|
throw new ApiError(400, 'This is a protected route. Please provide a valid token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo = await verifyHostToken(token);
|
||||||
|
const userId = Number(userInfo.id);
|
||||||
|
|
||||||
|
const activityXid = event.pathParameters?.activityXid
|
||||||
|
if (!activityXid) {
|
||||||
|
throw new ApiError(400, 'activityXid is required in path parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostId = await schedulingService.getHostIdByUserId(userId);
|
||||||
|
|
||||||
|
const result = await schedulingService.getVenueDurationByAct(Number(activityXid), Number(hostId));
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Details retrieved successfully',
|
||||||
|
data: result,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
APIGatewayProxyEvent,
|
||||||
|
APIGatewayProxyResult,
|
||||||
|
Context,
|
||||||
|
} from 'aws-lambda';
|
||||||
|
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 { SchedulingService } from '../../../services/activityScheduling.service';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
|
const schedulingService = new SchedulingService(prismaClient);
|
||||||
|
const hostService = new HostService(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.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate user using the shared authForHost function
|
||||||
|
const userInfo = await verifyHostToken(token);
|
||||||
|
const hostId = userInfo.id;
|
||||||
|
|
||||||
|
if (Number.isNaN(hostId)) {
|
||||||
|
throw new ApiError(400, 'Host id must be a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = await hostService.getHostIdByUserXid(hostId);
|
||||||
|
if (!host) {
|
||||||
|
throw new ApiError(404, 'Host not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: {
|
||||||
|
activityXid: number;
|
||||||
|
venueXid: number;
|
||||||
|
cancellations: { cancellationXid: number; }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
body = event.body ? JSON.parse(event.body) : {};
|
||||||
|
} catch {
|
||||||
|
throw new ApiError(400, 'Invalid JSON payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!body.activityXid ||
|
||||||
|
!body.venueXid ||
|
||||||
|
!Array.isArray(body.cancellations) ||
|
||||||
|
body.cancellations.length === 0
|
||||||
|
) {
|
||||||
|
throw new ApiError(400, 'Missing required fields');
|
||||||
|
}
|
||||||
|
|
||||||
|
const activity = await schedulingService.getActivityByXid(body.activityXid);
|
||||||
|
if (!activity) {
|
||||||
|
throw new ApiError(404, 'Activity not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const venueExists = await schedulingService.getVenueFromVenueXid(
|
||||||
|
body.venueXid,
|
||||||
|
body.activityXid,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!venueExists) {
|
||||||
|
throw new ApiError(404, `Venue not found for this activity`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await schedulingService.openCanceledSlot(
|
||||||
|
body.cancellations.map((item: any) => ({
|
||||||
|
cancellationXid: Number(item.cancellationXid),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await schedulingService.getVenueDurationByAct(
|
||||||
|
Number(body.activityXid),
|
||||||
|
Number(hostId),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Slot opened successfully',
|
||||||
|
data: result,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add suggestion handler for host applications
|
* Add suggestion handler for host applications
|
||||||
@@ -26,9 +25,8 @@ export const handler = safeHandler(async (
|
|||||||
// Verify token and get user info
|
// Verify token and get user info
|
||||||
const userInfo = await verifyHostToken(token);
|
const userInfo = await verifyHostToken(token);
|
||||||
|
|
||||||
|
// Accept agreement and get dynamic fields and PDF URL
|
||||||
// Add suggestion using service
|
const result = await hostService.acceptMinglarAgreement(userInfo.id);
|
||||||
await hostService.acceptMinglarAgreement(userInfo.id);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
@@ -39,7 +37,10 @@ export const handler = safeHandler(async (
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Application accepted successfully',
|
message: 'Application accepted successfully',
|
||||||
data: null,
|
data: {
|
||||||
|
filePath: result.filePath,
|
||||||
|
dynamicFields: result.dynamicFields,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { parseBody } from '../../../../../common/utils/validation/validation.utils';
|
import { sendWelcomeEmailToHost } from '../../../services/sendOTPEmail.service';
|
||||||
import { createPasswordSchema } from '../../../../../common/utils/validation/host/auth.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
@@ -24,10 +22,33 @@ export const handler = safeHandler(async (
|
|||||||
const userInfo = await verifyHostToken(token);
|
const userInfo = await verifyHostToken(token);
|
||||||
const user_xid = userInfo.id;
|
const user_xid = userInfo.id;
|
||||||
|
|
||||||
// Parse and validate request body using Zod
|
// Parse request body
|
||||||
const { password } = parseBody(event.body, createPasswordSchema);
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
await hostService.createPassword(user_xid, password);
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await hostService.createPassword(user_xid, password);
|
||||||
|
await sendWelcomeEmailToHost(result.emailAddress);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { MinglarService } from '../../../../minglaradmin/services/minglar.service';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { verifyMinglarAdminHostToken } from '../../../../../common/middlewares/jwt/authForMinglarAdminHost';
|
||||||
|
|
||||||
|
const minglarService = new MinglarService(prismaClient);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get suggestions handler
|
||||||
|
* Retrieves suggestions based on user's role and host assignments
|
||||||
|
*/
|
||||||
|
export const handler = safeHandler(async (
|
||||||
|
event: APIGatewayProxyEvent,
|
||||||
|
context?: Context
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
|
// ✅ Verify authentication token
|
||||||
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||||
|
if (!token) {
|
||||||
|
throw new ApiError(401, 'This is a protected route. Please provide a valid token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Verify token and extract user info
|
||||||
|
const userInfo = await verifyMinglarAdminHostToken(token);
|
||||||
|
|
||||||
|
// ✅ Extract activityXid from query parameters
|
||||||
|
const activityXidParam = event.queryStringParameters?.activityXid;
|
||||||
|
if (!activityXidParam) {
|
||||||
|
throw new ApiError(400, 'Missing required query parameter: activityXid');
|
||||||
|
}
|
||||||
|
|
||||||
|
const activityXid = Number(activityXidParam);
|
||||||
|
if (isNaN(activityXid) || activityXid <= 0) {
|
||||||
|
throw new ApiError(400, 'Invalid activityXid provided. Must be a positive number.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Fetch suggestions from the service
|
||||||
|
const suggestions = await minglarService.getHostSuggestionsForActivity(activityXid);
|
||||||
|
|
||||||
|
// ✅ Return response
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Suggestions retrieved successfully',
|
||||||
|
data: suggestions,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { verifyMinglarAdminHostToken } from '../../../../../common/middlewares/jwt/authForMinglarAdminHost';
|
||||||
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
|
import { HostService } from '../../../services/host.service';
|
||||||
|
|
||||||
|
const hostService = new HostService(prismaClient);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get latest active agreement for a specific host by hostXid.
|
||||||
|
* Accessible for Minglar Admin / Host Admin using admin-host token.
|
||||||
|
*/
|
||||||
|
export const handler = safeHandler(async (
|
||||||
|
event: APIGatewayProxyEvent,
|
||||||
|
context?: Context,
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
|
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.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate admin/host admin token
|
||||||
|
await verifyMinglarAdminHostToken(token);
|
||||||
|
|
||||||
|
const hostXidParam =
|
||||||
|
event.queryStringParameters?.hostXid ?? event.queryStringParameters?.host_xid;
|
||||||
|
|
||||||
|
const hostXid = Number(hostXidParam);
|
||||||
|
|
||||||
|
if (!hostXidParam) {
|
||||||
|
throw new ApiError(400, 'hostXid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isNaN(hostXid)) {
|
||||||
|
throw new ApiError(400, 'Invalid hostXid format');
|
||||||
|
}
|
||||||
|
|
||||||
|
const agreement = await hostService.getLatestHostAgreement(hostXid);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Latest host agreement retrieved successfully',
|
||||||
|
data: agreement,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
@@ -1,26 +1,36 @@
|
|||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { TokenService } from '../../../services/token.service';
|
import { TokenService } from '../../../services/token.service';
|
||||||
import { GetHostLoginResponseDTO } from '../../../dto/host.dto';
|
import { GetHostLoginResponseDTO } from '../../../dto/host.dto';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import * as bcrypt from 'bcryptjs';
|
|
||||||
import { parseBody } from '../../../../../common/utils/validation/validation.utils';
|
|
||||||
import { hostLoginSchema } from '../../../../../common/utils/validation/host/auth.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
const tokenService = new TokenService(prismaClient);
|
||||||
const tokenService = new TokenService(prismaService);
|
|
||||||
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<APIGatewayProxyResult> => {
|
): Promise<APIGatewayProxyResult> => {
|
||||||
// Parse and validate request body using Zod
|
// Parse request body
|
||||||
const { emailAddress, userPassword } = parseBody(event.body, hostLoginSchema);
|
let body: { emailAddress?: string; userPassword?: string };
|
||||||
|
|
||||||
const loginForHost = await hostService.loginForHost(emailAddress, userPassword);
|
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 emailToLowerCase = emailAddress.toLowerCase()
|
||||||
|
|
||||||
|
const loginForHost = await hostService.loginForHost(emailToLowerCase, userPassword);
|
||||||
|
|
||||||
if (!loginForHost) {
|
if (!loginForHost) {
|
||||||
throw new ApiError(400, 'Failed to login');
|
throw new ApiError(400, 'Failed to login');
|
||||||
@@ -30,15 +40,6 @@ export const handler = safeHandler(async (
|
|||||||
throw new ApiError(401, 'Invalid credentials');
|
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(
|
const generateTokenForHost = await tokenService.generateAuthToken(
|
||||||
loginForHost.id
|
loginForHost.id
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { MinglarService } from '../../../../minglaradmin/services/minglar.service';
|
import { MinglarService } from '../../../../minglaradmin/services/minglar.service';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const minglarService = new MinglarService(prismaClient);
|
||||||
const minglarService = new MinglarService(prismaService);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get suggestions handler
|
* Get suggestions handler
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import * as bcrypt from 'bcryptjs';
|
||||||
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
|
import { ROLE } from '../../../../../common/utils/constants/common.constant';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import * as bcrypt from 'bcryptjs';
|
|
||||||
import { OtpGeneratorSixDigit } from '../../../../../common/utils/helper/OtpGenerator';
|
import { OtpGeneratorSixDigit } from '../../../../../common/utils/helper/OtpGenerator';
|
||||||
import { encryptUserId } from '../../../../../common/utils/helper/CodeGenerator';
|
import { sendOtpEmailForHost } from '@/modules/host/services/sendOTPEmail.service';
|
||||||
import { HostService } from '../../../services/host.service';
|
|
||||||
import { sendOtpEmailForHost } from '../../../services/sendOTPEmail.service';
|
|
||||||
import { ROLE } from '../../../../../common/utils/constants/common.constant';
|
|
||||||
import { parseBody } from '../../../../../common/utils/validation/validation.utils';
|
|
||||||
import { hostSignUpSchema } from '../../../../../common/utils/validation/host/auth.validation';
|
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
export async function generateHostRefNumber(tx: any) {
|
export async function generateHostRefNumber(tx: any) {
|
||||||
const lastrecord = await tx.user.findFirst({
|
const lastrecord = await tx.user.findFirst({
|
||||||
@@ -26,21 +19,45 @@ export async function generateHostRefNumber(tx: any) {
|
|||||||
|
|
||||||
const nextId = lastrecord ? lastrecord.id + 1 : 1;
|
const nextId = lastrecord ? lastrecord.id + 1 : 1;
|
||||||
|
|
||||||
return `HS-${String(nextId).padStart(6, '0')}`;;
|
return `076-H-${String(nextId).padStart(6, '0')}`;;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<APIGatewayProxyResult> => {
|
): Promise<APIGatewayProxyResult> => {
|
||||||
// Parse and validate request body using Zod
|
// Parse request body
|
||||||
const { email } = parseBody(event.body, hostSignUpSchema);
|
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 emailToLowerCase = email.trim().toLowerCase();
|
||||||
|
|
||||||
|
if (!emailToLowerCase) {
|
||||||
|
throw new ApiError(400, 'Email is required');
|
||||||
|
}
|
||||||
|
|
||||||
// Use a single transaction for user creation/lookup and OTP storage
|
// Use a single transaction for user creation/lookup and OTP storage
|
||||||
const transactionResult = await prismaService.$transaction(async (tx) => {
|
const transactionResult = await prismaClient.$transaction(async (tx) => {
|
||||||
const user = await tx.user.findUnique({
|
const user = await tx.user.findUnique({
|
||||||
where: { emailAddress: email },
|
where: { emailAddress: emailToLowerCase },
|
||||||
select: { emailAddress: true, id: true, userPassword: true },
|
select: {
|
||||||
|
emailAddress: true,
|
||||||
|
id: true,
|
||||||
|
userPassword: true,
|
||||||
|
dataConsentAccepted: true,
|
||||||
|
dataConsentAcceptedOn: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user && user.userPassword) {
|
if (user && user.userPassword) {
|
||||||
@@ -57,7 +74,7 @@ export const handler = safeHandler(async (
|
|||||||
} else {
|
} else {
|
||||||
// create new user record within the transaction
|
// create new user record within the transaction
|
||||||
newUserLocal = await tx.user.create({
|
newUserLocal = await tx.user.create({
|
||||||
data: { emailAddress: email, roleXid: ROLE.HOST, userRefNumber: referenceNumber },
|
data: { emailAddress: emailToLowerCase, roleXid: ROLE.HOST, userRefNumber: referenceNumber },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +99,18 @@ export const handler = safeHandler(async (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const encryptedId = encryptUserId(String(newUserLocal.id));
|
await tx.user.update({
|
||||||
|
where: { id: Number(newUserLocal.id) },
|
||||||
|
data: {
|
||||||
|
dataConsentAccepted: true,
|
||||||
|
dataConsentAcceptedOn:
|
||||||
|
user?.dataConsentAccepted && user?.dataConsentAcceptedOn
|
||||||
|
? user.dataConsentAcceptedOn
|
||||||
|
: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return { newUser: newUserLocal, otp, encryptedId };
|
return { newUser: newUserLocal, otp };
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!transactionResult || !transactionResult.otp) {
|
if (!transactionResult || !transactionResult.otp) {
|
||||||
@@ -92,7 +118,7 @@ export const handler = safeHandler(async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send OTP email outside the DB transaction
|
// Send OTP email outside the DB transaction
|
||||||
// await sendOtpEmailForHost(transactionResult.newUser.emailAddress, transactionResult.otp);
|
await sendOtpEmailForHost(transactionResult.newUser.emailAddress, transactionResult.otp);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// modules/host/handlers/addCompanyDetails.ts
|
// modules/host/handlers/addCompanyDetails.ts
|
||||||
import config from '@/config/config';
|
import config from '../../../../../config/config';
|
||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
||||||
import AWS from 'aws-sdk';
|
import AWS from 'aws-sdk';
|
||||||
import Busboy from 'busboy';
|
import Busboy from 'busboy';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import {
|
import {
|
||||||
@@ -15,8 +15,17 @@ import {
|
|||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import { sendEmailToAM, sendEmailToMinglarAdmin } from '../../../services/sendHostResubmitEmailToAM.service';
|
import { sendEmailToAM, sendEmailToMinglarAdmin } from '../../../services/sendHostResubmitEmailToAM.service';
|
||||||
|
|
||||||
const prisma = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prisma);
|
|
||||||
|
function getExtensionFromMime(mimeType: string) {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
'image/jpeg': 'jpg',
|
||||||
|
'image/png': 'png',
|
||||||
|
'application/pdf': 'pdf',
|
||||||
|
'image/webp': 'webp',
|
||||||
|
};
|
||||||
|
return map[mimeType] || 'bin';
|
||||||
|
}
|
||||||
|
|
||||||
const s3 = new AWS.S3({
|
const s3 = new AWS.S3({
|
||||||
region: config.aws.region,
|
region: config.aws.region,
|
||||||
@@ -50,6 +59,25 @@ function cleanEmptyStrings(obj: any) {
|
|||||||
return cleaned;
|
return cleaned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getS3KeyFromUrl(url: string): string {
|
||||||
|
const base = `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/`;
|
||||||
|
return url.replace(base, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteFromS3(key: string) {
|
||||||
|
try {
|
||||||
|
await s3.deleteObject({
|
||||||
|
Bucket: config.aws.bucketName,
|
||||||
|
Key: key
|
||||||
|
}).promise();
|
||||||
|
|
||||||
|
console.log("✅ Deleted from S3:", key);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("❌ Failed to delete from S3:", key, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -112,6 +140,13 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
bb.end();
|
bb.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const deletedFiles = normalizeJsonField(fields, "deletedFiles") || [];
|
||||||
|
const parentDeletedFiles = normalizeJsonField(fields, "parentDeletedFiles") || [];
|
||||||
|
const deleteCompanyLogo =
|
||||||
|
fields.deleteCompanyLogo === 'true' || fields.deleteCompanyLogo === true;
|
||||||
|
const deleteParentCompanyLogo =
|
||||||
|
fields.deleteParentCompanyLogo === 'true' || fields.deleteParentCompanyLogo === true;
|
||||||
|
|
||||||
/** 4) Extract and clean isDraft flag */
|
/** 4) Extract and clean isDraft flag */
|
||||||
const isDraft = fields.isDraft === 'true' || fields.isDraft === true;
|
const isDraft = fields.isDraft === 'true' || fields.isDraft === true;
|
||||||
|
|
||||||
@@ -128,18 +163,59 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
companyDetailsRaw.parentCompany &&
|
||||||
|
Object.values(companyDetailsRaw.parentCompany).every(
|
||||||
|
(v) => v === undefined || v === null
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
companyDetailsRaw.parentCompany = null;
|
||||||
|
}
|
||||||
|
|
||||||
/** 6) Profile update if provided */
|
/** 6) Profile update if provided */
|
||||||
if (fields.userProfile) {
|
if (fields.userProfile) {
|
||||||
const userProfileRaw = normalizeJsonField(fields, 'userProfile');
|
const userProfileRaw = normalizeJsonField(fields, 'userProfile');
|
||||||
if (userProfileRaw) {
|
if (userProfileRaw) {
|
||||||
const { firstName, lastName, mobileNumber } = userProfileRaw;
|
const firstName =
|
||||||
|
typeof userProfileRaw.firstName === 'string'
|
||||||
|
? userProfileRaw.firstName.trim()
|
||||||
|
: undefined;
|
||||||
|
const lastName =
|
||||||
|
typeof userProfileRaw.lastName === 'string'
|
||||||
|
? userProfileRaw.lastName.trim()
|
||||||
|
: undefined;
|
||||||
|
const mobileNumber =
|
||||||
|
typeof userProfileRaw.mobileNumber === 'string'
|
||||||
|
? userProfileRaw.mobileNumber.trim()
|
||||||
|
: undefined;
|
||||||
|
|
||||||
await prisma.user.update({
|
if (mobileNumber) {
|
||||||
|
const existingUser = await prismaClient.user.findFirst({
|
||||||
|
where: {
|
||||||
|
mobileNumber,
|
||||||
|
id: {
|
||||||
|
not: Number(userInfo.id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
throw new ApiError(
|
||||||
|
409,
|
||||||
|
'Mobile number already exists for another user. Please use a different mobile number.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await prismaClient.user.update({
|
||||||
where: { id: userInfo.id },
|
where: { id: userInfo.id },
|
||||||
data: {
|
data: {
|
||||||
...(firstName && { firstName }),
|
...(firstName !== undefined && { firstName }),
|
||||||
...(lastName && { lastName }),
|
...(lastName !== undefined && { lastName }),
|
||||||
...(mobileNumber && { mobileNumber }),
|
...(mobileNumber !== undefined && { mobileNumber }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -178,13 +254,8 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
const file = files.find((f) => f.fieldName === doc.fieldName);
|
const file = files.find((f) => f.fieldName === doc.fieldName);
|
||||||
|
|
||||||
// In DRAFT mode → allow missing documents
|
// In DRAFT mode → allow missing documents
|
||||||
if (isDraft && !file) {
|
|
||||||
return { ...doc, file: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
// In FINAL mode → file must exist
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
throw new ApiError(400, `File not found for field: ${doc.fieldName}`);
|
return { ...doc, file: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...doc, file };
|
return { ...doc, file };
|
||||||
@@ -213,9 +284,65 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
parsedParentCompany = parsedCompany.parentCompany || null;
|
parsedParentCompany = parsedCompany.parentCompany || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 9.5) DELETE DOCUMENTS IF REQUESTED **/
|
||||||
|
if (Array.isArray(deletedFiles) && deletedFiles.length > 0) {
|
||||||
|
console.log(`🗑️ Deleting ${deletedFiles.length} documents...`);
|
||||||
|
|
||||||
|
for (const del of deletedFiles) {
|
||||||
|
const id = Number(del.id);
|
||||||
|
const url = del.url;
|
||||||
|
|
||||||
|
if (!id || !url) {
|
||||||
|
console.log("❌ Invalid delete entry:", del);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract S3 key
|
||||||
|
const s3Key = getS3KeyFromUrl(url);
|
||||||
|
|
||||||
|
// Delete from S3
|
||||||
|
await deleteFromS3(s3Key);
|
||||||
|
|
||||||
|
// Delete from DB
|
||||||
|
await prismaClient.hostDocuments.delete({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🗑️ Deleted host document ID ${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 9.6) DELETE PARENT DOCUMENTS **/
|
||||||
|
if (parsedCompany.isSubsidairy && Array.isArray(parentDeletedFiles) && parentDeletedFiles.length > 0) {
|
||||||
|
console.log(`🗑️ Deleting ${parentDeletedFiles.length} PARENT documents...`);
|
||||||
|
|
||||||
|
for (const del of parentDeletedFiles) {
|
||||||
|
const id = Number(del.id);
|
||||||
|
const url = del.url;
|
||||||
|
|
||||||
|
if (!id || !url) {
|
||||||
|
console.log("⚠️ Invalid parent delete entry:", del);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s3Key = getS3KeyFromUrl(url);
|
||||||
|
|
||||||
|
// Delete S3
|
||||||
|
await deleteFromS3(s3Key);
|
||||||
|
|
||||||
|
// Delete DB
|
||||||
|
await prismaClient.hostParenetDocuments.delete({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🗑️ Deleted PARENT document ID ${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 11) UPLOAD DOCUMENTS */
|
/** 11) UPLOAD DOCUMENTS */
|
||||||
async function uploadToS3(buffer, mimeType, originalName, folderType, documentTypeXid?, fieldName?) {
|
async function uploadToS3(buffer, mimeType, originalName, folderType, documentTypeXid?, fieldName?) {
|
||||||
const ext = originalName.split('.').pop() || 'jpg';
|
// const ext = originalName.split('.').pop() || 'jpg';
|
||||||
|
const ext = getExtensionFromMime(mimeType);
|
||||||
|
|
||||||
let s3Key = '';
|
let s3Key = '';
|
||||||
|
|
||||||
@@ -249,7 +376,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
/** Upload host docs */
|
/** Upload host docs */
|
||||||
const uploadedHostDocs: Array<any> = [];
|
const uploadedHostDocs: Array<any> = [];
|
||||||
for (const doc of hostDocs) {
|
for (const doc of hostDocs) {
|
||||||
if (isDraft && !doc.file) continue;
|
if (!doc.file) continue;
|
||||||
|
|
||||||
const path = await uploadToS3(
|
const path = await uploadToS3(
|
||||||
doc.file.buffer,
|
doc.file.buffer,
|
||||||
@@ -270,7 +397,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
/** Upload parent docs */
|
/** Upload parent docs */
|
||||||
const uploadedParentDocs: Array<any> = [];
|
const uploadedParentDocs: Array<any> = [];
|
||||||
for (const doc of parentDocs) {
|
for (const doc of parentDocs) {
|
||||||
if (!doc.file && isDraft) continue; // skip missing files in draft mode
|
if (!doc.file) continue; // skip missing files in draft mode
|
||||||
|
|
||||||
const path = await uploadToS3(
|
const path = await uploadToS3(
|
||||||
doc.file.buffer,
|
doc.file.buffer,
|
||||||
@@ -288,32 +415,125 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** DELETE EXISTING LOGO IF REQUESTED */
|
||||||
|
if (deleteCompanyLogo) {
|
||||||
|
const existingHost = await prismaClient.hostHeader.findFirst({
|
||||||
|
where: { userXid: userInfo.id },
|
||||||
|
select: { logoPath: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingHost?.logoPath) {
|
||||||
|
try {
|
||||||
|
const s3Key = getS3KeyFromUrl(existingHost.logoPath);
|
||||||
|
await deleteFromS3(s3Key);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('S3 delete failed for company logo:', existingHost.logoPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedCompany.logoPath = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** DELETE EXISTING PARENT COMPANY LOGO IF REQUESTED */
|
||||||
|
if (deleteParentCompanyLogo && parsedCompany.isSubsidairy) {
|
||||||
|
const existingHost = await prismaClient.hostHeader.findFirst({
|
||||||
|
where: { userXid: userInfo.id },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
hostParent: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
logoPath: true,
|
||||||
|
},
|
||||||
|
take: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingParent = Array.isArray(existingHost?.hostParent)
|
||||||
|
? existingHost.hostParent[0]
|
||||||
|
: existingHost?.hostParent;
|
||||||
|
|
||||||
|
if (existingParent?.logoPath) {
|
||||||
|
try {
|
||||||
|
const s3Key = getS3KeyFromUrl(existingParent.logoPath);
|
||||||
|
await deleteFromS3(s3Key);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('S3 delete failed for parent company logo:', existingParent.logoPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedParentCompany) {
|
||||||
|
parsedParentCompany.logoPath = null;
|
||||||
|
} else {
|
||||||
|
parsedParentCompany = {
|
||||||
|
logoPath: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** UPLOAD LOGO (if provided) */
|
/** UPLOAD LOGO (if provided) */
|
||||||
const logoFile = files.find((f) => f.fieldName === 'companyLogo' || f.fieldName === 'companyLogoFile');
|
const logoFile = files.find(
|
||||||
if (logoFile) {
|
(f) => f.fieldName === 'companyLogo' || f.fieldName === 'companyLogoFile'
|
||||||
const logoUrl = await uploadToS3(logoFile.buffer, logoFile.mimeType, logoFile.fileName, 'logo');
|
);
|
||||||
|
|
||||||
|
if (logoFile && logoFile.buffer && logoFile.fileName) {
|
||||||
|
const logoUrl = await uploadToS3(
|
||||||
|
logoFile.buffer,
|
||||||
|
logoFile.mimeType,
|
||||||
|
logoFile.fileName,
|
||||||
|
'logo'
|
||||||
|
);
|
||||||
parsedCompany.logoPath = logoUrl;
|
parsedCompany.logoPath = logoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** UPLOAD PARENT COMPANY LOGO (if provided) */
|
/** UPLOAD PARENT COMPANY LOGO (if provided) */
|
||||||
const parentLogoFile = files.find((f) => f.fieldName === 'parentCompanyLogo');
|
const parentLogoFile = files.find(
|
||||||
if (parentLogoFile) {
|
(f) => f.fieldName === 'parentCompanyLogo'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (parentLogoFile && parentLogoFile.buffer && parentLogoFile.mimeType) {
|
||||||
|
// 🔒 Only upload when an actual file is present
|
||||||
const parentLogoUrl = await uploadToS3(
|
const parentLogoUrl = await uploadToS3(
|
||||||
parentLogoFile.buffer,
|
parentLogoFile.buffer,
|
||||||
parentLogoFile.mimeType,
|
parentLogoFile.mimeType,
|
||||||
parentLogoFile.fileName,
|
parentLogoFile.fileName, // safe here because it's a real file
|
||||||
'parent_company_logo',
|
'parent_company_logo',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (parsedParentCompany) {
|
if (parsedParentCompany) {
|
||||||
parsedParentCompany.logoPath = parentLogoUrl;
|
parsedParentCompany.logoPath = parentLogoUrl;
|
||||||
} else {
|
} else {
|
||||||
// if no parent object exists yet (drafts or other flows), attach it safely
|
parsedParentCompany = {
|
||||||
parsedParentCompany = parsedParentCompany || {};
|
logoPath: parentLogoUrl,
|
||||||
parsedParentCompany.logoPath = parentLogoUrl;
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsedCompany.cityXid) {
|
||||||
|
const city = await prismaClient.cities.findUnique({
|
||||||
|
where: { id: Number(parsedCompany.cityXid) }
|
||||||
|
});
|
||||||
|
if (!city) {
|
||||||
|
throw new ApiError(400, `City with ID ${parsedCompany.cityXid} not found`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsedCompany.isSubsidairy) {
|
||||||
|
const parentDocuments = await hostService.getParentDocumentsByHostId(userInfo.id);
|
||||||
|
if (parentDocuments.length > 0) {
|
||||||
|
for (const doc of parentDocuments) {
|
||||||
|
try {
|
||||||
|
const s3Key = getS3KeyFromUrl(doc.filePath);
|
||||||
|
await deleteFromS3(s3Key);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("S3 delete failed:", doc.filePath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await hostService.deleteExistingParentRecords(userInfo.id)
|
||||||
|
}
|
||||||
|
|
||||||
/** 12) SAVE / UPDATE HOST ENTRY */
|
/** 12) SAVE / UPDATE HOST ENTRY */
|
||||||
const createdOrUpdated = await hostService.addOrUpdateCompanyDetails(
|
const createdOrUpdated = await hostService.addOrUpdateCompanyDetails(
|
||||||
userInfo.id,
|
userInfo.id,
|
||||||
@@ -322,6 +542,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|||||||
parsedParentCompany,
|
parsedParentCompany,
|
||||||
uploadedParentDocs,
|
uploadedParentDocs,
|
||||||
isDraft,
|
isDraft,
|
||||||
|
{ deleteCompanyLogo, deleteParentCompanyLogo },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!createdOrUpdated) throw new ApiError(400, 'Failed to add/update company details.');
|
if (!createdOrUpdated) throw new ApiError(400, 'Failed to add/update company details.');
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||||
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
|
||||||
import { PrismaService } from '../../../../../common/database/prisma.service';
|
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
|
||||||
import { HostService } from '../../../services/host.service';
|
import { HostService } from '../../../services/host.service';
|
||||||
import ApiError from '../../../../../common/utils/helper/ApiError';
|
import ApiError from '../../../../../common/utils/helper/ApiError';
|
||||||
import { verifyHostToken } from '@/common/middlewares/jwt/authForHost';
|
import { verifyHostToken } from '../../../../../common/middlewares/jwt/authForHost';
|
||||||
import { hostBankDetailsSchema } from '@/common/utils/validation/host/addPaymentDetails.validation';
|
import { hostBankDetailsSchema } from '../../../../../common/utils/validation/host/addPaymentDetails.validation';
|
||||||
|
|
||||||
const prismaService = new PrismaService();
|
const hostService = new HostService(prismaClient);
|
||||||
const hostService = new HostService(prismaService);
|
|
||||||
|
|
||||||
export const handler = safeHandler(async (
|
export const handler = safeHandler(async (
|
||||||
event: APIGatewayProxyEvent,
|
event: APIGatewayProxyEvent,
|
||||||
@@ -33,7 +32,7 @@ export const handler = safeHandler(async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse request body
|
// Parse request body
|
||||||
let body: { bankXid?: number; bankBranchXid?: number; accountNumber?: string; confirmAccountNumber?: string; accountHolderName?: string; ifscCode?: string; currencyXid?: number };
|
let body: { bankXid?: number; bankBranchXid?: number; accountNumber?: string; confirmAccountNumber?: string; accountHolderName?: string; currencyXid?: number };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
body = event.body ? JSON.parse(event.body) : {};
|
body = event.body ? JSON.parse(event.body) : {};
|
||||||
@@ -44,7 +43,7 @@ export const handler = safeHandler(async (
|
|||||||
// ✅ Validate payload using Zod
|
// ✅ Validate payload using Zod
|
||||||
const validationResult = hostBankDetailsSchema.safeParse({
|
const validationResult = hostBankDetailsSchema.safeParse({
|
||||||
...(body as object),
|
...(body as object),
|
||||||
hostXid: host.id, // inject hostId from token (not from user input)
|
hostXid: host.host.id, // inject hostId from token (not from user input)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!validationResult.success) {
|
if (!validationResult.success) {
|
||||||
@@ -54,7 +53,16 @@ export const handler = safeHandler(async (
|
|||||||
|
|
||||||
const validatedData = validationResult.data;
|
const validatedData = validationResult.data;
|
||||||
|
|
||||||
await hostService.addPaymentDetails(validatedData);
|
// Fetch IFSC code from bank branch
|
||||||
|
const bankBranch = await hostService.getBankBranchById(validatedData.bankBranchXid);
|
||||||
|
if (!bankBranch) {
|
||||||
|
throw new ApiError(404, 'Bank branch not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
await hostService.addPaymentDetails({
|
||||||
|
...validatedData,
|
||||||
|
ifscCode: bankBranch.ifscCode,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user