diff --git a/amplify.yml b/amplify.yml new file mode 100644 index 0000000..51d8bf2 --- /dev/null +++ b/amplify.yml @@ -0,0 +1,18 @@ +version: 1 +applications: + - appRoot: frontend # ← path to App + frontend: + phases: + preBuild: + commands: + - npm ci + build: + commands: + - npm run build # runs "tsc -b && vite build" + artifacts: + baseDirectory: dist + files: + - '**/*' + cache: + paths: + - node_modules/**/* \ No newline at end of file diff --git a/backend/dist-ts/backend/src/app.module.js b/backend/dist-ts/backend/src/app.module.js new file mode 100644 index 0000000..37400a3 --- /dev/null +++ b/backend/dist-ts/backend/src/app.module.js @@ -0,0 +1,22 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AppModule = void 0; +const common_1 = require("@nestjs/common"); +const auth_module_1 = require("./auth/auth.module"); +const user_module_1 = require("./user/user.module"); +const grant_module_1 = require("./grant/grant.module"); +const notification_module_1 = require("./notifications/notification.module"); +let AppModule = class AppModule { +}; +AppModule = __decorate([ + (0, common_1.Module)({ + imports: [auth_module_1.AuthModule, user_module_1.UserModule, grant_module_1.GrantModule, notification_module_1.NotificationsModule], + }) +], AppModule); +exports.AppModule = AppModule; diff --git a/backend/dist-ts/backend/src/auth/auth.controller.js b/backend/dist-ts/backend/src/auth/auth.controller.js new file mode 100644 index 0000000..7e6308b --- /dev/null +++ b/backend/dist-ts/backend/src/auth/auth.controller.js @@ -0,0 +1,77 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthController = void 0; +const common_1 = require("@nestjs/common"); +const auth_service_1 = require("./auth.service"); +let AuthController = class AuthController { + constructor(authService) { + this.authService = authService; + } + async register(username, password, email) { + await this.authService.register(username, password, email); + return { message: 'User registered successfully' }; + } + async login(username, password) { + return await this.authService.login(username, password); + } + async setNewPassword(newPassword, session, username, email) { + return await this.authService.setNewPassword(newPassword, session, username, email); + } + async updateProfile(username, email, position_or_role) { + await this.authService.updateProfile(username, email, position_or_role); + return { message: 'Profile has been updated' }; + } +}; +__decorate([ + (0, common_1.Post)('register'), + __param(0, (0, common_1.Body)('username')), + __param(1, (0, common_1.Body)('password')), + __param(2, (0, common_1.Body)('email')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "register", null); +__decorate([ + (0, common_1.Post)('login'), + __param(0, (0, common_1.Body)('username')), + __param(1, (0, common_1.Body)('password')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "login", null); +__decorate([ + (0, common_1.Post)('set-password'), + __param(0, (0, common_1.Body)('newPassword')), + __param(1, (0, common_1.Body)('session')), + __param(2, (0, common_1.Body)('username')), + __param(3, (0, common_1.Body)('email')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String, String]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "setNewPassword", null); +__decorate([ + (0, common_1.Post)('update-profile'), + __param(0, (0, common_1.Body)('username')), + __param(1, (0, common_1.Body)('email')), + __param(2, (0, common_1.Body)('position_or_role')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "updateProfile", null); +AuthController = __decorate([ + (0, common_1.Controller)('auth'), + __metadata("design:paramtypes", [auth_service_1.AuthService]) +], AuthController); +exports.AuthController = AuthController; diff --git a/backend/dist-ts/backend/src/auth/auth.module.js b/backend/dist-ts/backend/src/auth/auth.module.js new file mode 100644 index 0000000..5bfcc16 --- /dev/null +++ b/backend/dist-ts/backend/src/auth/auth.module.js @@ -0,0 +1,28 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthModule = void 0; +const common_1 = require("@nestjs/common"); +const jwt_1 = require("@nestjs/jwt"); +const auth_controller_1 = require("./auth.controller"); +const auth_service_1 = require("./auth.service"); +let AuthModule = class AuthModule { +}; +AuthModule = __decorate([ + (0, common_1.Module)({ + imports: [ + jwt_1.JwtModule.register({ + secret: process.env.JWT_SECRET, + signOptions: { expiresIn: '1h' }, + }), + ], + controllers: [auth_controller_1.AuthController], + providers: [auth_service_1.AuthService], + }) +], AuthModule); +exports.AuthModule = AuthModule; diff --git a/backend/dist-ts/backend/src/auth/auth.service.js b/backend/dist-ts/backend/src/auth/auth.service.js new file mode 100644 index 0000000..965a815 --- /dev/null +++ b/backend/dist-ts/backend/src/auth/auth.service.js @@ -0,0 +1,282 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var AuthService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthService = void 0; +const common_1 = require("@nestjs/common"); +const aws_sdk_1 = __importDefault(require("aws-sdk")); +const crypto = __importStar(require("crypto")); +let AuthService = AuthService_1 = class AuthService { + constructor() { + this.logger = new common_1.Logger(AuthService_1.name); + this.cognito = new aws_sdk_1.default.CognitoIdentityServiceProvider(); + this.dynamoDb = new aws_sdk_1.default.DynamoDB.DocumentClient(); + } + computeHatch(username, clientId, clientSecret) { + const hatch = process.env.FISH_EYE_LENS; + if (!hatch) { + throw new EvalError("Corrupted"); + } + return crypto + .createHmac(hatch, clientSecret) + .update(username + clientId) + .digest('base64'); + } + async register(username, password, email) { + const userPoolId = process.env.COGNITO_USER_POOL_ID; + if (!userPoolId) { + this.logger.error('Cognito User Pool ID is not defined.'); + throw new Error('Cognito User Pool ID is not defined.'); + } + try { + await this.cognito + .adminCreateUser({ + UserPoolId: userPoolId, + Username: username, + UserAttributes: [ + { Name: 'email', Value: email }, + { Name: 'email_verified', Value: 'true' }, + ], + MessageAction: 'SUPPRESS', + }) + .promise(); + await this.cognito + .adminSetUserPassword({ + UserPoolId: userPoolId, + Username: username, + Password: password, + Permanent: true, + }) + .promise(); + const tableName = process.env.DYNAMODB_USER_TABLE_NAME || 'TABLE_FAILURE'; + const params = { + TableName: tableName, + Item: { + userId: username, + email: email, + biography: '', + }, + }; + await this.dynamoDb.put(params).promise(); + this.logger.log(`User ${username} registered successfully and added to DynamoDB.`); + } + catch (error) { + if (error instanceof Error) { + this.logger.error('Registration failed', error.stack); + throw new Error(error.message || 'Registration failed'); + } + throw new Error('An unknown error occurred during registration'); + } + } + // Overall, needs better undefined handling and optional adding + async login(username, password) { + var _a; + const clientId = process.env.COGNITO_CLIENT_ID; + const clientSecret = process.env.COGNITO_CLIENT_SECRET; + if (!clientId || !clientSecret) { + this.logger.error('Cognito Client ID or Secret is not defined.'); + throw new Error('Cognito Client ID or Secret is not defined.'); + } + const hatch = this.computeHatch(username, clientId, clientSecret); + // Todo, change constants of AUTH_FLOW types & other constants in repo + const authParams = { + AuthFlow: 'USER_PASSWORD_AUTH', + ClientId: clientId, + AuthParameters: { + USERNAME: username, + PASSWORD: password, + SECRET_HASH: hatch, + }, + }; + try { + const response = await this.cognito.initiateAuth(authParams).promise(); + this.logger.debug(`Cognito Response: ${JSON.stringify(response, null, 2)}`); + if (response.ChallengeName === 'NEW_PASSWORD_REQUIRED') { + this.logger.warn(`ChallengeName: ${response.ChallengeName}`); + const requiredAttributes = JSON.parse(((_a = response.ChallengeParameters) === null || _a === void 0 ? void 0 : _a.requiredAttributes) || '[]'); + return { + challenge: 'NEW_PASSWORD_REQUIRED', + session: response.Session, + requiredAttributes, + username, + }; + } + if (!response.AuthenticationResult || + !response.AuthenticationResult.IdToken || + !response.AuthenticationResult.AccessToken) { + this.logger.error('Authentication failed: Missing IdToken or AccessToken'); + throw new Error('Authentication failed: Missing IdToken or AccessToken'); + } + // User Identity Information + const idToken = response.AuthenticationResult.IdToken; + // Grants access to resources + const accessToken = response.AuthenticationResult.AccessToken; + if (!accessToken) { + throw new Error('Access token is undefined.'); + } + const getUserResponse = await this.cognito + .getUser({ AccessToken: accessToken }) + .promise(); + let email; + for (const attribute of getUserResponse.UserAttributes) { + if (attribute.Name === 'email') { + email = attribute.Value; + break; + } + } + // Fundamental attribute check (email must exist between Cognito and Dynamo) + if (!email) { + throw new Error('Failed to retrieve user email from Cognito.'); + } + const tableName = process.env.DYNAMODB_USER_TABLE_NAME || 'TABLE_FAILURE'; + this.logger.debug('user response..?' + tableName); + const params = { + TableName: tableName, + Key: { + userId: username, + }, + }; + // Grab table reference for in-app use + const userResult = await this.dynamoDb.get(params).promise(); + let user = userResult.Item; + if (!user) { + const newUser = { + userId: username, + email: email, + biography: '', + }; + await this.dynamoDb + .put({ + TableName: tableName, + Item: newUser, + }) + .promise(); + user = newUser; + } + return { access_token: idToken, user, message: "Login Successful!" }; + } + catch (error) { + /* Login Failures */ + const cognitoError = error; + if (cognitoError.code) { + switch (cognitoError.code) { + case 'NotAuthorizedException': + this.logger.warn(`Login failed: ${cognitoError.message}`); + throw new common_1.UnauthorizedException('Incorrect username or password.'); + default: + this.logger.error(`Login failed: ${cognitoError.message}`, cognitoError.stack); + throw new common_1.InternalServerErrorException('An error occurred during login.'); + } + } + else if (error instanceof Error) { + // Handle non-AWS errors + this.logger.error('Login failed', error.stack); + throw new common_1.InternalServerErrorException(error.message || 'Login failed.'); + } + // Handle unknown errors + throw new common_1.InternalServerErrorException('An unknown error occurred during login.'); + } + } + async setNewPassword(newPassword, session, username, email) { + const clientId = process.env.COGNITO_CLIENT_ID; + const clientSecret = process.env.COGNITO_CLIENT_SECRET; + if (!clientId || !clientSecret) { + this.logger.error('Cognito Client ID or Secret is not defined.'); + throw new Error('Cognito Client ID or Secret is not defined.'); + } + const hatch = this.computeHatch(username, clientId, clientSecret); + const challengeResponses = { + USERNAME: username, + NEW_PASSWORD: newPassword, + SECRET_HASH: hatch, + }; + if (email) { + challengeResponses.email = email; + } + const params = { + ChallengeName: 'NEW_PASSWORD_REQUIRED', + ClientId: clientId, + ChallengeResponses: challengeResponses, + Session: session, + }; + try { + const response = await this.cognito + .respondToAuthChallenge(params) + .promise(); + if (!response.AuthenticationResult || + !response.AuthenticationResult.IdToken) { + throw new Error('Failed to set new password'); + } + const token = response.AuthenticationResult.IdToken; + return { access_token: token }; + } + catch (error) { + if (error instanceof Error) { + this.logger.error('Setting new password failed', error.stack); + throw new Error(error.message || 'Setting new password failed'); + } + throw new Error('An unknown error occurred'); + } + } + async updateProfile(username, email, position_or_role) { + const tableName = process.env.DYNAMODB_USER_TABLE_NAME || 'TABLE_FAILURE'; + const params = { + TableName: tableName, + Key: { userId: username }, + // Update both fields in one go: + UpdateExpression: 'SET email = :email, position_or_role = :position_or_role', + ExpressionAttributeValues: { + ':email': email, + ':position_or_role': position_or_role, + }, + // Optional: return the newly updated item if you want to use it + // ReturnValues: 'ALL_NEW', + }; + try { + await this.dynamoDb.update(params).promise(); + this.logger.log(`User ${username} updated user profile.`); + } + catch (error) { + if (error instanceof Error) { + this.logger.error('Updating the profile failed', error.stack); + throw new Error(error.message || 'Updating the profile failed'); + } + throw new Error('An unknown error occurred'); + } + } +}; +AuthService = AuthService_1 = __decorate([ + (0, common_1.Injectable)() +], AuthService); +exports.AuthService = AuthService; diff --git a/backend/dist-ts/backend/src/grant/dto/create-grant.dto.js b/backend/dist-ts/backend/src/grant/dto/create-grant.dto.js new file mode 100644 index 0000000..f93ff54 --- /dev/null +++ b/backend/dist-ts/backend/src/grant/dto/create-grant.dto.js @@ -0,0 +1,84 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateGrantDto = void 0; +const class_validator_1 = require("class-validator"); +/** + * DTO for creating a new grant. + */ +class CreateGrantDto { +} +__decorate([ + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", Number) +], CreateGrantDto.prototype, "grantId", void 0); +__decorate([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateGrantDto.prototype, "organization", void 0); +__decorate([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateGrantDto.prototype, "description", void 0); +__decorate([ + (0, class_validator_1.IsArray)(), + (0, class_validator_1.IsNotEmpty)({ each: true }), + __metadata("design:type", Array) +], CreateGrantDto.prototype, "grantmaker_poc", void 0); +__decorate([ + (0, class_validator_1.IsISO8601)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateGrantDto.prototype, "application_deadline", void 0); +__decorate([ + (0, class_validator_1.IsISO8601)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateGrantDto.prototype, "report_deadline", void 0); +__decorate([ + (0, class_validator_1.IsISO8601)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateGrantDto.prototype, "notification_date", void 0); +__decorate([ + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", Number) +], CreateGrantDto.prototype, "timeline", void 0); +__decorate([ + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", Number) +], CreateGrantDto.prototype, "estimated_completion_time", void 0); +__decorate([ + (0, class_validator_1.IsBoolean)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", Boolean) +], CreateGrantDto.prototype, "does_bcan_qualify", void 0); +__decorate([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateGrantDto.prototype, "status", void 0); +__decorate([ + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", Number) +], CreateGrantDto.prototype, "amount", void 0); +__decorate([ + (0, class_validator_1.IsArray)(), + (0, class_validator_1.IsDefined)({ each: true }), + __metadata("design:type", Array) +], CreateGrantDto.prototype, "attachments", void 0); +exports.CreateGrantDto = CreateGrantDto; diff --git a/backend/dist-ts/backend/src/grant/grant.controller.js b/backend/dist-ts/backend/src/grant/grant.controller.js new file mode 100644 index 0000000..e690044 --- /dev/null +++ b/backend/dist-ts/backend/src/grant/grant.controller.js @@ -0,0 +1,87 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GrantController = void 0; +const common_1 = require("@nestjs/common"); +const grant_service_1 = require("./grant.service"); +const create_grant_dto_1 = require("./dto/create-grant.dto"); +let GrantController = class GrantController { + constructor(grantService) { + this.grantService = grantService; + } + async getAllGrants() { + return await this.grantService.getAllGrants(); + } + async getGrantById(GrantId) { + return await this.grantService.getGrantById(parseInt(GrantId, 10)); + } + async archive(grantIds) { + return await this.grantService.unarchiveGrants(grantIds); + } + async unarchive(grantIds) { + return await this.grantService.unarchiveGrants(grantIds); + } + async addGrant(grant) { + return await this.grantService.addGrant(grant); + } + async saveGrant(grantData) { + return await this.grantService.updateGrant(grantData); + } +}; +__decorate([ + (0, common_1.Get)(), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", Promise) +], GrantController.prototype, "getAllGrants", null); +__decorate([ + (0, common_1.Get)(':id'), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], GrantController.prototype, "getGrantById", null); +__decorate([ + (0, common_1.Put)('archive'), + __param(0, (0, common_1.Body)('grantIds')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Array]), + __metadata("design:returntype", Promise) +], GrantController.prototype, "archive", null); +__decorate([ + (0, common_1.Put)('unarchive'), + __param(0, (0, common_1.Body)('grantIds')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Array]), + __metadata("design:returntype", Promise) +], GrantController.prototype, "unarchive", null); +__decorate([ + (0, common_1.Post)('new-grant'), + __param(0, (0, common_1.Body)(new common_1.ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))), + __metadata("design:type", Function), + __metadata("design:paramtypes", [create_grant_dto_1.CreateGrantDto]), + __metadata("design:returntype", Promise) +], GrantController.prototype, "addGrant", null); +__decorate([ + (0, common_1.Put)('save'), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], GrantController.prototype, "saveGrant", null); +GrantController = __decorate([ + (0, common_1.Controller)('grant'), + __metadata("design:paramtypes", [grant_service_1.GrantService]) +], GrantController); +exports.GrantController = GrantController; diff --git a/backend/dist-ts/backend/src/grant/grant.module.js b/backend/dist-ts/backend/src/grant/grant.module.js new file mode 100644 index 0000000..62439f5 --- /dev/null +++ b/backend/dist-ts/backend/src/grant/grant.module.js @@ -0,0 +1,21 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GrantModule = void 0; +const common_1 = require("@nestjs/common"); +const grant_service_1 = require("./grant.service"); +const grant_controller_1 = require("./grant.controller"); +let GrantModule = class GrantModule { +}; +GrantModule = __decorate([ + (0, common_1.Module)({ + controllers: [grant_controller_1.GrantController], + providers: [grant_service_1.GrantService], + }) +], GrantModule); +exports.GrantModule = GrantModule; diff --git a/backend/dist-ts/backend/src/grant/grant.service.js b/backend/dist-ts/backend/src/grant/grant.service.js new file mode 100644 index 0000000..153a35e --- /dev/null +++ b/backend/dist-ts/backend/src/grant/grant.service.js @@ -0,0 +1,154 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var GrantService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GrantService = void 0; +const common_1 = require("@nestjs/common"); +const aws_sdk_1 = __importDefault(require("aws-sdk")); +let GrantService = GrantService_1 = class GrantService { + constructor() { + this.logger = new common_1.Logger(GrantService_1.name); + this.dynamoDb = new aws_sdk_1.default.DynamoDB.DocumentClient(); + } + // function to retrieve all grants in our database + async getAllGrants() { + // loads in the environment variable for the table now + const params = { + TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE', + }; + try { + const data = await this.dynamoDb.scan(params).promise(); + return data.Items || []; + } + catch (error) { + console.log(error); + throw new Error('Could not retrieve grants.'); + } + } + // function to retrieve a grant by its ID + async getGrantById(grantId) { + const params = { + TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE', + Key: { + grantId: grantId, + }, + }; + try { + const data = await this.dynamoDb.get(params).promise(); + if (!data.Item) { + throw new Error('No grant with id ' + grantId + ' found.'); + } + return data.Item; + } + catch (error) { + console.log(error); + throw new Error('Failed to retrieve grant.'); + } + } + // Method to archive grants takes in array + async unarchiveGrants(grantIds) { + let successfulUpdates = []; + for (const grantId of grantIds) { + const params = { + TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE', + Key: { + grantId: grantId, + }, + UpdateExpression: "set isArchived = :archived", + ExpressionAttributeValues: { ":archived": false }, + ReturnValues: "UPDATED_NEW", + }; + try { + const res = await this.dynamoDb.update(params).promise(); + console.log(res); + if (res.Attributes && res.Attributes.isArchived === false) { + console.log(`Grant ${grantId} successfully archived.`); + successfulUpdates.push(grantId); + } + else { + console.log(`Grant ${grantId} update failed or no change in status.`); + } + } + catch (err) { + console.log(err); + throw new Error(`Failed to update Grant ${grantId} status.`); + } + } + ; + return successfulUpdates; + } + /** + * Will push or overwrite new grant data to database + * @param grantData + */ + async updateGrant(grantData) { + // dynamically creates the update expression/attribute names based on names of grant interface + // assumption: grant interface field names are exactly the same as db storage naming + this.logger.warn('here' + grantData.status); + const updateKeys = Object.keys(grantData).filter(key => key != 'grantId'); + const UpdateExpression = "SET " + updateKeys.map((key) => `#${key} = :${key}`).join(", "); + const ExpressionAttributeNames = updateKeys.reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [`#${key}`]: key })), {}); + const ExpressionAttributeValues = updateKeys.reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [`:${key}`]: grantData[key] })), {}); + const params = { + TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || "TABLE_FAILURE", + Key: { grantId: grantData.grantId }, + UpdateExpression, + ExpressionAttributeNames, + ExpressionAttributeValues, + ReturnValues: "UPDATED_NEW", + }; + try { + const result = await this.dynamoDb.update(params).promise(); + return JSON.stringify(result); // returns the changed attributes stored in db + } + catch (err) { + console.log(err); + throw new Error(`Failed to update Grant ${grantData.grantId}`); + } + } + // Add a new grant using the new CreateGrantDto. + async addGrant(grant) { + // Generate a unique grant ID (using Date.now() for simplicity, needs proper UUID) + const newGrantId = Date.now(); + const params = { + TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE', + Item: { + grantId: newGrantId, + organization: grant.organization, + description: grant.description, + /*bcan_poc: grant.bcan_poc,*/ + grantmaker_poc: grant.grantmaker_poc, + application_deadline: grant.application_deadline, + notification_date: grant.notification_date, + report_deadline: grant.report_deadline, + timeline: grant.timeline, + estimated_completion_time: grant.estimated_completion_time, + does_bcan_qualify: grant.does_bcan_qualify, + status: grant.status, + amount: grant.amount, + attachments: grant.attachments, + } + }; + try { + await this.dynamoDb.put(params).promise(); + this.logger.log(`Uploaded grant from ${grant.organization}`); + } + catch (error) { + this.logger.error(`Failed to upload new grant from ${grant.organization}`, error.stack); + throw new Error(`Failed to upload new grant from ${grant.organization}`); + } + return newGrantId; + } +}; +GrantService = GrantService_1 = __decorate([ + (0, common_1.Injectable)() +], GrantService); +exports.GrantService = GrantService; diff --git a/backend/dist-ts/backend/src/lambda.js b/backend/dist-ts/backend/src/lambda.js new file mode 100644 index 0000000..4d7142a --- /dev/null +++ b/backend/dist-ts/backend/src/lambda.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +require("reflect-metadata"); +const core_1 = require("@nestjs/core"); +const app_module_1 = require("./app.module"); +const serverless_express_1 = require("@vendia/serverless-express"); // NEW +let cachedHandler; +const handler = async (event, context) => { + // first invocation: boot Nest and create handler + if (!cachedHandler) { + const app = await core_1.NestFactory.create(app_module_1.AppModule); + await app.init(); + cachedHandler = (0, serverless_express_1.configure)({ + app: app.getHttpAdapter().getInstance(), + // optional: logLevel: 'info', + }); + } + // Vendia adapter understands both 1.0 and 2.0 events + return cachedHandler(event, context, () => console.log('healthy')); +}; +exports.handler = handler; diff --git a/backend/dist-ts/backend/src/main.js b/backend/dist-ts/backend/src/main.js new file mode 100644 index 0000000..75b977a --- /dev/null +++ b/backend/dist-ts/backend/src/main.js @@ -0,0 +1,45 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const core_1 = require("@nestjs/core"); +const app_module_1 = require("./app.module"); +const dotenv = __importStar(require("dotenv")); +const aws_sdk_1 = __importDefault(require("aws-sdk")); +/* ! */ +async function bootstrap() { + aws_sdk_1.default.config.update({ + region: process.env.AWS_REGION, + accessKeyId: process.env.OPEN_HATCH, + secretAccessKey: process.env.CLOSED_HATCH + }); + const app = await core_1.NestFactory.create(app_module_1.AppModule); + app.enableCors(); + await app.listen(3001); +} +dotenv.config(); +bootstrap(); diff --git a/backend/dist-ts/backend/src/notifications/notifcation.service.js b/backend/dist-ts/backend/src/notifications/notifcation.service.js new file mode 100644 index 0000000..4520fde --- /dev/null +++ b/backend/dist-ts/backend/src/notifications/notifcation.service.js @@ -0,0 +1,137 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NotificationService = void 0; +const common_1 = require("@nestjs/common"); +const AWS = __importStar(require("aws-sdk")); +let NotificationService = class NotificationService { + constructor() { + this.dynamoDb = new AWS.DynamoDB.DocumentClient(); + this.ses = new AWS.SES({ region: process.env.AWS_REGION }); + } + // function to create a notification + async createNotification(notification) { + const alertTime = new Date(notification.alertTime); // ensures a Date can be created from the given alertTime + const params = { + TableName: process.env.DYNAMODB_NOTIFICATION_TABLE_NAME || 'TABLE_FAILURE', + Item: Object.assign(Object.assign({}, notification), { alertTime: alertTime.toISOString() }), + }; + await this.dynamoDb.put(params).promise(); + return notification; + } + // function that returns array of notifications by user id (sorted by most recent notifications first) + async getNotificationByUserId(userId) { + // KeyConditionExpression specifies the query condition + // ExpressionAttributeValues specifies the actual value of the key + // IndexName specifies our Global Secondary Index, which was created in the BCANNotifs table to + // allow for querying by userId, as it is not a primary/partition key + const params = { + TableName: process.env.DYNAMODB_NOTIFICATION_TABLE_NAME || 'TABLE_FAILURE', + IndexName: 'userId-alertTime-index', + KeyConditionExpression: 'userId = :userId', + ExpressionAttributeValues: { + ':userId': userId, + }, + ScanIndexForward: false // sort in descending order + }; + try { + const data = await this.dynamoDb.query(params).promise(); + if (!data.Items) { + throw new Error('No notifications with user id ' + userId + ' found.'); + } + return data.Items; + } + catch (error) { + console.log(error); + throw new Error('Failed to retrieve notifications.'); + } + } + // function that returns array of notifications by notification id + async getNotificationByNotificationId(notificationId) { + // key condition expression specifies the query condition + // expression attribute values specifies the actual value of the key + const params = { + TableName: process.env.DYNAMODB_NOTIFICATION_TABLE_NAME || 'TABLE_FAILURE', + KeyConditionExpression: 'notificationId = :notificationId', + ExpressionAttributeValues: { + ':notificationId': notificationId, + }, + }; + try { + const data = await this.dynamoDb.query(params).promise(); + if (!data.Items) { + throw new Error('No notifications with notification id ' + notificationId + ' found.'); + } + return data.Items; + } + catch (error) { + console.log(error); + throw new Error('Failed to retrieve notification.'); + } + } + /** + * Send an email using AWS SES + * @param to The recipient email address + * @param subject The email subject + * @param body The email body + */ + async sendEmailNotification(to, subject, body) { + // Default to an invalid email to prevent non-verified sender mails + // if BCAN's is not defined in the environment + const fromEmail = process.env.NOTIFICATION_EMAIL_SENDER || + 'u&@nveR1ified-failure@dont-send.com'; + const params = { + Source: fromEmail, + Destination: { + ToAddresses: [to], + }, + Message: { + // UTF-8 is a top reliable way to define special characters and symbols in emails + Subject: { Charset: 'UTF-8', Data: subject }, + Body: { + Text: { Charset: 'UTF-8', Data: body }, + }, + }, + }; + try { + return await this.ses.sendEmail(params).promise(); + } + catch (err) { + console.error('Error sending email: ', err); + const errMessage = (err instanceof Error) ? err.message : 'Generic'; + throw new Error(`Failed to send email: ${errMessage}`); + } + } +}; +NotificationService = __decorate([ + (0, common_1.Injectable)() +], NotificationService); +exports.NotificationService = NotificationService; diff --git a/backend/dist-ts/backend/src/notifications/notification.controller.js b/backend/dist-ts/backend/src/notifications/notification.controller.js new file mode 100644 index 0000000..532a531 --- /dev/null +++ b/backend/dist-ts/backend/src/notifications/notification.controller.js @@ -0,0 +1,62 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NotificationController = void 0; +const common_1 = require("@nestjs/common"); +const notifcation_service_1 = require("./notifcation.service"); +let NotificationController = class NotificationController { + constructor(notificationService) { + this.notificationService = notificationService; + } + // allows to create a new notification + async create(notification) { + // call the service's createNotification method and return the result + return await this.notificationService.createNotification(notification); + } + // gets notifications based on the noticationId + async findByNotification(notificationId) { + return await this.notificationService.getNotificationByNotificationId(notificationId); + } + // gets notifications by user id (sorted by most recent notifications first) + async findByUser(userId) { + console.log("HERE"); + return await this.notificationService.getNotificationByUserId(userId); + } +}; +__decorate([ + (0, common_1.Post)(), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], NotificationController.prototype, "create", null); +__decorate([ + (0, common_1.Get)(':notificationId'), + __param(0, (0, common_1.Param)('notificationId')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], NotificationController.prototype, "findByNotification", null); +__decorate([ + (0, common_1.Get)('/user/:userId'), + __param(0, (0, common_1.Param)('userId')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], NotificationController.prototype, "findByUser", null); +NotificationController = __decorate([ + (0, common_1.Controller)('notifications'), + __metadata("design:paramtypes", [notifcation_service_1.NotificationService]) +], NotificationController); +exports.NotificationController = NotificationController; diff --git a/backend/dist-ts/backend/src/notifications/notification.module.js b/backend/dist-ts/backend/src/notifications/notification.module.js new file mode 100644 index 0000000..bec2a67 --- /dev/null +++ b/backend/dist-ts/backend/src/notifications/notification.module.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NotificationsModule = void 0; +const common_1 = require("@nestjs/common"); +const notification_controller_1 = require("./notification.controller"); +const notifcation_service_1 = require("./notifcation.service"); +let NotificationsModule = class NotificationsModule { +}; +NotificationsModule = __decorate([ + (0, common_1.Module)({ + providers: [notifcation_service_1.NotificationService], + controllers: [notification_controller_1.NotificationController], + // and are the starting point for anything happening + exports: [notifcation_service_1.NotificationService], // by putting it under exports, this service is available + // to other modules outside of this + }) +], NotificationsModule); +exports.NotificationsModule = NotificationsModule; diff --git a/backend/dist-ts/backend/src/tags/error/errortype.js b/backend/dist-ts/backend/src/tags/error/errortype.js new file mode 100644 index 0000000..ffe5538 --- /dev/null +++ b/backend/dist-ts/backend/src/tags/error/errortype.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ErrorType = void 0; +var ErrorType; +(function (ErrorType) { + ErrorType["Authentication"] = "AuthenticationError"; + ErrorType["Validation"] = "ValidationError"; + ErrorType["Unknown"] = "UnknownError"; + ErrorType["InternalServer"] = "InternalServerError"; +})(ErrorType = exports.ErrorType || (exports.ErrorType = {})); diff --git a/backend/dist-ts/backend/src/tags/logger/logcontext.js b/backend/dist-ts/backend/src/tags/logger/logcontext.js new file mode 100644 index 0000000..0eb48e1 --- /dev/null +++ b/backend/dist-ts/backend/src/tags/logger/logcontext.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LogContext = void 0; +var LogContext; +(function (LogContext) { + LogContext["Error"] = "Error"; + LogContext["UserAction"] = "UserAction"; + LogContext["GrantActivity"] = "GrantActivity"; +})(LogContext = exports.LogContext || (exports.LogContext = {})); diff --git a/backend/dist-ts/backend/src/tags/logger/logger.js b/backend/dist-ts/backend/src/tags/logger/logger.js new file mode 100644 index 0000000..22bd07c --- /dev/null +++ b/backend/dist-ts/backend/src/tags/logger/logger.js @@ -0,0 +1,26 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var LoggerService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LoggerService = void 0; +const common_1 = require("@nestjs/common"); +let LoggerService = LoggerService_1 = class LoggerService { + constructor() { + this.logger = new common_1.Logger(LoggerService_1.name); + } + log(message, context) { + this.logger.log(message, context); + } + error(message, trace, context) { + this.logger.error(message, trace, context); + } +}; +LoggerService = LoggerService_1 = __decorate([ + (0, common_1.Injectable)() +], LoggerService); +exports.LoggerService = LoggerService; diff --git a/backend/dist-ts/backend/src/user/user.controller.js b/backend/dist-ts/backend/src/user/user.controller.js new file mode 100644 index 0000000..29b7f1d --- /dev/null +++ b/backend/dist-ts/backend/src/user/user.controller.js @@ -0,0 +1,46 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserController = void 0; +const common_1 = require("@nestjs/common"); +const user_service_1 = require("./user.service"); +let UserController = class UserController { + constructor(userService) { + this.userService = userService; + } + async getAllUsers() { + return await this.userService.getAllUsers(); + } + async getUserById(userId) { + return await this.userService.getUserById(userId); + } +}; +__decorate([ + (0, common_1.Get)(), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", Promise) +], UserController.prototype, "getAllUsers", null); +__decorate([ + (0, common_1.Get)(':id'), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UserController.prototype, "getUserById", null); +UserController = __decorate([ + (0, common_1.Controller)('user'), + __metadata("design:paramtypes", [user_service_1.UserService]) +], UserController); +exports.UserController = UserController; diff --git a/backend/dist-ts/backend/src/user/user.module.js b/backend/dist-ts/backend/src/user/user.module.js new file mode 100644 index 0000000..6df2e64 --- /dev/null +++ b/backend/dist-ts/backend/src/user/user.module.js @@ -0,0 +1,21 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserModule = void 0; +const common_1 = require("@nestjs/common"); +const user_service_1 = require("./user.service"); +const user_controller_1 = require("./user.controller"); +let UserModule = class UserModule { +}; +UserModule = __decorate([ + (0, common_1.Module)({ + controllers: [user_controller_1.UserController], + providers: [user_service_1.UserService], + }) +], UserModule); +exports.UserModule = UserModule; diff --git a/backend/dist-ts/backend/src/user/user.service.js b/backend/dist-ts/backend/src/user/user.service.js new file mode 100644 index 0000000..754a0d3 --- /dev/null +++ b/backend/dist-ts/backend/src/user/user.service.js @@ -0,0 +1,53 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserService = void 0; +const common_1 = require("@nestjs/common"); +const aws_sdk_1 = __importDefault(require("aws-sdk")); +/** + * File could use safer 'User' typing after grabbing users, verifying type after the scan. + */ +let UserService = class UserService { + constructor() { + this.dynamoDb = new aws_sdk_1.default.DynamoDB.DocumentClient(); + } + async getAllUsers() { + const params = { + TableName: process.env.DYNAMODB_USER_TABLE_NAME || 'TABLE_FAILURE', + }; + try { + const data = await this.dynamoDb.scan(params).promise(); + return data.Items; + } + catch (error) { + throw new Error('Could not retrieve users.'); + } + } + async getUserById(userId) { + const params = { + TableName: process.env.DYNAMODB_USER_TABLE_NAME || 'TABLE_FAILURE', + Key: { + userId, + }, + }; + try { + const data = await this.dynamoDb.get(params).promise(); + return data.Item; + } + catch (error) { + throw new Error('Could not retrieve user.'); + } + } +}; +UserService = __decorate([ + (0, common_1.Injectable)() +], UserService); +exports.UserService = UserService; diff --git a/backend/dist-ts/backend/src/utils/date.js b/backend/dist-ts/backend/src/utils/date.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/backend/dist-ts/backend/src/utils/date.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/backend/dist-ts/backend/src/utils/error.js b/backend/dist-ts/backend/src/utils/error.js new file mode 100644 index 0000000..3918c74 --- /dev/null +++ b/backend/dist-ts/backend/src/utils/error.js @@ -0,0 +1 @@ +"use strict"; diff --git a/backend/dist-ts/middle-layer/types/Attachment.js b/backend/dist-ts/middle-layer/types/Attachment.js new file mode 100644 index 0000000..4a38ed1 --- /dev/null +++ b/backend/dist-ts/middle-layer/types/Attachment.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Different Types for an Attachment + * Can Be: + * (1) Scope Document to the Organization's Website + * (2) Internal BCAN Resources + */ +var AttachmentType; +(function (AttachmentType) { + AttachmentType[AttachmentType["SCOPE_DOCUMENT"] = 0] = "SCOPE_DOCUMENT"; + AttachmentType[AttachmentType["SUPPORTING_RESOURCE"] = 1] = "SUPPORTING_RESOURCE"; +})(AttachmentType || (AttachmentType = {})); diff --git a/backend/dist-ts/middle-layer/types/Grant.js b/backend/dist-ts/middle-layer/types/Grant.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/backend/dist-ts/middle-layer/types/Grant.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/backend/dist-ts/middle-layer/types/Notification.js b/backend/dist-ts/middle-layer/types/Notification.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/backend/dist-ts/middle-layer/types/Notification.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/backend/dist-ts/middle-layer/types/Status.js b/backend/dist-ts/middle-layer/types/Status.js new file mode 100644 index 0000000..20d73a8 --- /dev/null +++ b/backend/dist-ts/middle-layer/types/Status.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getColorStatus = exports.statusToString = exports.Status = void 0; +/** + * Status Object + * (1) Potential: Grants to be applied for + * (2) Active: Grants with funds succesfully allocated + * (3) Inactive: Grant earnings are used up + */ +var Status; +(function (Status) { + Status["Potential"] = "Potential"; + Status["Active"] = "Active"; + Status["Inactive"] = "Inactive"; +})(Status = exports.Status || (exports.Status = {})); +// TODO: 1) override stringify behavior of status enum 2) stringify, and then go back and modify enum (create helper function to generalize) +// 3) turn enums to string +// string rep of status +function statusToString(status) { + switch (status) { + case 'My Grants': return null; // no filter + case 'Active Grants': return Status.Active; + case 'Inactive Grants': return Status.Inactive; + case 'Potential Grants': return Status.Potential; + default: throw new Error(`Unknown status: ${status}`); + } +} +exports.statusToString = statusToString; +// color associated with status on UI, represented as a string +function getColorStatus(status) { + switch (status) { + case "Active": return "#5AB911"; // green + case "Inactive": return "#A9A9A9"; // gray + case "Potential": return "#E6CA15"; // TODO: no color given for potential yet + default: return 'Unknown'; + } +} +exports.getColorStatus = getColorStatus; diff --git a/backend/dist-ts/middle-layer/types/User.js b/backend/dist-ts/middle-layer/types/User.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/backend/dist-ts/middle-layer/types/User.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/backend/esbuild.mjs b/backend/esbuild.mjs new file mode 100644 index 0000000..75f92d3 --- /dev/null +++ b/backend/esbuild.mjs @@ -0,0 +1,22 @@ +import { build } from 'esbuild'; + +// Wildcards aren’t supported, so list explicit roots. +const externals = [ + 'cache-manager', + '@nestjs/microservices', + '@nestjs/websockets', + '@nestjs/websockets/socket-module', + '@nestjs/microservices/microservices-module', +]; + + await build({ + entryPoints: ['dist-ts/backend/src/lambda.js'], + bundle: true, + platform: 'node', + target: 'node20', + outfile: 'dist/bundle.js', + format: 'cjs', + external: externals, // <─ consolidated list + sourcemap: false, + logLevel: 'info', + }); diff --git a/backend/lambda.zip b/backend/lambda.zip new file mode 100644 index 0000000..43a26b2 Binary files /dev/null and b/backend/lambda.zip differ diff --git a/backend/package-lock.json b/backend/package-lock.json index 7ce82cb..1869a17 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,7 +13,9 @@ "@nestjs/jwt": "^8.0.0", "@nestjs/passport": "^8.0.0", "@nestjs/platform-express": "^8.4.7", + "@vendia/serverless-express": "^4.12.6", "aws-sdk": "^2.1030.0", + "aws-serverless-express": "^3.4.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "crypto": "^1.0.1", @@ -26,8 +28,11 @@ }, "devDependencies": { "@nestjs/testing": "^8.4.7", + "@types/aws-lambda": "^8.10.149", + "@types/aws-serverless-express": "^3.3.10", "@types/jest": "^27.0.0", "@types/node": "^20.0.0", + "esbuild": "^0.25.2", "jest": "^27.0.0", "ts-jest": "^27.0.0", "typescript": "^4.9.5", @@ -545,10 +550,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@codegenie/serverless-express": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@codegenie/serverless-express/-/serverless-express-4.16.0.tgz", + "integrity": "sha512-TlvAHQysphN3OW6Ziz6AD2DnN9IIy/KkKNImZwMJpwGHHqLATi/9hMg8H8VBRg2L15pGt8ad3R6j6mfvHVWrCg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", "cpu": [ "ppc64" ], @@ -559,13 +573,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", "cpu": [ "arm" ], @@ -576,13 +590,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", "cpu": [ "arm64" ], @@ -593,13 +607,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", "cpu": [ "x64" ], @@ -610,13 +624,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", "cpu": [ "arm64" ], @@ -627,13 +641,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", "cpu": [ "x64" ], @@ -644,13 +658,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", "cpu": [ "arm64" ], @@ -661,13 +675,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", "cpu": [ "x64" ], @@ -678,13 +692,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", "cpu": [ "arm" ], @@ -695,13 +709,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", "cpu": [ "arm64" ], @@ -712,13 +726,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", "cpu": [ "ia32" ], @@ -729,13 +743,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", "cpu": [ "loong64" ], @@ -746,13 +760,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", "cpu": [ "mips64el" ], @@ -763,13 +777,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", "cpu": [ "ppc64" ], @@ -780,13 +794,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", "cpu": [ "riscv64" ], @@ -797,13 +811,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", "cpu": [ "s390x" ], @@ -814,13 +828,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", "cpu": [ "x64" ], @@ -831,13 +845,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", "cpu": [ "x64" ], @@ -848,13 +879,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", "cpu": [ "x64" ], @@ -865,13 +913,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", "cpu": [ "x64" ], @@ -882,13 +930,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", "cpu": [ "arm64" ], @@ -899,13 +947,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", "cpu": [ "ia32" ], @@ -916,13 +964,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", "cpu": [ "x64" ], @@ -933,7 +981,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1775,6 +1823,25 @@ "node": ">= 6" } }, + "node_modules/@types/aws-lambda": { + "version": "8.10.149", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.149.tgz", + "integrity": "sha512-NXSZIhfJjnXqJgtS7IwutqIF/SOy1Wz5Px4gUY1RWITp3AYTyuJS4xaXr/bIJY1v15XMzrJ5soGnPM+7uigZjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/aws-serverless-express": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@types/aws-serverless-express/-/aws-serverless-express-3.3.10.tgz", + "integrity": "sha512-a/NGZQ9MDKX0PIXHgCa0L0fHbYTjvMJ9vKGr22PvkU1EBXSfnVNnNJgDmTLmMzrURqeHCS7uIIYZTawB1pPSrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/aws-lambda": "*", + "@types/express": "*", + "@types/node": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1820,6 +1887,27 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -1827,6 +1915,31 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz", + "integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -1837,6 +1950,13 @@ "@types/node": "*" } }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -1884,6 +2004,13 @@ "@types/node": "*" } }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.17.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.13.tgz", @@ -1900,6 +2027,43 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -1930,6 +2094,18 @@ "dev": true, "license": "MIT" }, + "node_modules/@vendia/serverless-express": { + "version": "4.12.6", + "resolved": "https://registry.npmjs.org/@vendia/serverless-express/-/serverless-express-4.12.6.tgz", + "integrity": "sha512-ePsIPk3VQwgm5nh/JGBtTKQs5ZOF7REjHxC+PKk/CHvhlKQkJuUU365uPOlxuLJhC+BAefDznDRReWxpnKjmYg==", + "license": "Apache-2.0", + "dependencies": { + "@codegenie/serverless-express": "^4.12.5" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@vitest/expect": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", @@ -2254,6 +2430,47 @@ "node": ">= 10.0.0" } }, + "node_modules/aws-serverless-express": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/aws-serverless-express/-/aws-serverless-express-3.4.0.tgz", + "integrity": "sha512-YG9ZjAOI9OpwqDDWzkRc3kKJYJuR7gTMjLa3kAWopO17myoprxskCUyCEee+RKe34tcR4UNrVtgAwW5yDe74bw==", + "license": "Apache-2.0", + "dependencies": { + "@vendia/serverless-express": "^3.4.0", + "binary-case": "^1.0.0", + "type-is": "^1.6.16" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-serverless-express/node_modules/@codegenie/serverless-express": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@codegenie/serverless-express/-/serverless-express-3.4.1.tgz", + "integrity": "sha512-PQ3v/wDflxx168B4TwuxbbKjfmvLkyRBdvHRFS8s48hDS0Wnukm+5Dp+HiLvqwXOU7PP2+FyrK47WX4WL15Rrw==", + "license": "Apache-2.0", + "dependencies": { + "binary-case": "^1.0.0", + "type-is": "^1.6.16" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-serverless-express/node_modules/@vendia/serverless-express": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@vendia/serverless-express/-/serverless-express-3.4.1.tgz", + "integrity": "sha512-4dJJvr9vQlq9iUClpfm5iFL+neoSctUI6Zkh9F4wjk/tpcM7QVD6niJi4ptiIzyzJCWoN97ACQCXyE0O8MznLQ==", + "license": "Apache-2.0", + "dependencies": { + "@codegenie/serverless-express": "^3.4.1", + "binary-case": "^1.0.0", + "type-is": "^1.6.16" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/axios": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", @@ -2391,6 +2608,12 @@ ], "license": "MIT" }, + "node_modules/binary-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/binary-case/-/binary-case-1.1.4.tgz", + "integrity": "sha512-9Kq8m6NZTAgy05Ryuh7U3Qc4/ujLQU1AZ5vMw4cr3igTdi5itZC6kCNrRr2X8NzPiDn2oUIFTfa71DKMnue/Zg==", + "license": "Apache-2.0" + }, "node_modules/body-parser": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", @@ -3180,9 +3403,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3190,32 +3413,34 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" } }, "node_modules/escalade": { @@ -7010,6 +7235,436 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "node_modules/vitest": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", diff --git a/backend/package.json b/backend/package.json index 5e281e3..215917d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,8 +5,9 @@ "scripts": { "start": "nest start", "start:dev": "nest start --watch", - "build": "tsc", - "test": "vitest --config vitest.config.ts" + "build": "tsc -p tsconfig.build.json && node esbuild.mjs", + "test": "vitest --config vitest.config.ts", + "zip": "cd dist && zip -r ../lambda.zip bundle.js node_modules" }, "dependencies": { "@nestjs/common": "^8.0.0", @@ -14,7 +15,9 @@ "@nestjs/jwt": "^8.0.0", "@nestjs/passport": "^8.0.0", "@nestjs/platform-express": "^8.4.7", + "@vendia/serverless-express": "^4.12.6", "aws-sdk": "^2.1030.0", + "aws-serverless-express": "^3.4.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "crypto": "^1.0.1", @@ -27,8 +30,11 @@ }, "devDependencies": { "@nestjs/testing": "^8.4.7", + "@types/aws-lambda": "^8.10.149", + "@types/aws-serverless-express": "^3.3.10", "@types/jest": "^27.0.0", "@types/node": "^20.0.0", + "esbuild": "^0.25.2", "jest": "^27.0.0", "ts-jest": "^27.0.0", "typescript": "^4.9.5", diff --git a/backend/serverless.yml b/backend/serverless.yml new file mode 100644 index 0000000..fba66e6 --- /dev/null +++ b/backend/serverless.yml @@ -0,0 +1,21 @@ +service: bcan-backend +frameworkVersion: '>=3' + +provider: + name: aws + runtime: nodejs20.x + region: us-east-2 + environment: + NODE_ENV: production + +functions: + api: + handler: dist/bundle.handler # /. + events: + - httpApi: # API Gateway v2 (cheaper/faster) + path: /{proxy+} + method: ANY + +package: + individually: false + artifact: lambda.zip # created by "npm run zip" diff --git a/backend/src/lambda.ts b/backend/src/lambda.ts new file mode 100644 index 0000000..998052a --- /dev/null +++ b/backend/src/lambda.ts @@ -0,0 +1,20 @@ +import 'reflect-metadata'; +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import { configure } from '@vendia/serverless-express'; // NEW + +let cachedHandler: ReturnType; + +export const handler = async (event: any, context: any) => { + // first invocation: boot Nest and create handler + if (!cachedHandler) { + const app = await NestFactory.create(AppModule); + await app.init(); + cachedHandler = configure({ + app: app.getHttpAdapter().getInstance(), + // optional: logLevel: 'info', + }); + } + // Vendia adapter understands both 1.0 and 2.0 events + return cachedHandler(event, context, () => console.log('healthy')); +}; diff --git a/backend/tsconfig.build.json b/backend/tsconfig.build.json new file mode 100644 index 0000000..c40b372 --- /dev/null +++ b/backend/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist-ts", + "emitDecoratorMetadata": true, + "experimentalDecorators": true + }, + "include": ["src/**/*", "../middle-layer/**/*"] + } \ No newline at end of file