diff --git a/.gitignore b/.gitignore index 76acaa4..43243ce 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ node_modules **.pem .vscode .terraform -*.tfvars \ No newline at end of file +*.tfvars diff --git a/auth-service/db/connect.js b/auth-service/db/connect.js index 84e2f8d..c0b2153 100644 --- a/auth-service/db/connect.js +++ b/auth-service/db/connect.js @@ -2,7 +2,7 @@ import mongoose from "mongoose" import { InternalServerError } from "#shared/errors/errors.js"; import { SecretService } from "#shared/utils/index.js" -const secret = await SecretService.getSecret("prod/cr-user-sv"); +const secret = await SecretService.getSecret(process.env.SERVICE_SECRET_NAME); const MONGO_USERNAME = secret.MONGO_USERNAME || process.env.MONGO_USERNAME; const MONGO_PASSWORD = secret.MONGO_PASSWORD || process.env.MONGO_PASSWORD; diff --git a/auth-service/package-lock.json b/auth-service/package-lock.json index facf2ca..83fc6d6 100644 --- a/auth-service/package-lock.json +++ b/auth-service/package-lock.json @@ -12,7 +12,6 @@ "@aws-sdk/client-secrets-manager": "^3.908.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", - "dotenv": "^17.2.1", "express": "^5.1.0", "fs": "^0.0.1-security", "ioredis": "^5.7.0", @@ -10735,18 +10734,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/driftless": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/driftless/-/driftless-2.0.3.tgz", diff --git a/auth-service/package.json b/auth-service/package.json index d17cd1a..1deb84e 100644 --- a/auth-service/package.json +++ b/auth-service/package.json @@ -19,7 +19,6 @@ "@aws-sdk/client-secrets-manager": "^3.908.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", - "dotenv": "^17.2.1", "express": "^5.1.0", "fs": "^0.0.1-security", "ioredis": "^5.7.0", diff --git a/auth-service/utils/jwt.js b/auth-service/utils/jwt.js index 25fa483..d735464 100644 --- a/auth-service/utils/jwt.js +++ b/auth-service/utils/jwt.js @@ -2,7 +2,7 @@ import jwt from "jsonwebtoken" import fs from 'fs' import { SecretService } from "#shared/utils/index.js" -const secret = await SecretService.getSecret("prod/cr-user-sv"); +const secret = await SecretService.getSecret(process.env.SERVICE_SECRET_NAME); export default class JwtFacade { static ACCESS_TOKEN_SECRET = secret.PRIVATE_KEY || fs.readFileSync("private.pem", "utf8"); diff --git a/class-service/api/studentServiceApi.js b/class-service/api/studentServiceApi.js index 75f66be..656c5d8 100644 --- a/class-service/api/studentServiceApi.js +++ b/class-service/api/studentServiceApi.js @@ -1,45 +1,56 @@ import axios from "axios"; -import { InternalServerError } from "#shared/errors/errors.js"; +import { LambdaInvoker } from "../utils/index.js"; -const URL = process.env.STUDENT_SERVICE_URL; +const STUDENT_SERVICE = process.env.STUDENT_SERVICE_API; // Development: localhost URL, Production: Lambda function name class StudentServiceClient { - constructor(baseURL) { - this.api = axios.create({ baseURL }); - } + constructor() { + this.isProd = process.env.NODE_ENV === "production"; - async getStudentByIds(ids, accessToken) { - try { - const res = await this.api.get(`/by-ids?ids=${ids}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; - } catch (err) { - throw new InternalServerError(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + this.api = axios.create({ baseURL: STUDENT_SERVICE }); + } else { + this.lambda = new LambdaInvoker(); } } - async getStudentById(id, accessToken) { + async getStudentByIds(ids, accessToken) { + const route = `/by-ids?ids=${ids}`; try { - const res = await this.api.get(`/${id}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, STUDENT_SERVICE); } catch (err) { - throw new InternalServerError(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("StudentService error:", err); + } } } - async deleteStudentById(id, accessToken) { + async getStudentById(id, accessToken) { + const route = `/${id}`; try { - const res = await this.api.delete(`/${id}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, STUDENT_SERVICE); } catch (err) { - throw new InternalServerError(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("StudentService error:", err); + } } } } -export default new StudentServiceClient(URL); +export default new StudentServiceClient(); \ No newline at end of file diff --git a/class-service/api/teacherServiceApi.js b/class-service/api/teacherServiceApi.js index 2289b26..bed3c58 100644 --- a/class-service/api/teacherServiceApi.js +++ b/class-service/api/teacherServiceApi.js @@ -1,23 +1,37 @@ import axios from "axios"; -import { InternalServerError } from "#shared/errors/errors.js"; +import { LambdaInvoker } from "../utils/index.js"; -const URL = process.env.TEACHER_SERVICE_URL; +const TEACHER_SERVICE = process.env.TEACHER_SERVICE_API; // Development: localhost URL, Production: Lambda function name class TeacherServiceClient { - constructor(baseURL) { - this.api = axios.create({ baseURL }); + constructor() { + this.isProd = process.env.NODE_ENV === "production"; + + if (!this.isProd) { + this.api = axios.create({ baseURL: TEACHER_SERVICE }); + } else { + this.lambda = new LambdaInvoker(); + } } async getTeacherById(id, accessToken) { + const route = `/${id}`; try { - const res = await this.api.get(`/${id}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, TEACHER_SERVICE); } catch (err) { - throw new InternalServerError(`Teacher API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("StudentService error:", err); + } } } } -export default new TeacherServiceClient(URL); \ No newline at end of file +export default new TeacherServiceClient(); \ No newline at end of file diff --git a/class-service/api/userServiceApi.js b/class-service/api/userServiceApi.js index a74dc0f..6f96ccd 100644 --- a/class-service/api/userServiceApi.js +++ b/class-service/api/userServiceApi.js @@ -1,23 +1,37 @@ import axios from "axios"; -import { InternalServerError } from "#shared/errors/errors.js"; +import { LambdaInvoker } from "../utils/index.js"; -const URL = process.env.USER_SERVICE_URL; +const USER_SERVICE = process.env.USER_SERVICE_API; // Development: localhost URL, Production: Lambda function name class UserServiceClient { - constructor(baseURL) { - this.api = axios.create({ baseURL }); + constructor() { + this.isProd = process.env.NODE_ENV === "production"; + + if (!this.isProd) { + this.api = axios.create({ baseURL: USER_SERVICE }); + } else { + this.lambda = new LambdaInvoker(); + } } async getUserByIds(ids, accessToken) { + const route = `/by-ids?ids=${ids}`; try { - const res = await this.api.get(`/by-ids?ids=${ids}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, USER_SERVICE); } catch (err) { - throw new InternalServerError(`Auth API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("StudentService error:", err); + } } } } -export default new UserServiceClient(URL); \ No newline at end of file +export default new UserServiceClient(); \ No newline at end of file diff --git a/class-service/package-lock.json b/class-service/package-lock.json index 46e0ded..7227ffa 100644 --- a/class-service/package-lock.json +++ b/class-service/package-lock.json @@ -9,12 +9,12 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@aws-sdk/client-lambda": "^3.947.0", "@aws-sdk/client-secrets-manager": "^3.908.0", "@prisma/client": "^6.15.0", "axios": "^1.11.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", - "dotenv": "^17.2.1", "express": "^5.1.0", "fs": "^0.0.1-security", "ioredis": "^5.7.0", @@ -22,14 +22,13 @@ "jsonwebtoken": "^9.0.2", "pino": "^9.9.0", "pino-http": "^10.5.0", - "prisma": "^6.14.0", - "serverless-http": "^4.0.0", - "uuid": "^11.1.0" + "serverless-http": "^4.0.0" }, "devDependencies": { "artillery": "^2.0.25", "nodemon": "^3.1.10", - "pino-pretty": "^13.1.1" + "pino-pretty": "^13.1.1", + "prisma": "^6.14.0" } }, "node_modules/@artilleryio/int-commons": { @@ -148,7 +147,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -716,61 +714,506 @@ } }, "node_modules/@aws-sdk/client-lambda": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.893.0.tgz", - "integrity": "sha512-mhu6KfOJtfuAUjtzT/6mEX3LJWqD4e8u+No5a+uyECfOE795lgOBege999A4GrEci3gIIBalAT2bHy66oUp65Q==", - "dev": true, + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.947.0.tgz", + "integrity": "sha512-uVsdIzMmtPNXrE8XZPx0eIdBmLtRxA1RL9tG3nS5MqOrVbbK/xk3ZZPJm4ETvL1TAePl54jUY4GmIUAVwkrtSg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.893.0", - "@aws-sdk/credential-provider-node": "3.893.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.893.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.893.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.893.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.11.1", - "@smithy/eventstream-serde-browser": "^4.1.1", - "@smithy/eventstream-serde-config-resolver": "^4.2.1", - "@smithy/eventstream-serde-node": "^4.1.1", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.3", - "@smithy/middleware-retry": "^4.2.4", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.3", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.3", - "@smithy/util-defaults-mode-node": "^4.1.3", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/util-waiter": "^4.1.1", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/credential-provider-node": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/eventstream-serde-browser": "^4.2.5", + "@smithy/eventstream-serde-config-resolver": "^4.3.5", + "@smithy/eventstream-serde-node": "^4.2.5", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.947.0.tgz", + "integrity": "sha512-sDwcO8SP290WSErY1S8pz8hTafeghKmmWjNVks86jDK30wx62CfazOTeU70IpWgrUBEygyXk/zPogHsUMbW2Rg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/core": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz", + "integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.947.0.tgz", + "integrity": "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.947.0.tgz", + "integrity": "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.947.0.tgz", + "integrity": "sha512-A2ZUgJUJZERjSzvCi2NR/hBVbVkTXPD0SdKcR/aITb30XwF+n3T963b+pJl90qhOspoy7h0IVYNR7u5Nr9tJdQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-login": "3.947.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.947.0", + "@aws-sdk/credential-provider-web-identity": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.947.0.tgz", + "integrity": "sha512-S0Zqebr71KyrT6J4uYPhwV65g4V5uDPHnd7dt2W34FcyPu+hVC7Hx4MFmsPyVLeT5cMCkkZvmY3kAoEzgUPJJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-ini": "3.947.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.947.0", + "@aws-sdk/credential-provider-web-identity": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.947.0.tgz", + "integrity": "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.947.0.tgz", + "integrity": "sha512-NktnVHTGaUMaozxycYrepvb3yfFquHTQ53lt6hBEVjYBzK3C4tVz0siUpr+5RMGLSiZ5bLBp2UjJPgwx4i4waQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.947.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/token-providers": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.947.0.tgz", + "integrity": "sha512-gokm/e/YHiHLrZgLq4j8tNAn8RJDPbIcglFRKgy08q8DmAqHQ8MXAKW3eS0QjAuRXU9mcMmUo1NrX6FRNBCCPw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.947.0.tgz", + "integrity": "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/nested-clients": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.947.0.tgz", + "integrity": "sha512-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/token-providers": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.947.0.tgz", + "integrity": "sha512-X/DyB8GuK44rsE89Tn5+s542B3PhGbXQSgV8lvqHDzvicwCt0tWny6790st6CPETrVVV2K3oJMfG5U3/jAmaZA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.947.0.tgz", + "integrity": "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/client-s3": { "version": "3.893.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.893.0.tgz", @@ -1565,114 +2008,373 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/core": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.893.0.tgz", - "integrity": "sha512-E1NAWHOprBXIJ9CVb6oTsRD/tNOozrKBD/Sb4t7WZd3dpby6KpYfM6FaEGfRGcJBIcB4245hww8Rmg16qDMJWg==", - "dev": true, + "node_modules/@aws-sdk/core": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.893.0.tgz", + "integrity": "sha512-E1NAWHOprBXIJ9CVb6oTsRD/tNOozrKBD/Sb4t7WZd3dpby6KpYfM6FaEGfRGcJBIcB4245hww8Rmg16qDMJWg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.893.0", + "@aws-sdk/xml-builder": "3.893.0", + "@smithy/core": "^3.11.1", + "@smithy/node-config-provider": "^4.2.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/signature-v4": "^5.2.1", + "@smithy/smithy-client": "^4.6.3", + "@smithy/types": "^4.5.0", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.893.0.tgz", + "integrity": "sha512-HUYDc/6/3UYM3HmJKFpxzhjSpz1bZC5u4xkwgnCjNChn4SVwrWAseYLXYT6wZEjn6kRd/0uNlDi6nA+PuXhIWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.893.0.tgz", + "integrity": "sha512-h4sYNk1iDrSZQLqFfbuD1GWY6KoVCvourfqPl6JZCYj8Vmnox5y9+7taPxwlU2VVII0hiV8UUbO79P35oPBSyA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.893.0.tgz", + "integrity": "sha512-xYoC7DRr++zWZ9jG7/hvd6YjCbGDQzsAu2fBHHf91RVmSETXUgdEaP9rOdfCM02iIK/MYlwiWEIVBcBxWY/GQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/property-provider": "^4.1.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.3", + "@smithy/types": "^4.5.0", + "@smithy/util-stream": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.893.0.tgz", + "integrity": "sha512-ZQWOl4jdLhJHHrHsOfNRjgpP98A5kw4YzkMOUoK+TgSQVLi7wjb957V0htvwpi6KmGr3f2F8J06D6u2OtIc62w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.893.0", + "@aws-sdk/credential-provider-env": "3.893.0", + "@aws-sdk/credential-provider-http": "3.893.0", + "@aws-sdk/credential-provider-process": "3.893.0", + "@aws-sdk/credential-provider-sso": "3.893.0", + "@aws-sdk/credential-provider-web-identity": "3.893.0", + "@aws-sdk/nested-clients": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/credential-provider-imds": "^4.1.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.947.0.tgz", + "integrity": "sha512-u7M3hazcB7aJiVwosNdJRbIJDzbwQ861NTtl6S0HmvWpixaVb7iyhJZWg8/plyUznboZGBm7JVEdxtxv3u0bTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/core": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz", + "integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.947.0.tgz", + "integrity": "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/nested-clients": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.947.0.tgz", + "integrity": "sha512-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.893.0", - "@smithy/core": "^3.11.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.3", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", - "fast-xml-parser": "5.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.893.0.tgz", - "integrity": "sha512-HUYDc/6/3UYM3HmJKFpxzhjSpz1bZC5u4xkwgnCjNChn4SVwrWAseYLXYT6wZEjn6kRd/0uNlDi6nA+PuXhIWA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-cognito-identity": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.893.0.tgz", - "integrity": "sha512-h4sYNk1iDrSZQLqFfbuD1GWY6KoVCvourfqPl6JZCYj8Vmnox5y9+7taPxwlU2VVII0hiV8UUbO79P35oPBSyA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.893.0.tgz", - "integrity": "sha512-xYoC7DRr++zWZ9jG7/hvd6YjCbGDQzsAu2fBHHf91RVmSETXUgdEaP9rOdfCM02iIK/MYlwiWEIVBcBxWY/GQw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.947.0.tgz", + "integrity": "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.3", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.893.0.tgz", - "integrity": "sha512-ZQWOl4jdLhJHHrHsOfNRjgpP98A5kw4YzkMOUoK+TgSQVLi7wjb957V0htvwpi6KmGr3f2F8J06D6u2OtIc62w==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.893.0", - "@aws-sdk/credential-provider-env": "3.893.0", - "@aws-sdk/credential-provider-http": "3.893.0", - "@aws-sdk/credential-provider-process": "3.893.0", - "@aws-sdk/credential-provider-sso": "3.893.0", - "@aws-sdk/credential-provider-web-identity": "3.893.0", - "@aws-sdk/nested-clients": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.893.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.893.0.tgz", @@ -4528,6 +5230,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.14.0.tgz", "integrity": "sha512-IwC7o5KNNGhmblLs23swnfBjADkacBb7wvyDXUWLwuvUQciKJZqyecU0jw0d7JRkswrj+XTL8fdr0y2/VerKQQ==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -4540,12 +5243,14 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.14.0.tgz", "integrity": "sha512-j4Lf+y+5QIJgQD4sJWSbkOD7geKx9CakaLp/TyTy/UDu9Wo0awvWCBH/BAxTHUaCpIl9USA5VS/KJhDqKJSwug==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.14.0.tgz", "integrity": "sha512-LhJjqsALFEcoAtF07nSaOkVguaxw/ZsgfROIYZ8bAZDobe7y8Wy+PkYQaPOK1iLSsFgV2MhCO/eNrI1gdSOj6w==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4559,12 +5264,14 @@ "version": "6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49.tgz", "integrity": "sha512-EgN9ODJpiX45yvwcngoStp3uQPJ3l+AEVoQ6dMMO2QvmwIlnxfApzKmJQExzdo7/hqQANrz5txHJdGYHzOnGHA==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.14.0.tgz", "integrity": "sha512-MPzYPOKMENYOaY3AcAbaKrfvXVlvTc6iHmTXsp9RiwCX+bPyfDMqMFVUSVXPYrXnrvEzhGHfyiFy0PRLHPysNg==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.14.0", @@ -4576,6 +5283,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.14.0.tgz", "integrity": "sha512-7VjuxKNwjnBhKfqPpMeWiHEa2sVjYzmHdl1slW6STuUCe9QnOY0OY1ljGSvz6wpG4U8DfbDqkG1yofd/1GINww==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.14.0" @@ -4700,12 +5408,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", - "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", + "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4740,15 +5448,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", - "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", + "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -4756,18 +5465,18 @@ } }, "node_modules/@smithy/core": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.15.0.tgz", - "integrity": "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==", + "version": "3.18.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz", + "integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" @@ -4777,15 +5486,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", - "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", + "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -4793,15 +5502,14 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.1.1.tgz", - "integrity": "sha512-PwkQw1hZwHTQB6X5hSUWz2OSeuj5Z6enWuAqke7DgWoP3t6vg3ktPpqPz3Erkn6w+tmsl8Oss6nrgyezoea2Iw==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.5.tgz", + "integrity": "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4809,14 +5517,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.1.1.tgz", - "integrity": "sha512-Q9QWdAzRaIuVkefupRPRFAasaG/droBqn1feiMnmLa+LLEUG45pqX1+FurHFmlqiCfobB3nUlgoJfeXZsr7MPA==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.5.tgz", + "integrity": "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4824,13 +5531,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.2.1.tgz", - "integrity": "sha512-oSUkF9zDN9zcOUBMtxp8RewJlh71E9NoHWU8jE3hU9JMYCsmW4assVTpgic/iS3/dM317j6hO5x18cc3XrfvEw==", - "dev": true, + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.5.tgz", + "integrity": "sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4838,14 +5544,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.1.1.tgz", - "integrity": "sha512-tn6vulwf/ScY0vjhzptSJuDJJqlhNtUjkxJ4wiv9E3SPoEqTEKbaq6bfqRO7nvhTG29ALICRcvfFheOUPl8KNA==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.5.tgz", + "integrity": "sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4853,14 +5558,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.1.1.tgz", - "integrity": "sha512-uLOAiM/Dmgh2CbEXQx+6/ssK7fbzFhd+LjdyFxXid5ZBCbLHTFHLdD/QbXw5aEDsLxQhgzDxLLsZhsftAYwHJA==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.5.tgz", + "integrity": "sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-codec": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4868,14 +5572,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.1.tgz", - "integrity": "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", + "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, @@ -4900,12 +5604,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", - "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", + "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -4930,12 +5634,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", - "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", + "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4992,13 +5696,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", - "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", + "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5006,18 +5710,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.1.tgz", - "integrity": "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==", + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.14.tgz", + "integrity": "sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/core": "^3.18.7", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -5025,18 +5729,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.1.tgz", - "integrity": "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/service-error-classification": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.14.tgz", + "integrity": "sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, @@ -5045,13 +5749,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", - "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", + "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5059,12 +5763,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", - "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", + "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5072,14 +5776,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", - "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", + "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5087,15 +5791,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", - "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", + "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/abort-controller": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5103,12 +5807,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", - "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", + "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5116,12 +5820,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", - "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", + "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5129,12 +5833,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", - "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", + "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, @@ -5143,12 +5847,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", - "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", + "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5156,24 +5860,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", - "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", + "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0" + "@smithy/types": "^4.9.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", - "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", + "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5181,16 +5885,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", - "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", + "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -5200,17 +5904,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.1.tgz", - "integrity": "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==", + "version": "4.9.10", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.10.tgz", + "integrity": "sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-endpoint": "^4.3.1", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/core": "^3.18.7", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" }, "engines": { @@ -5218,9 +5922,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", - "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", + "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5230,13 +5934,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", - "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", + "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/querystring-parser": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5307,14 +6011,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.0.tgz", - "integrity": "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==", + "version": "4.3.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.13.tgz", + "integrity": "sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5322,17 +6026,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.1.tgz", - "integrity": "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==", + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.16.tgz", + "integrity": "sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.3.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5340,13 +6044,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", - "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", + "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5366,12 +6070,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", - "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", + "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5379,13 +6083,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", - "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", + "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5393,14 +6097,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.0.tgz", - "integrity": "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", + "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.1", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", @@ -5437,14 +6141,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.1.1.tgz", - "integrity": "sha512-PJBmyayrlfxM7nbqjomF4YcT1sApQwZio0NHSsT0EzhJqljRmvhzqZua43TyEs80nJk2Cn2FGPg/N8phH6KeCQ==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.5.tgz", + "integrity": "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/abort-controller": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -6640,6 +7343,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -6668,6 +7372,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -6804,6 +7509,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -6835,6 +7541,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -7051,12 +7758,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -7301,6 +8010,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -7376,6 +8086,7 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, "license": "MIT" }, "node_modules/delayed-stream": { @@ -7428,6 +8139,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, "license": "MIT" }, "node_modules/detective-amd": { @@ -7738,18 +8450,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/driftless": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/driftless/-/driftless-2.0.3.tgz", @@ -7793,6 +8493,7 @@ "version": "3.16.12", "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -7826,6 +8527,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -8212,6 +8914,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "devOptional": true, "license": "MIT" }, "node_modules/extend": { @@ -8225,6 +8928,7 @@ "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, "funding": [ { "type": "individual", @@ -8693,6 +9397,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -9351,6 +10056,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -10043,6 +10749,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, "license": "MIT" }, "node_modules/node-gyp-build": { @@ -10176,6 +10883,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.1.tgz", "integrity": "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -10207,6 +10915,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, "license": "MIT" }, "node_modules/on-exit-leak-free": { @@ -10499,12 +11208,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, "license": "MIT" }, "node_modules/picocolors": { @@ -10605,6 +11316,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -10780,6 +11492,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.14.0.tgz", "integrity": "sha512-QEuCwxu+Uq9BffFw7in8In+WfbSUN0ewnaSUKloLkbJd42w6EyFckux4M0f7VwwHlM3A8ssaz4OyniCXlsn0WA==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -10903,6 +11616,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, "funding": [ { "type": "individual", @@ -11031,6 +11745,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -11089,6 +11804,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -11923,6 +12639,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "devOptional": true, "license": "MIT" }, "node_modules/tinyglobby": { @@ -12186,19 +12903,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/class-service/package.json b/class-service/package.json index fcc73b2..21ef77e 100644 --- a/class-service/package.json +++ b/class-service/package.json @@ -16,6 +16,7 @@ "#shared/*": "./shared-libs/*" }, "dependencies": { + "@aws-sdk/client-lambda": "^3.947.0", "@aws-sdk/client-secrets-manager": "^3.908.0", "@prisma/client": "^6.15.0", "axios": "^1.11.0", diff --git a/class-service/server.js b/class-service/server.js index 458064e..1f951fe 100644 --- a/class-service/server.js +++ b/class-service/server.js @@ -6,7 +6,7 @@ import { classRouter, enrollmentRouter, userClassRouter } from './routes/index.j import { authentication, errorHandler, routeNotFound, pinoLogger } from '#shared/middlewares/index.js'; import { SecretService } from "#shared/utils/index.js" -await SecretService.setDatabaseUrl("prod/cr-class-sv"); +await SecretService.setDatabaseUrl(process.env.SERVICE_SECRET_NAME); const app = express(); const PORT = 3003; diff --git a/class-service/utils/aws-lambda-invoke.js b/class-service/utils/aws-lambda-invoke.js new file mode 100644 index 0000000..4bb4d5d --- /dev/null +++ b/class-service/utils/aws-lambda-invoke.js @@ -0,0 +1,83 @@ +import { LambdaClient, InvokeCommand } from "@aws-sdk/client-lambda"; + +export class LambdaInvoker { + constructor() { + this.client = new LambdaClient({ region: process.env.AWS_REGION || "ap-southeast-1" }); + } + + async invoke(method, route, token, functionName, body = null) { + if (!method || typeof method !== "string") { + throw new Error("Method must be a non-empty string"); + } + + const validMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"]; + if (!validMethods.includes(method.toUpperCase())) { + throw new Error(`Invalid HTTP method: ${method}. Allowed: ${validMethods.join(", ")}`); + } + + if (!route || typeof route !== "string" || !route.startsWith("/")) { + throw new Error(`Route must be a string starting with "/". Received: ${route}`); + } + + if (!token || typeof token !== "string") { + throw new Error("Bearer token must be a non-empty string"); + } + + if (!functionName || typeof functionName !== "string") { + throw new Error("FunctionName must be a non-empty string"); + } + + let encodedBody = null; + if (body !== null) { + if (typeof body === "object") { + encodedBody = JSON.stringify(body); + } else if (typeof body === "string") { + encodedBody = body; + } else { + throw new Error("Body must be string or object"); + } + } + + const payload = { + version: "2.0", + + routeKey: `${method} ${route}`, + rawPath: route, + requestContext: { + http: { + method: method, + path: route + } + }, + + headers: { + "authorization": `Bearer ${token}`, + "content-type": "application/json" + }, + + body: encodedBody, + isBase64Encoded: false + }; + + const command = new InvokeCommand({ + FunctionName: functionName, + Payload: Buffer.from(JSON.stringify(payload)), + InvocationType: "RequestResponse" + }); + + const res = await this.client.send(command); + + if (res.FunctionError) { + throw new Error(`Lambda runtime error: ${res.FunctionError}`); + } + + const jsonString = Buffer.from(res.Payload).toString("utf8"); + const data = JSON.parse(jsonString); + + if (data.statusCode >= 400) { + throw new Error(`Lambda error ${data.statusCode}: ${data.body}`); + } + + return data.body ? JSON.parse(data.body) : null; + } +} \ No newline at end of file diff --git a/class-service/utils/index.js b/class-service/utils/index.js index 75d96b3..afd86a1 100644 --- a/class-service/utils/index.js +++ b/class-service/utils/index.js @@ -1 +1,2 @@ -export { checkPermission } from './check-permission.js'; \ No newline at end of file +export { checkPermission } from './check-permission.js'; +export { LambdaInvoker } from './aws-lambda-invoke.js' \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2abc831..b169af1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -120,6 +120,7 @@ services: - MONGO_HOST=user-db - MONGO_DB=userdb - CHOKIDAR_USEPOLLING=true + - APP_ENV=development depends_on: - user-db @@ -139,6 +140,7 @@ services: - DATABASE_URL=postgresql://admin:secret@student-db:5432/studentdb?schema=public - DIRECT_URL=postgresql://admin:secret@student-db:5432/studentdb?schema=public - CHOKIDAR_USEPOLLING=true + - APP_ENV=development env_file: - ./student-service/.env depends_on: @@ -160,6 +162,7 @@ services: - DATABASE_URL=postgresql://admin:secret@class-db:5432/classdb?schema=public - DIRECT_URL=postgresql://admin:secret@class-db:5432/classdb?schema=public - CHOKIDAR_USEPOLLING=true + - APP_ENV=development env_file: - ./class-service/.env depends_on: @@ -181,6 +184,7 @@ services: - DATABASE_URL=postgresql://admin:secret@tuition-db:5432/tuitiondb?schema=public - DIRECT_URL=postgresql://admin:secret@tuition-db:5432/tuitiondb?schema=public - CHOKIDAR_USEPOLLING=true + - APP_ENV=development env_file: - ./tuition-service/.env depends_on: @@ -204,6 +208,7 @@ services: - DATABASE_URL=postgresql://admin:secret@teacher-db:5432/teacherdb?schema=public - DIRECT_URL=postgresql://admin:secret@teacher-db:5432/teacherdb?schema=public - CHOKIDAR_USEPOLLING=true + - APP_ENV=development depends_on: - teacher-db diff --git a/shared-libs/middlewares/authentication.js b/shared-libs/middlewares/authentication.js index 8cc104d..8ef8c77 100644 --- a/shared-libs/middlewares/authentication.js +++ b/shared-libs/middlewares/authentication.js @@ -16,8 +16,13 @@ export default async function authentication(req, res, next) { // NOTE: RSA key cần xuống dòng đúng để RS256 verify hợp lệ, key lưu ở AWS Secret Manager NHẬP BẰNG PLAINTEXT, chuyển thành 1 dòng và \n ở nơi xuống dòng // NOTE: {"PUBLIC_KEY": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n...-----END PUBLIC KEY-----"} - const secret = await SecretService.getSecret("prod/cr-publickey"); - const publickey = secret.PUBLIC_KEY || fs.readFileSync("public.pem", "utf8"); + var publickey; + if (process.env.NODE_ENV === 'production') { + const secret = await SecretService.getSecret(process.env.PUBLIC_KEY_SECRET_NAME); + publickey = secret.PUBLIC_KEY; + } else { + publickey = fs.readFileSync("public.pem", "utf8"); + } jwt.verify(token, publickey, { algorithms: ["RS256"] }, (err, user) => { if (err) { diff --git a/shared-libs/utils/aws-secret-manager.js b/shared-libs/utils/aws-secret-manager.js index 54870fe..97d7c2b 100644 --- a/shared-libs/utils/aws-secret-manager.js +++ b/shared-libs/utils/aws-secret-manager.js @@ -7,7 +7,7 @@ export default class SecretService { return {}; const client = new SecretsManagerClient({ - region: "ap-southeast-1", + region: process.env.AWS_REGION || "ap-southeast-1", }); const res = await client.send(new GetSecretValueCommand({ SecretId: secretName })); diff --git a/student-service/package-lock.json b/student-service/package-lock.json index d9962ea..58fb0df 100644 --- a/student-service/package-lock.json +++ b/student-service/package-lock.json @@ -13,7 +13,6 @@ "@prisma/client": "^6.15.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", - "dotenv": "^17.2.1", "express": "^5.1.0", "fs": "^0.0.1-security", "ioredis": "^5.7.0", @@ -21,14 +20,13 @@ "jsonwebtoken": "^9.0.2", "pino": "^9.9.0", "pino-http": "^10.5.0", - "prisma": "^6.14.0", - "serverless-http": "^4.0.0", - "uuid": "^11.1.0" + "serverless-http": "^4.0.0" }, "devDependencies": { "artillery": "^2.0.25", "nodemon": "^3.1.10", - "pino-pretty": "^13.1.1" + "pino-pretty": "^13.1.1", + "prisma": "^6.14.0" } }, "node_modules/@artilleryio/int-commons": { @@ -4469,6 +4467,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.14.0.tgz", "integrity": "sha512-IwC7o5KNNGhmblLs23swnfBjADkacBb7wvyDXUWLwuvUQciKJZqyecU0jw0d7JRkswrj+XTL8fdr0y2/VerKQQ==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -4481,12 +4480,14 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.14.0.tgz", "integrity": "sha512-j4Lf+y+5QIJgQD4sJWSbkOD7geKx9CakaLp/TyTy/UDu9Wo0awvWCBH/BAxTHUaCpIl9USA5VS/KJhDqKJSwug==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.14.0.tgz", "integrity": "sha512-LhJjqsALFEcoAtF07nSaOkVguaxw/ZsgfROIYZ8bAZDobe7y8Wy+PkYQaPOK1iLSsFgV2MhCO/eNrI1gdSOj6w==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4500,12 +4501,14 @@ "version": "6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49.tgz", "integrity": "sha512-EgN9ODJpiX45yvwcngoStp3uQPJ3l+AEVoQ6dMMO2QvmwIlnxfApzKmJQExzdo7/hqQANrz5txHJdGYHzOnGHA==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.14.0.tgz", "integrity": "sha512-MPzYPOKMENYOaY3AcAbaKrfvXVlvTc6iHmTXsp9RiwCX+bPyfDMqMFVUSVXPYrXnrvEzhGHfyiFy0PRLHPysNg==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.14.0", @@ -4517,6 +4520,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.14.0.tgz", "integrity": "sha512-7VjuxKNwjnBhKfqPpMeWiHEa2sVjYzmHdl1slW6STuUCe9QnOY0OY1ljGSvz6wpG4U8DfbDqkG1yofd/1GINww==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.14.0" @@ -6571,6 +6575,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -6599,6 +6604,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -6735,6 +6741,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -6766,6 +6773,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -6983,12 +6991,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -7233,6 +7243,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -7308,6 +7319,7 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, "license": "MIT" }, "node_modules/delayed-stream": { @@ -7361,6 +7373,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, "license": "MIT" }, "node_modules/detective-amd": { @@ -7671,18 +7684,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/driftless": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/driftless/-/driftless-2.0.3.tgz", @@ -7726,6 +7727,7 @@ "version": "3.16.12", "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -7759,6 +7761,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -8146,6 +8149,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "devOptional": true, "license": "MIT" }, "node_modules/extend": { @@ -8159,6 +8163,7 @@ "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, "funding": [ { "type": "individual", @@ -8610,6 +8615,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -9269,6 +9275,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -9961,6 +9968,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, "license": "MIT" }, "node_modules/node-gyp-build": { @@ -10094,6 +10102,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.1.tgz", "integrity": "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -10125,6 +10134,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, "license": "MIT" }, "node_modules/on-exit-leak-free": { @@ -10417,12 +10427,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, "license": "MIT" }, "node_modules/picocolors": { @@ -10523,6 +10535,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -10698,6 +10711,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.14.0.tgz", "integrity": "sha512-QEuCwxu+Uq9BffFw7in8In+WfbSUN0ewnaSUKloLkbJd42w6EyFckux4M0f7VwwHlM3A8ssaz4OyniCXlsn0WA==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -10815,6 +10829,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, "funding": [ { "type": "individual", @@ -10943,6 +10958,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -11001,6 +11017,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -11835,6 +11852,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "devOptional": true, "license": "MIT" }, "node_modules/tinyglobby": { @@ -12098,19 +12116,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/student-service/server.js b/student-service/server.js index 4f5e1c5..77847f7 100644 --- a/student-service/server.js +++ b/student-service/server.js @@ -6,7 +6,7 @@ import router from './routes/studentRoutes.js' import { authentication, errorHandler, routeNotFound, pinoLogger } from '#shared/middlewares/index.js' import { SecretService } from "#shared/utils/index.js" -await SecretService.setDatabaseUrl("prod/cr-student-sv"); +await SecretService.setDatabaseUrl(process.env.SERVICE_SECRET_NAME); const app = express(); const PORT = 3002; diff --git a/teacher-service/package-lock.json b/teacher-service/package-lock.json index 5dff841..225a38a 100644 --- a/teacher-service/package-lock.json +++ b/teacher-service/package-lock.json @@ -15,7 +15,6 @@ "@prisma/client": "^6.15.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", - "dotenv": "^17.2.1", "express": "^5.1.0", "fs": "^0.0.1-security", "ioredis": "^5.7.0", @@ -24,14 +23,13 @@ "multer": "^2.0.2", "pino": "^9.9.0", "pino-http": "^10.5.0", - "prisma": "^6.14.0", "serverless-http": "^4.0.0", - "sharp": "^0.34.3", - "uuid": "^11.1.0" + "sharp": "^0.34.3" }, "devDependencies": { "nodemon": "^3.1.10", - "pino-pretty": "^13.1.1" + "pino-pretty": "^13.1.1", + "prisma": "^6.14.0" } }, "node_modules/@aws-crypto/crc32": { @@ -1945,6 +1943,7 @@ "version": "6.16.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.0.tgz", "integrity": "sha512-Q9TgfnllVehvQziY9lJwRJLGmziX0OimZUEQ/MhCUBoJMSScj2VivCjw/Of2vlO1FfyaHXxrvjZAr7ASl7DVcw==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -1957,12 +1956,14 @@ "version": "6.16.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.0.tgz", "integrity": "sha512-bxzro5vbVqAPkWyDs2A6GpQtRZunD8tyrLmSAchx9u0b+gWCDY6eV+oh5A0YtYT9245dIxQBswckayHuJG4u3w==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.0.tgz", "integrity": "sha512-RHJGCH/zi017W4CWYWqg0Sv1pquGGFVo8T3auJ9sodDNaiRzbeNldydjaQzszVS8nscdtcvLuJzy7e65C3puqQ==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1976,12 +1977,14 @@ "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.0.tgz", "integrity": "sha512-Mx5rml0XRIDizhB9eZxSP8c0nMoXYVITTiJJwxlWn9rNCel8mG8NAqIw+vJlN3gPR+kt3IBkP1SQVsplPPpYrA==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.16.0", @@ -1993,6 +1996,7 @@ "version": "6.16.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.0.tgz", "integrity": "sha512-eaJOOvAoGslSUTjiQrtE9E0hoBdfL43j8SymOGD6LbdrKRNtIoiy6qiBaEr2fNYD+R/Qns7QOwPhl7SVHJayKA==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.16.0" @@ -2903,6 +2907,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -2931,6 +2936,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -2946,6 +2952,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -2958,6 +2965,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -3025,6 +3033,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -3113,12 +3122,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -3204,6 +3215,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -3213,6 +3225,7 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, "license": "MIT" }, "node_modules/denque": { @@ -3237,6 +3250,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -3248,18 +3262,6 @@ "node": ">=8" } }, - "node_modules/dotenv": { - "version": "17.2.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz", - "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3293,6 +3295,7 @@ "version": "3.16.12", "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -3303,6 +3306,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -3427,12 +3431,14 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "devOptional": true, "license": "MIT" }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, "funding": [ { "type": "individual", @@ -3620,6 +3626,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -3844,6 +3851,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -4148,6 +4156,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, "license": "MIT" }, "node_modules/node-gyp-build": { @@ -4204,6 +4213,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.1.tgz", "integrity": "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -4244,6 +4254,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, "license": "MIT" }, "node_modules/on-exit-leak-free": { @@ -4299,12 +4310,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, "license": "MIT" }, "node_modules/picomatch": { @@ -4398,6 +4411,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -4409,6 +4423,7 @@ "version": "6.16.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.0.tgz", "integrity": "sha512-TTh+H1Kw8N68KN9cDzdAyMroqMOvdCO/Z+kS2wKEVYR1nuR21qH5Q/Db/bZHsAgw7l/TPHtM/veG5VABcdwPDw==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4481,6 +4496,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, "funding": [ { "type": "individual", @@ -4558,6 +4574,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -4990,6 +5007,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "devOptional": true, "license": "MIT" }, "node_modules/to-regex-range": { @@ -5072,19 +5090,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/teacher-service/server.js b/teacher-service/server.js index 2d4857a..1bd7d40 100644 --- a/teacher-service/server.js +++ b/teacher-service/server.js @@ -6,7 +6,7 @@ import { teacherRouter } from './routes/index.js' import { authentication, errorHandler, routeNotFound, pinoLogger } from '#shared/middlewares/index.js' import { SecretService } from "#shared/utils/index.js" -await SecretService.setDatabaseUrl("prod/cr-teacher-sv"); +await SecretService.setDatabaseUrl(process.env.SERVICE_SECRET_NAME); const app = express(); const PORT = 3005; diff --git a/teacher-service/utils/aws-s3.js b/teacher-service/utils/aws-s3.js index ffd8d7d..631c98f 100644 --- a/teacher-service/utils/aws-s3.js +++ b/teacher-service/utils/aws-s3.js @@ -1,13 +1,13 @@ import { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3" import { getSignedUrl } from "@aws-sdk/s3-request-presigner" -import { SecretService } from "#shared/utils/index.js" +// import { SecretService } from "#shared/utils/index.js" -const secret = await SecretService.getSecret("prod/cr-teacher-sv"); +// const secret = await SecretService.getSecret(process.env.SERVICE_SECRET_NAME); const bucketName = process.env.AWS_BUCKET_NAME; -const region = process.env.AWS_REGION; -const accessKeyId = secret.AWS_ACCESS_KEY_ID || process.env.AWS_ACCESS_KEY_ID; -const secretAccessKey = secret.AWS_SECRET_ACCESS_KEY || process.env.AWS_SECRET_ACCESS_KEY; +const region = process.env.AWS_REGION || "ap-southeast-1"; +// const accessKeyId = secret.AWS_ACCESS_KEY_ID || process.env.AWS_ACCESS_KEY_ID; +// const secretAccessKey = secret.AWS_SECRET_ACCESS_KEY || process.env.AWS_SECRET_ACCESS_KEY; export default class S3Service { static s3Client() { diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..39a6fbd --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.100.0" + constraints = "~> 5.0" + hashes = [ + "h1:edXOJWE4ORX8Fm+dpVpICzMZJat4AX0VRCAy/xkcOc0=", + "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", + "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", + "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", + "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", + "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", + "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", + "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", + "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", + "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", + "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", + "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", + "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", + "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", + ] +} diff --git a/terraform/backend.tf b/terraform/backend.tf new file mode 100644 index 0000000..21f5089 --- /dev/null +++ b/terraform/backend.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.6.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + backend "s3" { + bucket = "classroom-tf-state-114579" + key = "terraform/terraform.tfstate" + region = "ap-southeast-1" + } +} + +provider "aws" { + region = var.aws_region +} \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..f5c9e8c --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,274 @@ +locals { + # Chỉnh "true" khi tất cả ECR được tạo và push image + ecr_enabled = true +} + +module "networking" { + source = "./modules/networking" + + project_name = var.project_name + tags = var.tags + + region = var.aws_region + + vpc_cidr = "10.0.0.0/16" + public_subnets = ["10.0.1.0/24", "10.0.2.0/24"] + private_subnets = ["10.0.3.0/24", "10.0.4.0/24"] +} + +module "s3_bucket" { + source = "./modules/s3" + + project_name = var.project_name + tags = var.tags + + name = "${var.project_name}-teacher-function-image-bucket" + allowed_principals = [module.lambda.lambda_iam_arn.teacher] +} + +module "secrets" { + source = "./modules/secret_manager" + + project_name = var.project_name + tags = var.tags + + auth_service = { + mongo_username = "${var.auth.mongo_username}" + mongo_password = "${var.auth.mongo_password}" + mongo_host = "${var.auth.mongo_host}" + private_key = file("./key/private.pem") + refresh_token_secret = "${var.auth.refresh_token_secret}" + } + + supabase_services = { + student = { + database_url = "${var.student_db_url}" + direct_url = "${var.student_direct_url}" + } + teacher = { + database_url = "${var.teacher_db_url}" + direct_url = "${var.teacher_direct_url}" + } + class = { + database_url = "${var.class_db_url}" + direct_url = "${var.class_direct_url}" + } + tuition = { + database_url = "${var.tuition_db_url}" + direct_url = "${var.tuition_direct_url}" + } + } + + public_key = file("./key/public.pem") +} + +module "ecr" { + source = "./modules/ecr" + + project_name = var.project_name + tags = var.tags + + services = [ + "auth-service", + "class-service", + "student-service", + "tuition-service", + "teacher-service" + ] + + expired_days = 7 +} + +module "elasticache" { + source = "./modules/elasticache" + + project_name = var.project_name + tags = var.tags + + engine = "valkey" + + cache_storage_max_gb = 10 + ecpu_per_second_max = 5000 + + major_engine_version = "7" + kms_key_id = null + daily_snapshot_time = "05:00" + snapshot_retention_limit = 1 + + security_group_ids = [module.networking.elasticache_sg_id] + subnet_ids = module.networking.private_subnet_ids +} + +# NOTE: ECR phải được tạo và push image trước khi tạo Lambda +module "lambda" { + source = "./modules/lambda" + + project_name = var.project_name + tags = var.tags + + ecr_enabled = local.ecr_enabled + + lambda_services = { + auth = { + image = module.ecr.repository_urls["auth-service"] + memory = 1024 + timeout = 10 + architectures = ["arm64"] + environment = { + REDIS_HOST = module.elasticache.endpoint_address + REDIS_PORT = module.elasticache.endpoint_port + REDIS_PREFIX = "userSv:" + + PUBLIC_KEY_SECRET_NAME = module.secrets.public_key_secret_name + SERVICE_SECRET_NAME = module.secrets.auth_secret_name + } + } + class = { + image = module.ecr.repository_urls["class-service"] + memory = 512 + timeout = 10 + architectures = ["arm64"] + environment = { + REDIS_HOST = module.elasticache.endpoint_address + REDIS_PORT = module.elasticache.endpoint_port + REDIS_PREFIX = "classSv:" + + PUBLIC_KEY_SECRET_NAME = module.secrets.public_key_secret_name + SERVICE_SECRET_NAME = module.secrets.supabase_secret_names.class + + # AWS_REGION = var.aws_region # Bỏ vì AWS tự inject + # WARN: module không thể gọi output chính nó nên phải hardcode tên function + # NOTE: Do đó cần hardcode đúng tên function + TEACHE_SERVICE_API = "${var.project_name}-lambda-teacher-function" + STUDENT_SERVICE_API = "${var.project_name}-lambda-student-function" + USER_SERVICE_API = "${var.project_name}-lambda-auth-function" + } + } + student = { + image = module.ecr.repository_urls["student-service"] + memory = 512 + timeout = 10 + architectures = ["arm64"] + environment = { + REDIS_HOST = module.elasticache.endpoint_address + REDIS_PORT = module.elasticache.endpoint_port + REDIS_PREFIX = "studentSv:" + + PUBLIC_KEY_SECRET_NAME = module.secrets.public_key_secret_name + SERVICE_SECRET_NAME = module.secrets.supabase_secret_names.student + } + } + teacher = { + image = module.ecr.repository_urls["teacher-service"] + memory = 512 + timeout = 10 + architectures = ["arm64"] + environment = { + REDIS_HOST = module.elasticache.endpoint_address + REDIS_PORT = module.elasticache.endpoint_port + REDIS_PREFIX = "teacherSv:" + + PUBLIC_KEY_SECRET_NAME = module.secrets.public_key_secret_name + SERVICE_SECRET_NAME = module.secrets.supabase_secret_names.teacher + + # AWS_REGION = var.aws_region # Bỏ vì AWS tự inject + AWS_BUCKET_NAME = module.s3_bucket.bucket_name + } + } + tuition = { + image = module.ecr.repository_urls["tuition-service"] + memory = 512 + timeout = 10 + architectures = ["arm64"] + environment = { + REDIS_HOST = module.elasticache.endpoint_address + REDIS_PORT = module.elasticache.endpoint_port + REDIS_PREFIX = "tuitionSv:" + + PUBLIC_KEY_SECRET_NAME = module.secrets.public_key_secret_name + SERVICE_SECRET_NAME = module.secrets.supabase_secret_names.tuition + + # AWS_REGION = var.aws_region # Bỏ vì AWS tự inject + # WARN: Module không thể gọi output chính nó nên phải hardcode tên function + # NOTE: Do đó cần hardcode đúng tên function + CLASS_SERVICE_API = "${var.project_name}-lambda-class-function" + STUDENT_SERVICE_API = "${var.project_name}-lambda-student-function" + } + } + } + + # Quyền invoke của function -> function(s) + invoke_permissions = { + class = ["tuition"] + tuition = ["class"] + } + + s3_arn = module.s3_bucket.bucket_arn + auth_secret_arn = module.secrets.auth_secret_arn + public_key_secret_arn = module.secrets.public_key_secret_arn + supabase_secret_arns = module.secrets.supabase_secret_arns + + # VPC config + vpc_subnet_ids = module.networking.private_subnet_ids + vpc_security_group_ids = [module.networking.lambda_sg_id] +} + +module "api_gateway" { + source = "./modules/api_gateway" + + project_name = var.project_name + tags = var.tags + + api_routes = { + auth = { + prefix_path = "/users" + lambda_invoke_arn = module.lambda.lambda_functions.auth.invoke_arn + lambda_arn = module.lambda.lambda_functions.auth.arn + } + class = { + prefix_path = "/classes" + lambda_invoke_arn = module.lambda.lambda_functions.class.invoke_arn + lambda_arn = module.lambda.lambda_functions.class.arn + } + student = { + prefix_path = "/students" + lambda_invoke_arn = module.lambda.lambda_functions.student.invoke_arn + lambda_arn = module.lambda.lambda_functions.student.arn + } + teacher = { + prefix_path = "/teachers" + lambda_invoke_arn = module.lambda.lambda_functions.teacher.invoke_arn + lambda_arn = module.lambda.lambda_functions.teacher.arn + } + tuition = { + prefix_path = "/tuition" + lambda_invoke_arn = module.lambda.lambda_functions.tuition.invoke_arn + lambda_arn = module.lambda.lambda_functions.tuition.arn + } + } +} + +module "cloudwatch" { + source = "./modules/cloudwatch" + + project_name = var.project_name + tags = var.tags + + email = "phuocnt1611@gmail.com" + + function_name = { + auth = module.lambda.lambda_functions.auth.name + class = module.lambda.lambda_functions.class.name + student = module.lambda.lambda_functions.student.name + teacher = module.lambda.lambda_functions.teacher.name + tuition = module.lambda.lambda_functions.tuition.name + } + + apigw = { + api_name = module.api_gateway.api_name + api_id = module.api_gateway.api_id + api_stage = module.api_gateway.stage_name + } + + cache_serverless_name = module.elasticache.cache_name +} \ No newline at end of file diff --git a/terraform/modules/api_gateway/api_gateway.tf b/terraform/modules/api_gateway/api_gateway.tf new file mode 100644 index 0000000..03a71ff --- /dev/null +++ b/terraform/modules/api_gateway/api_gateway.tf @@ -0,0 +1,63 @@ +resource "aws_apigatewayv2_api" "lambda_http" { + name = "${var.project_name}-serverless-lambda-gw" + protocol_type = "HTTP" + + tags = merge(var.tags, { + Name = "${var.project_name}-serverless-lambda-gw" + }) +} + +resource "aws_apigatewayv2_stage" "lambda_stage" { + api_id = aws_apigatewayv2_api.lambda_http.id + name = "$default" + auto_deploy = true + + # # Chỉ định metric hiển thị log trên Cloudwatch (dùng khi có aws_cloudwatch_log_group) + # access_log_settings { + # destination_arn = var.cloudwatch_log_group + + # format = jsonencode({ + # requestId = "$context.requestId" + # sourceIp = "$context.identity.sourceIp" + # requestTime = "$context.requestTime" + # protocol = "$context.protocol" + # httpMethod = "$context.httpMethod" + # resourcePath = "$context.resourcePath" + # routeKey = "$context.routeKey" + # status = "$context.status" + # integrationErrorMessage = "$context.integrationErrorMessage" + # integrationStatus = "$context.integration.status" + # latency = "$context.responseLatency" + # }) + # } +} + +resource "aws_apigatewayv2_integration" "lambda_functions" { + for_each = var.api_routes + + integration_type = "AWS_PROXY" + api_id = aws_apigatewayv2_api.lambda_http.id + integration_uri = each.value.lambda_invoke_arn + payload_format_version = "2.0" +} + +resource "aws_apigatewayv2_route" "lambda_routes" { + for_each = var.api_routes + + api_id = aws_apigatewayv2_api.lambda_http.id + route_key = "ANY ${each.value.prefix_path}/{proxy+}" + + target = "integrations/${aws_apigatewayv2_integration.lambda_functions[each.key].id}" +} + +# Permission cho phép API Gateway invoke tới lambda function +resource "aws_lambda_permission" "allow_api" { + for_each = var.api_routes + + statement_id = "AllowAPIGatewayInvoke-${each.key}" + action = "lambda:InvokeFunction" + function_name = each.value.lambda_arn + principal = "apigateway.amazonaws.com" + + source_arn = "${aws_apigatewayv2_api.lambda_http.execution_arn}/*/*/*" +} \ No newline at end of file diff --git a/terraform/modules/api_gateway/outputs.tf b/terraform/modules/api_gateway/outputs.tf new file mode 100644 index 0000000..b64b12e --- /dev/null +++ b/terraform/modules/api_gateway/outputs.tf @@ -0,0 +1,24 @@ +output "api_name" { + description = "Tên of API Gateway HTTP API" + value = aws_apigatewayv2_api.lambda_http.name +} + +output "api_id" { + description = "ID of API Gateway HTTP API" + value = aws_apigatewayv2_api.lambda_http.id +} + +output "api_endpoint" { + description = "Basic endpoint of API Gateway (no stage)" + value = aws_apigatewayv2_api.lambda_http.api_endpoint +} + +output "stage_name" { + description = "Stage name of API Gateway" + value = aws_apigatewayv2_stage.lambda_stage.name +} + +output "stage_arn" { + description = "Stage ARN API Gateway" + value = aws_apigatewayv2_stage.lambda_stage.arn +} \ No newline at end of file diff --git a/terraform/modules/api_gateway/variables.tf b/terraform/modules/api_gateway/variables.tf new file mode 100644 index 0000000..e6a59d1 --- /dev/null +++ b/terraform/modules/api_gateway/variables.tf @@ -0,0 +1,19 @@ +variable "project_name" { + type = string + default = "" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "api_routes" { + description = "Corresponding routes for Lambda functions" + type = map(object({ + prefix_path = string + lambda_invoke_arn = string + lambda_arn = string + })) + default = {} +} \ No newline at end of file diff --git a/terraform/modules/cloudwatch/cloudwatch_api_gateway.tf b/terraform/modules/cloudwatch/cloudwatch_api_gateway.tf new file mode 100644 index 0000000..25ec979 --- /dev/null +++ b/terraform/modules/cloudwatch/cloudwatch_api_gateway.tf @@ -0,0 +1,232 @@ +resource "aws_cloudwatch_dashboard" "api_gateway" { + dashboard_name = "${var.project_name}-api-gateway-dashboard" + + dashboard_body = jsonencode({ + widgets = [ + { + type = "metric", + x = 0, y = 0, + width = 12, height = 6, + properties = { + title = "API Gateway 4XX Errors", + view = "timeSeries", + region = var.region, + stat = "Sum", + metrics = [ + ["AWS/ApiGateway", "4XXError", "ApiId", var.apigw.api_id, "Stage", var.apigw.api_stage] + ] + } + }, + { + type = "metric", + x = 12, y = 0, + width = 12, height = 6, + properties = { + title = "API Gateway 5XX Errors", + view = "timeSeries", + region = var.region, + stat = "Sum", + metrics = [ + ["AWS/ApiGateway", "5XXError", "ApiId", var.apigw.api_id, "Stage", var.apigw.api_stage] + ] + } + }, + { + type = "metric", + x = 0, y = 6, + width = 12, height = 6, + properties = { + title = "API Gateway Latency (End-to-End)", + view = "timeSeries", + region = var.region, + stat = "Average", + metrics = [ + ["AWS/ApiGateway", "Latency", "ApiId", var.apigw.api_id, "Stage", var.apigw.api_stage] + ] + } + }, + { + type = "metric", + x = 12, y = 6, + width = 12, height = 6, + properties = { + title = "API Gateway Integration Latency (Lambda Execution Time)", + view = "timeSeries", + region = var.region, + stat = "Average", + metrics = [ + ["AWS/ApiGateway", "IntegrationLatency", "ApiId", var.apigw.api_id, "Stage", var.apigw.api_stage] + ] + } + }, + { + type = "metric", + x = 0, y = 12, + width = 12, height = 6, + properties = { + title = "API Gateway Request Count", + view = "timeSeries", + region = var.region, + stat = "Sum", + metrics = [ + ["AWS/ApiGateway", "Count", "ApiId", var.apigw.api_id, "Stage", var.apigw.api_stage] + ] + } + }, + ] + }) +} + +# Theo dõi 4xx Errors +resource "aws_cloudwatch_metric_alarm" "apigw_4xx_errors" { + alarm_name = "${var.project_name}-apigw-4xx-errors" + alarm_description = "HTTP API 4XX error rate >= 5%" + comparison_operator = "GreaterThanThreshold" + threshold = 0.05 + evaluation_periods = 5 + + alarm_actions = [aws_sns_topic.notifications.arn] + + metric_query { + id = "m4xx" + metric { + namespace = "AWS/ApiGateway" + metric_name = "4xx" + period = 60 + stat = "Sum" + dimensions = { + ApiId = var.apigw.api_id + Stage = var.apigw.api_stage + } + } + return_data = false + } + + metric_query { + id = "mcount" + metric { + namespace = "AWS/ApiGateway" + metric_name = "Count" + period = 60 + stat = "Sum" + dimensions = { + ApiId = var.apigw.api_id + Stage = var.apigw.api_stage + } + } + return_data = false + } + + metric_query { + id = "e1" + label = "4xx_rate" + expression = "m4xx / mcount" + return_data = true + } + + tags = merge(var.tags, { + Name = "${var.project_name}-apigw-4xx-errors" + }) +} + +# Theo dõi 5xx Errors +resource "aws_cloudwatch_metric_alarm" "apigw_5xx_errors" { + alarm_name = "${var.project_name}-apigw-5xx-errors" + alarm_description = "HTTP API 5XX error rate >= 5%" + comparison_operator = "GreaterThanThreshold" + threshold = 0.05 + evaluation_periods = 3 + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + metric_query { + id = "m5xx" + metric { + namespace = "AWS/ApiGateway" + metric_name = "5xx" + period = 60 + stat = "Sum" + dimensions = { + ApiId = var.apigw.api_id + Stage = var.apigw.api_stage + } + } + return_data = false + } + + metric_query { + id = "mcount" + metric { + namespace = "AWS/ApiGateway" + metric_name = "Count" + period = 60 + stat = "Sum" + dimensions = { + ApiId = var.apigw.api_id + Stage = var.apigw.api_stage + } + } + return_data = false + } + + metric_query { + id = "e2" + expression = "m5xx / mcount" + label = "5xx_rate" + return_data = true + } + + tags = merge(var.tags, { + Name = "${var.project_name}-apigw-5xx-errors" + }) +} + +# Theo dõi latency - 10% users đang phải chờ ≥ 2.5s +resource "aws_cloudwatch_metric_alarm" "apigw_high_latency" { + alarm_name = "${var.project_name}-apigw-high-latency" + namespace = "AWS/ApiGateway" + metric_name = "Latency" + comparison_operator = "GreaterThanOrEqualToThreshold" + threshold = 2500 + period = 60 + evaluation_periods = 5 + # INFO: extended_statistic hỗ trợ chỉ số p0.0 - p100 + extended_statistic = "p90" # 10% request chậm nhất + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + ApiId = var.apigw.api_id + Stage = var.apigw.api_stage + } + + tags = merge(var.tags, { + Name = "${var.project_name}-apigw-high-latency" + }) +} + +# Theo dõi số traffic +resource "aws_cloudwatch_metric_alarm" "apigw_request_spike" { + alarm_name = "${var.project_name}-apigw-request-spike" + namespace = "AWS/ApiGateway" + metric_name = "Count" + comparison_operator = "GreaterThanThreshold" + threshold = 200 + period = 60 + evaluation_periods = 1 + statistic = "Sum" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + ApiId = var.apigw.api_id + Stage = var.apigw.api_stage + } + + tags = merge(var.tags, { + Name = "${var.project_name}-apigw-request-spike" + }) +} diff --git a/terraform/modules/cloudwatch/cloudwatch_elasticache.tf b/terraform/modules/cloudwatch/cloudwatch_elasticache.tf new file mode 100644 index 0000000..efefd48 --- /dev/null +++ b/terraform/modules/cloudwatch/cloudwatch_elasticache.tf @@ -0,0 +1,205 @@ +resource "aws_cloudwatch_dashboard" "elasticache" { + dashboard_name = "${var.project_name}-elasticache-dashboard" + + dashboard_body = jsonencode({ + widgets = [ + { + type = "metric", + x = 0, y = 0, + width = 12, height = 6, + properties = { + title = "ElastiCache CPU Utilization", + view = "timeSeries", + region = var.region, + stat = "Average", + metrics = [ + ["AWS/ElastiCache", "CPUUtilization", "CacheName", var.cache_serverless_name + ] + ] + } + }, + { + type = "metric", + x = 12, y = 0, + width = 12, height = 6, + properties = { + title = "ElastiCache Freeable Memory", + view = "timeSeries", + region = var.region, + stat = "Average", + metrics = [ + ["AWS/ElastiCache", "FreeableMemory", "CacheName", var.cache_serverless_name, {}] + ] + } + }, + { + type = "metric", + x = 0, y = 6, + width = 12, height = 6, + properties = { + title = "ElastiCache Current Connections", + view = "timeSeries", + region = var.region, + stat = "Average", + metrics = [ + ["AWS/ElastiCache", "CurrConnections", "CacheName", var.cache_serverless_name, {}] + ] + } + }, + { + type = "metric", + x = 12, y = 6, + width = 12, height = 6, + properties = { + title = "ElastiCache Evictions", + view = "timeSeries", + region = var.region, + stat = "Sum", + metrics = [ + ["AWS/ElastiCache", "Evictions", "CacheName", var.cache_serverless_name, {}] + ] + } + }, + ] + }) +} + +# Theo dõi CPU Utilization +# CPU Utilization WARN (> 75%) +resource "aws_cloudwatch_metric_alarm" "elasticache_high_cpu_warn" { + alarm_name = "${var.project_name}-elasticache-high-cpu-warn-alarm" + namespace = "AWS/ElastiCache" + metric_name = "CPUUtilization" + comparison_operator = "GreaterThanThreshold" + threshold = 75 + period = 300 + evaluation_periods = 3 + statistic = "Average" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + CacheName = var.cache_serverless_name + } + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache-high-cpu-warn-alarm" + }) +} + +# CPU Utilization CRITIAL (> 90%) +resource "aws_cloudwatch_metric_alarm" "elasticache_high_cpu_critical" { + alarm_name = "${var.project_name}-elasticache-high-cpu-critial-alarm" + namespace = "AWS/ElastiCache" + metric_name = "CPUUtilization" + comparison_operator = "GreaterThanThreshold" + threshold = 90 + period = 300 + evaluation_periods = 3 + statistic = "Average" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + CacheName = var.cache_serverless_name + } + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache-high-cpu-critical-alarm" + }) +} + +# Theo dõi FreeableMemory (Bộ nhớ còn trống) +# FreeableMemory WARN (< 200MB) +resource "aws_cloudwatch_metric_alarm" "elasticache_low_memory_warn" { + alarm_name = "${var.project_name}-elasticache-low-memory-warn" + namespace = "AWS/ElastiCache" + metric_name = "FreeableMemory" + comparison_operator = "LessThanThreshold" + threshold = 200000000 # 200 MB in bytes + period = 300 + evaluation_periods = 2 + statistic = "Average" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + CacheName = var.cache_serverless_name + } + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache-low-memory-warn" + }) +} + +# FreeableMemory CRITICAL (< 100MB) +resource "aws_cloudwatch_metric_alarm" "elasticache_low_memory_critical" { + alarm_name = "${var.project_name}-elasticache-low-memory-critical" + namespace = "AWS/ElastiCache" + metric_name = "FreeableMemory" + comparison_operator = "LessThanThreshold" + threshold = 100000000 # 100 MB + period = 300 + evaluation_periods = 1 + statistic = "Average" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + CacheName = var.cache_serverless_name + } + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache-low-memory-critical" + }) +} + +# Theo dõi Evictions (Redis đầy bộ nhớ và đang xoá key để sống sót) +resource "aws_cloudwatch_metric_alarm" "elasticache_evictions" { + alarm_name = "${var.project_name}-elasticache-evictions-alarm" + namespace = "AWS/ElastiCache" + metric_name = "Evictions" + comparison_operator = "GreaterThanThreshold" + threshold = 0 + period = 300 + evaluation_periods = 1 + statistic = "Sum" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + CacheName = var.cache_serverless_name + } + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache-evictions" + }) +} + +# Theo dõi các kết nối hiện tại +resource "aws_cloudwatch_metric_alarm" "elasticache_high_connections" { + alarm_name = "${var.project_name}-elasticache-high-connections" + namespace = "AWS/ElastiCache" + metric_name = "CurrConnections" + comparison_operator = "GreaterThanThreshold" + threshold = 500 + period = 300 + evaluation_periods = 2 + statistic = "Average" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + CacheName = var.cache_serverless_name + } + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache-high-connections" + }) +} \ No newline at end of file diff --git a/terraform/modules/cloudwatch/cloudwatch_lambda.tf b/terraform/modules/cloudwatch/cloudwatch_lambda.tf new file mode 100644 index 0000000..f551af1 --- /dev/null +++ b/terraform/modules/cloudwatch/cloudwatch_lambda.tf @@ -0,0 +1,249 @@ +resource "aws_cloudwatch_dashboard" "lambda" { + dashboard_name = "${var.project_name}-lambda-dashboard" + + dashboard_body = jsonencode({ + widgets = [ + { + type = "metric", + x = 0, y = 0, + width = 24, height = 6, + properties = { + title = "Lambda Errors (All Services)", + view = "timeSeries", + stacked = false, # Biểu thị đường xếp chồng (true) hoặc đường riêng biệt (false) + region = var.region, + stat = "Sum", + metrics = [ + ["AWS/Lambda", "Errors", "FunctionName", var.function_name.auth, { "label" : "auth" }], + ["AWS/Lambda", "Errors", "FunctionName", var.function_name.class, { "label" : "class" }], + ["AWS/Lambda", "Errors", "FunctionName", var.function_name.student, { "label" : "student" }], + ["AWS/Lambda", "Errors", "FunctionName", var.function_name.tuition, { "label" : "tuition" }], + ["AWS/Lambda", "Errors", "FunctionName", var.function_name.teacher, { "label" : "teacher" }] + ] + } + }, + { + type = "metric", + x = 0, y = 6, + width = 24, height = 6, + properties = { + title = "Lambda Duration p95 (All Services)", + view = "timeSeries", + region = var.region, + stat = "p95", + metrics = [ + ["AWS/Lambda", "Duration", "FunctionName", var.function_name.auth, { "label" : "auth" }], + ["AWS/Lambda", "Duration", "FunctionName", var.function_name.class, { "label" : "class" }], + ["AWS/Lambda", "Duration", "FunctionName", var.function_name.student, { "label" : "student" }], + ["AWS/Lambda", "Duration", "FunctionName", var.function_name.tuition, { "label" : "tuition" }], + ["AWS/Lambda", "Duration", "FunctionName", var.function_name.teacher, { "label" : "teacher" }] + ] + } + }, + { + type = "metric", + x = 0, y = 12, + width = 24, height = 6, + properties = { + title = "Lambda Throttles (All Services)", + view = "timeSeries", + region = var.region, + stat = "Sum", + metrics = [ + ["AWS/Lambda", "Throttles", "FunctionName", var.function_name.auth, { "label" : "auth" }], + ["AWS/Lambda", "Throttles", "FunctionName", var.function_name.class, { "label" : "class" }], + ["AWS/Lambda", "Throttles", "FunctionName", var.function_name.student, { "label" : "student" }], + ["AWS/Lambda", "Throttles", "FunctionName", var.function_name.tuition, { "label" : "tuition" }], + ["AWS/Lambda", "Throttles", "FunctionName", var.function_name.teacher, { "label" : "teacher" }] + ] + } + }, + { + type = "metric", + x = 0, y = 18, + width = 24, height = 6, + properties = { + title = "Concurrent Executions (All Services)", + view = "timeSeries", + region = var.region, + stat = "Maximum", + metrics = [ + ["AWS/Lambda", "ConcurrentExecutions", "FunctionName", var.function_name.auth, {}], + ["AWS/Lambda", "ConcurrentExecutions", "FunctionName", var.function_name.class, {}], + ["AWS/Lambda", "ConcurrentExecutions", "FunctionName", var.function_name.student, {}], + ["AWS/Lambda", "ConcurrentExecutions", "FunctionName", var.function_name.tuition, {}], + ["AWS/Lambda", "ConcurrentExecutions", "FunctionName", var.function_name.teacher, {}] + ] + } + }, + { + type = "metric", + x = 0, y = 24, + width = 24, height = 6, + properties = { + title = "Lambda Invocations (All Services)", + view = "timeSeries", + region = var.region, + stat = "Sum", + metrics = [ + ["AWS/Lambda", "Invocations", "FunctionName", var.function_name.auth, {}], + ["AWS/Lambda", "Invocations", "FunctionName", var.function_name.class, {}], + ["AWS/Lambda", "Invocations", "FunctionName", var.function_name.student, {}], + ["AWS/Lambda", "Invocations", "FunctionName", var.function_name.tuition, {}], + ["AWS/Lambda", "Invocations", "FunctionName", var.function_name.teacher, {}] + ] + } + } + ] + }) +} + +# Theo dõi Log +resource "aws_cloudwatch_log_group" "lambda" { + for_each = var.function_name + + name = "/aws/lambda/${each.value}" # Chỉ định nơi lưu trữ log + retention_in_days = 30 # Lưu trữ log trong X ngày + + tags = merge(var.tags, { + Name = "${var.project_name}-lambda-${each.key}-log" + Function = each.key + }) +} + +# Theo dõi Error - Có tổng số lỗi >= 1 trong 60s +resource "aws_cloudwatch_metric_alarm" "lambda_error" { + for_each = var.function_name + + alarm_name = "${var.project_name}-lambda-${each.key}-error" + namespace = "AWS/Lambda" + metric_name = "Errors" + comparison_operator = "GreaterThanThreshold" + threshold = 0 # Ngưỡng kích hoạt alarm + # INFO: CloudWatch theo dõi metric theo datapoint (ô thời gian), độ dài mỗi datapoint quy định bằng period + # INFO: Sau X datapoint (quy định bằng evaluation_periods), CloudWatch gom các metric với nhau và so sánh + # INFO: datapoints_to_alarm: Số datapoint trong X datapoint (evaluation_periods) phải vượt threshold để kích hoạt alarm + # INFO: Ví dụ: evaluation_periods = 5 & datapoints_to_alarm = 3 -> Cần 3/5 datapoint bị lỗi để kích hoạt alarm + period = 60 # Độ dài mỗi datapoint (sec) + evaluation_periods = 3 # Số datapoint cloudwatch so sánh để xem xét vượt ngưỡng + statistic = "Sum" + + # Hành động thực hiện khi alarm kích hoạt + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + # Filter theo giá trị + dimensions = { + FunctionName = each.value + } + + tags = merge(var.tags, { + Name = "${var.project_name}-lambda-${each.key}-error" + Function = each.key + }) +} + +# Theo dõi Duration - Duration p95 >= 80% timeout +resource "aws_cloudwatch_metric_alarm" "lambda_duration" { + for_each = var.function_name + + alarm_name = "${var.project_name}-lambda-${each.key}-duration" + namespace = "AWS/Lambda" + metric_name = "Duration" + comparison_operator = "GreaterThanOrEqualToThreshold" + threshold = 8000 + period = 60 + evaluation_periods = 15 + datapoints_to_alarm = 15 + extended_statistic = "p95" # percentile + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + FunctionName = each.value + } + + tags = merge(var.tags, { + Name = "${var.project_name}-lambda-${each.key}-duration" + Function = each.key + }) +} + +# Theo dõi Throttles (Lambda bị giới hạn tài nguyên, request) - Throttles > 0 +resource "aws_cloudwatch_metric_alarm" "lambda_throttle" { + for_each = var.function_name + + alarm_name = "${var.project_name}-lambda-${each.key}-throttle" + namespace = "AWS/Lambda" + metric_name = "Throttles" + comparison_operator = "GreaterThanThreshold" + threshold = 0 + period = 60 + evaluation_periods = 5 + statistic = "Sum" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + FunctionName = each.value + } + + tags = merge(var.tags, { + Name = "${var.project_name}-lambda-${each.key}-throttle" + Function = each.key + }) +} + +# Theo dõi Concurrent Evocation (Số instance lambda chạy đồng thời) +resource "aws_cloudwatch_metric_alarm" "lambda_high_concurrency" { + for_each = var.function_name + + alarm_name = "${var.project_name}-lambda-${each.key}-high-concurrency" + namespace = "AWS/Lambda" + metric_name = "ConcurrentExecutions" + comparison_operator = "GreaterThanOrEqualToThreshold" + threshold = 50 + period = 60 + evaluation_periods = 1 + statistic = "Maximum" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + FunctionName = each.value + } + + tags = merge(var.tags, { + Name = "${var.project_name}-lambda-${each.key}-high-concurrency" + Function = each.key + }) +} + +# Theo dõi Invocations (Tổng số lần gọi hàm lambda) +resource "aws_cloudwatch_metric_alarm" "lambda_invocation_spike" { + for_each = var.function_name + + alarm_name = "${var.project_name}-lambda-${each.key}-invocation-spike" + namespace = "AWS/Lambda" + metric_name = "Invocations" + comparison_operator = "GreaterThanThreshold" + threshold = 200 + period = 60 + evaluation_periods = 1 + statistic = "Sum" + + alarm_actions = [aws_sns_topic.notifications.arn] + ok_actions = [aws_sns_topic.notifications.arn] + + dimensions = { + FunctionName = each.value + } + + tags = merge(var.tags, { + Name = "${var.project_name}-lambda-${each.key}-invocation-spike" + Function = each.key + }) +} \ No newline at end of file diff --git a/terraform/modules/cloudwatch/sns.tf b/terraform/modules/cloudwatch/sns.tf new file mode 100644 index 0000000..e4b259f --- /dev/null +++ b/terraform/modules/cloudwatch/sns.tf @@ -0,0 +1,10 @@ +# Gửi mail khi có cảnh báo +resource "aws_sns_topic" "notifications" { + name = "${var.project_name}-cloudwatch-notify" +} + +resource "aws_sns_topic_subscription" "email" { + topic_arn = aws_sns_topic.notifications.arn + protocol = "email" + endpoint = var.email +} \ No newline at end of file diff --git a/terraform/modules/cloudwatch/variables.tf b/terraform/modules/cloudwatch/variables.tf new file mode 100644 index 0000000..2e15d8e --- /dev/null +++ b/terraform/modules/cloudwatch/variables.tf @@ -0,0 +1,46 @@ +variable "project_name" { + type = string + default = "" +} + +variable "region" { + type = string + default = "ap-southeast-1" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "email" { + description = "SNS email that CloudWatch alarm will send notification" + type = string + default = "" +} + +variable "function_name" { + description = "Lambda function name" + type = object({ + auth = string + class = string + student = string + tuition = string + teacher = string + }) +} + +variable "cache_serverless_name" { + description = "ElastiCache name" + type = string + default = "" +} + +variable "apigw" { + description = "API Gateway values" + type = object({ + api_name = string + api_id = string + api_stage = string + }) +} \ No newline at end of file diff --git a/terraform/modules/ecr/ecr.tf b/terraform/modules/ecr/ecr.tf new file mode 100644 index 0000000..53b08a6 --- /dev/null +++ b/terraform/modules/ecr/ecr.tf @@ -0,0 +1,54 @@ +resource "aws_ecr_repository" "repos" { + for_each = toset(var.services) # Tạo repository cho mỗi service trong list + + name = "${var.project_name}-${each.key}" + image_tag_mutability = "MUTABLE" # Cho phép tag ảnh có thể thay đổi + + image_scanning_configuration { + scan_on_push = true # Tự động quét lỗ hổng khi đẩy ảnh lên + } + + encryption_configuration { + encryption_type = "AES256" + } + + tags = merge(var.tags, { + Name = "${var.project_name}-${each.key}" + }) +} + +resource "aws_ecr_lifecycle_policy" "cleanup" { + for_each = toset(var.services) + + repository = aws_ecr_repository.repos[each.key].name + + policy = jsonencode({ + rules = [ + { + rulePriority = 1 + description = "Delete untagged images older than X days" + selection = { + tagStatus = "untagged" + countType = "sinceImagePushed" + countUnit = "days" + countNumber = var.expired_days + } + action = { + type = "expire" + } + }, + { + rulePriority = 2 + description = "Keep only maximum 10 images left" + selection = { + tagStatus = "any" + countType = "imageCountMoreThan" + countNumber = 10 + } + action = { + type = "expire" + } + } + ] + }) +} diff --git a/terraform/modules/ecr/outputs.tf b/terraform/modules/ecr/outputs.tf new file mode 100644 index 0000000..5e551b8 --- /dev/null +++ b/terraform/modules/ecr/outputs.tf @@ -0,0 +1,15 @@ +output "repository_urls" { + description = "Map: service_name -> ECR repo URL" + value = { + for svc, repo in aws_ecr_repository.repos : + svc => repo.repository_url + } +} + +output "repository_arns" { + description = "Map: service_name -> ECR repo ARN" + value = { + for svc, repo in aws_ecr_repository.repos : + svc => repo.arn + } +} \ No newline at end of file diff --git a/terraform/modules/ecr/variables.tf b/terraform/modules/ecr/variables.tf new file mode 100644 index 0000000..6f73121 --- /dev/null +++ b/terraform/modules/ecr/variables.tf @@ -0,0 +1,21 @@ +variable "project_name" { + type = string + default = "" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "services" { + type = list(string) + description = "Service list for ECR repository" + default = [] +} + +variable "expired_days" { + description = "Number of days untagged image stored" + type = number + default = 7 +} \ No newline at end of file diff --git a/terraform/modules/elasticache/elasticache.tf b/terraform/modules/elasticache/elasticache.tf new file mode 100644 index 0000000..038a7d1 --- /dev/null +++ b/terraform/modules/elasticache/elasticache.tf @@ -0,0 +1,29 @@ +resource "aws_elasticache_serverless_cache" "main" { + name = "${var.project_name}-elasticache" + engine = var.engine + + # Kiểm soát chi phí và hiệu suất bằng cách đặt giới hạn sử dụng bộ nhớ đệm + cache_usage_limits { + # Giới hạn dung lượng bộ nhớ RAM + data_storage { + maximum = var.cache_storage_max_gb + unit = "GB" + } + # Giới hạn throughput/performance dựa trên eCPU-per-second + ecpu_per_second { + maximum = var.ecpu_per_second_max + } + } + + major_engine_version = var.major_engine_version + kms_key_id = var.kms_key_id # KMS Key để mã hóa data at rest + daily_snapshot_time = var.daily_snapshot_time # Thời gian AWS tạo snapshot mỗi ngày (UTC) + snapshot_retention_limit = var.snapshot_retention_limit # Số ngày giữ snapshot + + security_group_ids = var.security_group_ids + subnet_ids = var.subnet_ids + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache" + }) +} \ No newline at end of file diff --git a/terraform/modules/elasticache/outputs.tf b/terraform/modules/elasticache/outputs.tf new file mode 100644 index 0000000..1e88943 --- /dev/null +++ b/terraform/modules/elasticache/outputs.tf @@ -0,0 +1,35 @@ +output "cache_name" { + description = "Serverless cache name" + value = aws_elasticache_serverless_cache.main.name +} + +output "cache_arn" { + description = "Serverless cache ARN" + value = aws_elasticache_serverless_cache.main.arn +} + +output "cache_id" { + description = "Serverless cache ID" + value = aws_elasticache_serverless_cache.main.id +} + +# Endpoint block nên được bỏ vào try() để tránh lỗi khi resource chưa được tạo +output "endpoint_address" { + description = "Primary endpoint DNS address" + value = try(aws_elasticache_serverless_cache.main.endpoint[0].address, null) +} + +output "endpoint_port" { + description = "Primary endpoint port" + value = try(aws_elasticache_serverless_cache.main.endpoint[0].port, null) +} + +output "reader_endpoint_address" { + description = "Reader endpoint DNS (if available) for read-only operations" + value = try(aws_elasticache_serverless_cache.main.reader_endpoint[0].address, null) +} + +output "reader_endpoint_port" { + description = "Reader endpoint port" + value = try(aws_elasticache_serverless_cache.main.reader_endpoint[0].port, null) +} diff --git a/terraform/modules/elasticache/variables.tf b/terraform/modules/elasticache/variables.tf new file mode 100644 index 0000000..edcbd97 --- /dev/null +++ b/terraform/modules/elasticache/variables.tf @@ -0,0 +1,64 @@ +variable "project_name" { + type = string + default = "" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "security_group_ids" { + description = "Security groups ID for ElastiCache" + type = list(string) + default = [] +} + +variable "subnet_ids" { + description = "List of private subnet IDs for ElastiCache Subnet Group" + type = list(string) + default = [] +} + +variable "engine" { + description = "ElastiCache serverless engine: valkey | redis | memcached" + type = string + default = "valkey" +} + +variable "major_engine_version" { + description = "Major engine version" + type = string + default = "7" +} + +# Đảm bảo Lambda role có quyền kms:Decrypt nếu dùng KMS key +variable "kms_key_id" { + description = "Optional KMS key ARN for at-rest encryption" + type = string + default = null +} + +variable "cache_storage_max_gb" { + description = "Maximum data storage (GB) used by serverless cache" + type = number + default = 10 +} + +variable "ecpu_per_second_max" { + description = "Maximum eCPU-per-second limit (throughput model) for serverless" + type = number + default = 5000 +} + +variable "snapshot_retention_limit" { + description = "Number of daily snapshots to keep (0 to disable)" + type = number + default = 1 +} + +variable "daily_snapshot_time" { + description = "Preferred daily snapshot time (HH:MM in UTC)" + type = string + default = null +} \ No newline at end of file diff --git a/terraform/modules/lambda/iam.tf b/terraform/modules/lambda/iam.tf new file mode 100644 index 0000000..5612ad9 --- /dev/null +++ b/terraform/modules/lambda/iam.tf @@ -0,0 +1,151 @@ +locals { + generic_policy_arns = [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole", + "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", + ] + + # Phân tách policy từ generic_policy_arns để tạo map 1-1 service-policy + # Vd: auth_AWSLambdaBasicExecutionRole = { service = "auth", policy_arn = "..." } + shared_policy_map = merge([ + for service, _ in var.lambda_services : { + for policy_arn in local.generic_policy_arns : + "${service}_${basename(policy_arn)}" => { + service = service + policy_arn = policy_arn + } + } + ]...) +} + +locals { + secrets_map = { + auth = [ + var.auth_secret_arn, + var.public_key_secret_arn + ] + + class = [ + var.supabase_secret_arns["class"], + var.public_key_secret_arn + ] + + student = [ + var.supabase_secret_arns["student"], + var.public_key_secret_arn + ] + + tuition = [ + var.supabase_secret_arns["tuition"], + var.public_key_secret_arn + ] + + teacher = [ + var.supabase_secret_arns["teacher"], + var.public_key_secret_arn + ] + } +} + +locals { + # Check secret_keys phải khớp với lambda_keys + # Lấy danh sách key + lambda_keys = keys(var.lambda_services) + secret_keys = keys(local.secrets_map) + + # Danh sách key không khớp (nếu có) + mismatched_keys = [ + for k in concat(local.lambda_keys, local.secret_keys) : + k if !( + contains(local.lambda_keys, k) && + contains(local.secret_keys, k) + ) + ] + + # Validate keys phải khớp + validate_keys = length(local.mismatched_keys) == 0 ? true : file("ERROR: Keys in lambda_services and secrets_map must match. Mismatches: ${local.mismatched_keys}") +} + +# TẠO IAM ROLE CHO 5 FUNCTIONS +resource "aws_iam_role" "lambda_role" { + for_each = var.lambda_services + + name = "${var.project_name}-${each.key}-lambda-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + } + ] + }) + + tags = merge(var.tags, { + Name = "${var.project_name}-${each.key}-lambda-role" + }) +} + +# GÁN POLICY +# Gán policy chung cho 5 lambda function +resource "aws_iam_role_policy_attachment" "shared" { + for_each = local.shared_policy_map + + role = aws_iam_role.lambda_role[each.value.service].name + policy_arn = each.value.policy_arn +} + +# NOTE: Gán each.key chéo (secret_map -> lambda_role, secrets) có thể gây lỗi nếu các element trong các biến có giá trị không đồng nhất +# NOTE: Buộc các biến lambda_services, secrets_map phải có các element tên auth, class, student, tuition, teacher +# Gán secret policy +resource "aws_iam_role_policy_attachment" "secrets_policy" { + for_each = local.secrets_map + + role = aws_iam_role.lambda_role[each.key].name + policy_arn = aws_iam_policy.secret_policy[each.key].arn +} + +# Gán policy cho teacher service dùng S3 +resource "aws_iam_role_policy_attachment" "teacher_lambda" { + role = aws_iam_role.lambda_role["teacher"].name + policy_arn = aws_iam_policy.s3_policy.arn +} + +# CUSTOM POLICY +resource "aws_iam_policy" "s3_policy" { + name = "${var.project_name}-s3-policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow", + Action = [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject" + ], + Resource = "${var.s3_arn}/*" + }] + }) +} + +resource "aws_iam_policy" "secret_policy" { + for_each = local.secrets_map + + name = "${var.project_name}-${each.key}-secrets-policy" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ + Effect = "Allow" + Action = [ + "secretsmanager:GetSecretValue" + ] + Resource = each.value + }] + }) +} \ No newline at end of file diff --git a/terraform/modules/lambda/lambda.tf b/terraform/modules/lambda/lambda.tf new file mode 100644 index 0000000..ec6ecdd --- /dev/null +++ b/terraform/modules/lambda/lambda.tf @@ -0,0 +1,44 @@ +resource "aws_lambda_function" "functions" { + for_each = var.ecr_enabled ? var.lambda_services : {} + + function_name = "${var.project_name}-lambda-${each.key}-function" + + role = aws_iam_role.lambda_role[each.key].arn + package_type = "Image" + image_uri = "${each.value.image}:latest" + + memory_size = each.value.memory + timeout = each.value.timeout + + architectures = each.value.architectures + + # Cấu hình VPC cho truy cập elasticache trong VPC + vpc_config { + subnet_ids = var.vpc_subnet_ids + security_group_ids = var.vpc_security_group_ids + } + + # Biến môi trường + environment { + variables = each.value.environment + } + + tags = merge(var.tags, { + FunctionName = "${var.project_name}-lambda-${each.key}-function" + }) +} + +# Tạo quyền gọi API giữa các function +resource "aws_lambda_permission" "allow_invoke" { + for_each = var.ecr_enabled ? { + for fnc, targets in var.invoke_permissions : + fnc => targets + } : {} + + statement_id = "AllowLambdaInvoke-${each.key}" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.functions[each.value[0]].function_name + principal = "lambda.amazonaws.com" + + source_arn = aws_lambda_function.functions[each.key].arn +} \ No newline at end of file diff --git a/terraform/modules/lambda/outputs.tf b/terraform/modules/lambda/outputs.tf new file mode 100644 index 0000000..39f7a26 --- /dev/null +++ b/terraform/modules/lambda/outputs.tf @@ -0,0 +1,19 @@ +output "lambda_functions" { + description = "Map of all Lambda function ARNs and names" + value = var.ecr_enabled ? { + for svc, lambda in aws_lambda_function.functions : + svc => { + arn = lambda.arn + invoke_arn = lambda.invoke_arn + name = lambda.function_name + } + } : {} +} + +output "lambda_iam_arn" { + description = "ARN of lambda functions IAM role" + value = { + for svc, iam in aws_iam_role.lambda_role : + svc => iam.arn + } +} \ No newline at end of file diff --git a/terraform/modules/lambda/variables.tf b/terraform/modules/lambda/variables.tf new file mode 100644 index 0000000..20b421c --- /dev/null +++ b/terraform/modules/lambda/variables.tf @@ -0,0 +1,68 @@ +variable "project_name" { + type = string + default = "" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "ecr_enabled" { + description = "Whether ECR created and had pushed image" + type = bool + default = false +} + +variable "lambda_services" { + description = "Lambda function configs" + type = map(object({ + image = string + memory = number + timeout = number + architectures = list(string) + environment = map(any) + })) +} + +variable "s3_arn" { + description = "ARN of S3 bucket storing profile image" + type = string + default = "" +} + +variable "auth_secret_arn" { + description = "ARN of secret manager - auth secret" + type = string + default = "" +} + +variable "public_key_secret_arn" { + description = "ARN of secret manager - public token secret" + type = string + default = "" +} + +variable "supabase_secret_arns" { + description = "ARN of secret manager (supabase services)" + type = map(string) + default = {} +} + +variable "vpc_subnet_ids" { + description = "List of VPC subnet IDs for Lambda functions" + type = list(string) + default = [] +} + +variable "vpc_security_group_ids" { + description = "List of VPC security group IDs for Lambda functions" + type = list(string) + default = [] +} + +variable "invoke_permissions" { + description = "Map of which lambda services can invoke which services" + type = map(list(string)) + default = {} +} \ No newline at end of file diff --git a/terraform/modules/networking/outputs.tf b/terraform/modules/networking/outputs.tf new file mode 100644 index 0000000..47254bd --- /dev/null +++ b/terraform/modules/networking/outputs.tf @@ -0,0 +1,24 @@ +output "vpc_id" { + description = "VPC ID" + value = aws_vpc.main.id +} + +output "public_subnet_ids" { + description = "List of public subnets" + value = aws_subnet.public[*].id +} + +output "private_subnet_ids" { + description = "List of private subnets" + value = aws_subnet.private[*].id +} + +output "lambda_sg_id" { + description = "Security Group ID for Lambda" + value = aws_security_group.lambda_sg.id +} + +output "elasticache_sg_id" { + description = "Security Group ID for elasticache" + value = aws_security_group.elasticache_sg.id +} \ No newline at end of file diff --git a/terraform/modules/networking/security_groups.tf b/terraform/modules/networking/security_groups.tf new file mode 100644 index 0000000..fbde3c0 --- /dev/null +++ b/terraform/modules/networking/security_groups.tf @@ -0,0 +1,66 @@ +# SECURITY GROUP LAMBDA +resource "aws_security_group" "lambda_sg" { + name = "${var.project_name}-lambda-sg" + vpc_id = aws_vpc.main.id + description = "Lambda security group" + + tags = merge(var.tags, { + Name = "${var.project_name}-lambda-sg" + }) +} + +resource "aws_vpc_security_group_egress_rule" "lambda_allow_http" { + security_group_id = aws_security_group.lambda_sg.id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" +} + +# SECURITY GROUP ELASTICACHE +resource "aws_security_group" "elasticache_sg" { + name = "${var.project_name}-elasticache-sg" + description = "Allow ElastiCache to access Lambda" + vpc_id = aws_vpc.main.id + + tags = merge(var.tags, { + Name = "${var.project_name}-elasticache-sg" + }) +} + +resource "aws_vpc_security_group_ingress_rule" "elasticache_allow_lambda" { + security_group_id = aws_security_group.elasticache_sg.id + referenced_security_group_id = aws_security_group.lambda_sg.id + from_port = 6379 + to_port = 6379 + ip_protocol = "tcp" +} + +resource "aws_vpc_security_group_egress_rule" "elasticache_allow_http" { + security_group_id = aws_security_group.elasticache_sg.id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" +} + +# SECURITY GROUP VPC ENDPOINT +resource "aws_security_group" "vpce_sg" { + name = "${var.project_name}-ecr-vpce-sg" + description = "Security group for ECR VPC Endpoints" + vpc_id = aws_vpc.main.id + + tags = merge(var.tags, { + Name = "${var.project_name}-ecr-vpce-sg" + }) +} + +resource "aws_vpc_security_group_ingress_rule" "vpc_endpoint_allow_lambda" { + security_group_id = aws_security_group.vpce_sg.id + referenced_security_group_id = aws_security_group.lambda_sg.id + from_port = 443 + to_port = 443 + ip_protocol = "tcp" +} + +resource "aws_vpc_security_group_egress_rule" "vpc_endpoint_allow_http" { + security_group_id = aws_security_group.vpce_sg.id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" +} \ No newline at end of file diff --git a/terraform/modules/networking/variables.tf b/terraform/modules/networking/variables.tf new file mode 100644 index 0000000..a7c98f7 --- /dev/null +++ b/terraform/modules/networking/variables.tf @@ -0,0 +1,29 @@ +variable "project_name" { + type = string + default = "" +} + +variable "region" { + type = string + default = "ap-southeast-1" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "vpc_cidr" { + description = "CIDR block for VPC" + type = string +} + +variable "public_subnets" { + description = "CIDR for public subnets" + type = list(string) +} + +variable "private_subnets" { + description = "CIDR for private subnets" + type = list(string) +} \ No newline at end of file diff --git a/terraform/modules/networking/vpc.tf b/terraform/modules/networking/vpc.tf new file mode 100644 index 0000000..7985058 --- /dev/null +++ b/terraform/modules/networking/vpc.tf @@ -0,0 +1,106 @@ +# VPC CHÍNH +resource "aws_vpc" "main" { + cidr_block = var.vpc_cidr + enable_dns_support = true # Bật hỗ trợ DNS bên trong VPC + enable_dns_hostnames = true # Cho phép tạo hostname cho instance trong VPC + + # Gắn tag: tag chung (var.tags) + tag riêng + tags = merge(var.tags, { + Name = "${var.project_name}-vpc" + }) +} + +# INTERNET GATEWAY — Cho phép subnet public truy cập Internet +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.main.id + tags = merge(var.tags, { + Name = "${var.project_name}-igw" + }) +} + +# PUBLIC SUBNETS +resource "aws_subnet" "public" { + count = length(var.public_subnets) # Tạo nhiều subnet tùy theo số CIDR trong danh sách + vpc_id = aws_vpc.main.id + cidr_block = var.public_subnets[count.index] # Tham chiếu đến CIDR tương ứng trong mảng cho từng subnet + map_public_ip_on_launch = true # Tự động gán IP public khi tạo EC2 (cho Public Subnet ra IGW) + availability_zone = data.aws_availability_zones.available.names[count.index] # Mỗi subnet ở 1 AZ khác nhau + + tags = merge(var.tags, { + Name = "${var.project_name}-public-sn-${count.index + 1}" + }) +} + +# PRIVATE SUBNETS +resource "aws_subnet" "private" { + count = length(var.private_subnets) + vpc_id = aws_vpc.main.id + cidr_block = var.private_subnets[count.index] + availability_zone = data.aws_availability_zones.available.names[count.index] + + tags = merge(var.tags, { + Name = "${var.project_name}-private-sn-${count.index + 1}" + }) +} + +# NAT GATEWAY — Cho phép subnet private ra Internet (qua subnet public) +# Tạo Elastic IP (EIP) cho NAT Gateway +resource "aws_eip" "nat" { + domain = "vpc" + tags = merge(var.tags, { + Name = "${var.project_name}-nat-eip" + }) +} + +# NAT Gateway đặt trong public subnet đầu tiên +resource "aws_nat_gateway" "nat_gw" { + allocation_id = aws_eip.nat.id + subnet_id = aws_subnet.public[0].id # NAT nằm trong public subnet + tags = merge(var.tags, { + Name = "${var.project_name}-natgw" + }) +} + +# ROUTE TABLES — Quy định đường đi của traffic trong VPC +# Route Table cho public subnet +resource "aws_route_table" "public" { + vpc_id = aws_vpc.main.id + route { + cidr_block = "0.0.0.0/0" # Mọi traffic ra ngoài + gateway_id = aws_internet_gateway.igw.id # Thông qua Internet Gateway + } + tags = merge(var.tags, { + Name = "${var.project_name}-public-rt" + }) +} + +# Route Table cho private subnet +resource "aws_route_table" "private" { + vpc_id = aws_vpc.main.id + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat_gw.id + } + tags = merge(var.tags, { + Name = "${var.project_name}-private-rt" + }) +} + +# Gắn tất cả public subnet với route table public +resource "aws_route_table_association" "public_assoc" { + count = length(aws_subnet.public) + subnet_id = aws_subnet.public[count.index].id + route_table_id = aws_route_table.public.id +} + +# Gắn tất cả private subnet với route table private +resource "aws_route_table_association" "private_assoc" { + count = length(aws_subnet.private) + subnet_id = aws_subnet.private[count.index].id + route_table_id = aws_route_table.private.id +} + +# Lấy danh sách AZ khả dụng của region hiện tại +data "aws_availability_zones" "available" { + state = "available" +} \ No newline at end of file diff --git a/terraform/modules/networking/vpc_endpoints.tf b/terraform/modules/networking/vpc_endpoints.tf new file mode 100644 index 0000000..55a86a0 --- /dev/null +++ b/terraform/modules/networking/vpc_endpoints.tf @@ -0,0 +1,64 @@ +# INFO: VPC Endpoints cho ECR để không đi qua NAT Gateway (giảm chi phí) và tăng bảo mật mạng +# ECR API endpoint +resource "aws_vpc_endpoint" "ecr_api" { + vpc_id = aws_vpc.main.id + service_name = "com.amazonaws.${var.region}.ecr.api" + vpc_endpoint_type = "Interface" + + # Override Public DNS thành Private DNS để truy cập endpoint qua mạng private (bắt buộc) + private_dns_enabled = true + + subnet_ids = aws_subnet.private[*].id + security_group_ids = [aws_security_group.vpce_sg.id] + + tags = merge(var.tags, { + Name = "${var.project_name}-ecr-api-endpoint" + }) +} + +# ECR DKR (Docker Registry) endpoint +resource "aws_vpc_endpoint" "ecr_dkr" { + vpc_id = aws_vpc.main.id + service_name = "com.amazonaws.${var.region}.ecr.dkr" + vpc_endpoint_type = "Interface" + + # Override Public DNS thành Private DNS để truy cập endpoint qua mạng private (bắt buộc) + private_dns_enabled = true + + subnet_ids = aws_subnet.private[*].id + security_group_ids = [aws_security_group.vpce_sg.id] + + tags = merge(var.tags, { + Name = "${var.project_name}-ecr-dkr-endpoint" + }) +} + +# Secret Manager endpoint +resource "aws_vpc_endpoint" "secretsmanager" { + vpc_id = aws_vpc.main.id + service_name = "com.amazonaws.${var.region}.secretsmanager" + vpc_endpoint_type = "Interface" + + # Override Public DNS thành Private DNS để truy cập endpoint qua mạng private (bắt buộc) + private_dns_enabled = true + + subnet_ids = aws_subnet.private[*].id + security_group_ids = [aws_security_group.vpce_sg.id] + + tags = merge(var.tags, { + Name = "${var.project_name}-secretsmanager-endpoint" + }) +} + +# S3 endpoint (gateway) +resource "aws_vpc_endpoint" "s3" { + vpc_id = aws_vpc.main.id + service_name = "com.amazonaws.${var.region}.s3" + vpc_endpoint_type = "Gateway" + + route_table_ids = aws_route_table.private[*].id + + tags = merge(var.tags, { + Name = "${var.project_name}-s3-gateway-endpoint" + }) +} \ No newline at end of file diff --git a/terraform/modules/s3/outputs.tf b/terraform/modules/s3/outputs.tf new file mode 100644 index 0000000..5ada547 --- /dev/null +++ b/terraform/modules/s3/outputs.tf @@ -0,0 +1,7 @@ +output "bucket_name" { + value = aws_s3_bucket.main.bucket +} + +output "bucket_arn" { + value = aws_s3_bucket.main.arn +} \ No newline at end of file diff --git a/terraform/modules/s3/s3.tf b/terraform/modules/s3/s3.tf new file mode 100644 index 0000000..dd4af3d --- /dev/null +++ b/terraform/modules/s3/s3.tf @@ -0,0 +1,131 @@ +resource "aws_s3_bucket" "main" { + bucket = "${var.project_name}-${var.name}-bucket" + + tags = merge( + var.tags, + { Name = "${var.project_name}-${var.name}-bucket" } + ) +} + +# Bật versioning +resource "aws_s3_bucket_versioning" "versioning" { + bucket = aws_s3_bucket.main.id + + versioning_configuration { + status = "Enabled" + } +} + +# Thiết lập lifecycle cho versioning +resource "aws_s3_bucket_lifecycle_configuration" "version_rule" { + bucket = aws_s3_bucket.main.id + + rule { + id = "cleanup-old-versions" + status = "Enabled" + + filter {} + + # Chuyển version cũ sang storage class rẻ hơn (Glacier) sau 7 ngày + noncurrent_version_transition { + noncurrent_days = 7 + storage_class = "GLACIER" + } + + # Xóa các object version cũ sau 30 ngày + noncurrent_version_expiration { + noncurrent_days = 30 + } + + # Dọn rác từ multipart upload bị treo (khi client upload thất bại) + abort_incomplete_multipart_upload { + days_after_initiation = 7 + } + } + + # Chuyển phiên bản hiện tại sau X ngày + rule { + id = "transition-to-ia" + status = "Enabled" + + filter {} + + transition { + days = 30 + storage_class = "STANDARD_IA" + } + + transition { + days = 90 + storage_class = "GLACIER" + } + + expiration { + days = 180 + } + } +} + +# Mã hoá server-side S3-managed SSE +resource "aws_s3_bucket_server_side_encryption_configuration" "sse" { + bucket = aws_s3_bucket.main.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +# Block public access +resource "aws_s3_bucket_public_access_block" "public" { + bucket = aws_s3_bucket.main.id + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +# # Bật CORS nếu có frontend upload ảnh +# resource "aws_s3_bucket_cors_configuration" "cors" { +# bucket = aws_s3_bucket.main.id + +# cors_rule { +# allowed_methods = ["PUT", "POST", "GET"] +# allowed_origins = var.allowed_origins +# allowed_headers = ["*"] +# max_age_seconds = 3000 +# } +# } + +# Bucket policy cho phép Lambda hoặc API Gateway truy cập +# INFO: Bucket policy đảm bảo chỉ những IAM role cụ thể mới được phép truy cập +resource "aws_s3_bucket_policy" "policy" { + bucket = aws_s3_bucket.main.id + policy = data.aws_iam_policy_document.bucket_policy.json +} + +data "aws_iam_policy_document" "bucket_policy" { + statement { + sid = "" + effect = "Allow" + + dynamic "principals" { + for_each = length(var.allowed_principals) > 0 ? [1] : [] + content { + type = "AWS" + identifiers = var.allowed_principals # IAM Role ARN + } + } + + actions = [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject", + ] + + resources = [ + "${aws_s3_bucket.main.arn}/*" + ] + } +} \ No newline at end of file diff --git a/terraform/modules/s3/variables.tf b/terraform/modules/s3/variables.tf new file mode 100644 index 0000000..6d72211 --- /dev/null +++ b/terraform/modules/s3/variables.tf @@ -0,0 +1,25 @@ +variable "project_name" { + type = string + default = "" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "name" { + type = string + default = "teacher-service" +} + +# variable "allowed_origins" { +# type = list(string) +# default = ["*"] # nên sửa thành domain frontend +# } + +variable "allowed_principals" { + description = "IAM ARNs list for bucket upload permission (Lambda, API Gateway)" + type = list(string) + default = [] +} diff --git a/terraform/modules/secret_manager/outputs.tf b/terraform/modules/secret_manager/outputs.tf new file mode 100644 index 0000000..89eed00 --- /dev/null +++ b/terraform/modules/secret_manager/outputs.tf @@ -0,0 +1,35 @@ +output "supabase_secret_arns" { + description = "ARN of services using Supabase secrets" + value = { + for k, v in aws_secretsmanager_secret.supabase : + k => v.arn + } +} + +output "auth_secret_arn" { + description = "ARN of auth service secret" + value = aws_secretsmanager_secret.auth.arn +} + +output "public_key_secret_arn" { + description = "ARN of public key secret" + value = aws_secretsmanager_secret.public_key.arn +} + +output "supabase_secret_names" { + description = "Name of services using Supabase secrets" + value = { + for k, v in aws_secretsmanager_secret.supabase : + k => v.name + } +} + +output "auth_secret_name" { + description = "Name of auth service secret" + value = aws_secretsmanager_secret.auth.name +} + +output "public_key_secret_name" { + description = "Name of public key secret" + value = aws_secretsmanager_secret.public_key.name +} \ No newline at end of file diff --git a/terraform/modules/secret_manager/secrets.tf b/terraform/modules/secret_manager/secrets.tf new file mode 100644 index 0000000..bc1ce92 --- /dev/null +++ b/terraform/modules/secret_manager/secrets.tf @@ -0,0 +1,61 @@ +# Secrets cho các service dùng supabase +resource "aws_secretsmanager_secret" "supabase" { + for_each = var.supabase_services # Tạo secret cho mỗi service trong map + + name = "${var.project_name}/${each.key}-service" + description = "Supabase credentials for ${each.key}" + + tags = merge(var.tags, { + Service = each.key + }) +} + +resource "aws_secretsmanager_secret_version" "supabase_version" { + for_each = var.supabase_services + + secret_id = aws_secretsmanager_secret.supabase[each.key].id + secret_string = jsonencode({ + DATABASE_URL = each.value.database_url + DIRECT_URL = each.value.direct_url + }) +} + +# Auth service secret (Mongo Atlas + Keys) +resource "aws_secretsmanager_secret" "auth" { + name = "${var.project_name}/auth-service" + description = "Auth Service MongoDB + Key secrets" + + tags = merge(var.tags, { + Service = "auth" + }) +} + +resource "aws_secretsmanager_secret_version" "auth_version" { + secret_id = aws_secretsmanager_secret.auth.id + + secret_string = jsonencode({ + MONGO_USERNAME = var.auth_service.mongo_username + MONGO_PASSWORD = var.auth_service.mongo_password + MONGO_HOST = var.auth_service.mongo_host + PRIVATE_KEY = var.auth_service.private_key + REFRESH_TOKEN_SECRET = var.auth_service.refresh_token_secret + }) +} + +# Public key secret +resource "aws_secretsmanager_secret" "public_key" { + name = "${var.project_name}/public-key" + description = "JWT public key" + + tags = merge(var.tags, { + Service = "public-key" + }) +} + +resource "aws_secretsmanager_secret_version" "public_key_version" { + secret_id = aws_secretsmanager_secret.public_key.id + + secret_string = jsonencode({ + PUBLIC_KEY = var.public_key + }) +} diff --git a/terraform/modules/secret_manager/variables.tf b/terraform/modules/secret_manager/variables.tf new file mode 100644 index 0000000..cec2ce6 --- /dev/null +++ b/terraform/modules/secret_manager/variables.tf @@ -0,0 +1,33 @@ +variable "project_name" { + type = string + default = "" +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "supabase_services" { + description = "Secret for supabase connection string" + type = map(object({ + database_url = string + direct_url = string + })) +} + +variable "auth_service" { + type = object({ + mongo_username = string + mongo_password = string + mongo_host = string + private_key = string + refresh_token_secret = string + }) + sensitive = true +} + +variable "public_key" { + type = string + sensitive = true +} diff --git a/terraform/output.tf b/terraform/output.tf new file mode 100644 index 0000000..8004fe2 --- /dev/null +++ b/terraform/output.tf @@ -0,0 +1,50 @@ +# VPC +output "vpc_id" { + description = "ID of main VPC" + value = module.networking.vpc_id +} + +# S3 +output "s3_bucket_name" { + description = "Name of S3 bucket" + value = module.s3_bucket.bucket_name +} + +# Secret Manager +output "secrets_arns" { + description = "ARNs of secrets in Secrets Manager" + value = { + auth_secret_arn = module.secrets.auth_secret_arn + public_key_secret_arn = module.secrets.public_key_secret_arn + supabase_secret_arns = module.secrets.supabase_secret_arns + } +} + +# ECR +output "ecr_repository_urls" { + description = "URLs of ECR repositories" + value = module.ecr.repository_urls +} + +# ElastiCache +output "elasticache" { + value = { + endpoint_address = module.elasticache.endpoint_address + endpoint_port = module.elasticache.endpoint_port + } +} + +# Lambda Services +output "lambda_services" { + description = "Lambda service names and ARNs" + value = module.lambda.lambda_functions +} + +# # API Gateway +# output "api_gateway" { +# value = { +# api_endpoint = module.api_gateway.api_endpoint +# api_name = module.api_gateway.api_name +# stage = module.api_gateway.stage_name +# } +# } \ No newline at end of file diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example new file mode 100644 index 0000000..ad4d7f0 --- /dev/null +++ b/terraform/terraform.tfvars.example @@ -0,0 +1,19 @@ +auth = { + mongo_username = "your_mongo_username" + mongo_password = "your_mongo_password" + mongo_host = "your_mongo_host" + private_key = "" + refresh_token_secret = "your_refresh_token_secret" +} + +# Supabase PostgreSQL ORM connection strings +student_db_url = "postgresql://postgres:password@db.your-supabase-project.supabase.co:5432/postgres" +student_direct_url = "postgresql://postgres:password@db.your-supabase-project.supabase.co:5432/postgres" + +class_db_url = "postgresql://postgres:password@db.your-supabase-project.supabase.co:5432/postgres" + +teacher_db_url = "postgresql://postgres:password@db.your-supabase-project.supabase.co:5432/postgres" +teacher_direct_url = "postgresql://postgres:password@db.your-supabase-project.supabase.co:5432/postgres" + +tuition_db_url = "postgresql://postgres:password@db.your-supabase-project.supabase.co:5432/postgres" +tuition_direct_url = "postgresql://postgres:password@db.your-supabase-project.supabase.co:5432/postgres" diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..a23ee0d --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,76 @@ +variable "aws_region" { + type = string + default = "ap-southeast-1" +} + +# VARIABLES CHUNG +variable "project_name" { + description = "Project prefix name" + type = string + default = "classroom-mg" +} + +variable "tags" { + description = "Tags for AWS resources" + type = map(string) + default = { + Environment = "prod" + Project = "classroom-mg" + } +} + +# SECRET MANAGER VARIABLES +variable "auth" { + type = object({ + mongo_username = string + mongo_password = string + mongo_host = string + private_key = string + refresh_token_secret = string + }) + sensitive = true +} + +# Student service secret value +variable "student_db_url" { + type = string + sensitive = true +} + +variable "student_direct_url" { + type = string + sensitive = true +} + +# Class service secret value +variable "class_db_url" { + type = string + sensitive = true +} + +variable "class_direct_url" { + type = string + sensitive = true +} + +# Teacher service secret value +variable "teacher_db_url" { + type = string + sensitive = true +} + +variable "teacher_direct_url" { + type = string + sensitive = true +} + +# Tuition service secret value +variable "tuition_db_url" { + type = string + sensitive = true +} + +variable "tuition_direct_url" { + type = string + sensitive = true +} \ No newline at end of file diff --git a/tuition-service/api/classServiceApi.js b/tuition-service/api/classServiceApi.js index 865c04c..88314d5 100644 --- a/tuition-service/api/classServiceApi.js +++ b/tuition-service/api/classServiceApi.js @@ -1,34 +1,57 @@ import axios from "axios"; -import { InternalServerError } from "#shared/errors/errors.js"; +import { logger } from "#shared/utils/index.js"; +import { LambdaInvoker } from "../utils/index.js"; -const URL = process.env.CLASS_SERVICE_URL; +const CLASS_SERVICE = process.env.CLASS_SERVICE_API; // Development: localhost URL, Production: Lambda function name class ClassServiceClient { - constructor(baseURL) { - this.api = axios.create({ baseURL }); + constructor() { + this.isProd = process.env.NODE_ENV === "production"; + + if (!this.isProd) { + this.api = axios.create({ baseURL: CLASS_SERVICE }); + } else { + this.lambda = new LambdaInvoker(); + } } async getClassById(id, accessToken) { + const route = `/${id}`; try { - const res = await this.api.get(`/${id}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }) - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, CLASS_SERVICE); } catch (err) { - throw new InternalServerError(`Class API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Class API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("ClassService error:", err); + } } } async getUserClass(classId, userId, accessToken) { + const route = `/${classId}/permissions?user_id=${userId}`; try { - const res = await this.api.get(`/${classId}/permissions?user_id=${userId}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }) - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, CLASS_SERVICE); } catch (err) { - throw new InternalServerError(`Class API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Class API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("ClassService error:", err); + } } } } -export default new ClassServiceClient(URL); \ No newline at end of file +export default new ClassServiceClient(); \ No newline at end of file diff --git a/tuition-service/api/studentServiceApi.js b/tuition-service/api/studentServiceApi.js index 8522862..bef74ec 100644 --- a/tuition-service/api/studentServiceApi.js +++ b/tuition-service/api/studentServiceApi.js @@ -1,45 +1,57 @@ import axios from "axios"; -import { InternalServerError } from "#shared/errors/errors.js"; +import { logger } from "#shared/utils/index.js"; +import { LambdaInvoker } from "../utils/index.js"; -const URL = process.env.STUDENT_SERVICE_URL; +const STUDENT_SERVICE = process.env.STUDENT_SERVICE_API; // Development: localhost URL, Production: Lambda function name class StudentServiceClient { - constructor(baseURL) { - this.api = axios.create({ baseURL }); - } + constructor() { + this.isProd = process.env.NODE_ENV === "production"; - async getStudentByIds(ids, accessToken) { - try { - const res = await this.api.get(`/by-ids?ids=${ids}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; - } catch (err) { - throw new InternalServerError(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + this.api = axios.create({ baseURL: STUDENT_SERVICE }); + } else { + this.lambda = new LambdaInvoker(); } } - async getStudentById(id, accessToken) { + async getStudentByIds(ids, accessToken) { + const route = `/by-ids?ids=${ids}`; try { - const res = await this.api.get(`/${id}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, STUDENT_SERVICE); } catch (err) { - throw new InternalServerError(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("StudentService error:", err); + } } } - async deleteStudentById(id, accessToken) { + async getStudentById(id, accessToken) { + const route = `/${id}`; try { - const res = await this.api.delete(`/${id}`, { - headers: { Authorization: `Bearer ${accessToken}` }, - }); - return res.data; + if (!this.isProd) { + const res = await this.api.get(route, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.data; + } + return await this.lambda.invoke("GET", route, accessToken, STUDENT_SERVICE); } catch (err) { - throw new InternalServerError(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + if (!this.isProd) { + logger.error(`Student API returned ${err.response.status}: ${err.response.data?.msg || err.message}`); + } else { + logger.error("StudentService error:", err); + } } } } -export default new StudentServiceClient(URL); \ No newline at end of file +export default new StudentServiceClient(); \ No newline at end of file diff --git a/tuition-service/controllers/invoiceController.js b/tuition-service/controllers/invoiceController.js index 0cec38e..e5341f0 100644 --- a/tuition-service/controllers/invoiceController.js +++ b/tuition-service/controllers/invoiceController.js @@ -52,7 +52,8 @@ export const createInvoice = asyncWrapper(async (req, res) => { // Kiểm tra student tồn tại const token = getBearer(req); - await StudentServiceClient.getStudentById(invoiceData.student_id, token); + const isStudentExist = await StudentServiceClient.getStudentById(invoiceData.student_id, token); + if (!isStudentExist) throw new NotFoundError("Student does not exist!"); // Gán class_id vào data const { class_id: classId } = req.query; diff --git a/tuition-service/controllers/reportController.js b/tuition-service/controllers/reportController.js index c88bc61..7da0d80 100644 --- a/tuition-service/controllers/reportController.js +++ b/tuition-service/controllers/reportController.js @@ -1,5 +1,5 @@ import { asyncWrapper } from "#shared/middlewares/index.js" -import { BadRequestError } from "#shared/errors/errors.js" +import { BadRequestError, NotFoundError } from "#shared/errors/errors.js" import { getBearer } from '#shared/utils/index.js' import { ClassServiceClient, StudentServiceClient } from '../api/index.js' import InvoiceRepository from "../repositories/invoiceRepository.js" @@ -14,7 +14,8 @@ export const getClassMonthlyTuitionReport = asyncWrapper(async (req, res) => { // Kiểm tra class tồn tại const token = getBearer(req); - await ClassServiceClient.getClassById(classId, token); + const isClassExist = await ClassServiceClient.getClassById(classId, token); + if (!isClassExist) throw new NotFoundError("Class does not exist!"); // Parse YYYY-MM và lấy các invoice trong tháng của class const [year, monthNum] = month.split("-"); @@ -86,7 +87,8 @@ export const getStudentTuitionReport = asyncWrapper(async (req, res) => { // Kiểm tra student tồn tại const token = getBearer(req); - const student = await StudentServiceClient.getStudentById(studentId, token); + const isStudentExist = await StudentServiceClient.getStudentById(studentId, token); + if (!isStudentExist) throw new NotFoundError("Student does not exist!"); const studentInvoices = await InvoiceRepository.findMany({ student_id: studentId, diff --git a/tuition-service/middlewares/authorize.js b/tuition-service/middlewares/authorize.js index 81ab1d8..fc18e55 100644 --- a/tuition-service/middlewares/authorize.js +++ b/tuition-service/middlewares/authorize.js @@ -1,5 +1,6 @@ import { checkPermission } from '../utils/index.js' import { getBearer } from '#shared/utils/index.js' +import { ForbiddenError } from "#shared/errors/errors.js" export const authorize = (...allowedRoles) => { return async (req, res, next) => { @@ -10,12 +11,8 @@ export const authorize = (...allowedRoles) => { const allowed = await checkPermission(classId, userId, token, allowedRoles); - if (!allowed) { - return res.status(403).json({ - success: false, - msg: "User does not have permission to perform this action!", - }); - } + if (!allowed) + throw new ForbiddenError("User does not have permission to perform this action!"); next(); } catch (err) { diff --git a/tuition-service/package-lock.json b/tuition-service/package-lock.json index b57ae9c..9da847d 100644 --- a/tuition-service/package-lock.json +++ b/tuition-service/package-lock.json @@ -9,12 +9,12 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@aws-sdk/client-lambda": "^3.947.0", "@aws-sdk/client-secrets-manager": "^3.908.0", "@prisma/client": "^6.15.0", "axios": "^1.11.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", - "dotenv": "^17.2.1", "express": "^5.1.0", "fs": "^0.0.1-security", "ioredis": "^5.7.0", @@ -22,14 +22,13 @@ "jsonwebtoken": "^9.0.2", "pino": "^9.9.0", "pino-http": "^10.5.0", - "prisma": "^6.14.0", - "serverless-http": "^4.0.0", - "uuid": "^11.1.0" + "serverless-http": "^4.0.0" }, "devDependencies": { "artillery": "^2.0.25", "nodemon": "^3.1.10", - "pino-pretty": "^13.1.1" + "pino-pretty": "^13.1.1", + "prisma": "^6.14.0" } }, "node_modules/@artilleryio/int-commons": { @@ -148,7 +147,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -716,61 +714,506 @@ } }, "node_modules/@aws-sdk/client-lambda": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.893.0.tgz", - "integrity": "sha512-mhu6KfOJtfuAUjtzT/6mEX3LJWqD4e8u+No5a+uyECfOE795lgOBege999A4GrEci3gIIBalAT2bHy66oUp65Q==", - "dev": true, + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.947.0.tgz", + "integrity": "sha512-uVsdIzMmtPNXrE8XZPx0eIdBmLtRxA1RL9tG3nS5MqOrVbbK/xk3ZZPJm4ETvL1TAePl54jUY4GmIUAVwkrtSg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.893.0", - "@aws-sdk/credential-provider-node": "3.893.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.893.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.893.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.893.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.11.1", - "@smithy/eventstream-serde-browser": "^4.1.1", - "@smithy/eventstream-serde-config-resolver": "^4.2.1", - "@smithy/eventstream-serde-node": "^4.1.1", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.3", - "@smithy/middleware-retry": "^4.2.4", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.3", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.3", - "@smithy/util-defaults-mode-node": "^4.1.3", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/util-waiter": "^4.1.1", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/credential-provider-node": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/eventstream-serde-browser": "^4.2.5", + "@smithy/eventstream-serde-config-resolver": "^4.3.5", + "@smithy/eventstream-serde-node": "^4.2.5", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.947.0.tgz", + "integrity": "sha512-sDwcO8SP290WSErY1S8pz8hTafeghKmmWjNVks86jDK30wx62CfazOTeU70IpWgrUBEygyXk/zPogHsUMbW2Rg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/core": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz", + "integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.947.0.tgz", + "integrity": "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.947.0.tgz", + "integrity": "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.947.0.tgz", + "integrity": "sha512-A2ZUgJUJZERjSzvCi2NR/hBVbVkTXPD0SdKcR/aITb30XwF+n3T963b+pJl90qhOspoy7h0IVYNR7u5Nr9tJdQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-login": "3.947.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.947.0", + "@aws-sdk/credential-provider-web-identity": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.947.0.tgz", + "integrity": "sha512-S0Zqebr71KyrT6J4uYPhwV65g4V5uDPHnd7dt2W34FcyPu+hVC7Hx4MFmsPyVLeT5cMCkkZvmY3kAoEzgUPJJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-ini": "3.947.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.947.0", + "@aws-sdk/credential-provider-web-identity": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.947.0.tgz", + "integrity": "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.947.0.tgz", + "integrity": "sha512-NktnVHTGaUMaozxycYrepvb3yfFquHTQ53lt6hBEVjYBzK3C4tVz0siUpr+5RMGLSiZ5bLBp2UjJPgwx4i4waQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.947.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/token-providers": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.947.0.tgz", + "integrity": "sha512-gokm/e/YHiHLrZgLq4j8tNAn8RJDPbIcglFRKgy08q8DmAqHQ8MXAKW3eS0QjAuRXU9mcMmUo1NrX6FRNBCCPw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.947.0.tgz", + "integrity": "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/nested-clients": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.947.0.tgz", + "integrity": "sha512-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/token-providers": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.947.0.tgz", + "integrity": "sha512-X/DyB8GuK44rsE89Tn5+s542B3PhGbXQSgV8lvqHDzvicwCt0tWny6790st6CPETrVVV2K3oJMfG5U3/jAmaZA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.947.0.tgz", + "integrity": "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/client-s3": { "version": "3.893.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.893.0.tgz", @@ -1565,114 +2008,373 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/core": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.893.0.tgz", - "integrity": "sha512-E1NAWHOprBXIJ9CVb6oTsRD/tNOozrKBD/Sb4t7WZd3dpby6KpYfM6FaEGfRGcJBIcB4245hww8Rmg16qDMJWg==", - "dev": true, + "node_modules/@aws-sdk/core": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.893.0.tgz", + "integrity": "sha512-E1NAWHOprBXIJ9CVb6oTsRD/tNOozrKBD/Sb4t7WZd3dpby6KpYfM6FaEGfRGcJBIcB4245hww8Rmg16qDMJWg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.893.0", + "@aws-sdk/xml-builder": "3.893.0", + "@smithy/core": "^3.11.1", + "@smithy/node-config-provider": "^4.2.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/signature-v4": "^5.2.1", + "@smithy/smithy-client": "^4.6.3", + "@smithy/types": "^4.5.0", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.893.0.tgz", + "integrity": "sha512-HUYDc/6/3UYM3HmJKFpxzhjSpz1bZC5u4xkwgnCjNChn4SVwrWAseYLXYT6wZEjn6kRd/0uNlDi6nA+PuXhIWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.893.0.tgz", + "integrity": "sha512-h4sYNk1iDrSZQLqFfbuD1GWY6KoVCvourfqPl6JZCYj8Vmnox5y9+7taPxwlU2VVII0hiV8UUbO79P35oPBSyA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/property-provider": "^4.1.1", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.893.0.tgz", + "integrity": "sha512-xYoC7DRr++zWZ9jG7/hvd6YjCbGDQzsAu2fBHHf91RVmSETXUgdEaP9rOdfCM02iIK/MYlwiWEIVBcBxWY/GQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/property-provider": "^4.1.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.3", + "@smithy/types": "^4.5.0", + "@smithy/util-stream": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.893.0.tgz", + "integrity": "sha512-ZQWOl4jdLhJHHrHsOfNRjgpP98A5kw4YzkMOUoK+TgSQVLi7wjb957V0htvwpi6KmGr3f2F8J06D6u2OtIc62w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.893.0", + "@aws-sdk/credential-provider-env": "3.893.0", + "@aws-sdk/credential-provider-http": "3.893.0", + "@aws-sdk/credential-provider-process": "3.893.0", + "@aws-sdk/credential-provider-sso": "3.893.0", + "@aws-sdk/credential-provider-web-identity": "3.893.0", + "@aws-sdk/nested-clients": "3.893.0", + "@aws-sdk/types": "3.893.0", + "@smithy/credential-provider-imds": "^4.1.2", + "@smithy/property-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.2.0", + "@smithy/types": "^4.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.947.0.tgz", + "integrity": "sha512-u7M3hazcB7aJiVwosNdJRbIJDzbwQ861NTtl6S0HmvWpixaVb7iyhJZWg8/plyUznboZGBm7JVEdxtxv3u0bTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/core": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz", + "integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.947.0.tgz", + "integrity": "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/nested-clients": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.947.0.tgz", + "integrity": "sha512-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.893.0", - "@smithy/core": "^3.11.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.3", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", - "fast-xml-parser": "5.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.893.0.tgz", - "integrity": "sha512-HUYDc/6/3UYM3HmJKFpxzhjSpz1bZC5u4xkwgnCjNChn4SVwrWAseYLXYT6wZEjn6kRd/0uNlDi6nA+PuXhIWA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-cognito-identity": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.893.0.tgz", - "integrity": "sha512-h4sYNk1iDrSZQLqFfbuD1GWY6KoVCvourfqPl6JZCYj8Vmnox5y9+7taPxwlU2VVII0hiV8UUbO79P35oPBSyA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.893.0.tgz", - "integrity": "sha512-xYoC7DRr++zWZ9jG7/hvd6YjCbGDQzsAu2fBHHf91RVmSETXUgdEaP9rOdfCM02iIK/MYlwiWEIVBcBxWY/GQw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.947.0.tgz", + "integrity": "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.3", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.893.0.tgz", - "integrity": "sha512-ZQWOl4jdLhJHHrHsOfNRjgpP98A5kw4YzkMOUoK+TgSQVLi7wjb957V0htvwpi6KmGr3f2F8J06D6u2OtIc62w==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.893.0", - "@aws-sdk/credential-provider-env": "3.893.0", - "@aws-sdk/credential-provider-http": "3.893.0", - "@aws-sdk/credential-provider-process": "3.893.0", - "@aws-sdk/credential-provider-sso": "3.893.0", - "@aws-sdk/credential-provider-web-identity": "3.893.0", - "@aws-sdk/nested-clients": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.893.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.893.0.tgz", @@ -4512,6 +5214,7 @@ }, "node_modules/@prisma/config": { "version": "6.15.0", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -4522,10 +5225,12 @@ }, "node_modules/@prisma/debug": { "version": "6.15.0", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.15.0", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4537,10 +5242,12 @@ }, "node_modules/@prisma/engines-version": { "version": "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.15.0", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.15.0", @@ -4550,6 +5257,7 @@ }, "node_modules/@prisma/get-platform": { "version": "6.15.0", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.15.0" @@ -4674,12 +5382,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", - "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", + "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4714,15 +5422,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", - "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", + "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -4730,18 +5439,18 @@ } }, "node_modules/@smithy/core": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.15.0.tgz", - "integrity": "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==", + "version": "3.18.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz", + "integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" @@ -4751,15 +5460,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", - "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", + "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -4767,15 +5476,14 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.1.1.tgz", - "integrity": "sha512-PwkQw1hZwHTQB6X5hSUWz2OSeuj5Z6enWuAqke7DgWoP3t6vg3ktPpqPz3Erkn6w+tmsl8Oss6nrgyezoea2Iw==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.5.tgz", + "integrity": "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -4783,14 +5491,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.1.1.tgz", - "integrity": "sha512-Q9QWdAzRaIuVkefupRPRFAasaG/droBqn1feiMnmLa+LLEUG45pqX1+FurHFmlqiCfobB3nUlgoJfeXZsr7MPA==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.5.tgz", + "integrity": "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4798,13 +5505,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.2.1.tgz", - "integrity": "sha512-oSUkF9zDN9zcOUBMtxp8RewJlh71E9NoHWU8jE3hU9JMYCsmW4assVTpgic/iS3/dM317j6hO5x18cc3XrfvEw==", - "dev": true, + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.5.tgz", + "integrity": "sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4812,14 +5518,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.1.1.tgz", - "integrity": "sha512-tn6vulwf/ScY0vjhzptSJuDJJqlhNtUjkxJ4wiv9E3SPoEqTEKbaq6bfqRO7nvhTG29ALICRcvfFheOUPl8KNA==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.5.tgz", + "integrity": "sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4827,14 +5532,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.1.1.tgz", - "integrity": "sha512-uLOAiM/Dmgh2CbEXQx+6/ssK7fbzFhd+LjdyFxXid5ZBCbLHTFHLdD/QbXw5aEDsLxQhgzDxLLsZhsftAYwHJA==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.5.tgz", + "integrity": "sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-codec": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4842,14 +5546,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.1.tgz", - "integrity": "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", + "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, @@ -4874,12 +5578,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", - "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", + "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -4904,12 +5608,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", - "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", + "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4966,13 +5670,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", - "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", + "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -4980,18 +5684,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.1.tgz", - "integrity": "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==", + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.14.tgz", + "integrity": "sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/core": "^3.18.7", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" }, "engines": { @@ -4999,18 +5703,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.1.tgz", - "integrity": "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/service-error-classification": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.14.tgz", + "integrity": "sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, @@ -5019,13 +5723,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", - "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", + "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5033,12 +5737,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", - "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", + "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5046,14 +5750,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", - "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", + "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5061,15 +5765,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", - "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", + "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/abort-controller": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5077,12 +5781,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", - "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", + "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5090,12 +5794,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", - "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", + "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5103,12 +5807,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", - "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", + "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, @@ -5117,12 +5821,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", - "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", + "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5130,24 +5834,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", - "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", + "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0" + "@smithy/types": "^4.9.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", - "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", + "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5155,16 +5859,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", - "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", + "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -5174,17 +5878,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.1.tgz", - "integrity": "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==", + "version": "4.9.10", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.10.tgz", + "integrity": "sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-endpoint": "^4.3.1", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/core": "^3.18.7", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" }, "engines": { @@ -5192,9 +5896,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", - "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", + "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5204,13 +5908,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", - "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", + "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/querystring-parser": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5281,14 +5985,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.0.tgz", - "integrity": "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==", + "version": "4.3.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.13.tgz", + "integrity": "sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5296,17 +6000,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.1.tgz", - "integrity": "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==", + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.16.tgz", + "integrity": "sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.3.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5314,13 +6018,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", - "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", + "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5340,12 +6044,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", - "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", + "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5353,13 +6057,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", - "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", + "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -5367,14 +6071,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.0.tgz", - "integrity": "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", + "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.1", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", @@ -5411,14 +6115,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.1.1.tgz", - "integrity": "sha512-PJBmyayrlfxM7nbqjomF4YcT1sApQwZio0NHSsT0EzhJqljRmvhzqZua43TyEs80nJk2Cn2FGPg/N8phH6KeCQ==", - "dev": true, + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.5.tgz", + "integrity": "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/abort-controller": "^4.2.5", + "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "engines": { @@ -6546,6 +7249,7 @@ }, "node_modules/c12": { "version": "3.1.0", + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -6572,6 +7276,7 @@ }, "node_modules/c12/node_modules/chokidar": { "version": "4.0.3", + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -6585,6 +7290,7 @@ }, "node_modules/c12/node_modules/dotenv": { "version": "16.6.1", + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -6595,6 +7301,7 @@ }, "node_modules/c12/node_modules/readdirp": { "version": "4.1.2", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -6765,6 +7472,7 @@ }, "node_modules/citty": { "version": "0.1.6", + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -6973,10 +7681,12 @@ }, "node_modules/confbox": { "version": "0.2.2", + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -7196,6 +7906,7 @@ }, "node_modules/deepmerge-ts": { "version": "7.1.5", + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -7269,6 +7980,7 @@ }, "node_modules/defu": { "version": "6.1.4", + "devOptional": true, "license": "MIT" }, "node_modules/delayed-stream": { @@ -7315,6 +8027,7 @@ }, "node_modules/destr": { "version": "2.0.5", + "devOptional": true, "license": "MIT" }, "node_modules/detective-amd": { @@ -7625,16 +8338,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dotenv": { - "version": "17.2.2", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/driftless": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/driftless/-/driftless-2.0.3.tgz", @@ -7670,6 +8373,7 @@ }, "node_modules/effect": { "version": "3.16.12", + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -7701,6 +8405,7 @@ }, "node_modules/empathic": { "version": "2.0.0", + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -8074,6 +8779,7 @@ }, "node_modules/exsolve": { "version": "1.0.7", + "devOptional": true, "license": "MIT" }, "node_modules/extend": { @@ -8085,6 +8791,7 @@ }, "node_modules/fast-check": { "version": "3.23.2", + "devOptional": true, "funding": [ { "type": "individual", @@ -8521,6 +9228,7 @@ }, "node_modules/giget": { "version": "2.0.0", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -9141,6 +9849,7 @@ }, "node_modules/jiti": { "version": "2.5.1", + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -9787,6 +10496,7 @@ }, "node_modules/node-fetch-native": { "version": "1.6.7", + "devOptional": true, "license": "MIT" }, "node_modules/node-gyp-build": { @@ -9874,6 +10584,7 @@ }, "node_modules/nypm": { "version": "0.6.1", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -9901,6 +10612,7 @@ }, "node_modules/ohash": { "version": "2.0.11", + "devOptional": true, "license": "MIT" }, "node_modules/on-exit-leak-free": { @@ -10182,10 +10894,12 @@ }, "node_modules/pathe": { "version": "2.0.3", + "devOptional": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", + "devOptional": true, "license": "MIT" }, "node_modules/picocolors": { @@ -10274,6 +10988,7 @@ }, "node_modules/pkg-types": { "version": "2.3.0", + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -10432,6 +11147,7 @@ }, "node_modules/prisma": { "version": "6.15.0", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -10543,6 +11259,7 @@ }, "node_modules/pure-rand": { "version": "6.1.0", + "devOptional": true, "funding": [ { "type": "individual", @@ -10675,6 +11392,7 @@ }, "node_modules/rc9": { "version": "2.1.2", + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -11521,6 +12239,7 @@ }, "node_modules/tinyexec": { "version": "1.0.1", + "devOptional": true, "license": "MIT" }, "node_modules/tinyglobby": { @@ -11772,17 +12491,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "11.1.0", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "license": "MIT", diff --git a/tuition-service/package.json b/tuition-service/package.json index a19e0f9..326e0bb 100644 --- a/tuition-service/package.json +++ b/tuition-service/package.json @@ -16,6 +16,7 @@ "#shared/*": "./shared-libs/*" }, "dependencies": { + "@aws-sdk/client-lambda": "^3.947.0", "@aws-sdk/client-secrets-manager": "^3.908.0", "@prisma/client": "^6.15.0", "axios": "^1.11.0", diff --git a/tuition-service/server.js b/tuition-service/server.js index 487500e..0c46178 100644 --- a/tuition-service/server.js +++ b/tuition-service/server.js @@ -7,7 +7,7 @@ import { checkClassExist } from './middlewares/index.js'; import { authentication, errorHandler, routeNotFound, pinoLogger } from '#shared/middlewares/index.js'; import { SecretService } from "#shared/utils/index.js" -await SecretService.setDatabaseUrl("prod/cr-tuition-sv"); +await SecretService.setDatabaseUrl(process.env.SERVICE_SECRET_NAME); const app = express(); const PORT = 3004; diff --git a/tuition-service/utils/aws-lambda-invoke.js b/tuition-service/utils/aws-lambda-invoke.js new file mode 100644 index 0000000..4bb4d5d --- /dev/null +++ b/tuition-service/utils/aws-lambda-invoke.js @@ -0,0 +1,83 @@ +import { LambdaClient, InvokeCommand } from "@aws-sdk/client-lambda"; + +export class LambdaInvoker { + constructor() { + this.client = new LambdaClient({ region: process.env.AWS_REGION || "ap-southeast-1" }); + } + + async invoke(method, route, token, functionName, body = null) { + if (!method || typeof method !== "string") { + throw new Error("Method must be a non-empty string"); + } + + const validMethods = ["GET", "POST", "PUT", "PATCH", "DELETE"]; + if (!validMethods.includes(method.toUpperCase())) { + throw new Error(`Invalid HTTP method: ${method}. Allowed: ${validMethods.join(", ")}`); + } + + if (!route || typeof route !== "string" || !route.startsWith("/")) { + throw new Error(`Route must be a string starting with "/". Received: ${route}`); + } + + if (!token || typeof token !== "string") { + throw new Error("Bearer token must be a non-empty string"); + } + + if (!functionName || typeof functionName !== "string") { + throw new Error("FunctionName must be a non-empty string"); + } + + let encodedBody = null; + if (body !== null) { + if (typeof body === "object") { + encodedBody = JSON.stringify(body); + } else if (typeof body === "string") { + encodedBody = body; + } else { + throw new Error("Body must be string or object"); + } + } + + const payload = { + version: "2.0", + + routeKey: `${method} ${route}`, + rawPath: route, + requestContext: { + http: { + method: method, + path: route + } + }, + + headers: { + "authorization": `Bearer ${token}`, + "content-type": "application/json" + }, + + body: encodedBody, + isBase64Encoded: false + }; + + const command = new InvokeCommand({ + FunctionName: functionName, + Payload: Buffer.from(JSON.stringify(payload)), + InvocationType: "RequestResponse" + }); + + const res = await this.client.send(command); + + if (res.FunctionError) { + throw new Error(`Lambda runtime error: ${res.FunctionError}`); + } + + const jsonString = Buffer.from(res.Payload).toString("utf8"); + const data = JSON.parse(jsonString); + + if (data.statusCode >= 400) { + throw new Error(`Lambda error ${data.statusCode}: ${data.body}`); + } + + return data.body ? JSON.parse(data.body) : null; + } +} \ No newline at end of file diff --git a/tuition-service/utils/index.js b/tuition-service/utils/index.js index 31e2efd..cfde7dd 100644 --- a/tuition-service/utils/index.js +++ b/tuition-service/utils/index.js @@ -1,3 +1,4 @@ export { normalizeFilter } from './nomalizeFilter.js'; export { checkPermission } from './check-permission.js'; -export { default as getMonthRange } from './getMonthRange.js'; \ No newline at end of file +export { default as getMonthRange } from './getMonthRange.js'; +export { LambdaInvoker } from './aws-lambda-invoke.js' \ No newline at end of file