From b049146664315ab1a73376dfe92d530ac833533d Mon Sep 17 00:00:00 2001 From: Mayank Mishra Date: Fri, 5 Dec 2025 13:45:49 +0530 Subject: [PATCH] Refactor serverless configuration to rename service, add Prisma layer support, and include AM_INVITATION_LINK in environment variables. Update validation schema and email invitation content for improved user experience. --- layers/prisma/nodejs/package-lock.json | 228 ++++++++++++++++++ layers/prisma/nodejs/package.json | 10 + serverless.yml | 41 +++- .../host/hostCompanyDetails.validation.ts | 2 - src/config/config.ts | 3 + .../settings/teammates/inviteTeammate.ts | 3 +- .../services/inviteTeammatesEmail.service.ts | 17 +- 7 files changed, 287 insertions(+), 17 deletions(-) create mode 100644 layers/prisma/nodejs/package-lock.json create mode 100644 layers/prisma/nodejs/package.json diff --git a/layers/prisma/nodejs/package-lock.json b/layers/prisma/nodejs/package-lock.json new file mode 100644 index 0000000..389329f --- /dev/null +++ b/layers/prisma/nodejs/package-lock.json @@ -0,0 +1,228 @@ +{ + "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" + } + }, + "node_modules/@prisma/adapter-pg": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.0.1.tgz", + "integrity": "sha512-01GpPPhLMoDMF4ipgfZz0L87fla/TV/PBQcmHy+9vV1ml6gUoqF8dUIRNI5Yf2YKpOwzQg9sn8C7dYD1Yio9Ug==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/driver-adapter-utils": "7.0.1", + "pg": "^8.16.3", + "postgres-array": "3.0.4" + } + }, + "node_modules/@prisma/client": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.0.1.tgz", + "integrity": "sha512-O74T6xcfaGAq5gXwCAvfTLvI6fmC3and2g5yLRMkNjri1K8mSpEgclDNuUWs9xj5AwNEMQ88NeD3asI+sovm1g==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/client-runtime-utils": "7.0.1" + }, + "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.0.1", + "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.0.1.tgz", + "integrity": "sha512-R26BVX9D/iw4toUmZKZf3jniM/9pMGHHdZN5LVP2L7HNiCQKNQQx/9LuMtjepbgRqSqQO3oHN0yzojHLnKTGEw==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/debug": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.0.1.tgz", + "integrity": "sha512-5+25XokVeAK2Z2C9W457AFw7Hk032Q3QI3G58KYKXPlpgxy+9FvV1+S1jqfJ2d4Nmq9LP/uACrM6OVhpJMSr8w==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/driver-adapter-utils": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.0.1.tgz", + "integrity": "sha512-sBbxm/yysHLLF2iMAB+qcX/nn3WFgsiC4DQNz0uM6BwGSIs8lIvgo0u8nR9nxe5gvFgKiIH8f4z2fgOEMeXc8w==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.0.1" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "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==", + "license": "ISC", + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "ISC", + "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==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/layers/prisma/nodejs/package.json b/layers/prisma/nodejs/package.json new file mode 100644 index 0000000..47d0046 --- /dev/null +++ b/layers/prisma/nodejs/package.json @@ -0,0 +1,10 @@ +{ + "name": "prisma-layer", + "version": "1.0.0", + "description": "Lambda layer for Prisma 7 with pg driver adapter", + "dependencies": { + "@prisma/client": "^7.0.1", + "@prisma/adapter-pg": "^7.0.1", + "pg": "^8.13.0" + } +} diff --git a/serverless.yml b/serverless.yml index 04eba8f..098b89b 100644 --- a/serverless.yml +++ b/serverless.yml @@ -1,16 +1,20 @@ -service: minglarDev - +service: minglar + provider: name: aws runtime: nodejs22.x region: ap-south-1 versionFunctions: false memorySize: 512 + # Apply Prisma layer to all functions + # Use the published layer version ARN (works for full deploy and `deploy function`) + layers: + - ${cf:${self:service}-${sls:stage}.PrismaLambdaLayerQualifiedArn} apiGateway: binaryMediaTypes: - '*/*' minimumCompressionSize: 1024 - + environment: DATABASE_URL: ${env:DATABASE_URL} DB_USERNAME: ${env:DB_USERNAME} @@ -38,7 +42,8 @@ provider: 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} + iam: role: statements: @@ -51,23 +56,37 @@ provider: Resource: - 'arn:aws:s3:::${env:S3_BUCKET_NAME}' - 'arn:aws:s3:::${env:S3_BUCKET_NAME}/*' - + custom: + serverless-offline: + reloadHandler: true + +build: esbuild: bundle: true minify: true sourcemap: false target: node20 platform: node - concurrency: 5 external: + # These are provided by the Prisma layer - '@prisma/client' + - '@prisma/adapter-pg' - '.prisma' + - 'pg' exclude: - 'aws-sdk' - serverless-offline: - reloadHandler: true - + +# 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: individually: true patterns: @@ -82,13 +101,13 @@ package: - '!*.config.js' - '!.git/**' - '!.github/**' - + # Import function definitions from separate files organized by module functions: - ${file(./serverless/functions/host.yml)} - ${file(./serverless/functions/minglaradmin.yml)} - ${file(./serverless/functions/prepopulate.yml)} - ${file(./serverless/functions/pqq.yml)} - + plugins: - serverless-offline \ No newline at end of file diff --git a/src/common/utils/validation/host/hostCompanyDetails.validation.ts b/src/common/utils/validation/host/hostCompanyDetails.validation.ts index 17d0959..0012e4e 100644 --- a/src/common/utils/validation/host/hostCompanyDetails.validation.ts +++ b/src/common/utils/validation/host/hostCompanyDetails.validation.ts @@ -25,8 +25,6 @@ export const parentCompanySchema = z.object({ .max(400, "Logo path cannot exceed 400 characters") .optional(), - isSubsidairy: z.boolean().optional(), - registrationNumber: z.string() .max(30, "Registration number cannot exceed 30 characters") .optional(), diff --git a/src/config/config.ts b/src/config/config.ts index d7c2d66..9c7097c 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -81,6 +81,8 @@ const envVarsSchema = yup DB_PORT: yup.number().default(3306).required('DB Port is required'), //OTP Bypass 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') }) .noUnknown(true); @@ -158,6 +160,7 @@ function getConfig() { //Minglar admin MinglarAdminEmail: envVars.MINGLAR_ADMIN_EMAIL, MinglarAdminName: envVars.MINGLAR_ADMIN_NAME, + AM_INVITATION_LINK: envVars.AM_INVITATION_LINK, // oneSignal: { // appID: envVars.ONESIGNAL_APPID, // restApiKey: envVars.ONESIGNAL_REST_APIKEY, diff --git a/src/modules/minglaradmin/handlers/settings/teammates/inviteTeammate.ts b/src/modules/minglaradmin/handlers/settings/teammates/inviteTeammate.ts index d5c0c8c..b285571 100644 --- a/src/modules/minglaradmin/handlers/settings/teammates/inviteTeammate.ts +++ b/src/modules/minglaradmin/handlers/settings/teammates/inviteTeammate.ts @@ -6,6 +6,7 @@ import { safeHandler } from '../../../../../common/utils/handlers/safeHandler'; import ApiError from '../../../../../common/utils/helper/ApiError'; import { sendInvitationEmailForMinglarAdmin } from '../../../services/inviteTeammatesEmail.service'; import { MinglarService } from '../../../services/minglar.service'; +import config from '../../../../../config/config'; const prismaService = new PrismaService(); const minglarService = new MinglarService(prismaService); @@ -87,7 +88,7 @@ export const handler = safeHandler(async ( ); // send email after transaction commits - await sendInvitationEmailForMinglarAdmin(emailAddress); + await sendInvitationEmailForMinglarAdmin(emailAddress, config.AM_INVITATION_LINK); return { statusCode: 200, diff --git a/src/modules/minglaradmin/services/inviteTeammatesEmail.service.ts b/src/modules/minglaradmin/services/inviteTeammatesEmail.service.ts index e0c39b8..368880e 100644 --- a/src/modules/minglaradmin/services/inviteTeammatesEmail.service.ts +++ b/src/modules/minglaradmin/services/inviteTeammatesEmail.service.ts @@ -3,6 +3,7 @@ import ApiError from "@/common/utils/helper/ApiError"; export async function sendInvitationEmailForMinglarAdmin( emailAddress: string, + link: string ): Promise<{ sent: boolean; // messageId: string @@ -11,9 +12,19 @@ export async function sendInvitationEmailForMinglarAdmin( const subject = "Minglar Admin: Your Team Invitation"; const htmlContent = ` -

Dear,

-

You are invited to join the Minglar Admin team. Please click the link below to create your account.

-

Best regards,
Minglar Admin Team

+

Hi there,

+

We're excited to invite you to join the Minglar Admin Team!
+Please use the link below to set up your account and get started.

+ +

Access Your Invitation:
+${link}

+ +

If you have any questions or need assistance, feel free to reach out — we’re here to help.
+We look forward to having you on board!

+ +

Welcome aboard!
+The Minglar Admin Team

+ `; try {