├── .dockerignore ├── .eslintignore ├── .prettierignore ├── src ├── api │ ├── enums │ │ └── MeetProvider.ts │ ├── models │ │ ├── LastLogin.model.ts │ │ ├── Meeting.model.ts │ │ ├── Email.model.ts │ │ ├── ExecutiveBoard.model.ts │ │ ├── Organization.model.ts │ │ ├── Contact.model.ts │ │ ├── BoardMember.model.ts │ │ ├── Event.model.ts │ │ ├── Webinar.model.ts │ │ ├── TopSpeaker.model.ts │ │ ├── Application.model.ts │ │ └── User.model.ts │ ├── services │ │ ├── Service.constant.ts │ │ ├── Organization.service.ts │ │ ├── BoardMember.service.ts │ │ ├── Contact.service.ts │ │ ├── ExecutiveBoard.service.ts │ │ ├── index.ts │ │ ├── Meeting.service.ts │ │ ├── Event.service.ts │ │ ├── TopSpeaker.service.ts │ │ └── Webinar.service.ts │ ├── validations │ │ ├── index.ts │ │ └── Application.validate.schema.ts │ ├── middleware │ │ ├── index.ts │ │ ├── Validation.middleware.ts │ │ └── Auth.middleware.ts │ └── controllers │ │ ├── Organization.controller.ts │ │ ├── BoardMember.controller.ts │ │ ├── Meeting.controller.ts │ │ ├── Contact.controller.ts │ │ ├── ExecutiveBoard.controller.ts │ │ ├── index.ts │ │ ├── TopSpeaker.controller.ts │ │ ├── Event.controller.ts │ │ └── Webinar.controller.ts ├── util │ ├── cron.ts │ ├── logger.ts │ ├── database.connection.ts │ ├── image.handler.ts │ ├── queue.config.ts │ ├── response.handler.ts │ └── email.handler.ts ├── interfaces │ ├── IMeeting.ts │ ├── IInterview.ts │ ├── IEmail.ts │ ├── IOrganization.ts │ ├── IContact.ts │ ├── IExecutiveBoard.ts │ ├── IWebinar.ts │ ├── IBoardMember.ts │ ├── IApplication.ts │ ├── IEvent.ts │ ├── ITopSpeaker.ts │ ├── IConfig.ts │ ├── index.ts │ └── IUser.ts ├── config │ ├── storage.config.ts │ └── index.ts ├── app.ts └── templates │ └── Email-Template.html ├── .prettierrc ├── .gitignore ├── docs └── CODEOWNERS ├── Dockerfile ├── tsconfig.json ├── .github ├── templates │ ├── pull_request_template.md │ └── ISSUE_TEMPLATE.md └── workflows │ ├── docker-image.yml │ ├── node-build.yml │ ├── master_mswebserver.yml │ └── development_msdevwebserver.yml ├── .eslintrc ├── .husky └── pre-commit ├── typings └── express │ └── index.d.ts ├── package.json └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | dist/* 3 | .idea/* 4 | .vscode 5 | node_modules/* -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist/* 3 | .idea/* 4 | .vscode 5 | node_modules/* -------------------------------------------------------------------------------- /src/api/enums/MeetProvider.ts: -------------------------------------------------------------------------------- 1 | enum MeetProvider { 2 | GOOGLEMEET = "googleMeet", 3 | MSMEET = "msMeet" 4 | } 5 | 6 | export { MeetProvider }; -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": false, 7 | "printWidth": 120, 8 | "arrowParens": "always" 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | yarn.lock 4 | ms-storage-server.json 5 | ms-storage-server-local.json 6 | .env.development 7 | .env.production 8 | .env 9 | 10 | # Editor directories and files 11 | .idea 12 | .vscode 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | *.sw? -------------------------------------------------------------------------------- /docs/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. 2 | # Following code owners will be requested for review when someone 3 | # opens a pull request. 4 | @rusiruavb 5 | @senuravihanjayadeva 6 | @CandyPanda-LS 7 | @nisalrenuja 8 | @miyurugunarathna -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.18-alpine AS BUILD_IMAGE 2 | RUN apk add --no-cache nodejs npm 3 | WORKDIR /ms-webserver 4 | COPY ["package.json", "./"] 5 | RUN npm install 6 | COPY . . 7 | RUN npm run build 8 | 9 | FROM node:14.18-alpine 10 | WORKDIR /app 11 | COPY --from=BUILD_IMAGE /ms-webserver /app/ 12 | EXPOSE 8087 13 | ENTRYPOINT [ "npm", "run" ] 14 | CMD [ "start" ] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "rootDir": "./src", 6 | "moduleResolution": "node", 7 | "outDir": "./build", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "skipLibCheck": true, 12 | "typeRoots": ["./typings", "./node_modules/@types"] 13 | }, 14 | "exclude": ["node_modules", "**/*.spec.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /src/api/models/LastLogin.model.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Schema } from "mongoose"; 2 | import { ILastLoggedUser } from "../../interfaces/IUser"; 3 | 4 | const LastLoggedUserSchema = new Schema( 5 | { 6 | loggedAt: { type: Date, required: true, default: null }, 7 | user: { type: Schema.Types.ObjectId, required: true, ref: "users" }, 8 | }, 9 | { 10 | timestamps: true, 11 | } 12 | ); 13 | 14 | const LastLoggedUserModel = mongoose.model("loggins", LastLoggedUserSchema); 15 | export default LastLoggedUserModel; 16 | -------------------------------------------------------------------------------- /src/util/cron.ts: -------------------------------------------------------------------------------- 1 | import { CronJob } from "cron"; 2 | import logger from "./logger"; 3 | import { sendEmailWithTemplate } from "./email.handler"; 4 | 5 | const cronInit = () => { 6 | const crons = []; 7 | 8 | crons.push( 9 | new CronJob({ 10 | cronTime: "*/20 * * * * *", 11 | onTick: async () => { 12 | await sendEmailWithTemplate(); 13 | }, 14 | timeZone: "Asia/Colombo", 15 | }) 16 | ); 17 | 18 | for (const cron of crons) { 19 | logger.info("📅 Starting MS Cron Processes"); 20 | cron.start(); 21 | } 22 | }; 23 | 24 | export { cronInit }; 25 | -------------------------------------------------------------------------------- /.github/templates/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### 🎯 Feature Name - _Write the feature name here_ 2 | 3 | ### 📝 Summary 4 | 5 | _Write your summary of the feature in here_ 6 | 7 | ### ✅ Check following steps are fulfilled
8 | 9 | - [ ] 👀 Add more than 2 developers to review the code. 10 | - [ ] 🏷️ Attach necessary labels (**high-priority**, **medium-priority**, **low-priority**). 11 | - [ ] 🚩 Add the PR to the project. 12 | - [ ] ⁉️ Link the issue for the PR (If available). 13 | - [ ] ✔️ Complete the PR template. 14 | 15 | ### 🧑🏻‍💻 Contributors 16 | 17 | _Mention who contributed to this feature_ 18 | -------------------------------------------------------------------------------- /src/api/services/Service.constant.ts: -------------------------------------------------------------------------------- 1 | const EmailType = { 2 | Application: "Application", 3 | Interview: "Interview", 4 | ContactUs: "ContactUs", 5 | Selected: "Selected", 6 | }; 7 | 8 | const EmailTemplate = { 9 | Application: "Application-Email-Template.html", 10 | Interview: "Interview-Email-Template.html", 11 | ContactUs: "Contact-Us-Email-Template.html", 12 | Selected: "Selected-Email-Template.html", 13 | }; 14 | 15 | const EmailStatus = { 16 | Waiting: "WAITING", 17 | InProgress: "IN-PROGRESS", 18 | Delivered: "DELIVERED", 19 | }; 20 | 21 | export { EmailType, EmailTemplate, EmailStatus }; 22 | -------------------------------------------------------------------------------- /.github/templates/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Issue/ Feature Request :bricks: 2 | 3 | ⚠️ _You must fill all the required fields to place you issue correctly._ 4 | | Fields | Description | Requirement | 5 | | ------ | ----------- | ------------------ | 6 | | Name | Name of the feature/ issue. | ✔️ | 7 | | About | A clear description about the feature or issue. | ✔️ | 8 | | Labels | Appropriate labels for the feature or issue. | ✔️ | 9 | | Assignees | Who is going to take the feature or issue | ❌ | 10 | | Project | Appropriate project that feature or issue belongs to | ✔️ | 11 | 12 | **Required** - ✔️
13 | **Optional** - ❌ 14 | -------------------------------------------------------------------------------- /src/interfaces/IMeeting.ts: -------------------------------------------------------------------------------- 1 | import { Document, Schema } from "mongoose"; 2 | 3 | interface IUpdatedBy { 4 | user: Schema.Types.ObjectId; 5 | updatedAt: Date; 6 | } 7 | interface IMeeting extends Document { 8 | meetingId: string; 9 | meetingName: string; 10 | startDateTime: string; 11 | endDateTime: string; 12 | emailList: string[]; 13 | scheduledLink: string; 14 | type: string; 15 | meetProvider: string; 16 | deletedAt?: null | Date | string; 17 | updatedBy: IUpdatedBy[]; 18 | deletedBy?: Schema.Types.ObjectId | null; 19 | } 20 | 21 | interface IMeetingRequest extends Document { 22 | meetingName: string; 23 | startDateTime: Date; 24 | endDateTime: Date; 25 | emailList: string[]; 26 | } 27 | 28 | export type { IMeeting, IMeetingRequest }; 29 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es2021": true, 4 | "node": true 5 | }, 6 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 7 | "parser": "@typescript-eslint/parser", 8 | "parserOptions": { 9 | "sourceType": "module" 10 | }, 11 | "plugins": ["@typescript-eslint"], 12 | "rules": { 13 | "indent": ["error", "tab"], 14 | "quotes": ["error", "double"], 15 | "semi": ["error", "always"], 16 | "@typescript-eslint/no-explicit-any": "off", 17 | "no-const-assign": "error", 18 | "max-len": ["error", { "code": 180, "ignoreUrls": true, "ignoreRegExpLiterals": true }], 19 | "no-unused-vars": "error", 20 | "no-console": "error", 21 | "no-duplicate-imports": ["error", { "includeExports": true }], 22 | "default-case": "error" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build the Docker image of the MS Club Web Server and push it 2 | # to the GitHub Container Registry (GHCR) 3 | 4 | name: Docker Image CI 5 | 6 | on: 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | name: Check out code 17 | 18 | - uses: mr-smithers-excellent/docker-build-push@v5 19 | name: Build & Push Docker image 20 | with: 21 | image: msclubwebserver 22 | tags: v1.0.0, latest 23 | registry: ghcr.io 24 | dockerfile: Dockerfile 25 | username: ${{ secrets.DOCKER_USERNAME }} 26 | password: ${{ secrets.DOCKER_PASSWORD }} 27 | -------------------------------------------------------------------------------- /.github/workflows/node-build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, 2 | # cache/restore them, build the source code and run tests across 3 | # different versions of node 4 | 5 | name: Node Build CI 6 | 7 | on: 8 | push: 9 | branches: [master, development] 10 | pull_request: 11 | branches: [master, development] 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | node-version: [14.x] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v2 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | cache: "npm" 27 | - run: npm install 28 | - run: npm ci 29 | - run: npm run build 30 | -------------------------------------------------------------------------------- /src/api/validations/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { applicationSchema } from "./Application.validate.schema"; 25 | 26 | export default { 27 | applicationSchema, 28 | }; 29 | -------------------------------------------------------------------------------- /src/api/middleware/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { authenticate } from "./Auth.middleware"; 25 | import { validateRequest } from "./Validation.middleware"; 26 | 27 | export default { 28 | authenticate, 29 | validateRequest, 30 | }; 31 | -------------------------------------------------------------------------------- /src/interfaces/IInterview.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document } from "mongoose"; 25 | 26 | interface IInterview extends Document { 27 | format: string; 28 | startDateTime: Date; 29 | endDateTime: Date; 30 | attendees: any; 31 | } 32 | 33 | export type { IInterview }; 34 | -------------------------------------------------------------------------------- /src/interfaces/IEmail.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Tue Mar 01 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document } from "mongoose"; 25 | 26 | interface IEmail extends Document { 27 | templateName: string; 28 | to: string; 29 | subject: string; 30 | status: string; 31 | body?: string; 32 | type: string; 33 | } 34 | 35 | export type { IEmail }; 36 | -------------------------------------------------------------------------------- /src/util/logger.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import pino from "pino"; 25 | 26 | const LOGGER = pino({ 27 | transport: { 28 | target: "pino-pretty", 29 | options: { 30 | colorize: true, 31 | translateTime: "SYS:dd-mm-yyyy HH:MM:ss", 32 | ignore: "pid,hostname", 33 | }, 34 | }, 35 | }); 36 | 37 | export default LOGGER; 38 | -------------------------------------------------------------------------------- /src/interfaces/IOrganization.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { IUpdatedBy } from "."; 25 | 26 | interface IOrganization { 27 | name: string; 28 | email: string; 29 | phoneNumber?: string | null; 30 | university: string | null; 31 | address: string | null; 32 | website: string | null; 33 | updatedBy: IUpdatedBy[]; 34 | imagePath: string; 35 | } 36 | 37 | export type { IOrganization }; 38 | -------------------------------------------------------------------------------- /src/interfaces/IContact.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document } from "mongoose"; 25 | 26 | interface IContact extends Document { 27 | name: string; 28 | email: string; 29 | message: string; 30 | createdAt: Date; 31 | updatedAt: Date; 32 | deletedAt?: null | Date | string; 33 | replies: string[]; 34 | } 35 | 36 | interface IInquiryReply extends Document { 37 | reply: string; 38 | } 39 | 40 | export type { IContact, IInquiryReply }; 41 | -------------------------------------------------------------------------------- /src/api/models/Meeting.model.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Schema } from "mongoose"; 2 | import { IMeeting } from "../../interfaces"; 3 | 4 | const MeetingSchema = new Schema( 5 | { 6 | meetingId: { type: String, required: true }, 7 | meetingName: { type: String, required: true }, 8 | startDateTime: { type: String, required: true }, 9 | endDateTime: { type: String, required: true }, 10 | emailList: [{ type: String, required: true }], 11 | scheduledLink: { type: String, required: true }, 12 | type: { 13 | type: String, 14 | enum: ["INTERNAL", "INTERVIEW"], 15 | required: true, 16 | }, 17 | meetProvider: { 18 | type: String, 19 | enum: ["MSMEET", "GOOGLEMEET"], 20 | required: true, 21 | }, 22 | deletedAt: { type: Date, required: false, default: null }, 23 | deletedBy: { 24 | type: Schema.Types.ObjectId, 25 | required: false, 26 | default: null, 27 | ref: "users", 28 | }, 29 | updatedBy: [ 30 | { 31 | user: { type: Schema.Types.ObjectId, required: false, ref: "users" }, 32 | updatedAt: { type: Date, required: false }, 33 | }, 34 | ], 35 | 36 | }, 37 | { timestamps: true } 38 | ); 39 | 40 | const MeetingModel = mongoose.model("meeting", MeetingSchema); 41 | 42 | export default MeetingModel; 43 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | echo '🏗️👷 Hi, my name is Bob. I am responsible for styling, testing and building your project before commit' 5 | 6 | # Check Prettier standards 7 | npm run check-format || 8 | ( 9 | echo '🤢🤮 Prettier Check Failed. Your code styling not looking good. 🤢🤮 Run npm run format, add changes and try commit again.'; 10 | false; 11 | ) 12 | 13 | # Check ESLint Standards 14 | npm run check-lint || 15 | ( 16 | echo '😤🏀 ESLint Check Failed. Your code may have some linting issues. 👋😤 Make the required changes listed above, add changes and try to commit again.' 17 | false; 18 | ) 19 | 20 | # Check tsconfig standards 21 | npm run check-types || 22 | ( 23 | echo '❌❌ Failed Type check. ❌❌ Are you seriously trying to write that? Make the changes required above.' 24 | false; 25 | ) 26 | 27 | # If everything passes... Now we can commit 28 | echo '🤔🤔 Alright.... Code looks good to me... Trying to build now. 🤔🤔' 29 | 30 | npm run build || 31 | ( 32 | echo '🔨❌ Better call Bob... Because your build failed 🔨❌ Next build failed: View the errors above to see why.' 33 | false; 34 | ) 35 | 36 | # If everything passes... Now we can commit 37 | echo '✅✅ You win this time... I am committing this now. ✅✅' 38 | 39 | -------------------------------------------------------------------------------- /src/api/middleware/Validation.middleware.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | 26 | const validateRequest = (schema: any) => { 27 | return async (request: Request, response: Response, next: NextFunction) => { 28 | try { 29 | await schema.validate(request.body); 30 | next(); 31 | } catch (error: any) { 32 | request.handleResponse.errorRespond(response)(error.message); 33 | } 34 | }; 35 | }; 36 | 37 | export { validateRequest }; 38 | -------------------------------------------------------------------------------- /src/interfaces/IExecutiveBoard.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document, Schema } from "mongoose"; 25 | import { IBoardMember } from "./IBoardMember"; 26 | 27 | interface IUpdatedBy { 28 | user: Schema.Types.ObjectId; 29 | updatedAt: Date; 30 | } 31 | 32 | interface IExecutiveBoard extends Document { 33 | year: string; 34 | board: IBoardMember[]; 35 | deletedAt?: Date; 36 | createdBy: Schema.Types.ObjectId; 37 | updatedBy: IUpdatedBy[]; 38 | deletedBy?: Schema.Types.ObjectId; 39 | } 40 | export type { IExecutiveBoard }; 41 | -------------------------------------------------------------------------------- /src/config/storage.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Storage } from "@google-cloud/storage"; 25 | import { configs } from "."; 26 | 27 | const storage = new Storage({ 28 | projectId: "ms-storage-server-fb22b", 29 | credentials: { 30 | client_email: configs.firebase.clientEmail, 31 | private_key: configs.firebase.privateKey.replace(/\\n/gm, "\n"), 32 | }, 33 | }); 34 | 35 | const bucketName = configs.firebase.bucketName; 36 | 37 | const StorageBucket = storage.bucket(bucketName); 38 | 39 | export default StorageBucket; 40 | -------------------------------------------------------------------------------- /src/interfaces/IWebinar.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document, Schema } from "mongoose"; 25 | 26 | interface IUpdatedBy { 27 | user: Schema.Types.ObjectId; 28 | updatedAt: Date; 29 | } 30 | 31 | interface IWebinar extends Document { 32 | title: string; 33 | description: string; 34 | imageUrl: string; 35 | dateTime: Date; 36 | tags?: string[]; 37 | link?: string; 38 | registrationLink?: string; 39 | webinarType: string; 40 | deletedAt?: Date | null; 41 | createdBy: Schema.Types.ObjectId; 42 | updatedBy: IUpdatedBy[]; 43 | deletedBy?: Schema.Types.ObjectId | null; 44 | } 45 | 46 | export type { IWebinar }; 47 | -------------------------------------------------------------------------------- /src/interfaces/IBoardMember.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document, Schema } from "mongoose"; 25 | 26 | interface IUpdatedBy { 27 | user: Schema.Types.ObjectId; 28 | updatedAt: Date; 29 | } 30 | 31 | interface IBoardMember extends Document { 32 | name: string; 33 | position: string; 34 | imageUrl?: string; 35 | socialMedia: ISocialMedia; 36 | deletedAt?: Date; 37 | createdBy: Schema.Types.ObjectId; 38 | updatedBy: IUpdatedBy[]; 39 | deletedBy?: Schema.Types.ObjectId; 40 | } 41 | 42 | interface ISocialMedia { 43 | facebook: string; 44 | instagram: string; 45 | twitter: string; 46 | linkedIn: string; 47 | } 48 | 49 | export type { IBoardMember }; 50 | -------------------------------------------------------------------------------- /src/interfaces/IApplication.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document, Schema } from "mongoose"; 25 | 26 | interface IApplication extends Document { 27 | studentId: string; 28 | name: string; 29 | email: string; 30 | contactNumber: string; 31 | currentAcademicYear: string; 32 | selfIntroduction: string; 33 | reasonForJoin: string; 34 | linkedIn: string; 35 | gitHub: string; 36 | blog?: string; 37 | experiences?: string; 38 | challenges?: string; 39 | goal: string; 40 | skillsAndTalents: string[]; 41 | pastWork?: string; 42 | deletedAt?: Date | null; 43 | status: string; 44 | meeting: Schema.Types.ObjectId; 45 | } 46 | 47 | export type { IApplication }; 48 | -------------------------------------------------------------------------------- /src/interfaces/IEvent.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document, Schema } from "mongoose"; 25 | 26 | interface IUpdatedBy { 27 | user: Schema.Types.ObjectId; 28 | updatedAt: Date; 29 | } 30 | 31 | interface IEvent extends Document { 32 | title: string; 33 | description: string; 34 | imageUrl: string; 35 | dateTime: Date; 36 | tags?: string[]; 37 | link?: string; 38 | registrationLink?: string; 39 | eventType: string; 40 | createdAt: Date; 41 | updatedAt: Date; 42 | deletedAt?: null | Date | string; 43 | createdBy: Schema.Types.ObjectId; 44 | updatedBy: IUpdatedBy[]; 45 | deletedBy?: Schema.Types.ObjectId | null; 46 | } 47 | 48 | export type { IEvent, IUpdatedBy }; 49 | -------------------------------------------------------------------------------- /src/util/database.connection.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import Mongoose from "mongoose"; 25 | import logger from "./logger"; 26 | import { configs } from "../config"; 27 | 28 | let database: Mongoose.Connection; 29 | 30 | const connect = async () => { 31 | const databaseConnectionString = configs.mongodb.uri; 32 | 33 | if (database) { 34 | return; 35 | } 36 | 37 | Mongoose.connect(databaseConnectionString) 38 | .then((connection) => { 39 | database = connection.connection; 40 | logger.info("✅ Database Synced"); 41 | }) 42 | .catch((error: any) => { 43 | logger.error("Error connecting to database: ", error.message); 44 | }); 45 | }; 46 | 47 | export default connect; 48 | -------------------------------------------------------------------------------- /src/interfaces/ITopSpeaker.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document, Schema } from "mongoose"; 25 | 26 | interface IUpdatedBy { 27 | user: Schema.Types.ObjectId; 28 | updatedAt: Date; 29 | } 30 | 31 | interface ITopSpeaker extends Document { 32 | imageUrl?: string; 33 | title: string; 34 | description: string; 35 | socialMediaURLs: ITopSpeakerMedia; 36 | deletedAt?: Date; 37 | createdBy: Schema.Types.ObjectId; 38 | updatedBy: IUpdatedBy[]; 39 | deletedBy?: Schema.Types.ObjectId; 40 | } 41 | 42 | interface ITopSpeakerMedia { 43 | facebook: string; 44 | instagram: string; 45 | twitter: string; 46 | linkedIn: string; 47 | web: string; 48 | } 49 | 50 | export type { ITopSpeaker }; 51 | -------------------------------------------------------------------------------- /src/api/models/Email.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Tue Mar 01 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | import mongoose, { Schema } from "mongoose"; 24 | import { IEmail } from "../../interfaces/IEmail"; 25 | 26 | const EmailSchema = new Schema( 27 | { 28 | templateName: { type: String, required: true }, 29 | to: { type: String, required: true }, 30 | subject: { type: String, required: true }, 31 | status: { 32 | type: String, 33 | enum: ["WAITING", "IN-PROGRESS", "DELIVERED"], 34 | default: "WAITING", 35 | required: true, 36 | }, 37 | body: { type: Schema.Types.Mixed, required: true }, 38 | type: { 39 | type: String, 40 | enum: ["Application", "ContactUs", "Selected", "Interview"], 41 | required: true, 42 | }, 43 | }, 44 | { 45 | timestamps: true, 46 | } 47 | ); 48 | 49 | const EmailModel = mongoose.model("emails", EmailSchema); 50 | 51 | export default EmailModel; 52 | -------------------------------------------------------------------------------- /.github/workflows/master_mswebserver.yml: -------------------------------------------------------------------------------- 1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy 2 | # More GitHub Actions for Azure: https://github.com/Azure/actions 3 | 4 | name: Build and deploy container app to Azure Web App - mswebserver 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | runs-on: "ubuntu-latest" 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v1 21 | 22 | - name: Log in to registry 23 | uses: docker/login-action@v1 24 | with: 25 | registry: https://mscontainers.azurecr.io/ 26 | username: ${{ secrets.ACR_USERNAME }} 27 | password: ${{ secrets.ACR_PASSWORD }} 28 | 29 | - name: Build and push container image to registry 30 | uses: docker/build-push-action@v2 31 | with: 32 | push: true 33 | tags: mscontainers.azurecr.io/${{ secrets.ACR_USERNAME }}/mswebserver:${{ github.sha }} 34 | file: ./Dockerfile 35 | 36 | deploy: 37 | runs-on: ubuntu-latest 38 | needs: build 39 | environment: 40 | name: "Production" 41 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} 42 | 43 | steps: 44 | - name: Deploy to Azure Web App 45 | id: deploy-to-webapp 46 | uses: azure/webapps-deploy@v2 47 | with: 48 | app-name: "mswebserver" 49 | slot-name: "production" 50 | publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_PROD }} 51 | images: "mscontainers.azurecr.io/${{ secrets.ACR_USERNAME }}/mswebserver:${{ github.sha }}" 52 | -------------------------------------------------------------------------------- /.github/workflows/development_msdevwebserver.yml: -------------------------------------------------------------------------------- 1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy 2 | # More GitHub Actions for Azure: https://github.com/Azure/actions 3 | 4 | name: Build and deploy container app to Azure Web App - msdevwebserver 5 | 6 | on: 7 | push: 8 | branches: 9 | - development 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | runs-on: "ubuntu-latest" 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v1 21 | 22 | - name: Log in to registry 23 | uses: docker/login-action@v1 24 | with: 25 | registry: https://mscontainers.azurecr.io/ 26 | username: ${{ secrets.ACR_USERNAME }} 27 | password: ${{ secrets.ACR_PASSWORD }} 28 | 29 | - name: Build and push container image to registry 30 | uses: docker/build-push-action@v2 31 | with: 32 | push: true 33 | tags: mscontainers.azurecr.io/${{ secrets.ACR_USERNAME }}/mswebdevserver:${{ github.sha }} 34 | file: ./Dockerfile 35 | 36 | deploy: 37 | runs-on: ubuntu-latest 38 | needs: build 39 | environment: 40 | name: "Development" 41 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} 42 | 43 | steps: 44 | - name: Deploy to Azure Web App 45 | id: deploy-to-webapp 46 | uses: azure/webapps-deploy@v2 47 | with: 48 | app-name: "msdevwebserver" 49 | slot-name: "production" 50 | publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_DEV }} 51 | images: "mscontainers.azurecr.io/${{ secrets.ACR_USERNAME }}/mswebdevserver:${{ github.sha }}" 52 | -------------------------------------------------------------------------------- /src/api/models/ExecutiveBoard.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import { IExecutiveBoard } from "../../interfaces/IExecutiveBoard"; 26 | 27 | const ExecutiveBoardSchema = new Schema({ 28 | year: { type: String, required: true }, 29 | board: [{ type: mongoose.Schema.Types.ObjectId, ref: "boardmember" }], 30 | deletedAt: { type: Date, required: false, default: null }, 31 | createdBy: { type: Schema.Types.ObjectId, required: true, ref: "users" }, 32 | updatedBy: [ 33 | { 34 | user: { type: Schema.Types.ObjectId, required: false, ref: "users" }, 35 | updatedAt: { type: Date, required: false }, 36 | }, 37 | ], 38 | deletedBy: { 39 | type: Schema.Types.ObjectId, 40 | required: false, 41 | default: null, 42 | ref: "users", 43 | }, 44 | }); 45 | 46 | const ExecutiveBoardModel = mongoose.model("ececutiveboard", ExecutiveBoardSchema); 47 | 48 | export default ExecutiveBoardModel; 49 | -------------------------------------------------------------------------------- /src/interfaces/IConfig.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | interface IConfig { 25 | ip: string; 26 | port: string; 27 | environment: string; 28 | mongodb: { 29 | uri: string; 30 | }; 31 | auth: { 32 | secret: string; 33 | }; 34 | email: { 35 | host: string; 36 | port: string; 37 | secure: boolean; 38 | pool: boolean; 39 | secureConnection: boolean; 40 | auth: { 41 | user: string; 42 | pass: string; 43 | }; 44 | tls: { 45 | rejectUnauthorized: boolean; 46 | }; 47 | sendGrid: { 48 | user: string; 49 | apiKey: string; 50 | }; 51 | }; 52 | firebase: { 53 | projectId: string; 54 | clientEmail: string; 55 | privateKey: string; 56 | storageBucket: string; 57 | emailTemplateBucket: string; 58 | applicationImageBucket: string; 59 | bucketName: string; 60 | }; 61 | queue: { 62 | messageBrokerURL: string; 63 | exchangeName: string; 64 | emailQueue: string; 65 | emailService: string; 66 | }; 67 | } 68 | 69 | export type { IConfig }; 70 | -------------------------------------------------------------------------------- /src/util/image.handler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import StorageBucket from "../config/storage.config"; 25 | import logger from "./logger"; 26 | class ImageService { 27 | static uploadImage = async (file: any, folderName: string): Promise => { 28 | return new Promise((resolve, reject) => { 29 | const { buffer } = file; 30 | 31 | const blob = StorageBucket.file(`${folderName}/${this.generateImageName()}`); 32 | const blobStream = blob.createWriteStream({ 33 | resumable: false, 34 | gzip: true, 35 | }); 36 | 37 | blobStream 38 | .on("finish", () => { 39 | resolve(blob.name); 40 | }) 41 | .on("error", (error) => { 42 | logger.error(error.message); 43 | reject(`Upload Error: ${error.message}`); 44 | }) 45 | .end(buffer); 46 | }); 47 | }; 48 | 49 | static generateImageName = () => { 50 | const fileName = new Date().getTime() + "-" + Math.floor(Math.random() * 1000000 + 1) + ".jpg"; 51 | return fileName; 52 | }; 53 | } 54 | 55 | export default ImageService; 56 | -------------------------------------------------------------------------------- /src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { IUser, IUserModel, IUserRequest, ILastLoggedUser } from "./IUser"; 25 | import { ITopSpeaker } from "./ITopSpeaker"; 26 | import { IContact, IInquiryReply } from "./IContact"; 27 | import { IConfig } from "./IConfig"; 28 | import { IEvent, IUpdatedBy } from "./IEvent"; 29 | import { IWebinar } from "./IWebinar"; 30 | import { IExecutiveBoard } from "./IExecutiveBoard"; 31 | import { IBoardMember } from "./IBoardMember"; 32 | import { IApplication } from "./IApplication"; 33 | import { IInterview } from "./IInterview"; 34 | import { IOrganization } from "./IOrganization"; 35 | import { IMeeting, IMeetingRequest } from "./IMeeting"; 36 | 37 | export { 38 | IUser, 39 | IUserModel, 40 | IUserRequest, 41 | ILastLoggedUser, 42 | IContact, 43 | IConfig, 44 | IEvent, 45 | IUpdatedBy, 46 | IWebinar, 47 | IExecutiveBoard, 48 | IBoardMember, 49 | IApplication, 50 | ITopSpeaker, 51 | IInterview, 52 | IOrganization, 53 | IInquiryReply, 54 | IMeeting, 55 | IMeetingRequest, 56 | }; 57 | -------------------------------------------------------------------------------- /src/api/models/Organization.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import { IOrganization } from "../../interfaces"; 26 | 27 | const OrganizationSchema = new Schema( 28 | { 29 | name: { type: String, required: true, trim: true }, 30 | email: { type: String, required: true, trim: true }, 31 | university: { type: String, required: true, trim: true }, 32 | phoneNumber: { type: String, required: false, trim: true }, 33 | address: { type: String, required: true, trim: true }, 34 | website: { type: String, required: true, trim: true }, 35 | imagePath: { type: String, required: true, trim: true }, 36 | updatedBy: [ 37 | { 38 | user: { type: Schema.Types.ObjectId, required: false, ref: "users" }, 39 | updatedAt: { type: Date, required: false }, 40 | }, 41 | ], 42 | }, 43 | { 44 | timestamps: true, 45 | } 46 | ); 47 | 48 | const OrganizationModel = mongoose.model("organization", OrganizationSchema); 49 | 50 | export default OrganizationModel; 51 | -------------------------------------------------------------------------------- /src/api/models/Contact.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import validator from "validator"; 26 | import { IContact } from "../../interfaces"; 27 | 28 | const ContactSchema = new Schema( 29 | { 30 | name: { 31 | type: String, 32 | required: [true, "Name is required"], 33 | trim: true, 34 | }, 35 | email: { 36 | type: String, 37 | required: [true, "Email is required"], 38 | trim: true, 39 | validate(value: string) { 40 | if (!validator.isEmail(value)) { 41 | throw new Error("Email address is not valid"); 42 | } 43 | }, 44 | }, 45 | message: { 46 | type: String, 47 | required: [true, "Message is required"], 48 | trim: true, 49 | }, 50 | deletedAt: { type: Date, required: false, default: null }, 51 | replies: [ 52 | { 53 | type: String, 54 | required: false, 55 | }, 56 | ], 57 | }, 58 | { 59 | timestamps: true, 60 | } 61 | ); 62 | 63 | const ContactModel = mongoose.model("contacts", ContactSchema); 64 | 65 | export default ContactModel; 66 | -------------------------------------------------------------------------------- /src/api/models/BoardMember.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import { IBoardMember } from "../../interfaces/IBoardMember"; 26 | 27 | const BoardMemberSchema = new Schema({ 28 | name: { type: String, required: true }, 29 | position: { type: String, required: true }, 30 | imageUrl: { type: String, required: false, default: null }, 31 | socialMedia: { 32 | facebook: { type: String, required: true }, 33 | instagram: { type: String, required: true }, 34 | twitter: { type: String, required: true }, 35 | linkedIn: { type: String, required: true }, 36 | }, 37 | deletedAt: { type: Date, required: false, default: null }, 38 | createdBy: { type: Schema.Types.ObjectId, required: true, ref: "users" }, 39 | updatedBy: [ 40 | { 41 | user: { type: Schema.Types.ObjectId, required: false, ref: "users" }, 42 | updatedAt: { type: Date, required: false }, 43 | }, 44 | ], 45 | deletedBy: { 46 | type: Schema.Types.ObjectId, 47 | required: false, 48 | default: null, 49 | ref: "users", 50 | }, 51 | }); 52 | 53 | const BoardMemberModel = mongoose.model("boardmember", BoardMemberSchema); 54 | 55 | export default BoardMemberModel; 56 | -------------------------------------------------------------------------------- /src/util/queue.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import amqp, { Channel } from "amqplib"; 25 | import { configs } from "../config"; 26 | 27 | // Create a channel 28 | const createChannel = async () => { 29 | const connection = await amqp.connect(configs.queue.messageBrokerURL); 30 | const channel = await connection.createChannel(); 31 | await channel.assertQueue(configs.queue.emailQueue, { durable: false }); 32 | return channel; 33 | }; 34 | 35 | // Publish the messages 36 | const publishMessage = (channel: Channel, message: any) => { 37 | channel.sendToQueue(configs.queue.emailQueue, Buffer.from(JSON.stringify(message))); 38 | }; 39 | 40 | // Subscribe to messages 41 | const subscribeMessages = async (channel: Channel, service: any) => { 42 | const serviceQueue = await channel.assertQueue(configs.queue.emailQueue, { durable: false }); 43 | channel.consume(serviceQueue.queue, (data) => { 44 | if (data) { 45 | const queueItem = JSON.parse(JSON.parse(data.content.toString())); 46 | service.sendEmailWithTemplate(queueItem); 47 | channel.ack(data); // Send acknowledgement to queue after consume the message 48 | } 49 | }); 50 | }; 51 | 52 | export default { createChannel, publishMessage, subscribeMessages }; 53 | -------------------------------------------------------------------------------- /src/util/response.handler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Response } from "express"; 25 | 26 | const successRespond = (response: Response, status = 201) => { 27 | return function (data: any) { 28 | if (!data) { 29 | return response.status(204).json({ status: 204, message: "Not Found" }); 30 | } 31 | return response.status(status).json(data); 32 | }; 33 | }; 34 | 35 | const notFoundRespond = (response: Response) => { 36 | return function (data: any) { 37 | if (!data) { 38 | return response.status(204).json({ status: 204, message: "Not Found" }); 39 | } 40 | return response.status(404).json({ status: 404, details: "Not Found" }); 41 | }; 42 | }; 43 | 44 | const errorRespond = (response: Response) => { 45 | return function (error: any) { 46 | return response.status(400).json({ status: 400, details: error }); 47 | }; 48 | }; 49 | 50 | const unauthorizedRespond = (response: Response) => { 51 | return function (data: any) { 52 | if (!data) { 53 | return response.status(204).json({ status: 204, message: "Not Found" }); 54 | } 55 | return response.status(401).json({ details: data }); 56 | }; 57 | }; 58 | 59 | export default { successRespond, notFoundRespond, errorRespond, unauthorizedRespond }; 60 | -------------------------------------------------------------------------------- /src/api/models/Event.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import { IEvent } from "../../interfaces"; 26 | 27 | const EventSchema = new Schema( 28 | { 29 | title: { type: String, required: true }, 30 | description: { type: String, required: true }, 31 | imageUrl: { type: String, required: true }, 32 | dateTime: { type: Date, required: true }, 33 | tags: [{ type: String, required: false }], 34 | link: { type: String, required: false }, 35 | registrationLink: { type: String, required: false }, 36 | eventType: { type: String, enum: ["PAST", "UPCOMING"], required: true }, 37 | deletedAt: { type: Date, required: false, default: null }, 38 | createdBy: { type: Schema.Types.ObjectId, required: true, ref: "users" }, 39 | updatedBy: [ 40 | { 41 | user: { type: Schema.Types.ObjectId, required: false, ref: "users" }, 42 | updatedAt: { type: Date, required: false }, 43 | }, 44 | ], 45 | deletedBy: { 46 | type: Schema.Types.ObjectId, 47 | required: false, 48 | default: null, 49 | ref: "users", 50 | }, 51 | }, 52 | { timestamps: true } 53 | ); 54 | 55 | const EventModel = mongoose.model("event", EventSchema); 56 | 57 | export default EventModel; 58 | -------------------------------------------------------------------------------- /src/api/models/Webinar.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import { IWebinar } from "../../interfaces"; 26 | 27 | const WebinarSchema = new Schema( 28 | { 29 | title: { type: String, required: true }, 30 | description: { type: String, required: true }, 31 | imageUrl: { type: String, required: true }, 32 | dateTime: { type: Date, required: true }, 33 | tags: [{ type: String, required: false }], 34 | link: { type: String, required: true }, 35 | registrationLink: { type: String, required: false }, 36 | webinarType: { type: String, enum: ["PAST", "UPCOMING"], required: true }, 37 | deletedAt: { type: Date, required: false, default: null }, 38 | createdBy: { type: Schema.Types.ObjectId, required: true, ref: "users" }, 39 | updatedBy: [ 40 | { 41 | user: { type: Schema.Types.ObjectId, required: false, ref: "users" }, 42 | updatedAt: { type: Date, required: false }, 43 | }, 44 | ], 45 | deletedBy: { 46 | type: Schema.Types.ObjectId, 47 | required: false, 48 | default: null, 49 | ref: "users", 50 | }, 51 | }, 52 | { timestamps: true } 53 | ); 54 | 55 | const WebinarModel = mongoose.model("webinar", WebinarSchema); 56 | 57 | export default WebinarModel; 58 | -------------------------------------------------------------------------------- /src/api/models/TopSpeaker.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import { ITopSpeaker } from "../../interfaces"; 26 | 27 | const TopSpeakerSchema = new Schema( 28 | { 29 | imageUrl: { type: String, required: false, default: null }, 30 | title: { type: String, required: true }, 31 | description: { type: String, required: false }, 32 | socialMediaURLs: { 33 | facebook: { type: String, required: true }, 34 | instagram: { type: String, required: true }, 35 | twitter: { type: String, required: true }, 36 | linkedIn: { type: String, required: true }, 37 | web: { type: String, required: true }, 38 | }, 39 | deletedAt: { type: Date, required: false, default: null }, 40 | createdBy: { type: Schema.Types.ObjectId, required: true, ref: "users" }, 41 | updatedBy: [ 42 | { 43 | user: { type: Schema.Types.ObjectId, required: false, ref: "users" }, 44 | updatedAt: { type: Date, required: false }, 45 | }, 46 | ], 47 | deletedBy: { 48 | type: Schema.Types.ObjectId, 49 | required: false, 50 | default: null, 51 | ref: "users", 52 | }, 53 | }, 54 | { timestamps: true } 55 | ); 56 | 57 | const TopSpeakerModel = mongoose.model("topSpeaker", TopSpeakerSchema); 58 | 59 | export default TopSpeakerModel; 60 | -------------------------------------------------------------------------------- /src/interfaces/IUser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Document, Model, Schema } from "mongoose"; 25 | 26 | interface IUserRequest { 27 | firstName: string; 28 | lastName: string; 29 | email: string; 30 | phoneNumber01: string; 31 | phoneNumber02: string; 32 | userName: string; 33 | password: string; 34 | profileImage?: any; 35 | authToken?: string; 36 | persistedFaceId?: string; 37 | permissionLevel: string; 38 | } 39 | 40 | interface IUserDocument extends Document { 41 | firstName: string; 42 | lastName: string; 43 | email: string; 44 | phoneNumber01: string; 45 | phoneNumber02: string; 46 | userName: string; 47 | password: string; 48 | profileImage?: any; 49 | authToken?: string; 50 | persistedFaceId?: string; 51 | permissionLevel: string; 52 | deletedAt?: Date | null; 53 | deletedBy?: Schema.Types.ObjectId | null; 54 | } 55 | 56 | interface ILastLoggedUser extends Document { 57 | loggedAt: string | null; 58 | user: Schema.Types.ObjectId | null; 59 | } 60 | 61 | // Object level functions for the schema 62 | interface IUser extends IUserDocument { 63 | generateAuthToken(): string; 64 | } 65 | 66 | // Static functions for the schema 67 | interface IUserModel extends Model { 68 | // eslint-disable-next-line no-unused-vars 69 | findByUsernamePassword(userName: string, password: string): IUser; 70 | } 71 | 72 | export type { IUser, IUserModel, IUserRequest, ILastLoggedUser }; 73 | -------------------------------------------------------------------------------- /src/api/models/Application.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import { IApplication } from "../../interfaces"; 26 | 27 | const ApplicationSchema = new Schema( 28 | { 29 | studentId: { type: String, required: true }, 30 | name: { type: String, required: true }, 31 | email: { type: String, required: true }, 32 | contactNumber: { type: String, required: true }, 33 | currentAcademicYear: { type: String, required: true }, 34 | selfIntroduction: { type: String, required: true }, 35 | reasonForJoin: { type: String, required: true }, 36 | linkedIn: { type: String, required: true }, 37 | gitHub: { type: String, required: true }, 38 | blog: { type: String, required: false }, 39 | experiences: { type: String, required: false }, 40 | challenges: { type: String, required: false }, 41 | goal: { type: String, required: true }, 42 | skillsAndTalents: [{ type: String, required: true }], 43 | pastWork: { type: String, required: false }, 44 | deletedAt: { type: Date, required: false, default: null }, 45 | status: { 46 | type: String, 47 | enum: ["PENDING", "INTERVIEW", "SELECTED", "REJECTED"], 48 | required: false, 49 | default: "PENDING", 50 | }, 51 | meeting: { type: mongoose.Schema.Types.ObjectId, ref: "meeting" }, 52 | }, 53 | { timestamps: true } 54 | ); 55 | 56 | const ApplicationModel = mongoose.model("application", ApplicationSchema); 57 | 58 | export default ApplicationModel; 59 | -------------------------------------------------------------------------------- /src/api/middleware/Auth.middleware.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import jwt from "jsonwebtoken"; 26 | import logger from "../../util/logger"; 27 | import UserModel from "../models/User.model"; 28 | 29 | /** 30 | * User authentication middleware function 31 | * @param request 32 | * @param response 33 | * @param next 34 | * @returns user and authToken 35 | * 36 | * For every private API endpoint, this function will execute first to identify 37 | * the user in the system. If the authentication success, then only necessary 38 | * method will execute. 39 | */ 40 | export const authenticate = async (request: Request, response: Response, next: NextFunction) => { 41 | try { 42 | const secret = process.env.JWT_SECRET as string; 43 | 44 | if (secret) { 45 | const authToken = request.header("Authorization") as string; 46 | const decode = jwt.verify(authToken, secret); 47 | const user = await UserModel.findOne({ 48 | _id: decode as string, 49 | authToken: authToken, 50 | }); 51 | 52 | if (!user) { 53 | const useNotFoundResponse = JSON.stringify({ 54 | status: 401, 55 | message: "User not found in the system", 56 | }); 57 | throw new Error(useNotFoundResponse); 58 | } 59 | 60 | request.authToken = authToken; 61 | request.user = user; 62 | 63 | logger.info(`Authentication Token for ID ${user._id} is Accepted`); 64 | next(); 65 | } else { 66 | throw new Error("Token Secret is not found"); 67 | } 68 | } catch (error: any) { 69 | logger.warn(error.message); 70 | return request.handleResponse.unauthorizedRespond(response)(error.message); 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /src/api/validations/Application.validate.schema.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { object, string, array } from "yup"; 25 | 26 | const webURLRegEx = RegExp( 27 | // eslint-disable-next-line 28 | /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)/g 29 | ); 30 | 31 | const contactNumberRegEx = RegExp( 32 | //eslint-disable-next-line 33 | /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/ 34 | ); 35 | 36 | const applicationSchema = object({ 37 | studentId: string().length(10).required("SLIIT ID is required"), 38 | name: string().required("Name is required"), 39 | email: string().required("Email is required").email("Email is not valid"), 40 | contactNumber: string() 41 | .required("Phone number is required") 42 | .min(10) 43 | .max(10) 44 | .matches(contactNumberRegEx, "Invalid phone number"), 45 | currentAcademicYear: string().required("Academic year is required"), 46 | selfIntroduction: string().required("Self introduction is required"), 47 | reasonForJoin: string().required("Reason for join is required"), 48 | linkedIn: string().required("LinkedIn profile is required").matches(webURLRegEx, "Invalid link"), 49 | gitHub: string().required("GitHub profile is required").matches(webURLRegEx, "Invalid link"), 50 | blog: string().matches(webURLRegEx, "Invalid link"), 51 | experiences: string().max(1500, "Character count exceed: (Maximum: 1500)"), 52 | challenges: string().max(1500, "Character count exceed: (Maximum: 1500)"), 53 | goal: string().required("Goal is required"), 54 | skillsAndTalents: array().of(string()), 55 | pastWork: string().max(1500, "Character count exceed: (Maximum: 1500)"), 56 | }); 57 | 58 | export { applicationSchema }; 59 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | // eslint-disable-next-line @typescript-eslint/no-var-requires 25 | const dotenv = require("dotenv"); 26 | dotenv.config(); 27 | import express, { Express, Request, Response, NextFunction } from "express"; 28 | import cors from "cors"; 29 | import logger from "./util/logger"; 30 | import responseHandler from "./util/response.handler"; 31 | import routes from "./api/routes"; 32 | import { configs } from "./config"; 33 | import connect from "./util/database.connection"; 34 | import messageQueue from "./util/queue.config"; 35 | import { Channel } from "amqplib"; 36 | import { cronInit } from "./util/cron"; 37 | 38 | export const app: Express = express(); 39 | const PORT: string = configs.port; 40 | const ENVIRONMENT = configs.environment; 41 | const MONGO_URI = configs.mongodb.uri; 42 | let channel: Channel; 43 | 44 | messageQueue.createChannel().then((channelData) => { 45 | channel = channelData; 46 | }); 47 | 48 | // Register Middleware Chain 49 | app.use(cors()); 50 | app.use(express.json({ limit: "50mb" })); 51 | app.use(express.urlencoded({ extended: true })); 52 | 53 | // Inject Response Handler 54 | app.use((req: Request, res: Response, next: NextFunction) => { 55 | req.handleResponse = responseHandler; 56 | req.channel = channel; 57 | req.queue = messageQueue; 58 | next(); 59 | }); 60 | 61 | // Root API Call 62 | app.get("/", (req: Request, res: Response, next: NextFunction) => { 63 | res.send("

MS CLUB SLIIT - WEB API

"); 64 | next(); 65 | }); 66 | 67 | // Start the Server 68 | app.listen(PORT, () => { 69 | logger.info(`✨ Starting on ${ENVIRONMENT} Environment`); 70 | logger.info(`🔗 ${MONGO_URI}`); 71 | // Connect to Database 72 | connect(); 73 | // Inject Routes 74 | routes(app); 75 | cronInit(); 76 | logger.info(`🚀 API Server up and running on PORT ${PORT}`); 77 | }); 78 | -------------------------------------------------------------------------------- /typings/express/index.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | /* eslint-disable no-unused-vars */ 25 | import { Channel } from "amqplib"; 26 | declare global { 27 | namespace Express { 28 | interface Request { 29 | handleResponse: { 30 | successRespond(response: Response): (data: any) => Response; 31 | notFoundRespond(response: Response): (data: any) => Response; 32 | errorRespond(response: Response): (error: any) => Response; 33 | unauthorizedRespond(response: Response): (data: any) => Response; 34 | }; 35 | queue: { 36 | /** 37 | * Publish messages to the queue. This function wil take 3 parameters. 38 | * @param channel - RabbitMQ Channel 39 | * @param bindingKey - Binding key to bind the messages to the queue 40 | * @param message - Actual message data that add to the queue 41 | * @example 42 | * let channel = request.queue.channel; 43 | * let bindingKey = configs.queue.emailService; 44 | * let message = { 45 | * template: "Email-Template.html", 46 | * body: "Email template to queue" 47 | * }; 48 | * 49 | * request.queue.publishMessage(channel, bindingKey, message); 50 | */ 51 | publishMessage(channel: Channel, message: any): void; 52 | 53 | /** 54 | * Consume the messages that are published to the message queue. The function will take 3 parameters. 55 | * @param channel - RabbitMQ Channel 56 | * @param service - Class name of the service 57 | * @example 58 | * let channel = request.queue.channel; 59 | * let emailService = new EmailService(); 60 | * 61 | * request.queue.subscribeMessages(channel, emailService); 62 | */ 63 | subscribeMessages(channel: Channel, service: EmailService): Promise; 64 | }; 65 | authToken: any; 66 | user: any; 67 | channel: Channel; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "msclubwebserver", 3 | "version": "1.0.0", 4 | "description": "MS Club Web Site Server", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "NODE_ENV=Production node build/app.js", 8 | "local:server": "cross-env NODE_ENV=Local nodemon src/app.ts", 9 | "dev:server": "cross-env NODE_ENV=Development nodemon src/app.ts", 10 | "prod:server": "cross-env NODE_ENV=Production nodemon src/app.ts", 11 | "build": "tsc -p .", 12 | "build:docker": "docker build -t ghcr.io/ms-club-sliit/msclubwebserver .", 13 | "prepare": "husky install", 14 | "check-types": "tsc --pretty --noEmit", 15 | "check-format": "prettier --check .", 16 | "check-lint": "eslint . --ext ts --ext tsx --ext js", 17 | "format": "prettier --write ." 18 | }, 19 | "keywords": [ 20 | "nodejs", 21 | "typescript", 22 | "express" 23 | ], 24 | "author": "MS CLUB SLIIT", 25 | "license": "ISC", 26 | "dependencies": { 27 | "@google-cloud/storage": "^5.16.1", 28 | "@sendgrid/mail": "^7.6.0", 29 | "@types/bcrypt": "^5.0.0", 30 | "@types/bcryptjs": "^2.4.2", 31 | "@types/cors": "^2.8.12", 32 | "@types/cron": "^1.7.3", 33 | "@types/firebase": "^3.2.1", 34 | "@types/handlebars": "^4.1.0", 35 | "@types/imagemin": "^7.0.1", 36 | "@types/jsonwebtoken": "^8.5.6", 37 | "@types/mongoose": "^5.11.97", 38 | "@types/multer": "^1.4.7", 39 | "@types/nodemailer": "^6.4.4", 40 | "@types/sharp": "^0.29.4", 41 | "@types/uuid": "^8.3.3", 42 | "@types/validator": "^13.7.0", 43 | "@types/whatwg-url": "^8.2.1", 44 | "amqplib": "^0.8.0", 45 | "axios": "^0.24.0", 46 | "bcryptjs": "^2.4.3", 47 | "body-parser": "^1.19.0", 48 | "cors": "^2.8.5", 49 | "cron": "^1.8.2", 50 | "cross-fetch": "^3.1.4", 51 | "dayjs": "^1.10.7", 52 | "dotenv": "^10.0.0", 53 | "env-cmd": "^10.1.0", 54 | "express": "^4.17.1", 55 | "firebase": "^9.5.0", 56 | "imagemin": "^7.0.1", 57 | "is-jpg": "^3.0.0", 58 | "jsonwebtoken": "^8.5.1", 59 | "moment": "^2.29.1", 60 | "multer": "^1.4.3", 61 | "node-fetch": "^3.1.0", 62 | "nodemailer": "^6.7.2", 63 | "nodemailer-sendgrid": "^1.0.3", 64 | "pino": "^7.4.0", 65 | "pino-pretty": "^7.2.0", 66 | "sharp": "^0.29.3", 67 | "validator": "^13.7.0", 68 | "yup": "^0.32.11" 69 | }, 70 | "devDependencies": { 71 | "@types/amqplib": "^0.8.2", 72 | "@types/express": "^4.17.13", 73 | "@types/node": "^16.11.9", 74 | "@typescript-eslint/eslint-plugin": "^5.9.0", 75 | "@typescript-eslint/parser": "^5.9.0", 76 | "cross-env": "^7.0.3", 77 | "eslint": "^8.6.0", 78 | "eslint-config-prettier": "^8.3.0", 79 | "firebase-admin": "^10.0.0", 80 | "husky": "^7.0.0", 81 | "nodemon": "^2.0.15", 82 | "prettier": "^2.5.1", 83 | "pretty-quick": "^3.1.3", 84 | "ts-node": "^10.4.0", 85 | "typescript": "^4.5.2", 86 | "uuid": "^8.3.2" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/api/controllers/Organization.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import ImageService from "../../util/image.handler"; 26 | import OrganizationService from "../services"; 27 | 28 | export const insertOrganization = async (request: Request, response: Response, next: NextFunction) => { 29 | const bucketDirectoryName = "organization-images"; 30 | const organizationImagePath = await ImageService.uploadImage(request.file, bucketDirectoryName); 31 | request.body.imagePath = organizationImagePath; 32 | 33 | await OrganizationService.createOrganization(request.body, request.user._id) 34 | .then((data) => { 35 | request.handleResponse.successRespond(response)(data); 36 | next(); 37 | }) 38 | .catch((error: any) => { 39 | request.handleResponse.errorRespond(response)(error.message); 40 | next(); 41 | }); 42 | }; 43 | 44 | export const getOrganization = async (request: Request, response: Response, next: NextFunction) => { 45 | await OrganizationService.getOrganizationInfo() 46 | .then((data) => { 47 | request.handleResponse.successRespond(response)(data); 48 | next(); 49 | }) 50 | .catch((error: any) => { 51 | request.handleResponse.errorRespond(response)(error.message); 52 | next(); 53 | }); 54 | }; 55 | 56 | export const getOrganizationForAdmin = async (request: Request, response: Response, next: NextFunction) => { 57 | await OrganizationService.getOrganizationInfoForAdmin() 58 | .then((data) => { 59 | request.handleResponse.successRespond(response)(data); 60 | next(); 61 | }) 62 | .catch((error: any) => { 63 | request.handleResponse.errorRespond(response)(error.message); 64 | next(); 65 | }); 66 | }; 67 | 68 | export const updateOrganization = async (request: Request, response: Response, next: NextFunction) => { 69 | if (request.file) { 70 | const bucketDirectoryName = "organization-images"; 71 | const organizationImagePath = await ImageService.uploadImage(request.file, bucketDirectoryName); 72 | request.body.imagePath = organizationImagePath; 73 | } 74 | const updatedBy = request.user && request.user._id ? request.user._id : null; 75 | const organizationId = request.body.organizationId; 76 | 77 | await OrganizationService.updateOrganizationInfo(organizationId, request.body, updatedBy) 78 | .then((data) => { 79 | request.handleResponse.successRespond(response)(data); 80 | next(); 81 | }) 82 | .catch((error: any) => { 83 | request.handleResponse.errorRespond(response)(error.message); 84 | next(); 85 | }); 86 | }; 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MS Club Web Server 2 | 3 | Backend API application for MS Club of SLIIT 🌀
4 | Read further to know how **you** can start contributing to MS Club! 5 | 6 | ## Quick Links 7 | 8 | - [Tech Stack](#tech-stack) 9 | - [How to contribute](#how-to-contribute) 10 | - [Learning Resources](#how-can-i-get-start) 11 | - [About Docker Image](#about-docker-image) 12 | 13 | ## [Tech Stack](#Tech-stack) 14 | 15 | ![banner-whitegb-spacing-10x(2)](https://firebasestorage.googleapis.com/v0/b/msclubofsliit-v2.appspot.com/o/tech_stack_logo.png?alt=media&token=74635794-1141-411c-8b82-5e90f6113aae) 16 | 17 | ## [How to contribute](#how-to) 18 | 19 | 1. Fork the repo on GitHub :octocat:. 20 | 2. Clone the project to your own machine.
21 | 22 | ``` 23 | git clone https://github.com//msclubwebsite.git 24 | ``` 25 | 26 | 3. Create a branch using the git checkout command. Branch name prefix should be one of these.
27 | `feature/`
28 | `fix/`
29 | 30 | ``` 31 | git checkout -b 32 | ``` 33 | 34 | 4. Stage your changes and commit with a meaningful commit message. **First letter of the commit should be capital**
35 | 36 | ``` 37 | git add . 38 | ``` 39 | 40 | ``` 41 | git commit -m "" 42 | ``` 43 | 44 | 5. Push your work back up to your fork.
45 | 46 | ``` 47 | git push origin 48 | ``` 49 | 50 | 6. Submit a Pull request so that we can review your changes. [Learn about creating a pull request.](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) 51 | 52 | ## [How can I get start](#resources) 53 | 54 | We got your back. Here are some **FREE** resources for you to strengthen your web development skills and start firing some commits. 55 | 56 | **TypeScript** 57 | 58 | - [Official Documentation](https://www.typescriptlang.org/docs/) 59 | - [TypeScript For JS Developers](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) 60 | - [TypeScript Basics by freeCodeCamp](https://www.freecodecamp.org/news/learn-typescript-basics/) 61 | 62 | **Node JS / Express** 63 | 64 | - [Node JS 14.x Official Documentaion](https://nodejs.org/docs/latest-v14.x/api/) 65 | - [Express Official Documentation](https://expressjs.com/) 66 | 67 | ## [About Docker Image](#about-docker-image) 68 | 69 | We use Docker :whale: image to deploy this API application to the server. You can build and run the docker container in your local machine. Follow below steps to build and run the Docker container.
70 | :memo: In order to build and run the Docker container, you must have Docker install on you computer.
71 | 72 | 1. Build the Docker image. :building_construction: 73 | 74 | ``` 75 | docker build -t ghcr.io/ms-club-sliit/msclubwebserver:v1.0.0 . 76 | ``` 77 | 78 | 2. Check the build Docker images. :heavy_check_mark: 79 | 80 | ``` 81 | docker image ls 82 | ``` 83 | 84 | 3. Run the Docker container. After run this command the container will start running. :package:
85 | `docker run -p : -d --name "":`
86 | **`-d`** - Run in ditach mode
87 | **`-p`** - Port mapping between Docker container and application 88 | 89 | ``` 90 | docker run -p 9096:8078 -d --name "mswebserver" ghcr.io/ms-club-sliit/msclubwebserver:v1.0.0 91 | ``` 92 | 93 | 4. Open your web browser and paste below URL :earth_asia: 94 | 95 | ``` 96 | http://localhost:9096 97 | ``` 98 | 99 | 5. View the running container :eyes: 100 | 101 | ``` 102 | docker ps 103 | ``` 104 | 105 | 6. Stop the running container :stop_sign: 106 | 107 | ``` 108 | docker stop mswebserver 109 | ``` 110 | 111 | 7. Remove the Docker container :coffin: 112 | 113 | ``` 114 | docker rm mswebserver 115 | ``` 116 | -------------------------------------------------------------------------------- /src/api/services/Organization.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import OrganizationModel from "../models/Organization.model"; 25 | import { DocumentDefinition, Schema } from "mongoose"; 26 | import { IOrganization, IUpdatedBy } from "../../interfaces"; 27 | 28 | // Insert the organization information 29 | export const createOrganization = async ( 30 | organizationData: DocumentDefinition, 31 | user: Schema.Types.ObjectId 32 | ) => { 33 | return OrganizationModel.create(organizationData) 34 | .then(async (organization) => { 35 | const initialUpdatedBy: IUpdatedBy = { 36 | user: user, 37 | updatedAt: new Date(), 38 | }; 39 | organization.updatedBy.push(initialUpdatedBy); 40 | return await organization.save(); 41 | }) 42 | .catch((error) => { 43 | throw new Error(error.message); 44 | }); 45 | }; 46 | 47 | // Get organization information for dashboard 48 | export const getOrganizationInfo = async () => { 49 | return OrganizationModel.findOne() 50 | .select("name email phoneNumber address website university imagePath") 51 | .then((organization) => { 52 | return organization; 53 | }) 54 | .catch((error) => { 55 | throw new Error(error.message); 56 | }); 57 | }; 58 | 59 | // Get organization information for admin 60 | export const getOrganizationInfoForAdmin = async () => { 61 | return OrganizationModel.findOne() 62 | .populate({ 63 | path: "updatedBy", 64 | populate: { 65 | path: "user", 66 | select: "firstName lastName email permissionLevel profileImage", 67 | }, 68 | select: "updatedAt", 69 | }) 70 | .then((organization) => { 71 | return organization; 72 | }) 73 | .catch((error) => { 74 | throw new Error(error.message); 75 | }); 76 | }; 77 | 78 | // Update organization information 79 | export const updateOrganizationInfo = async ( 80 | organizationId: string, 81 | updateInfo: DocumentDefinition, 82 | user: Schema.Types.ObjectId 83 | ) => { 84 | if (organizationId) { 85 | return OrganizationModel.findById(organizationId) 86 | .then(async (organization) => { 87 | if (organization) { 88 | if (updateInfo.name) organization.name = updateInfo.name; 89 | if (updateInfo.email) organization.email = updateInfo.email; 90 | if (updateInfo.phoneNumber) organization.phoneNumber = updateInfo.phoneNumber; 91 | if (updateInfo.university) organization.university = updateInfo.university; 92 | if (updateInfo.address) organization.address = updateInfo.address; 93 | if (updateInfo.website) organization.website = updateInfo.website; 94 | if (updateInfo.imagePath) organization.imagePath = updateInfo.imagePath; 95 | 96 | const updateUserInfo: IUpdatedBy = { 97 | user: user, 98 | updatedAt: new Date(), 99 | }; 100 | organization.updatedBy.push(updateUserInfo); 101 | return await organization.save(); 102 | } 103 | }) 104 | .catch((error) => { 105 | throw new Error(error.message); 106 | }); 107 | } else { 108 | throw new Error("Organization ID not Passed"); 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /src/api/models/User.model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import mongoose, { Schema } from "mongoose"; 25 | import jwt from "jsonwebtoken"; 26 | import bcrypt from "bcryptjs"; 27 | import { IUserModel, IUser } from "../../interfaces"; 28 | import validator from "validator"; 29 | 30 | const UserSchema = new Schema( 31 | { 32 | firstName: { type: String, required: true }, 33 | lastName: { type: String, required: true }, 34 | phoneNumber01: { 35 | type: String, 36 | required: [true, "Phone number 1 is required"], 37 | trim: true, 38 | max: [10, "Phone number should have 10 numbers"], 39 | validate(value: string) { 40 | if (!validator.isMobilePhone(value)) { 41 | throw new Error("Phone number 1 is not valid"); 42 | } 43 | }, 44 | }, 45 | phoneNumber02: { 46 | type: String, 47 | required: false, 48 | trim: true, 49 | max: [10, "Phone number should have 10 numbers"], 50 | validate(value: string) { 51 | if (!validator.isMobilePhone(value) && value.length > 0) { 52 | throw new Error("Phone number 2 is not valid"); 53 | } 54 | }, 55 | }, 56 | email: { 57 | type: String, 58 | required: [true, "Email is required"], 59 | trim: true, 60 | unique: true, 61 | validate(value: string) { 62 | if (!validator.isEmail(value)) { 63 | throw new Error("Email address is not valid"); 64 | } 65 | }, 66 | }, 67 | userName: { type: String, required: false, unique: true }, 68 | password: { type: String, required: false }, 69 | profileImage: { type: String, required: false, default: null }, 70 | authToken: { type: String, required: false }, 71 | persistedFaceId: { type: String, required: false }, 72 | permissionLevel: { 73 | type: String, 74 | required: true, 75 | enum: ["ROOT_ADMIN", "ADMIN", "EDITOR", "VIEWER"], 76 | default: "EDITOR", 77 | }, 78 | deletedAt: { type: Date, required: false, default: null }, 79 | deletedBy: { type: Schema.Types.ObjectId, required: false, default: null }, 80 | }, 81 | { 82 | timestamps: true, 83 | } 84 | ); 85 | 86 | // Hash the user password 87 | UserSchema.pre("save", async function (next) { 88 | const user = this as IUser; 89 | const password: any = user.password; 90 | 91 | if (!user.isModified("password")) { 92 | return next(); 93 | } 94 | 95 | // Number of rounds hash function will execute 96 | const salt = await bcrypt.genSalt(10); 97 | 98 | const hash = await bcrypt.hashSync(password, salt); 99 | user.password = hash; 100 | return next(); 101 | }); 102 | 103 | UserSchema.methods.generateAuthToken = async function () { 104 | const user = this as IUser; 105 | const secret = process.env.JWT_SECRET as string; 106 | 107 | const authToken = jwt.sign({ _id: user._id }, secret); 108 | user.authToken = authToken; 109 | await user.save(); 110 | return authToken; 111 | }; 112 | 113 | UserSchema.statics.findByUsernamePassword = async (userName: string, password: string): Promise => { 114 | const user = await UserModel.findOne({ userName: userName }); 115 | 116 | if (!user) { 117 | throw new Error("User not found"); 118 | } 119 | 120 | const isPasswordMatch = await bcrypt.compare(password, user.password as string); 121 | 122 | if (!isPasswordMatch) { 123 | throw new Error("Password is incorrect"); 124 | } 125 | 126 | return user; 127 | }; 128 | 129 | const UserModel = mongoose.model("users", UserSchema); 130 | 131 | export default UserModel; 132 | -------------------------------------------------------------------------------- /src/api/controllers/BoardMember.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import BoardMemberService from "../services"; 26 | import ImageService from "../../util/image.handler"; 27 | 28 | /** 29 | * @param {Request} request - Request from the frontend 30 | * @param {Response} response - Response that need to send to the client 31 | * @param {NextFunction} next - Next function 32 | * @returns boardMember 33 | */ 34 | export const getBoardMemberbyID = async (request: Request, response: Response, next: NextFunction) => { 35 | const boardMemberId = request.params.boardMemberId; 36 | if (boardMemberId) { 37 | await BoardMemberService.getBoardMemberbyID(request.params.boardMemberId) 38 | .then((data) => { 39 | request.handleResponse.successRespond(response)(data); 40 | next(); 41 | }) 42 | .catch((error: any) => { 43 | request.handleResponse.errorRespond(response)(error.message); 44 | next(); 45 | }); 46 | } else { 47 | request.handleResponse.errorRespond(response)("Board Member ID not found"); 48 | } 49 | }; 50 | /** 51 | * @param {Request} request - Request from the frontend 52 | * @param {Response} response - Response that need to send to the client 53 | * @param {NextFunction} next - Next function 54 | * @returns boardMember[] 55 | */ 56 | export const getAllBoardMembers = async (request: Request, response: Response, next: NextFunction) => { 57 | await BoardMemberService.getAllBoardMembers() 58 | .then((data) => { 59 | request.handleResponse.successRespond(response)(data); 60 | next(); 61 | }) 62 | .catch((error: any) => { 63 | request.handleResponse.errorRespond(response)(error.message); 64 | next(); 65 | }); 66 | }; 67 | /** 68 | * @param {Request} request - Request from the frontend 69 | * @param {Response} response - Response that need to send to the client 70 | * @param {NextFunction} next - Next function 71 | * @returns updated boardMember 72 | */ 73 | export const updateBoardMemberDetails = async (request: Request, response: Response, next: NextFunction) => { 74 | if (request.file) { 75 | const bucketDirectoryName = "boardmember-flyers"; 76 | 77 | const boardMemberFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 78 | request.body.imageUrl = boardMemberFlyerPath; 79 | } 80 | const boardMemberId = request.params.boardMemberId; 81 | const updatedBy = request.user && request.user._id ? request.user._id : null; 82 | 83 | if (boardMemberId) { 84 | await BoardMemberService.updateBoardMemberDetails(request.params.boardMemberId, request.body, updatedBy) 85 | .then((data) => { 86 | request.handleResponse.successRespond(response)(data); 87 | next(); 88 | }) 89 | .catch((error: any) => { 90 | request.handleResponse.errorRespond(response)(error.message); 91 | next(); 92 | }); 93 | } else { 94 | request.handleResponse.errorRespond(response)("Board Member ID not found"); 95 | } 96 | }; 97 | /** 98 | * @param {Request} request - Request from the frontend 99 | * @param {Response} response - Response that need to send to the client 100 | * @param {NextFunction} next - Next function 101 | * @returns updated boardMember 102 | */ 103 | export const deleteBoardMemberDetails = async (request: Request, response: Response, next: NextFunction) => { 104 | const boardMemberId = request.params.boardMemberId; 105 | const deletedBy = request.user && request.user._id ? request.user._id : null; 106 | 107 | if (boardMemberId) { 108 | await BoardMemberService.deleteBoardMemberDetails(request.params.boardMemberId, deletedBy) 109 | .then((data) => { 110 | request.handleResponse.successRespond(response)(data); 111 | next(); 112 | }) 113 | .catch((error: any) => { 114 | request.handleResponse.errorRespond(response)(error.message); 115 | next(); 116 | }); 117 | } else { 118 | request.handleResponse.errorRespond(response)("Board Member ID not found"); 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /src/util/email.handler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import handlebars from "handlebars"; 25 | import fs from "fs"; 26 | import Email from "../api/models/Email.model"; 27 | import { EmailStatus } from "../api/services/Service.constant"; 28 | import logger from "./logger"; 29 | import { configs } from "../config"; 30 | import moment from "moment"; 31 | import fetch from "cross-fetch"; 32 | import sgMail from "@sendgrid/mail"; 33 | 34 | // HTML Configuration 35 | require.extensions[".html"] = (module: any, fileName: string) => { 36 | module.exports = fs.readFileSync(fileName, "utf8"); 37 | }; 38 | 39 | let template: HandlebarsTemplateDelegate; 40 | let htmlToSend: string; 41 | 42 | const sendEmailWithTemplate = async () => { 43 | logger.info(`#### Step 00 - Starting the Email Queue Items at ${new Date().getMinutes()}`); 44 | logger.info("#### Step 01 - Fetch the email that in the WAITING & IN-PROGRESS state"); 45 | const email = await Email.findOne({ $or: [{ status: EmailStatus.Waiting }, { status: EmailStatus.InProgress }] }); 46 | 47 | if (email && email._id) { 48 | // Change the status from "WAITING" -> "IN-PROGRESS"; 49 | logger.info("#### Step 02 - Change the status from 'WAITING' -> 'IN-PROGRESS'"); 50 | await Email.findByIdAndUpdate(email._id, { status: EmailStatus.InProgress }); 51 | 52 | logger.info("#### Step 03 - Get the Email template"); 53 | const emailTemplate = await getEmailTemplatePath(email.templateName); 54 | logger.info("#### Step 04 - Compile the Email template"); 55 | template = handlebars.compile(emailTemplate); 56 | htmlToSend = template(email.body); 57 | 58 | logger.info("#### Step 04 - Send the Email Notification"); 59 | retry( 60 | 5, // Retry count 61 | async () => { 62 | try { 63 | await sendEmail(email.to, email.subject, htmlToSend); 64 | logger.info("#### Step 06 - Change the status from 'IN-PROGRESS' -> 'DELIVERED'"); 65 | await Email.findByIdAndUpdate(email._id, { status: EmailStatus.Delivered }); 66 | } catch (error: any) { 67 | logger.error(error.message); 68 | } 69 | }, 70 | "sendEmail" 71 | ); 72 | } else { 73 | logger.info(`#### Step 07 - Email Queue is empty at ${new Date().getMinutes()}`); 74 | } 75 | }; 76 | 77 | const getEmailTemplatePath = async (fileName: string) => { 78 | const emailBucketLink = `${configs.firebase.storageBucket}/${configs.firebase.bucketName}/${configs.firebase.emailTemplateBucket}`; 79 | const templatePath = (await fetch(`${emailBucketLink}/${fileName}`)).text(); 80 | 81 | return templatePath; 82 | }; 83 | 84 | const sendEmail = (to: string, subject: string, htmlTemplate: any) => { 85 | return new Promise((resolve, reject) => { 86 | sgMail.setApiKey(configs.email.sendGrid.apiKey); 87 | const msg = { 88 | to: to, // Change to your recipient 89 | from: { name: "MS Club of SLIIT", email: configs.email.sendGrid.user }, // Change to your verified sender 90 | cc: "msclubofsliit@gmail.com", 91 | subject: subject, 92 | text: htmlTemplate, 93 | html: htmlTemplate, 94 | }; 95 | sgMail 96 | .send(msg) 97 | .then((responseData: any) => { 98 | logger.info(`#### Step 05 - Email sent to ${to}`); 99 | return resolve(responseData); 100 | }) 101 | .catch((error: any) => { 102 | logger.error("Send Email Error: " + error); 103 | return reject(error); 104 | }); 105 | }); 106 | }; 107 | 108 | const retry = (maxRetries: number, retryFunction: any, retryFunctionName: string) => { 109 | logger.info("#### Retry Count: " + maxRetries); 110 | 111 | return retryFunction().catch(() => { 112 | if (maxRetries <= 0) { 113 | const RetryFailedDateAndTime = moment().utcOffset("+05.30").format("MMMM Do YYYY, h:mm:ss a"); 114 | logger.error(RetryFailedDateAndTime); 115 | /** 116 | * @todo - to send Email to the system admin about the failure 117 | */ 118 | } 119 | 120 | return retry(maxRetries - 1, retryFunction, retryFunctionName); 121 | }); 122 | }; 123 | 124 | export { sendEmailWithTemplate }; 125 | -------------------------------------------------------------------------------- /src/api/controllers/Meeting.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from "express"; 2 | import * as MeetingService from "../services/Meeting.service"; 3 | import { MeetProvider } from "../enums/MeetProvider"; 4 | 5 | export const scheduleInternalMeeting = async (request: Request, response: Response, next: NextFunction) => { 6 | try{ 7 | 8 | let provider = request.query.meetProvider as MeetProvider; 9 | let data; 10 | 11 | switch(provider) { 12 | case MeetProvider.GOOGLEMEET: 13 | data = await MeetingService.scheduleInternalGoogleMeeting(request.body); 14 | break; 15 | 16 | case MeetProvider.MSMEET: 17 | data = await MeetingService.scheduleInternalMeetingMSTeams(request.body); 18 | break; 19 | 20 | default: 21 | throw new Error("Invalid request. is meetProvider set?"); 22 | } 23 | 24 | request.handleResponse.successRespond(response)(data); 25 | }catch(error: any){ 26 | console.error(error) 27 | request.handleResponse.errorRespond(response)(error.message); 28 | } 29 | 30 | next(); 31 | }; 32 | 33 | export const getAllInternalMeetings = async (request: Request, response: Response, next: NextFunction) => { 34 | await MeetingService.getAllInternalMeetingsMSTeams() 35 | .then((data) => { 36 | request.handleResponse.successRespond(response)(data); 37 | next(); 38 | }) 39 | .catch((error: any) => { 40 | request.handleResponse.errorRespond(response)(error.message); 41 | next(); 42 | }); 43 | }; 44 | 45 | export const deleteMeeting = async (request: Request, response: Response, next: NextFunction) => { 46 | const meetingId = request.params.meetingId; 47 | const deletedBy = request.user && request.user._id ? request.user._id : null; 48 | 49 | if (meetingId) { 50 | await MeetingService.deleteMeeting(meetingId, deletedBy) 51 | .then((data: any) => { 52 | request.handleResponse.successRespond(response)(data); 53 | next(); 54 | }) 55 | .catch((error: any) => { 56 | request.handleResponse.errorRespond(response)(error.message); 57 | next(); 58 | }); 59 | } else { 60 | request.handleResponse.errorRespond(response)("Meeting ID not found"); 61 | } 62 | 63 | 64 | }; 65 | 66 | export const getInternalMeetingById = async (request: Request, response: Response, next: NextFunction) => { 67 | const meetingId = request.params.meetingId; 68 | if (meetingId) { 69 | await MeetingService.fetchMeetingById(request.params.meetingId) 70 | .then((data) => { 71 | request.handleResponse.successRespond(response)(data); 72 | next(); 73 | }) 74 | .catch((error: any) => { 75 | request.handleResponse.errorRespond(response)(error.message); 76 | next(); 77 | }); 78 | } else { 79 | request.handleResponse.errorRespond(response)("MeetingId not found"); 80 | } 81 | }; 82 | 83 | export const updateMeeting = async (request: Request, response: Response, next: NextFunction) => { 84 | const meetingId = request.params.meetingId; 85 | const updatedBy = request.user && request.user._id ? request.user._id : null; 86 | 87 | if (meetingId) { 88 | await MeetingService.updateMeeting(meetingId, request.body, updatedBy) 89 | .then((data: any) => { 90 | request.handleResponse.successRespond(response)(data); 91 | next(); 92 | }) 93 | .catch((error: any) => { 94 | request.handleResponse.errorRespond(response)(error.message); 95 | next(); 96 | }); 97 | } else { 98 | request.handleResponse.errorRespond(response)("MeetingId not found"); 99 | } 100 | }; 101 | 102 | export const deleteMeetingPermanently = async (request: Request, response: Response, next: NextFunction) => { 103 | try{ 104 | const meetingId = request.params.meetingId; 105 | 106 | if(!meetingId) { 107 | request.handleResponse.errorRespond(response)("MeetingId not found"); 108 | return; 109 | } 110 | 111 | let data = await MeetingService.deleteMeetingPermanently(meetingId); 112 | 113 | request.handleResponse.successRespond(response)(data); 114 | }catch(error : any){ 115 | console.error(error); 116 | request.handleResponse.errorRespond(response)(error.message); 117 | } 118 | 119 | next(); 120 | }; 121 | 122 | export const scheduleInterviewMeeting = async (request: Request, response: Response, next: NextFunction) => { 123 | try{ 124 | let provider = request.query.meetProvider as MeetProvider; 125 | let data; 126 | 127 | switch(provider) { 128 | case MeetProvider.GOOGLEMEET: 129 | data = await MeetingService.scheduleInterviewGoogleMeeting(request.body); 130 | break; 131 | 132 | case MeetProvider.MSMEET: 133 | data = await MeetingService.scheduleInterviewMeetingMSTeams(request.body); 134 | break; 135 | 136 | default: 137 | throw new Error("Invalid request. is meetProvider set?"); 138 | } 139 | 140 | request.handleResponse.successRespond(response)(data); 141 | }catch(error : any){ 142 | request.handleResponse.errorRespond(response)(error.message); 143 | } 144 | 145 | next(); 146 | }; -------------------------------------------------------------------------------- /src/api/services/BoardMember.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { DocumentDefinition, Schema } from "mongoose"; 25 | import { IBoardMember, IUpdatedBy } from "../../interfaces"; 26 | import BoardMemberModel from "../models/BoardMember.model"; 27 | 28 | /** 29 | add a new Board Member to the database 30 | */ 31 | export const insertBoardMember = async (BoardMemberData: DocumentDefinition) => { 32 | return await BoardMemberModel.create(BoardMemberData) 33 | .then(async (boardMember) => { 34 | const initialUpdatedBy: IUpdatedBy = { 35 | user: boardMember.createdBy, 36 | updatedAt: new Date(), 37 | }; 38 | boardMember.updatedBy.push(initialUpdatedBy); 39 | await boardMember.save(); 40 | return boardMember; 41 | }) 42 | .catch((error) => { 43 | throw new Error(error.message); 44 | }); 45 | }; 46 | /** 47 | get the Board Memberby ID from the database 48 | * @param boardMemberId @type string 49 | */ 50 | export const getBoardMemberbyID = async (boardMemberId: string) => { 51 | return await BoardMemberModel.findById(boardMemberId) 52 | .then(async (boardMember) => { 53 | return boardMember; 54 | }) 55 | .catch((error) => { 56 | throw new Error(error.message); 57 | }); 58 | }; 59 | /** 60 | get all Board Memberbys from the database 61 | */ 62 | export const getAllBoardMembers = async () => { 63 | return await BoardMemberModel.find() 64 | .then(async (boardMembers) => { 65 | return boardMembers; 66 | }) 67 | .catch((error) => { 68 | throw new Error(error.message); 69 | }); 70 | }; 71 | /** 72 | update details of the member 73 | * @param boardMemberId @type string 74 | * @param updateData @type DocumentDefinition 75 | */ 76 | 77 | export const updateBoardMemberDetails = async ( 78 | boardMemberId: string, 79 | updateData: DocumentDefinition, 80 | updatedBy: Schema.Types.ObjectId 81 | ) => { 82 | return await BoardMemberModel.findById(boardMemberId) 83 | .then(async (boardMemberDetails) => { 84 | if (boardMemberDetails) { 85 | if (!boardMemberDetails.deletedAt) { 86 | if (updateData.name) { 87 | boardMemberDetails.name = updateData.name; 88 | } 89 | if (updateData.position) { 90 | boardMemberDetails.position = updateData.position; 91 | } 92 | if (updateData.imageUrl) { 93 | boardMemberDetails.imageUrl = updateData.imageUrl; 94 | } 95 | if (updateData.socialMedia) { 96 | if (updateData.socialMedia.facebook) { 97 | boardMemberDetails.socialMedia.facebook = updateData.socialMedia.facebook; 98 | } 99 | if (updateData.socialMedia.instagram) { 100 | boardMemberDetails.socialMedia.instagram = updateData.socialMedia.instagram; 101 | } 102 | if (updateData.socialMedia.linkedIn) { 103 | boardMemberDetails.socialMedia.linkedIn = updateData.socialMedia.linkedIn; 104 | } 105 | if (updateData.socialMedia.twitter) { 106 | boardMemberDetails.socialMedia.twitter = updateData.socialMedia.twitter; 107 | } 108 | } 109 | const updateUserInfo: IUpdatedBy = { 110 | user: updatedBy, 111 | updatedAt: new Date(), 112 | }; 113 | 114 | boardMemberDetails.updatedBy.push(updateUserInfo); 115 | return await boardMemberDetails.save(); 116 | } else { 117 | throw new Error("Board Member is not found"); 118 | } 119 | } else { 120 | return null; 121 | } 122 | }) 123 | .catch((error) => { 124 | throw new Error(error.message); 125 | }); 126 | }; 127 | 128 | /** 129 | delete member 130 | * @param boardMemberId @type string 131 | */ 132 | export const deleteBoardMemberDetails = async (boardMemberId: string, deletedBy: Schema.Types.ObjectId) => { 133 | return await BoardMemberModel.findById(boardMemberId) 134 | .then(async (boardMemberDetails) => { 135 | if (boardMemberDetails && boardMemberDetails.deletedAt === null) { 136 | boardMemberDetails.deletedAt = new Date(); 137 | boardMemberDetails.deletedBy = deletedBy; 138 | return await boardMemberDetails.save(); 139 | } else { 140 | return null; 141 | } 142 | }) 143 | .catch((error) => { 144 | throw new Error(error.message); 145 | }); 146 | }; 147 | -------------------------------------------------------------------------------- /src/api/controllers/Contact.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import { IContact } from "../../interfaces"; 26 | import ContactService from "../services"; 27 | 28 | /** 29 | * @param {Request} request - Request from the frontend 30 | * @param {Response} response - Response that need to send to the client 31 | * @param {NextFunction} next - Next function 32 | * @returns {IContact} Contact document 33 | */ 34 | export const createContact = async (request: Request, response: Response, next: NextFunction) => { 35 | await ContactService.insertContact(request, request.body) 36 | .then((data) => { 37 | request.handleResponse.successRespond(response)(data); 38 | next(); 39 | }) 40 | .catch((error: any) => { 41 | request.handleResponse.errorRespond(response)(error.message); 42 | next(); 43 | }); 44 | }; 45 | 46 | /** 47 | * @param {Request} request - Request from the frontend 48 | * @param {Response} response - Response that need to send to the client 49 | * @param {NextFunction} next - Next function 50 | * @returns {IContact[]} Contacts 51 | */ 52 | export const getAllContacts = async (request: Request, response: Response, next: NextFunction) => { 53 | await ContactService.fetchContactInfo() 54 | .then((contacts) => { 55 | request.handleResponse.successRespond(response)(contacts); 56 | }) 57 | .catch((error) => { 58 | request.handleResponse.errorRespond(response)(error.message); 59 | next(); 60 | }); 61 | }; 62 | 63 | /** 64 | * @param {Request} request - Request from the frontend 65 | * @param {Response} response - Response that need to send to the client 66 | * @param {NextFunction} next - Next function 67 | * @returns {IContact[]} Removed contact information 68 | */ 69 | export const removeContact = async (request: Request, response: Response, next: NextFunction) => { 70 | await ContactService.archiveContact(request.params.contactId) 71 | .then((deletedContactData) => { 72 | request.handleResponse.successRespond(response)(deletedContactData); 73 | }) 74 | .catch((error) => { 75 | request.handleResponse.errorRespond(response)(error.message); 76 | next(); 77 | }); 78 | }; 79 | 80 | export const removedContacts = async (request: Request, response: Response, next: NextFunction) => { 81 | await ContactService.getArchivedContacts() 82 | .then((data: IContact[]) => { 83 | request.handleResponse.successRespond(response)(data); 84 | }) 85 | .catch((error) => { 86 | request.handleResponse.errorRespond(response)(error.message); 87 | next(); 88 | }); 89 | }; 90 | 91 | /** 92 | * @param {Request} request - Request from the frontend 93 | * @param {Response} response - Response that need to send to the client 94 | * @param {NextFunction} next - Next function 95 | * @returns {IContact[]} Removed contact information 96 | */ 97 | export const removeContactPermanently = async (request: Request, response: Response, next: NextFunction) => { 98 | await ContactService.deleteContactPermanently(request.params.contactId) 99 | .then((data) => { 100 | request.handleResponse.successRespond(response)(data); 101 | }) 102 | .catch((error) => { 103 | request.handleResponse.errorRespond(response)(error.message); 104 | next(); 105 | }); 106 | }; 107 | 108 | export const recoverRemovedInquiry = async (request: Request, response: Response, next: NextFunction) => { 109 | const inquiryId = request.params.inquiryId; 110 | if (inquiryId) { 111 | await ContactService.recoverDeletedInquiry(inquiryId) 112 | .then((data) => { 113 | request.handleResponse.successRespond(response)(data); 114 | next(); 115 | }) 116 | .catch((error) => { 117 | request.handleResponse.errorRespond(response)(error.message); 118 | next(); 119 | }); 120 | } else { 121 | request.handleResponse.errorRespond(response)("Inquiry ID not found"); 122 | } 123 | }; 124 | 125 | export const replyInquiry = async (request: Request, response: Response, next: NextFunction) => { 126 | await ContactService.replyInquiry(request, request.params.inquiryId, request.body) 127 | .then((data) => { 128 | request.handleResponse.successRespond(response)(data); 129 | next(); 130 | }) 131 | .catch((error: any) => { 132 | request.handleResponse.errorRespond(response)(error.message); 133 | next(); 134 | }); 135 | }; 136 | -------------------------------------------------------------------------------- /src/api/services/Contact.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request } from "express"; 25 | import { DocumentDefinition } from "mongoose"; 26 | import { IContact, IInquiryReply } from "../../interfaces"; 27 | import { EmailTemplate, EmailType, EmailStatus } from "./Service.constant"; 28 | import ContactModel from "../models/Contact.model"; 29 | import moment from "moment"; 30 | import EmailModel from "../models/Email.model"; 31 | 32 | /** 33 | * @param {IContact} contactData 34 | * @returns {IContact} Contact data 35 | */ 36 | export const insertContact = async (request: Request, contactData: DocumentDefinition) => { 37 | return await ContactModel.create(contactData) 38 | .then(async (data) => { 39 | const email = { 40 | templateName: EmailTemplate.ContactUs, 41 | to: data.email, 42 | subject: "MS Club SLIIT - Contact Us", 43 | body: { 44 | name: data.name, 45 | email: data.email, 46 | message: data.message, 47 | date_time: moment(data.createdAt).format("LLL"), 48 | }, 49 | status: EmailStatus.Waiting, 50 | type: EmailType.ContactUs, 51 | }; 52 | 53 | await EmailModel.create(email); 54 | return data; 55 | }) 56 | .catch((error) => { 57 | throw new Error(error.message); 58 | }); 59 | }; 60 | 61 | /** 62 | * @param {string} contactId 63 | * @returns {IContact} Updated contact data 64 | */ 65 | export const archiveContact = async (contactId: string) => { 66 | return await ContactModel.findById(contactId) 67 | .then(async (contactData) => { 68 | if (contactData && contactData.deletedAt === null) { 69 | contactData.deletedAt = new Date(); 70 | return await contactData.save(); 71 | } else { 72 | return "Contact not found"; 73 | } 74 | }) 75 | .catch((error) => { 76 | throw new Error(error.message); 77 | }); 78 | }; 79 | 80 | /** 81 | * @returns {IContact[]} All available contacts 82 | */ 83 | export const fetchContactInfo = async () => { 84 | return await ContactModel.aggregate([{ $match: { deletedAt: { $eq: null } } }]) 85 | .sort({ createdAt: -1 }) 86 | .then((contacts) => { 87 | return contacts; 88 | }) 89 | .catch((error) => { 90 | throw new Error(error.message); 91 | }); 92 | }; 93 | 94 | /** 95 | Get deleted inquiries - admin 96 | */ 97 | export const getArchivedContacts = async () => { 98 | return await ContactModel.aggregate([{ $match: { deletedAt: { $ne: null } } }]) 99 | .then((contacts) => { 100 | return contacts; 101 | }) 102 | .catch((error) => { 103 | throw new Error(error.message); 104 | }); 105 | }; 106 | 107 | /** 108 | * Delete Contact Permenently 109 | @returns {IContact[]} Deleted Contact Details 110 | */ 111 | export const deleteContactPermanently = async (contactId: string) => { 112 | if (contactId) { 113 | return ContactModel.findByIdAndDelete(contactId) 114 | .then((contacts) => { 115 | if (contacts != null) return contacts; 116 | else return "Contact Not Found"; 117 | }) 118 | .catch((error) => { 119 | throw new Error(error.message); 120 | }); 121 | } else { 122 | throw new Error("Contact ID not Found"); 123 | } 124 | }; 125 | 126 | // Recover deleted inquiries 127 | export const recoverDeletedInquiry = async (inquiryId: string) => { 128 | if (inquiryId) { 129 | return await ContactModel.findById(inquiryId) 130 | .then(async (inquiryDetails) => { 131 | if (inquiryDetails) { 132 | if (inquiryDetails.deletedAt !== null) { 133 | inquiryDetails.deletedAt = null; 134 | return await inquiryDetails.save(); 135 | } else { 136 | return "Inquiry is already recovered"; 137 | } 138 | } 139 | }) 140 | .catch((error) => { 141 | throw new Error(error.message); 142 | }); 143 | } else { 144 | throw new Error("Inquiry ID not Passed"); 145 | } 146 | }; 147 | 148 | export const replyInquiry = async ( 149 | request: Request, 150 | inquiryId: string, 151 | replyData: DocumentDefinition 152 | ) => { 153 | return await ContactModel.findById(inquiryId) 154 | .then(async (data) => { 155 | if (data) { 156 | const to = data.email; 157 | const subject = "MS Club of SLIIT"; 158 | 159 | const email = { 160 | to: to, 161 | subject: subject, 162 | body: replyData, 163 | }; 164 | 165 | const channel = request.channel; 166 | request.queue.publishMessage(channel, JSON.stringify(email)); 167 | 168 | data.replies.push(replyData.reply); 169 | return await data.save(); 170 | } else { 171 | return null; 172 | } 173 | }) 174 | .catch((error) => { 175 | throw new Error(error.message); 176 | }); 177 | }; 178 | -------------------------------------------------------------------------------- /src/api/services/ExecutiveBoard.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { DocumentDefinition, Schema } from "mongoose"; 25 | import { IExecutiveBoard, IBoardMember, IUpdatedBy } from "../../interfaces"; 26 | import ExecutiveBoardModel from "../models/ExecutiveBoard.model"; 27 | import { insertBoardMember } from "../services/BoardMember.service"; 28 | 29 | /** 30 | add a new executive board to the database 31 | */ 32 | export const insertExecutiveBoard = async (executiveBoardData: DocumentDefinition) => { 33 | return await ExecutiveBoardModel.create(executiveBoardData) 34 | .then(async (executiveBoard) => { 35 | const initialUpdatedBy: IUpdatedBy = { 36 | user: executiveBoard.createdBy, 37 | updatedAt: new Date(), 38 | }; 39 | executiveBoard.updatedBy.push(initialUpdatedBy); 40 | await executiveBoard.save(); 41 | return executiveBoard; 42 | }) 43 | .catch((error) => { 44 | throw new Error(error.message); 45 | }); 46 | }; 47 | /** 48 | get the executive board by ID from the database (the details of the existing board members should be populated) 49 | * @param executiveBoardId @type string 50 | */ 51 | export const getExecutiveBoardbyID = async (executiveBoardId: string) => { 52 | return await ExecutiveBoardModel.findById(executiveBoardId) 53 | .populate({ path: "board", match: { deletedAt: null } }) 54 | .then(async (executiveBoard) => { 55 | return executiveBoard; 56 | }) 57 | .catch((error) => { 58 | throw new Error(error.message); 59 | }); 60 | }; 61 | /** 62 | get all the executive boards from the database (the details of the existing board members should be populated) 63 | */ 64 | export const getExecutiveBoard = async () => { 65 | return await ExecutiveBoardModel.find({ deletedAt: null }) 66 | .populate({ 67 | path: "board", 68 | match: { deletedAt: null }, 69 | }) 70 | .then(async (executiveBoards) => { 71 | return executiveBoards; 72 | }) 73 | .catch((error) => { 74 | throw new Error(error.message); 75 | }); 76 | }; 77 | /** 78 | add members to executiveboard 79 | * @param boardId @type string 80 | * @param insertData @type DocumentDefinition 81 | */ 82 | export const addBoardMember = async ( 83 | executiveBoardId: string, 84 | insertData: DocumentDefinition, 85 | updatedBy: Schema.Types.ObjectId 86 | ) => { 87 | return await insertBoardMember(insertData) 88 | .then(async (createdBoardMember: IBoardMember) => { 89 | const executiveBoard = await ExecutiveBoardModel.findById(executiveBoardId); 90 | if (executiveBoard) { 91 | executiveBoard.board.unshift(createdBoardMember); 92 | const updateUserInfo: IUpdatedBy = { 93 | user: updatedBy, 94 | updatedAt: new Date(), 95 | }; 96 | executiveBoard.updatedBy.push(updateUserInfo); 97 | return await executiveBoard.save(); 98 | } else { 99 | return null; 100 | } 101 | }) 102 | .catch((error) => { 103 | throw new Error(error.message); 104 | }); 105 | }; 106 | 107 | /** 108 | update details of members in the executiveboard 109 | * @param boardId @type string 110 | * @param updateData @type DocumentDefinition 111 | */ 112 | export const updateExecutiveBoardDetails = async ( 113 | boardId: string, 114 | updateData: DocumentDefinition, 115 | updatedBy: Schema.Types.ObjectId 116 | ) => { 117 | return await ExecutiveBoardModel.findById(boardId) 118 | .then(async (executiveBoardDetails) => { 119 | if (executiveBoardDetails) { 120 | executiveBoardDetails.year = updateData.year; 121 | const updateUserInfo: IUpdatedBy = { 122 | user: updatedBy, 123 | updatedAt: new Date(), 124 | }; 125 | 126 | executiveBoardDetails.updatedBy.push(updateUserInfo); 127 | return await executiveBoardDetails.save(); 128 | } else { 129 | return null; 130 | } 131 | }) 132 | .catch((error) => { 133 | throw new Error(error.message); 134 | }); 135 | }; 136 | /** 137 | delete members from executiveboard 138 | * @param boardId @type string 139 | * @param boardMemberId @type string 140 | */ 141 | export const deleteExecutiveBoardDetails = async (boardId: string, deletedBy: Schema.Types.ObjectId) => { 142 | return await ExecutiveBoardModel.findById(boardId) 143 | .then(async (executiveBoardDetails) => { 144 | if (executiveBoardDetails && executiveBoardDetails.deletedAt === null) { 145 | executiveBoardDetails.deletedAt = new Date(); 146 | executiveBoardDetails.deletedBy = deletedBy; 147 | return await executiveBoardDetails.save(); 148 | } else { 149 | return null; 150 | } 151 | }) 152 | .catch((error) => { 153 | throw new Error(error.message); 154 | }); 155 | }; 156 | -------------------------------------------------------------------------------- /src/api/controllers/ExecutiveBoard.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import ExecutiveBoardService from "../services"; 26 | import ImageService from "../../util/image.handler"; 27 | 28 | /** 29 | * @param request 30 | * @param response 31 | * @param next 32 | * @returns void 33 | */ 34 | export const insertExecutiveBoard = async (request: Request, response: Response, next: NextFunction) => { 35 | request.body.createdBy = request.user && request.user._id ? request.user._id : null; 36 | await ExecutiveBoardService.insertExecutiveBoard(request.body) 37 | .then((data) => { 38 | request.handleResponse.successRespond(response)(data); 39 | next(); 40 | }) 41 | .catch((error: any) => { 42 | request.handleResponse.errorRespond(response)(error.message); 43 | next(); 44 | }); 45 | }; 46 | /** 47 | * @param request 48 | * @param response 49 | * @param next 50 | * @returns DocumentDefinition 51 | */ 52 | export const getExecutiveBoardbyID = async (request: Request, response: Response, next: NextFunction) => { 53 | const executiveBoardId = request.params.executiveBoardId; 54 | if (executiveBoardId) { 55 | await ExecutiveBoardService.getExecutiveBoardbyID(request.params.executiveBoardId) 56 | .then((data) => { 57 | request.handleResponse.successRespond(response)(data); 58 | next(); 59 | }) 60 | .catch((error: any) => { 61 | request.handleResponse.errorRespond(response)(error.message); 62 | next(); 63 | }); 64 | } else { 65 | request.handleResponse.errorRespond(response)("Executive board ID not found"); 66 | } 67 | }; 68 | /** 69 | * @param request 70 | * @param response 71 | * @param next 72 | * @returns [DocumentDefinition] 73 | */ 74 | export const getExecutiveBoard = async (request: Request, response: Response, next: NextFunction) => { 75 | await ExecutiveBoardService.getExecutiveBoard() 76 | .then((data) => { 77 | request.handleResponse.successRespond(response)(data); 78 | next(); 79 | }) 80 | .catch((error: any) => { 81 | request.handleResponse.errorRespond(response)(error.message); 82 | next(); 83 | }); 84 | }; 85 | /** 86 | * @param request 87 | * @param response 88 | * @param next 89 | * @returns new Board member 90 | */ 91 | export const addBoardMember = async (request: Request, response: Response, next: NextFunction) => { 92 | if (request.file) { 93 | const bucketDirectoryName = "boardmember-flyers"; 94 | 95 | const boardMemberFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 96 | request.body.imageUrl = boardMemberFlyerPath; 97 | } 98 | request.body.createdBy = request.user && request.user._id ? request.user._id : null; 99 | const executiveBoardId = request.params.executiveBoardId; 100 | const updatedBy = request.user && request.user._id ? request.user._id : null; 101 | if (executiveBoardId) { 102 | await ExecutiveBoardService.addBoardMember(request.params.executiveBoardId, request.body, updatedBy) 103 | .then((data) => { 104 | request.handleResponse.successRespond(response)(data); 105 | next(); 106 | }) 107 | .catch((error: any) => { 108 | request.handleResponse.errorRespond(response)(error.message); 109 | next(); 110 | }); 111 | } else { 112 | request.handleResponse.errorRespond(response)("Executive Board Id not found"); 113 | } 114 | }; 115 | /** 116 | * @param request 117 | * @param response 118 | * @param next 119 | * @returns updated ExecutiveBoard member 120 | */ 121 | export const updateExecutiveBoardDetails = async (request: Request, response: Response, next: NextFunction) => { 122 | const executiveBoardId = request.params.executiveBoardId; 123 | const updatedBy = request.user && request.user._id ? request.user._id : null; 124 | 125 | if (executiveBoardId) { 126 | await ExecutiveBoardService.updateExecutiveBoardDetails(request.params.executiveBoardId, request.body, updatedBy) 127 | .then((data) => { 128 | request.handleResponse.successRespond(response)(data); 129 | next(); 130 | }) 131 | .catch((error: any) => { 132 | request.handleResponse.errorRespond(response)(error.message); 133 | next(); 134 | }); 135 | } else { 136 | request.handleResponse.errorRespond(response)("Executive board ID not found"); 137 | } 138 | }; 139 | /** 140 | * @param request 141 | * @param response 142 | * @param next 143 | * @returns deleted ExecutiveBoard member 144 | */ 145 | export const deleteExecutiveBoardDetails = async (request: Request, response: Response, next: NextFunction) => { 146 | const executiveBoardId = request.params.executiveBoardId; 147 | const deletedBy = request.user && request.user._id ? request.user._id : null; 148 | 149 | if (executiveBoardId) { 150 | await ExecutiveBoardService.deleteExecutiveBoardDetails(request.params.executiveBoardId, deletedBy) 151 | .then((data) => { 152 | request.handleResponse.successRespond(response)(data); 153 | next(); 154 | }) 155 | .catch((error: any) => { 156 | request.handleResponse.errorRespond(response)(error.message); 157 | next(); 158 | }); 159 | } else { 160 | request.handleResponse.errorRespond(response)("Executive board ID not found"); 161 | } 162 | }; 163 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { IConfig } from "../interfaces"; 25 | let configs: IConfig; 26 | const environment = process.env.NODE_ENV?.trim() as string; 27 | 28 | if (environment == "Local") { 29 | configs = { 30 | ip: process.env.IP || "localhost", 31 | port: (process.env.PORT as string) || "8087", 32 | environment: process.env.LOCAL_ENVIRONMENT as string, 33 | mongodb: { 34 | uri: process.env.DEV_LOCAL_MONGO_URI as string, 35 | }, 36 | auth: { 37 | secret: process.env.DEV_JWT_SECRET as string, 38 | }, 39 | email: { 40 | host: process.env.EMAIL_HOST as string, 41 | port: process.env.EMAIL_PORT as string, 42 | secure: true, 43 | pool: true, 44 | secureConnection: true, 45 | auth: { 46 | user: process.env.EMAIL_AUTH_USER as string, 47 | pass: process.env.EMAIL_AUTH_PASSWORD as string, 48 | }, 49 | tls: { 50 | rejectUnauthorized: false, 51 | }, 52 | sendGrid: { 53 | user: process.env.EMAIL_SENDGRID_USER as string, 54 | apiKey: process.env.SENDGRID_API_KEY as string, 55 | }, 56 | }, 57 | firebase: { 58 | projectId: process.env.LOCAL_STORAGE_PROJECT_ID as string, 59 | clientEmail: process.env.LOCAL_STORAGE_CLIENT_EMAIL as string, 60 | privateKey: process.env.LOCAL_STORAGE_PRIVATE_KEY as string, 61 | storageBucket: process.env.LOCAL_STORAGE_BUCKET_URL as string, 62 | bucketName: process.env.LOCAL_BUCKET_NAME as string, 63 | applicationImageBucket: process.env.LOCAL_APPLICATION_IMAGES_BUCKET as string, 64 | emailTemplateBucket: process.env.LOCAL_EMAIL_TEMPLATE_BUCKET as string, 65 | }, 66 | queue: { 67 | messageBrokerURL: process.env.DEV_MESSAGE_BROKER_URL as string, 68 | exchangeName: process.env.EXCHANGE_NAME as string, 69 | emailService: process.env.EMAIL_SERVICE_NAME as string, 70 | emailQueue: process.env.EMAIL_QUEUE_NAME as string, 71 | }, 72 | }; 73 | } 74 | 75 | if (environment == "Development") { 76 | configs = { 77 | ip: process.env.IP || "localhost", 78 | port: (process.env.PORT as string) || "8087", 79 | environment: process.env.DEV_ENVIRONMENT as string, 80 | mongodb: { 81 | uri: process.env.DEV_MONGO_URI as string, 82 | }, 83 | auth: { 84 | secret: process.env.DEV_JWT_SECRET as string, 85 | }, 86 | email: { 87 | host: process.env.EMAIL_HOST as string, 88 | port: process.env.EMAIL_PORT as string, 89 | secure: true, 90 | pool: true, 91 | secureConnection: true, 92 | auth: { 93 | user: process.env.EMAIL_AUTH_USER as string, 94 | pass: process.env.EMAIL_AUTH_PASSWORD as string, 95 | }, 96 | tls: { 97 | rejectUnauthorized: false, 98 | }, 99 | sendGrid: { 100 | user: process.env.EMAIL_SENDGRID_USER as string, 101 | apiKey: process.env.SENDGRID_API_KEY as string, 102 | }, 103 | }, 104 | firebase: { 105 | projectId: process.env.STORAGE_PROJECT_ID as string, 106 | clientEmail: process.env.STORAGE_CLIENT_EMAIL as string, 107 | privateKey: process.env.STORAGE_PRIVATE_KEY as string, 108 | storageBucket: process.env.STORAGE_BUCKET_URL as string, 109 | bucketName: process.env.BUCKET_NAME as string, 110 | applicationImageBucket: process.env.APPLICATION_IMAGES_BUCKET as string, 111 | emailTemplateBucket: process.env.EMAIL_TEMPLATE_BUCKET as string, 112 | }, 113 | queue: { 114 | messageBrokerURL: process.env.DEV_MESSAGE_BROKER_URL as string, 115 | exchangeName: process.env.EXCHANGE_NAME as string, 116 | emailService: process.env.EMAIL_SERVICE_NAME as string, 117 | emailQueue: process.env.EMAIL_QUEUE_NAME as string, 118 | }, 119 | }; 120 | } 121 | 122 | if (environment == "Production") { 123 | configs = { 124 | ip: process.env.IP || "localhost", 125 | port: (process.env.PORT as string) || "8087", 126 | environment: process.env.PROD_ENVIRONMENT as string, 127 | mongodb: { 128 | uri: process.env.PROD_MONGO_URI as string, 129 | }, 130 | auth: { 131 | secret: process.env.PROD_JWT_SECRET as string, 132 | }, 133 | email: { 134 | host: process.env.EMAIL_HOST as string, 135 | port: process.env.EMAIL_PORT as string, 136 | secure: true, 137 | pool: true, 138 | secureConnection: true, 139 | auth: { 140 | user: process.env.EMAIL_AUTH_USER as string, 141 | pass: process.env.EMAIL_AUTH_PASSWORD as string, 142 | }, 143 | tls: { 144 | rejectUnauthorized: false, 145 | }, 146 | sendGrid: { 147 | user: process.env.EMAIL_SENDGRID_USER as string, 148 | apiKey: process.env.SENDGRID_API_KEY as string, 149 | }, 150 | }, 151 | firebase: { 152 | projectId: process.env.STORAGE_PROJECT_ID as string, 153 | clientEmail: process.env.STORAGE_CLIENT_EMAIL as string, 154 | privateKey: process.env.STORAGE_PRIVATE_KEY as string, 155 | storageBucket: process.env.STORAGE_BUCKET_URL as string, 156 | bucketName: process.env.BUCKET_NAME as string, 157 | applicationImageBucket: process.env.APPLICATION_IMAGES_BUCKET as string, 158 | emailTemplateBucket: process.env.EMAIL_TEMPLATE_BUCKET as string, 159 | }, 160 | queue: { 161 | messageBrokerURL: process.env.PROD_MESSAGE_BROKER_URL as string, 162 | exchangeName: process.env.EXCHANGE_NAME as string, 163 | emailService: process.env.EMAIL_SERVICE_NAME as string, 164 | emailQueue: process.env.EMAIL_QUEUE_NAME as string, 165 | }, 166 | }; 167 | } 168 | 169 | export { configs }; 170 | -------------------------------------------------------------------------------- /src/api/controllers/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { 25 | createUser, 26 | login, 27 | loginByFaceAuthentication, 28 | getAuthUser, 29 | getAllUsers, 30 | updateUser, 31 | adminUpdateUser, 32 | removeUser, 33 | removeUserPermenently, 34 | recoverUser, 35 | getRemovedUsers, 36 | getLogins, 37 | } from "./User.controller"; 38 | 39 | import { 40 | createContact, 41 | getAllContacts, 42 | removeContact, 43 | removedContacts, 44 | removeContactPermanently, 45 | recoverRemovedInquiry, 46 | replyInquiry, 47 | } from "./Contact.controller"; 48 | 49 | import { 50 | insertEvent, 51 | getEvent, 52 | getEvents, 53 | getPastEvents, 54 | getUpcomingEvent, 55 | updateEvent, 56 | deleteEvent, 57 | eventsForAdmin, 58 | deletedEventsForAdmin, 59 | deleteEventPermanently, 60 | recoverRemovedEvent, 61 | } from "./Event.controller"; 62 | 63 | import { 64 | insertWebinar, 65 | getWebinarById, 66 | getWebinars, 67 | getPastWebinars, 68 | getUpcomingWebinar, 69 | updateWebinar, 70 | deleteWebinar, 71 | webinarsForAdmin, 72 | deletedWebinarsForAdmin, 73 | recoverRemovedWebinar, 74 | deleteWebinarPermanently, 75 | } from "./Webinar.controller"; 76 | 77 | import { 78 | insertTopSpeaker, 79 | getTopSpeaker, 80 | getTopSpeakers, 81 | updateTopSpeaker, 82 | deleteTopSpeaker, 83 | recoverDeletedTopSpeaker, 84 | getAllTopSpeakersForAdmin, 85 | getDeletedTopSpeakersForAdmin, 86 | permenentDeleteTopSpeaker, 87 | } from "./TopSpeaker.controller"; 88 | 89 | import { 90 | addApplication, 91 | getApplicationById, 92 | getApplications, 93 | setApplicationArchive, 94 | changeApplicationStatusIntoInterview, 95 | changeApplicationStatusIntoSelected, 96 | changeApplicationStatusIntoRejected, 97 | fetchPendingApplications, 98 | fetchSelectedApplications, 99 | fetchInterviewApplications, 100 | fetchRejectedApplications, 101 | getDeletedApplicationsForAdmin, 102 | recoverRemovedApplication, 103 | deleteApplicationPermanently, 104 | } from "./Application.controller"; 105 | 106 | import { 107 | getBoardMemberbyID, 108 | getAllBoardMembers, 109 | updateBoardMemberDetails, 110 | deleteBoardMemberDetails, 111 | } from "./BoardMember.controller"; 112 | 113 | import { 114 | insertExecutiveBoard, 115 | getExecutiveBoardbyID, 116 | getExecutiveBoard, 117 | addBoardMember, 118 | updateExecutiveBoardDetails, 119 | deleteExecutiveBoardDetails, 120 | } from "./ExecutiveBoard.controller"; 121 | 122 | import { 123 | insertOrganization, 124 | getOrganization, 125 | getOrganizationForAdmin, 126 | updateOrganization, 127 | } from "./Organization.controller"; 128 | 129 | import { 130 | scheduleInternalMeeting, 131 | getAllInternalMeetings, 132 | deleteMeeting, 133 | getInternalMeetingById, 134 | updateMeeting, 135 | deleteMeetingPermanently, 136 | scheduleInterviewMeeting, 137 | } from "./Meeting.controller"; 138 | 139 | export default { 140 | //User Controllers 141 | createUser, 142 | login, 143 | loginByFaceAuthentication, 144 | getAuthUser, 145 | getAllUsers, 146 | updateUser, 147 | adminUpdateUser, 148 | removeUser, 149 | removeUserPermenently, 150 | recoverUser, 151 | getRemovedUsers, 152 | getLogins, 153 | //Contact Controllers 154 | createContact, 155 | getAllContacts, 156 | removeContact, 157 | removedContacts, 158 | recoverRemovedInquiry, 159 | replyInquiry, 160 | removeContactPermanently, 161 | //Event Controllers 162 | insertEvent, 163 | getEvent, 164 | getEvents, 165 | getPastEvents, 166 | getUpcomingEvent, 167 | updateEvent, 168 | deleteEvent, 169 | eventsForAdmin, 170 | deletedEventsForAdmin, 171 | deleteEventPermanently, 172 | recoverRemovedEvent, 173 | //Webinar Controllers 174 | insertWebinar, 175 | getWebinarById, 176 | getWebinars, 177 | getPastWebinars, 178 | getUpcomingWebinar, 179 | updateWebinar, 180 | deleteWebinar, 181 | webinarsForAdmin, 182 | deletedWebinarsForAdmin, 183 | recoverRemovedWebinar, 184 | deleteWebinarPermanently, 185 | //Executive board Controllers 186 | insertExecutiveBoard, 187 | getExecutiveBoardbyID, 188 | getExecutiveBoard, 189 | addBoardMember, 190 | updateExecutiveBoardDetails, 191 | deleteExecutiveBoardDetails, 192 | //Top Speaker Controllers 193 | insertTopSpeaker, 194 | getTopSpeaker, 195 | getTopSpeakers, 196 | updateTopSpeaker, 197 | deleteTopSpeaker, 198 | recoverDeletedTopSpeaker, 199 | getAllTopSpeakersForAdmin, 200 | getDeletedTopSpeakersForAdmin, 201 | permenentDeleteTopSpeaker, 202 | //Board Member Controllers 203 | getBoardMemberbyID, 204 | getAllBoardMembers, 205 | updateBoardMemberDetails, 206 | deleteBoardMemberDetails, 207 | //Application Controllers 208 | addApplication, 209 | getApplicationById, 210 | getApplications, 211 | setApplicationArchive, 212 | changeApplicationStatusIntoInterview, 213 | changeApplicationStatusIntoSelected, 214 | changeApplicationStatusIntoRejected, 215 | fetchPendingApplications, 216 | fetchSelectedApplications, 217 | fetchInterviewApplications, 218 | fetchRejectedApplications, 219 | getDeletedApplicationsForAdmin, 220 | recoverRemovedApplication, 221 | deleteApplicationPermanently, 222 | // Organization Controllers 223 | insertOrganization, 224 | getOrganization, 225 | getOrganizationForAdmin, 226 | updateOrganization, 227 | // Meeting Controllers 228 | scheduleInternalMeeting, 229 | getAllInternalMeetings, 230 | deleteMeeting, 231 | updateMeeting, 232 | getInternalMeetingById, 233 | deleteMeetingPermanently, 234 | scheduleInterviewMeeting, 235 | }; 236 | -------------------------------------------------------------------------------- /src/api/services/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { 25 | insertUser, 26 | authenticateUser, 27 | authenticateUserByFace, 28 | getUsers, 29 | updateUser, 30 | adminUpdateUser, 31 | deleteUser, 32 | deleteUserPermenently, 33 | recoverUser, 34 | fetchDeletedUsers, 35 | getLogins, 36 | } from "./User.service"; 37 | 38 | import { 39 | insertContact, 40 | archiveContact, 41 | fetchContactInfo, 42 | getArchivedContacts, 43 | deleteContactPermanently, 44 | recoverDeletedInquiry, 45 | replyInquiry, 46 | } from "./Contact.service"; 47 | 48 | import { 49 | insertEvent, 50 | getEvent, 51 | getEvents, 52 | getPastEvents, 53 | getUpcomingEvent, 54 | updateEvent, 55 | deleteEvent, 56 | getAllEventsForAdmin, 57 | getDeletedEventsForAdmin, 58 | recoverDeletedEvent, 59 | deleteEventPermanently, 60 | } from "./Event.service"; 61 | 62 | import { 63 | insertWebinar, 64 | fetchWebinarById, 65 | fetchWebinars, 66 | fetchPastWebinars, 67 | fetchUpcomingWebinar, 68 | updateWebinar, 69 | removeWebinar, 70 | getAllWebinarsForAdmin, 71 | getDeletedWebinarsForAdmin, 72 | recoverDeletedWebinar, 73 | deleteWebinarPermanently, 74 | } from "./Webinar.service"; 75 | 76 | import { 77 | insertTopSpeaker, 78 | getTopSpeaker, 79 | getTopSpeakers, 80 | updateTopSpeaker, 81 | deleteTopSpeaker, 82 | recoverDeletedTopSpeaker, 83 | getAllTopSpeakersForAdmin, 84 | getDeletedTopSpeakersForAdmin, 85 | permenentDeleteTopSpeaker, 86 | } from "./TopSpeaker.service"; 87 | 88 | import { 89 | addApplication, 90 | fetchApplicationById, 91 | fetchApplications, 92 | archiveApplication, 93 | changeApplicationStatusIntoInterview, 94 | changeApplicationStatusIntoSelected, 95 | changeApplicationStatusIntoRejected, 96 | fetchPendingApplications, 97 | fetchInterviewApplications, 98 | fetchSelectedApplications, 99 | fetchRejectedApplications, 100 | getDeletedApplicationsForAdmin, 101 | recoverDeletedApplication, 102 | deleteApplicationPermanently, 103 | } from "./Application.service"; 104 | 105 | import { 106 | insertBoardMember, 107 | getBoardMemberbyID, 108 | getAllBoardMembers, 109 | updateBoardMemberDetails, 110 | deleteBoardMemberDetails, 111 | } from "./BoardMember.service"; 112 | 113 | import { 114 | insertExecutiveBoard, 115 | getExecutiveBoardbyID, 116 | getExecutiveBoard, 117 | addBoardMember, 118 | updateExecutiveBoardDetails, 119 | deleteExecutiveBoardDetails, 120 | } from "./ExecutiveBoard.service"; 121 | 122 | import { 123 | createOrganization, 124 | getOrganizationInfo, 125 | getOrganizationInfoForAdmin, 126 | updateOrganizationInfo, 127 | } from "./Organization.service"; 128 | 129 | import { 130 | scheduleInternalMeetingMSTeams, 131 | getAllInternalMeetingsMSTeams, 132 | deleteMeeting, 133 | fetchMeetingById, 134 | updateMeeting, 135 | deleteMeetingPermanently, 136 | scheduleInterviewMeetingMSTeams, 137 | } from "./Meeting.service"; 138 | 139 | export default { 140 | // User services 141 | insertUser, 142 | authenticateUser, 143 | authenticateUserByFace, 144 | getUsers, 145 | updateUser, 146 | adminUpdateUser, 147 | deleteUser, 148 | deleteUserPermenently, 149 | recoverUser, 150 | fetchDeletedUsers, 151 | getLogins, 152 | // Contact services 153 | insertContact, 154 | fetchContactInfo, 155 | archiveContact, 156 | getArchivedContacts, 157 | deleteContactPermanently, 158 | recoverDeletedInquiry, 159 | replyInquiry, 160 | // Event services 161 | insertEvent, 162 | getEvent, 163 | getEvents, 164 | getPastEvents, 165 | getUpcomingEvent, 166 | updateEvent, 167 | deleteEvent, 168 | getAllEventsForAdmin, 169 | getDeletedEventsForAdmin, 170 | recoverDeletedEvent, 171 | deleteEventPermanently, 172 | // Webinar services 173 | insertWebinar, 174 | fetchWebinarById, 175 | fetchWebinars, 176 | fetchPastWebinars, 177 | fetchUpcomingWebinar, 178 | updateWebinar, 179 | removeWebinar, 180 | getAllWebinarsForAdmin, 181 | getDeletedWebinarsForAdmin, 182 | recoverDeletedWebinar, 183 | deleteWebinarPermanently, 184 | // TopSpeaker Service 185 | insertTopSpeaker, 186 | getTopSpeaker, 187 | getTopSpeakers, 188 | updateTopSpeaker, 189 | deleteTopSpeaker, 190 | recoverDeletedTopSpeaker, 191 | getAllTopSpeakersForAdmin, 192 | getDeletedTopSpeakersForAdmin, 193 | permenentDeleteTopSpeaker, 194 | // Application Service 195 | addApplication, 196 | fetchApplicationById, 197 | fetchApplications, 198 | archiveApplication, 199 | changeApplicationStatusIntoInterview, 200 | changeApplicationStatusIntoSelected, 201 | changeApplicationStatusIntoRejected, 202 | fetchPendingApplications, 203 | fetchInterviewApplications, 204 | fetchSelectedApplications, 205 | fetchRejectedApplications, 206 | getDeletedApplicationsForAdmin, 207 | recoverDeletedApplication, 208 | deleteApplicationPermanently, 209 | // BoardMember Service 210 | insertBoardMember, 211 | getBoardMemberbyID, 212 | getAllBoardMembers, 213 | updateBoardMemberDetails, 214 | deleteBoardMemberDetails, 215 | // ExecutiveBoardMember Service 216 | insertExecutiveBoard, 217 | getExecutiveBoardbyID, 218 | getExecutiveBoard, 219 | addBoardMember, 220 | updateExecutiveBoardDetails, 221 | deleteExecutiveBoardDetails, 222 | // Organization Service 223 | createOrganization, 224 | getOrganizationInfo, 225 | getOrganizationInfoForAdmin, 226 | updateOrganizationInfo, 227 | 228 | // Meeting Service 229 | scheduleInternalMeetingMSTeams, 230 | getAllInternalMeetingsMSTeams, 231 | deleteMeeting, 232 | fetchMeetingById, 233 | updateMeeting, 234 | deleteMeetingPermanently, 235 | scheduleInterviewMeetingMSTeams, 236 | }; 237 | -------------------------------------------------------------------------------- /src/api/services/Meeting.service.ts: -------------------------------------------------------------------------------- 1 | import { DocumentDefinition, Schema } from "mongoose"; 2 | import { IMeeting, IMeetingRequest, IUpdatedBy } from "../../interfaces"; 3 | import MeetingModel from "../models/Meeting.model"; 4 | import axios from "axios"; 5 | 6 | export const scheduleInternalMeetingMSTeams = (meetingData: DocumentDefinition) => { 7 | return axios 8 | .post(`${process.env.MS_MEETING_MANAGER_API}/api/msteams/internalmeeting/schedule`, meetingData) 9 | .then(async (sceduleMeeting) => { 10 | const meetingInfo = new MeetingModel({ 11 | meetingId: sceduleMeeting.data.body.id, 12 | meetingName: meetingData.meetingName, 13 | startDateTime: meetingData.startDateTime, 14 | endDateTime: meetingData.endDateTime, 15 | emailList: meetingData.emailList, 16 | scheduledLink: sceduleMeeting.data.body.onlineMeeting.joinUrl, 17 | type: "INTERNAL", 18 | meetProvider: "MSMEET", 19 | }); 20 | 21 | return await meetingInfo 22 | .save() 23 | .then((createdMeeting) => { 24 | return createdMeeting; 25 | }) 26 | .catch((error) => { 27 | throw new Error(error.message); 28 | }); 29 | }) 30 | .catch((error) => { 31 | throw new Error(error.message); 32 | }); 33 | }; 34 | 35 | export const getAllInternalMeetingsMSTeams = async () => { 36 | return await MeetingModel.aggregate([{ $match: { deletedAt: { $eq: null }, type: { $eq: "INTERNAL" } } }]) 37 | .sort({ createdAt: -1 }) 38 | .then((meetings) => { 39 | return meetings; 40 | }) 41 | .catch((error) => { 42 | throw new Error(error.message); 43 | }); 44 | }; 45 | 46 | export const deleteMeeting = async (meetingId: string, deletedBy: Schema.Types.ObjectId) => { 47 | return await MeetingModel.findById(meetingId) 48 | .then(async (meetingDetails) => { 49 | if (meetingDetails && meetingDetails.deletedAt === null) { 50 | meetingDetails.deletedAt = new Date(); 51 | meetingDetails.deletedBy = deletedBy; 52 | return await meetingDetails.save(); 53 | } else { 54 | return "Meeting not found"; 55 | } 56 | }) 57 | .catch((error) => { 58 | throw new Error(error.message); 59 | }); 60 | }; 61 | 62 | export const fetchMeetingById = async (meetingId: string) => { 63 | return await MeetingModel.findById(meetingId) 64 | .then((meeting) => { 65 | return meeting; 66 | }) 67 | .catch((error) => { 68 | throw new Error(error.message); 69 | }); 70 | }; 71 | 72 | export const scheduleInterviewMeetingMSTeams = (meetingData: DocumentDefinition) => { 73 | return axios 74 | .post(`${process.env.MS_MEETING_MANAGER_API}/api/msteams/schedule`, meetingData) 75 | .then(async (sceduleMeeting) => { 76 | const meetingInfo = new MeetingModel({ 77 | meetingId: sceduleMeeting.data.body.id, 78 | meetingName: meetingData.meetingName, 79 | startDateTime: sceduleMeeting.data.body.start.dateTime, 80 | endDateTime: sceduleMeeting.data.body.end.dateTime, 81 | emailList: meetingData.emailList, 82 | scheduledLink: sceduleMeeting.data.body.onlineMeeting.joinUrl, 83 | type: "INTERVIEW", 84 | meetProvider: "MSMEET" 85 | }); 86 | 87 | return await meetingInfo 88 | .save() 89 | .then((createdMeeting) => { 90 | return createdMeeting; 91 | }) 92 | .catch((error) => { 93 | throw new Error(error.message); 94 | }); 95 | }) 96 | .catch((error) => { 97 | throw new Error(error.message); 98 | }); 99 | }; 100 | export const scheduleInterviewGoogleMeeting = async (meetingData: DocumentDefinition) => { 101 | 102 | let res = await axios.post(`${process.env.MS_MEETING_MANAGER_API}/api/googlemeet/internalmeeting/schedule`, meetingData); 103 | 104 | if(res.status != 200) 105 | throw new Error("Something went wrong in the meeting service"); 106 | 107 | let data = res.data; 108 | 109 | let new_meeting = new MeetingModel({ 110 | ...data, 111 | meetProvider: "GOOGLEMEET", 112 | type: "INTERVIEW" 113 | }); 114 | 115 | return await new_meeting.save(); 116 | } 117 | 118 | export const scheduleInternalGoogleMeeting = async (meetingData: DocumentDefinition) => { 119 | 120 | let res = await axios.post(`${process.env.MS_MEETING_MANAGER_API}/api/googlemeet/internalmeeting/schedule`, meetingData); 121 | 122 | if(res.status != 200) 123 | throw new Error("Something went wrong in the meeting service"); 124 | 125 | let data = res.data; 126 | 127 | let new_meeting = new MeetingModel({ 128 | ...data, 129 | meetProvider: "GOOGLEMEET", 130 | type: "INTERNAL" 131 | }); 132 | 133 | return await new_meeting.save(); 134 | } 135 | 136 | export const updateMeeting = async ( 137 | meetingId: string, 138 | updateInfo: any, 139 | user: Schema.Types.ObjectId 140 | ) => { 141 | const meeting = await MeetingModel.findById(meetingId).exec(); 142 | 143 | if(!meeting) 144 | throw new Error("Meeting ID not found"); 145 | 146 | let res; 147 | 148 | switch(meeting.meetProvider){ 149 | case "GOOGLEMEET": 150 | res = await axios.patch(`${process.env.MS_MEETING_MANAGER_API}/api/googlemeet/meeting/${meeting.meetingId}`, updateInfo); 151 | break; 152 | case "MSMEET": 153 | res = await axios.patch(`${process.env.MS_MEETING_MANAGER_API}/api/msteams/meeting/${meeting.meetingId}`, updateInfo) 154 | break; 155 | default: 156 | throw new Error("Document has no meetProvider set"); 157 | } 158 | 159 | if(!res || res.status != 200) 160 | throw new Error("Something went wrong in the meeting service"); 161 | 162 | for(const key in updateInfo){ 163 | let d = updateInfo[key]; 164 | if(d) meeting.set(key, d); 165 | } 166 | 167 | const updateUserInfo: IUpdatedBy = { 168 | user: user, 169 | updatedAt: new Date(), 170 | }; 171 | 172 | meeting.updatedBy.push(updateUserInfo); 173 | return await meeting.save(); 174 | } 175 | 176 | 177 | export const deleteMeetingPermanently = async (meetingId : string) => { 178 | const meeting = await MeetingModel.findById(meetingId); 179 | 180 | if(!meeting) 181 | throw new Error("Meeting ID not found"); 182 | 183 | let res; 184 | switch(meeting.meetProvider){ 185 | case "MSMEET": 186 | res = await axios.delete(`${process.env.MS_MEETING_MANAGER_API}/api/msteams/internalmeeting/${meeting.meetingId}`) 187 | break; 188 | case "GOOGLEMEET": 189 | res = await axios.delete(`${process.env.MS_MEETING_MANAGER_API}/api/googlemeet/meeting/${meeting.meetingId}`); 190 | break; 191 | default: 192 | throw new Error("Document has no meetProvider set"); 193 | } 194 | 195 | if(res.status != 200) 196 | throw new Error("Something went wrong in the meeting service."); 197 | 198 | return await MeetingModel.findByIdAndDelete(meetingId); 199 | } 200 | -------------------------------------------------------------------------------- /src/api/controllers/TopSpeaker.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import TopSpeakerService from "../services"; 26 | import ImageService from "../../util/image.handler"; 27 | 28 | /** 29 | * @param {Request} request - Request from the frontend 30 | * @param {Response} response - Response that need to send to the client 31 | * @param {NextFunction} next - Next function 32 | * @returns { ITopSpeaker } topSpeaker document 33 | */ 34 | 35 | export const insertTopSpeaker = async (request: Request, response: Response, next: NextFunction) => { 36 | const bucketDirectoryName = "topspeaker-flyers"; 37 | 38 | const topSpeakerFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 39 | request.body.createdBy = request.user && request.user._id ? request.user._id : null; 40 | request.body.imageUrl = topSpeakerFlyerPath; 41 | await TopSpeakerService.insertTopSpeaker(request.body) 42 | .then((data) => { 43 | request.handleResponse.successRespond(response)(data); 44 | next(); 45 | }) 46 | .catch((error: any) => { 47 | request.handleResponse.errorRespond(response)(error.message); 48 | next(); 49 | }); 50 | }; 51 | 52 | /** 53 | * @param {Request} request - Request from the frontend 54 | * @param {Response} response - Response that need to send to the client 55 | * @param {NextFunction} next - Next function 56 | * @returns { ITopSpeaker } topSpeaker document 57 | */ 58 | 59 | export const getTopSpeaker = async (request: Request, response: Response, next: NextFunction) => { 60 | const topSpeakerId = request.params.topSpeakerId; 61 | 62 | if (topSpeakerId) { 63 | await TopSpeakerService.getTopSpeaker(request.params.topSpeakerId).then((data) => { 64 | request.handleResponse.successRespond(response)(data); 65 | next(); 66 | }); 67 | } else { 68 | request.handleResponse.errorRespond(response)("Top Speaker ID not found"); 69 | } 70 | }; 71 | 72 | /** 73 | * @param {Request} request - Request from the frontend 74 | * @param {Response} response - Response that need to send to the client 75 | * @param {NextFunction} next - Next function 76 | * @returns { ITopSpeaker[] } All top speakers in the system 77 | */ 78 | 79 | export const getTopSpeakers = async (request: Request, response: Response, next: NextFunction) => { 80 | await TopSpeakerService.getTopSpeakers() 81 | .then((data) => { 82 | request.handleResponse.successRespond(response)(data); 83 | next(); 84 | }) 85 | .catch((error: any) => { 86 | request.handleResponse.errorRespond(response)(error.message); 87 | next(); 88 | }); 89 | }; 90 | 91 | /** 92 | * @param {Request} request - Request from the frontend 93 | * @param {Response} response - Response that need to send to the client 94 | * @param {NextFunction} next - Next function 95 | * @returns { ITopSpeaker } - Updated top speaker details 96 | */ 97 | 98 | export const updateTopSpeaker = async (request: Request, response: Response, next: NextFunction) => { 99 | if (request.file) { 100 | const bucketDirectoryName = "topspeaker-flyers"; 101 | 102 | const topSpeakerFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 103 | request.body.imageUrl = topSpeakerFlyerPath; 104 | } 105 | const topSpeakerId = request.params.topSpeakerId; 106 | const updatedBy = request.user && request.user._id ? request.user._id : null; 107 | 108 | if (topSpeakerId) { 109 | await TopSpeakerService.updateTopSpeaker(request.params.topSpeakerId, request.body, updatedBy).then((data) => { 110 | request.handleResponse.successRespond(response)(data); 111 | next(); 112 | }); 113 | } else { 114 | request.handleResponse.errorRespond(response)("Top Speaker ID not found"); 115 | } 116 | }; 117 | 118 | /** 119 | * @param {Request} request - Request from the frontend 120 | * @param {Response} response - Response that need to send to the client 121 | * @param {NextFunction} next - Next function 122 | * @returns { ITopSpeaker } - Deleted top speaker details 123 | */ 124 | 125 | export const deleteTopSpeaker = async (request: Request, response: Response, next: NextFunction) => { 126 | const topSpeakerId = request.params.topSpeakerId; 127 | const deletedBy = request.user && request.user._id ? request.user._id : null; 128 | 129 | if (topSpeakerId) { 130 | await TopSpeakerService.deleteTopSpeaker(request.params.topSpeakerId, deletedBy).then((data) => { 131 | request.handleResponse.successRespond(response)(data); 132 | next(); 133 | }); 134 | } else { 135 | request.handleResponse.errorRespond(response)("Top Speaker ID not found"); 136 | } 137 | }; 138 | 139 | /** 140 | * @param {Request} request - Request from the frontend 141 | * @param {Response} response - Response that need to send to the client 142 | * @param {NextFunction} next - Next function 143 | * @returns { ITopSpeaker } - Recovered top speaker details 144 | */ 145 | 146 | export const recoverDeletedTopSpeaker = async (request: Request, response: Response, next: NextFunction) => { 147 | const topSpeakerId = request.params.topSpeakerId; 148 | 149 | if (topSpeakerId) { 150 | await TopSpeakerService.recoverDeletedTopSpeaker(topSpeakerId).then((data) => { 151 | request.handleResponse.successRespond(response)(data); 152 | next(); 153 | }); 154 | } else { 155 | request.handleResponse.errorRespond(response)({ message: "Top Speaker ID not found", dateTime: new Date() }); 156 | next(); 157 | } 158 | }; 159 | 160 | export const getAllTopSpeakersForAdmin = async (request: Request, response: Response, next: NextFunction) => { 161 | await TopSpeakerService.getAllTopSpeakersForAdmin() 162 | .then((data: any) => { 163 | request.handleResponse.successRespond(response)(data); 164 | next(); 165 | }) 166 | .catch((error: any) => { 167 | request.handleResponse.errorRespond(response)(error.message); 168 | next(); 169 | }); 170 | }; 171 | 172 | export const getDeletedTopSpeakersForAdmin = async (request: Request, response: Response, next: NextFunction) => { 173 | await TopSpeakerService.getDeletedTopSpeakersForAdmin() 174 | .then((data: any) => { 175 | request.handleResponse.successRespond(response)(data); 176 | next(); 177 | }) 178 | .catch((error: any) => { 179 | request.handleResponse.errorRespond(response)(error.message); 180 | next(); 181 | }); 182 | }; 183 | 184 | export const permenentDeleteTopSpeaker = async (request: Request, response: Response, next: NextFunction) => { 185 | const topSpeakerId = request.params.topSpeakerId; 186 | if (topSpeakerId) { 187 | await TopSpeakerService.permenentDeleteTopSpeaker(topSpeakerId) 188 | .then((data) => { 189 | request.handleResponse.successRespond(response)(data); 190 | next(); 191 | }) 192 | .catch((error) => { 193 | request.handleResponse.errorRespond(response)(error.message); 194 | next(); 195 | }); 196 | } else { 197 | request.handleResponse.errorRespond(response)("TopSpeaker ID not found"); 198 | } 199 | }; 200 | -------------------------------------------------------------------------------- /src/api/services/Event.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { DocumentDefinition, Schema } from "mongoose"; 25 | import { IEvent, IUpdatedBy } from "../../interfaces"; 26 | import EventModel from "../models/Event.model"; 27 | 28 | /** 29 | save an event in the database 30 | */ 31 | export const insertEvent = async (eventData: DocumentDefinition) => { 32 | return await EventModel.create(eventData) 33 | .then(async (event) => { 34 | const initialUpdatedBy: IUpdatedBy = { 35 | user: event.createdBy, 36 | updatedAt: new Date(), 37 | }; 38 | 39 | event.updatedBy.push(initialUpdatedBy); 40 | await event.save(); 41 | return event; 42 | }) 43 | .catch((error) => { 44 | throw new Error(error.message); 45 | }); 46 | }; 47 | 48 | /** 49 | fetch an event in the system 50 | * @param eventId @type string 51 | */ 52 | export const getEvent = async (eventId: string) => { 53 | return await EventModel.findById(eventId) 54 | .then((event) => { 55 | return event; 56 | }) 57 | .catch((error) => { 58 | throw new Error(error.message); 59 | }); 60 | }; 61 | 62 | /** 63 | fetch all the events in the system 64 | */ 65 | export const getEvents = async () => { 66 | return await EventModel.aggregate([{ $match: { deletedAt: { $eq: null } } }]) 67 | .sort({ createdAt: -1 }) 68 | .then((events) => { 69 | return events; 70 | }) 71 | .catch((error) => { 72 | throw new Error(error.message); 73 | }); 74 | }; 75 | 76 | /** 77 | fetch an past events in the system 78 | */ 79 | export const getPastEvents = async () => { 80 | return await EventModel.find({ eventType: "PAST" }) 81 | .sort({ createdAt: -1 }) 82 | .then((events) => { 83 | return events; 84 | }) 85 | .catch((error) => { 86 | throw new Error(error.message); 87 | }); 88 | }; 89 | 90 | /** 91 | fetch an upcoming event in the system 92 | */ 93 | export const getUpcomingEvent = async () => { 94 | return await EventModel.findOne({ eventType: "UPCOMING" }) 95 | .limit(1) 96 | .sort({ $natural: -1 }) 97 | .then((event) => { 98 | return event; 99 | }) 100 | .catch((error) => { 101 | throw new Error(error.message); 102 | }); 103 | }; 104 | 105 | /** 106 | update an event in the system 107 | * @param eventId @type string 108 | * @param updateData @type DocumentDefinition 109 | */ 110 | export const updateEvent = async ( 111 | eventId: string, 112 | eventData: DocumentDefinition, 113 | updatedBy: Schema.Types.ObjectId 114 | ) => { 115 | return await EventModel.findById(eventId) 116 | .then(async (eventDetails) => { 117 | if (eventDetails) { 118 | if (!eventDetails.deletedAt) { 119 | if (eventData.title) { 120 | eventDetails.title = eventData.title; 121 | } 122 | if (eventData.description) { 123 | eventDetails.description = eventData.description; 124 | } 125 | if (eventData.imageUrl) { 126 | eventDetails.imageUrl = eventData.imageUrl; 127 | } 128 | if (eventData.link) { 129 | eventDetails.link = eventData.link; 130 | } 131 | if (eventData.registrationLink) { 132 | eventDetails.registrationLink = eventData.registrationLink; 133 | } 134 | if (eventData.tags) { 135 | eventDetails.tags = eventData.tags; 136 | } 137 | if (eventData.dateTime) { 138 | eventDetails.dateTime = eventData.dateTime; 139 | } 140 | if (eventData.eventType) { 141 | eventDetails.eventType = eventData.eventType; 142 | } 143 | 144 | const updateUserInfo: IUpdatedBy = { 145 | user: updatedBy, 146 | updatedAt: new Date(), 147 | }; 148 | 149 | eventDetails.updatedBy.push(updateUserInfo); 150 | return await eventDetails.save(); 151 | } else { 152 | throw new Error("Event is not found"); 153 | } 154 | } else { 155 | return null; 156 | } 157 | }) 158 | .catch((error) => { 159 | throw new Error(error.message); 160 | }); 161 | }; 162 | 163 | /** 164 | delete an event 165 | * @param eventId @type string 166 | */ 167 | export const deleteEvent = async (eventId: string, deletedBy: Schema.Types.ObjectId) => { 168 | return await EventModel.findById(eventId) 169 | .then(async (eventDetails) => { 170 | if (eventDetails && eventDetails.deletedAt === null) { 171 | eventDetails.deletedAt = new Date(); 172 | eventDetails.deletedBy = deletedBy; 173 | return await eventDetails.save(); 174 | } else { 175 | return "Event not found"; 176 | } 177 | }) 178 | .catch((error) => { 179 | throw new Error(error.message); 180 | }); 181 | }; 182 | 183 | /** 184 | Get all events - admin 185 | */ 186 | export const getAllEventsForAdmin = async () => { 187 | return await EventModel.find({ deletedAt: null }) 188 | .populate({ 189 | path: "createdBy", 190 | select: "firstName lastName email permissionLevel profileImage", 191 | }) 192 | .populate({ 193 | path: "updatedBy", 194 | populate: { 195 | path: "user", 196 | select: "firstName lastName email permissionLevel profileImage", 197 | }, 198 | select: "updatedAt", 199 | }) 200 | .sort({ createdAt: -1 }) 201 | .then((events) => { 202 | return events; 203 | }) 204 | .catch((error) => { 205 | throw new Error(error.message); 206 | }); 207 | }; 208 | 209 | /** 210 | Get deleted events - admin 211 | */ 212 | export const getDeletedEventsForAdmin = async () => { 213 | return await EventModel.find({ deletedAt: { $ne: null } }) 214 | .populate({ 215 | path: "createdBy", 216 | select: "firstName lastName email permissionLevel profileImage", 217 | }) 218 | .populate({ 219 | path: "updatedBy", 220 | populate: { 221 | path: "user", 222 | select: "firstName lastName email permissionLevel profileImage", 223 | }, 224 | select: "updatedAt", 225 | }) 226 | .sort({ createdAt: -1 }) 227 | .then((events) => { 228 | return events; 229 | }) 230 | .catch((error) => { 231 | throw new Error(error.message); 232 | }); 233 | }; 234 | 235 | // Recover deleted events 236 | export const recoverDeletedEvent = async (eventId: string) => { 237 | if (eventId) { 238 | return await EventModel.findById(eventId) 239 | .then(async (eventDetails) => { 240 | if (eventDetails) { 241 | if (eventDetails.deletedAt !== null) { 242 | eventDetails.deletedAt = null; 243 | eventDetails.deletedBy = null; 244 | return await eventDetails.save(); 245 | } else { 246 | return "Event is already recovered"; 247 | } 248 | } 249 | }) 250 | .catch((error) => { 251 | throw new Error(error.message); 252 | }); 253 | } else { 254 | throw new Error("Event ID not Passed"); 255 | } 256 | }; 257 | 258 | // Delete event permanently 259 | export const deleteEventPermanently = async (eventId: string) => { 260 | if (eventId) { 261 | return await EventModel.findByIdAndDelete(eventId) 262 | .then((deletedEvent) => { 263 | return deletedEvent; 264 | }) 265 | .catch((error) => { 266 | throw new Error(error.message); 267 | }); 268 | } else { 269 | throw new Error("Event ID not Passed"); 270 | } 271 | }; 272 | -------------------------------------------------------------------------------- /src/api/services/TopSpeaker.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { DocumentDefinition, Schema } from "mongoose"; 25 | import { ITopSpeaker, IUpdatedBy } from "../../interfaces"; 26 | import TopSpeakerModel from "../models/TopSpeaker.model"; 27 | /** 28 | save a speaker in the database 29 | */ 30 | export const insertTopSpeaker = async (topSpeakerData: DocumentDefinition) => { 31 | return await TopSpeakerModel.create(topSpeakerData) 32 | .then(async (topSpeaker) => { 33 | const initialUpdatedBy: IUpdatedBy = { 34 | user: topSpeaker.createdBy, 35 | updatedAt: new Date(), 36 | }; 37 | topSpeaker.updatedBy.push(initialUpdatedBy); 38 | await topSpeaker.save(); 39 | return topSpeaker; 40 | }) 41 | .catch((error) => { 42 | throw new Error(error.message); 43 | }); 44 | }; 45 | 46 | /** 47 | fetch a TopSpeaker in the system 48 | * @param topSpeakerId @type string 49 | */ 50 | export const getTopSpeaker = async (topSpeakerId: string) => { 51 | return await TopSpeakerModel.findById(topSpeakerId) 52 | .then((topSpeaker) => { 53 | if (topSpeaker && topSpeaker.deletedAt == null) { 54 | return topSpeaker; 55 | } else { 56 | throw new Error("Speaker is not found"); 57 | } 58 | }) 59 | .catch((error) => { 60 | throw new Error(error.message); 61 | }); 62 | }; 63 | 64 | /** 65 | fetch all the TopSpeakers in the system 66 | */ 67 | export const getTopSpeakers = async () => { 68 | return await TopSpeakerModel.aggregate([{ $match: { deletedAt: { $eq: null } } }]) 69 | .then((topSpeakers) => { 70 | return topSpeakers; 71 | }) 72 | .catch((error) => { 73 | throw new Error(error.message); 74 | }); 75 | }; 76 | 77 | /** 78 | update a TopSpeaker in the system 79 | * @param topSpeakerId @type string 80 | * @param updateData @type DocumentDefinition 81 | */ 82 | export const updateTopSpeaker = async ( 83 | topSpeakerId: string, 84 | updateData: DocumentDefinition, 85 | updatedBy: Schema.Types.ObjectId 86 | ) => { 87 | return await TopSpeakerModel.findById(topSpeakerId) 88 | .then(async (topSpeakerDetails) => { 89 | if (topSpeakerDetails) { 90 | if (updateData.title) { 91 | topSpeakerDetails.title = updateData.title; 92 | } 93 | 94 | if (updateData.description) { 95 | topSpeakerDetails.description = updateData.description; 96 | } 97 | 98 | if (updateData.imageUrl) { 99 | topSpeakerDetails.imageUrl = updateData.imageUrl; 100 | } 101 | 102 | if (updateData.socialMediaURLs && updateData.socialMediaURLs.facebook) { 103 | topSpeakerDetails.socialMediaURLs.facebook = updateData.socialMediaURLs.facebook; 104 | } 105 | 106 | if (updateData.socialMediaURLs && updateData.socialMediaURLs.instagram) { 107 | topSpeakerDetails.socialMediaURLs.instagram = updateData.socialMediaURLs.instagram; 108 | } 109 | 110 | if (updateData.socialMediaURLs && updateData.socialMediaURLs.linkedIn) { 111 | topSpeakerDetails.socialMediaURLs.linkedIn = updateData.socialMediaURLs.linkedIn; 112 | } 113 | 114 | if (updateData.socialMediaURLs && updateData.socialMediaURLs.twitter) { 115 | topSpeakerDetails.socialMediaURLs.twitter = updateData.socialMediaURLs.twitter; 116 | } 117 | 118 | if (updateData.socialMediaURLs && updateData.socialMediaURLs.web) { 119 | topSpeakerDetails.socialMediaURLs.web = updateData.socialMediaURLs.web; 120 | } 121 | 122 | const updateUserInfo: IUpdatedBy = { 123 | user: updatedBy, 124 | updatedAt: new Date(), 125 | }; 126 | 127 | topSpeakerDetails.updatedBy.push(updateUserInfo); 128 | return await topSpeakerDetails.save(); 129 | } else { 130 | return null; 131 | } 132 | }) 133 | .catch((error) => { 134 | throw new Error(error.message); 135 | }); 136 | }; 137 | 138 | /** 139 | delete a past event 140 | * @param topSpeakerId @type string 141 | */ 142 | export const deleteTopSpeaker = async (topSpeakerId: string, deletedBy: Schema.Types.ObjectId) => { 143 | return await TopSpeakerModel.findById(topSpeakerId) 144 | .then(async (topSpeakerDetails) => { 145 | if (topSpeakerDetails && topSpeakerDetails.deletedAt === null) { 146 | topSpeakerDetails.deletedAt = new Date(); 147 | topSpeakerDetails.deletedBy = deletedBy; 148 | return await topSpeakerDetails.save(); 149 | } else { 150 | return null; 151 | } 152 | }) 153 | .catch((error) => { 154 | throw new Error(error.message); 155 | }); 156 | }; 157 | 158 | /** 159 | recover a past event 160 | * @param topSpeakerId @type string 161 | */ 162 | export const recoverDeletedTopSpeaker = async (topSpeakerId: string) => { 163 | return await TopSpeakerModel.findById(topSpeakerId) 164 | .then(async (topSpeakerDetails) => { 165 | if (topSpeakerDetails && topSpeakerDetails.deletedAt) { 166 | topSpeakerDetails.deletedAt = undefined; 167 | topSpeakerDetails.deletedBy = undefined; 168 | return await topSpeakerDetails.save(); 169 | } else { 170 | const errorData = { 171 | message: "Top speaker information not found", 172 | dateTime: new Date(), 173 | }; 174 | 175 | throw new Error(JSON.stringify(errorData)); 176 | } 177 | }) 178 | .catch((error) => { 179 | throw new Error(error.message); 180 | }); 181 | }; 182 | 183 | /** 184 | Get all top speakers - admin 185 | */ 186 | export const getAllTopSpeakersForAdmin = async () => { 187 | return await TopSpeakerModel.find({ deletedAt: null }) 188 | .populate({ 189 | path: "createdBy", 190 | select: "firstName lastName email permissionLevel profileImage", 191 | }) 192 | .populate({ 193 | path: "updatedBy", 194 | populate: { 195 | path: "user", 196 | select: "firstName lastName email permissionLevel profileImage", 197 | }, 198 | select: "updatedAt", 199 | }) 200 | .then((topSpeakers) => { 201 | return topSpeakers; 202 | }) 203 | .catch((error) => { 204 | throw new Error(error.message); 205 | }); 206 | }; 207 | 208 | /** 209 | Get all deleted top speakers - admin 210 | */ 211 | export const getDeletedTopSpeakersForAdmin = async () => { 212 | return await TopSpeakerModel.find({ deletedAt: { $ne: null } }) 213 | .populate({ 214 | path: "createdBy", 215 | select: "firstName lastName email permissionLevel profileImage", 216 | }) 217 | .populate({ 218 | path: "updatedBy", 219 | populate: { 220 | path: "user", 221 | select: "firstName lastName email permissionLevel profileImage", 222 | }, 223 | select: "updatedAt", 224 | }) 225 | .then((topSpeakers) => { 226 | return topSpeakers; 227 | }) 228 | .catch((error) => { 229 | throw new Error(error.message); 230 | }); 231 | }; 232 | 233 | /** 234 | delete a past event 235 | * @param topSpeakerId @type string 236 | */ 237 | export const permenentDeleteTopSpeaker = async (topSpeakerId: string) => { 238 | if (topSpeakerId) { 239 | return await TopSpeakerModel.findByIdAndDelete(topSpeakerId) 240 | .then((deletedTopSpeaker) => { 241 | return deletedTopSpeaker; 242 | }) 243 | .catch((error) => { 244 | throw new Error(error.message); 245 | }); 246 | } else { 247 | throw new Error("TopSpeaker ID not Passed"); 248 | } 249 | }; 250 | -------------------------------------------------------------------------------- /src/api/services/Webinar.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { DocumentDefinition, Schema } from "mongoose"; 25 | import { IWebinar, IUpdatedBy } from "../../interfaces"; 26 | import WebinarModel from "../models/Webinar.model"; 27 | /** 28 | * Save a webinar in the database 29 | * @param {IWebinar} webinarData 30 | * @returns {IWebinar} New webinar document 31 | */ 32 | export const insertWebinar = async (webinarData: DocumentDefinition) => { 33 | return await WebinarModel.create(webinarData) 34 | .then(async (webinar) => { 35 | const initialUpdatedBy: IUpdatedBy = { 36 | user: webinar.createdBy, 37 | updatedAt: new Date(), 38 | }; 39 | webinar.updatedBy.push(initialUpdatedBy); 40 | await webinar.save(); 41 | return webinar; 42 | }) 43 | .catch((error) => { 44 | throw new Error(error.message); 45 | }); 46 | }; 47 | 48 | /** 49 | * Fetch a webinar in the database 50 | * @param webinarId @type string 51 | * @returns {IWebinar} Webinar document for relevant ID 52 | */ 53 | export const fetchWebinarById = async (webinarId: string) => { 54 | return await WebinarModel.findById(webinarId) 55 | .then((webinar) => { 56 | return webinar; 57 | }) 58 | .catch((error) => { 59 | throw new Error(error.message); 60 | }); 61 | }; 62 | 63 | /** 64 | * Fetch all the webinars in the database 65 | * @returns {IWebinar} All webinar documents in the database 66 | */ 67 | export const fetchWebinars = async () => { 68 | return await WebinarModel.aggregate([{ $match: { deletedAt: { $eq: null } } }]) 69 | .sort({ dateTime: -1 }) 70 | .then((webinars) => { 71 | return webinars; 72 | }) 73 | .catch((error) => { 74 | throw new Error(error.message); 75 | }); 76 | }; 77 | 78 | /** 79 | * Fetch all the past webinars in the database 80 | * @returns {IWebinar} All the past webinar documents in the database 81 | */ 82 | export const fetchPastWebinars = async () => { 83 | return await WebinarModel.find({ webinarType: "PAST" }) 84 | .sort({ dateTime: -1 }) 85 | .then((webinars) => { 86 | return webinars; 87 | }) 88 | .catch((error) => { 89 | throw new Error(error.message); 90 | }); 91 | }; 92 | 93 | /** 94 | * Fetch an upcoming webinars in the database 95 | * @returns {IWebinar} All the upcoming webinar documents in the database 96 | */ 97 | export const fetchUpcomingWebinar = async () => { 98 | return await WebinarModel.findOne({ webinarType: "UPCOMING" }) 99 | .limit(1) 100 | .sort({ $natural: -1 }) 101 | .then((webinar) => { 102 | return webinar; 103 | }) 104 | .catch((error) => { 105 | throw new Error(error.message); 106 | }); 107 | }; 108 | 109 | /** 110 | * Update a webinar in the database 111 | * @param webinarId @type string 112 | * @param updateData @type DocumentDefinition 113 | */ 114 | export const updateWebinar = async ( 115 | webinarId: string, 116 | webinarData: DocumentDefinition, 117 | updatedBy: Schema.Types.ObjectId 118 | ) => { 119 | return await WebinarModel.findById(webinarId) 120 | .then(async (webinarDetails) => { 121 | if (webinarDetails) { 122 | if (!webinarDetails.deletedAt) { 123 | if (webinarData.title) { 124 | webinarDetails.title = webinarData.title; 125 | } 126 | 127 | if (webinarData.description) { 128 | webinarDetails.description = webinarData.description; 129 | } 130 | 131 | if (webinarData.imageUrl) { 132 | webinarDetails.imageUrl = webinarData.imageUrl; 133 | } 134 | 135 | if (webinarData.dateTime) { 136 | webinarDetails.dateTime = webinarData.dateTime; 137 | } 138 | 139 | if (webinarData.tags) { 140 | webinarDetails.tags = webinarData.tags; 141 | } 142 | 143 | if (webinarData.link) { 144 | webinarDetails.link = webinarData.link; 145 | } 146 | 147 | if (webinarData.registrationLink) { 148 | webinarDetails.registrationLink = webinarData.registrationLink; 149 | } 150 | 151 | if (webinarData.webinarType) { 152 | webinarDetails.webinarType = webinarData.webinarType; 153 | } 154 | 155 | const updateUserInfo: IUpdatedBy = { 156 | user: updatedBy, 157 | updatedAt: new Date(), 158 | }; 159 | webinarDetails.updatedBy.push(updateUserInfo); 160 | return await webinarDetails.save(); 161 | } else { 162 | throw new Error("Webinar is not found"); 163 | } 164 | } else { 165 | return null; 166 | } 167 | }) 168 | .catch((error) => { 169 | throw new Error(error.message); 170 | }); 171 | }; 172 | 173 | /** 174 | * Delete a webinar in the database 175 | * @param webinarId @type string 176 | */ 177 | export const removeWebinar = async (webinarId: string, deletedBy: Schema.Types.ObjectId) => { 178 | return await WebinarModel.findById(webinarId) 179 | .then(async (webinarDetails) => { 180 | if (webinarDetails && webinarDetails.deletedAt === null) { 181 | webinarDetails.deletedAt = new Date(); 182 | webinarDetails.deletedBy = deletedBy; 183 | return await webinarDetails.save(); 184 | } else { 185 | return "Webinar not found"; 186 | } 187 | }) 188 | .catch((error) => { 189 | throw new Error(error.message); 190 | }); 191 | }; 192 | 193 | export const getAllWebinarsForAdmin = async () => { 194 | return await WebinarModel.find({ deletedAt: null }) 195 | .populate({ 196 | path: "createdBy", 197 | select: "firstName lastName email permissionLevel profileImage", 198 | }) 199 | .populate({ 200 | path: "updatedBy", 201 | populate: { 202 | path: "user", 203 | select: "firstName lastName email permissionLevel profileImage", 204 | }, 205 | select: "updatedAt", 206 | }) 207 | .sort({ dateTime: -1 }) 208 | .then((webinars) => { 209 | return webinars; 210 | }) 211 | .catch((error) => { 212 | throw new Error(error.message); 213 | }); 214 | }; 215 | 216 | export const getDeletedWebinarsForAdmin = async () => { 217 | return await WebinarModel.find({ deletedAt: { $ne: null } }) 218 | .populate({ 219 | path: "createdBy", 220 | select: "firstName lastName email permissionLevel profileImage", 221 | }) 222 | .populate({ 223 | path: "updatedBy", 224 | populate: { 225 | path: "user", 226 | select: "firstName lastName email permissionLevel profileImage", 227 | }, 228 | select: "updatedAt", 229 | }) 230 | .sort({ dateTime: -1 }) 231 | .then((webinars) => { 232 | return webinars; 233 | }) 234 | .catch((error) => { 235 | throw new Error(error.message); 236 | }); 237 | }; 238 | 239 | /** 240 | * Recover Deleted webinar 241 | * @param webinarId @type string 242 | */ 243 | export const recoverDeletedWebinar = async (webinarId: string) => { 244 | return await WebinarModel.findById(webinarId) 245 | .then(async (webinarDetails) => { 246 | if (webinarDetails) { 247 | if (webinarDetails.deletedAt !== null) { 248 | webinarDetails.deletedAt = null; 249 | webinarDetails.deletedBy = null; 250 | 251 | return await webinarDetails.save(); 252 | } else { 253 | return "Webinar is already recovered"; 254 | } 255 | } else { 256 | return "Webinar not found"; 257 | } 258 | }) 259 | .catch((error) => { 260 | throw new Error(error.message); 261 | }); 262 | }; 263 | 264 | /** 265 | delete an webinar in the system 266 | * @param webinarId @type string 267 | */ 268 | 269 | export const deleteWebinarPermanently = async (webinarId: string) => { 270 | if (webinarId) { 271 | return await WebinarModel.findByIdAndDelete(webinarId) 272 | .then((webinar) => { 273 | return webinar; 274 | }) 275 | .catch((error) => { 276 | throw new Error(error.message); 277 | }); 278 | } else { 279 | throw new Error("Webinar ID not Passed"); 280 | } 281 | }; 282 | -------------------------------------------------------------------------------- /src/api/controllers/Event.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import EventService from "../services"; 26 | import ImageService from "../../util/image.handler"; 27 | 28 | /** 29 | * @param {Request} request - Request from the frontend 30 | * @param {Response} response - Response that need to send to the client 31 | * @param {NextFunction} next - Next function 32 | * @returns {IEvent} Event document 33 | */ 34 | export const insertEvent = async (request: Request, response: Response, next: NextFunction) => { 35 | const bucketDirectoryName = "event-flyers"; 36 | 37 | const eventFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 38 | request.body.createdBy = request.user && request.user._id ? request.user._id : null; 39 | request.body.imageUrl = eventFlyerPath; 40 | await EventService.insertEvent(request.body) 41 | .then((data) => { 42 | request.handleResponse.successRespond(response)(data); 43 | next(); 44 | }) 45 | .catch((error: any) => { 46 | request.handleResponse.errorRespond(response)(error.message); 47 | next(); 48 | }); 49 | }; 50 | 51 | /** 52 | * @param {Request} request - Request from the frontend 53 | * @param {Response} response - Response that need to send to the client 54 | * @param {NextFunction} next - Next function 55 | * @returns {IEvent} Event document 56 | */ 57 | export const getEvent = async (request: Request, response: Response, next: NextFunction) => { 58 | const eventId = request.params.eventId; 59 | if (eventId) { 60 | await EventService.getEvent(request.params.eventId) 61 | .then((data) => { 62 | request.handleResponse.successRespond(response)(data); 63 | next(); 64 | }) 65 | .catch((error: any) => { 66 | request.handleResponse.errorRespond(response)(error.message); 67 | next(); 68 | }); 69 | } else { 70 | request.handleResponse.errorRespond(response)("Event ID not found"); 71 | } 72 | }; 73 | 74 | /** 75 | * @param {Request} request - Request from the frontend 76 | * @param {Response} response - Response that need to send to the client 77 | * @param {NextFunction} next - Next function 78 | * @returns {IEvnet[]} All events in the system 79 | */ 80 | export const getEvents = async (request: Request, response: Response, next: NextFunction) => { 81 | await EventService.getEvents() 82 | .then((data) => { 83 | request.handleResponse.successRespond(response)(data); 84 | next(); 85 | }) 86 | .catch((error: any) => { 87 | request.handleResponse.errorRespond(response)(error.message); 88 | next(); 89 | }); 90 | }; 91 | 92 | /** 93 | * @param {Request} request - Request from the frontend 94 | * @param {Response} response - Response that need to send to the client 95 | * @param {NextFunction} next - Next function 96 | * @returns {IEvnet[]} All past events in the system 97 | */ 98 | export const getPastEvents = async (request: Request, response: Response, next: NextFunction) => { 99 | await EventService.getPastEvents() 100 | .then((data) => { 101 | request.handleResponse.successRespond(response)(data); 102 | next(); 103 | }) 104 | .catch((error: any) => { 105 | request.handleResponse.errorRespond(response)(error.message); 106 | next(); 107 | }); 108 | }; 109 | 110 | /** 111 | * @param {Request} request - Request from the frontend 112 | * @param {Response} response - Response that need to send to the client 113 | * @param {NextFunction} next - Next function 114 | * @returns {IEvnet} Upcoming event details 115 | */ 116 | export const getUpcomingEvent = async (request: Request, response: Response, next: NextFunction) => { 117 | await EventService.getUpcomingEvent() 118 | .then((data) => { 119 | request.handleResponse.successRespond(response)(data); 120 | next(); 121 | }) 122 | .catch((error: any) => { 123 | request.handleResponse.errorRespond(response)(error.message); 124 | next(); 125 | }); 126 | }; 127 | 128 | /** 129 | * @param {Request} request - Request from the frontend 130 | * @param {Response} response - Response that need to send to the client 131 | * @param {NextFunction} next - Next function 132 | * @returns {IEvent} - Updated event details 133 | */ 134 | export const updateEvent = async (request: Request, response: Response, next: NextFunction) => { 135 | if (request.file) { 136 | const bucketDirectoryName = "event-flyers"; 137 | 138 | const eventFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 139 | request.body.imageUrl = eventFlyerPath; 140 | } 141 | const eventId = request.params.eventId; 142 | const updatedBy = request.user && request.user._id ? request.user._id : null; 143 | 144 | if (eventId) { 145 | await EventService.updateEvent(eventId, request.body, updatedBy) 146 | .then((data) => { 147 | request.handleResponse.successRespond(response)(data); 148 | next(); 149 | }) 150 | .catch((error: any) => { 151 | request.handleResponse.errorRespond(response)(error.message); 152 | next(); 153 | }); 154 | } else { 155 | request.handleResponse.errorRespond(response)("Event ID not found"); 156 | } 157 | }; 158 | 159 | /** 160 | * @param {Request} request - Request from the frontend 161 | * @param {Response} response - Response that need to send to the client 162 | * @param {NextFunction} next - Next function 163 | * @returns {IEvent} - Deleted event details 164 | */ 165 | export const deleteEvent = async (request: Request, response: Response, next: NextFunction) => { 166 | const eventId = request.params.eventId; 167 | const deletedBy = request.user && request.user._id ? request.user._id : null; 168 | if (eventId) { 169 | await EventService.deleteEvent(eventId, deletedBy) 170 | .then((data) => { 171 | request.handleResponse.successRespond(response)(data); 172 | next(); 173 | }) 174 | .catch((error: any) => { 175 | request.handleResponse.errorRespond(response)(error.message); 176 | next(); 177 | }); 178 | } else { 179 | request.handleResponse.errorRespond(response)("Event ID not found"); 180 | } 181 | }; 182 | 183 | export const eventsForAdmin = async (request: Request, response: Response, next: NextFunction) => { 184 | await EventService.getAllEventsForAdmin() 185 | .then((data: any) => { 186 | request.handleResponse.successRespond(response)(data); 187 | next(); 188 | }) 189 | .catch((error: any) => { 190 | request.handleResponse.errorRespond(response)(error.message); 191 | next(); 192 | }); 193 | }; 194 | 195 | export const deletedEventsForAdmin = async (request: Request, response: Response, next: NextFunction) => { 196 | await EventService.getDeletedEventsForAdmin() 197 | .then((data: any) => { 198 | request.handleResponse.successRespond(response)(data); 199 | next(); 200 | }) 201 | .catch((error: any) => { 202 | request.handleResponse.errorRespond(response)(error.message); 203 | next(); 204 | }); 205 | }; 206 | 207 | export const recoverRemovedEvent = async (request: Request, response: Response, next: NextFunction) => { 208 | const eventId = request.params.eventId; 209 | if (eventId) { 210 | await EventService.recoverDeletedEvent(eventId) 211 | .then((data) => { 212 | request.handleResponse.successRespond(response)(data); 213 | next(); 214 | }) 215 | .catch((error) => { 216 | request.handleResponse.errorRespond(response)(error.message); 217 | next(); 218 | }); 219 | } else { 220 | request.handleResponse.errorRespond(response)("Event ID not found"); 221 | } 222 | }; 223 | 224 | export const deleteEventPermanently = async (request: Request, response: Response, next: NextFunction) => { 225 | const eventId = request.params.eventId; 226 | if (eventId) { 227 | await EventService.deleteEventPermanently(eventId) 228 | .then((data) => { 229 | request.handleResponse.successRespond(response)(data); 230 | next(); 231 | }) 232 | .catch((error) => { 233 | request.handleResponse.errorRespond(response)(error.message); 234 | next(); 235 | }); 236 | } else { 237 | request.handleResponse.errorRespond(response)("Event ID not found"); 238 | } 239 | }; 240 | -------------------------------------------------------------------------------- /src/api/controllers/Webinar.controller.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sat Feb 12 2022 3 | * 4 | * The GNU General Public License v3.0 5 | * Copyright (c) 2022 MS Club SLIIT Authors 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program at 14 | * 15 | * https://www.gnu.org/licenses/ 16 | * 17 | * This program is distributed in the hope that it will be useful 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | */ 23 | 24 | import { Request, Response, NextFunction } from "express"; 25 | import WebinarService from "../services"; 26 | import ImageService from "../../util/image.handler"; 27 | 28 | /** 29 | * @param {Request} request - Request from the frontend 30 | * @param {Response} response - Response that need to send to the client 31 | * @param {NextFunction} next - Next function 32 | * @returns {IWebinar} New webinar document 33 | */ 34 | export const insertWebinar = async (request: Request, response: Response, next: NextFunction) => { 35 | const bucketDirectoryName = "webinar-flyers"; 36 | const webinarFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 37 | request.body.createdBy = request.user && request.user._id ? request.user._id : null; 38 | request.body.imageUrl = webinarFlyerPath; 39 | await WebinarService.insertWebinar(request.body) 40 | .then((data) => { 41 | request.handleResponse.successRespond(response)(data); 42 | next(); 43 | }) 44 | .catch((error: any) => { 45 | request.handleResponse.errorRespond(response)(error.message); 46 | next(); 47 | }); 48 | }; 49 | /** 50 | * @param {Request} request - Request from the frontend 51 | * @param {Response} response - Response that need to send to the client 52 | * @param {NextFunction} next - Next function 53 | * @returns {IWebinar} Webinar document for relevent ID 54 | */ 55 | export const getWebinarById = async (request: Request, response: Response, next: NextFunction) => { 56 | const webinarId = request.params.webinarId; 57 | if (webinarId) { 58 | await WebinarService.fetchWebinarById(request.params.webinarId) 59 | .then((data) => { 60 | request.handleResponse.successRespond(response)(data); 61 | next(); 62 | }) 63 | .catch((error: any) => { 64 | request.handleResponse.errorRespond(response)(error.message); 65 | next(); 66 | }); 67 | } else { 68 | request.handleResponse.errorRespond(response)("WebinarId not found"); 69 | } 70 | }; 71 | /** 72 | * @param {Request} request - Request from the frontend 73 | * @param {Response} response - Response that need to send to the client 74 | * @param {NextFunction} next - Next function 75 | * @returns {IWebinar} Get all webinars in the system 76 | */ 77 | export const getWebinars = async (request: Request, response: Response, next: NextFunction) => { 78 | await WebinarService.fetchWebinars() 79 | .then((data) => { 80 | request.handleResponse.successRespond(response)(data); 81 | next(); 82 | }) 83 | .catch((error: any) => { 84 | request.handleResponse.errorRespond(response)(error.message); 85 | next(); 86 | }); 87 | }; 88 | /** 89 | * @param {Request} request - Request from the frontend 90 | * @param {Response} response - Response that need to send to the client 91 | * @param {NextFunction} next - Next function 92 | * @returns {IWebinar[]} Get past webinars 93 | */ 94 | export const getPastWebinars = async (request: Request, response: Response, next: NextFunction) => { 95 | await WebinarService.fetchPastWebinars() 96 | .then((data) => { 97 | request.handleResponse.successRespond(response)(data); 98 | next(); 99 | }) 100 | .catch((error: any) => { 101 | request.handleResponse.errorRespond(response)(error.message); 102 | next(); 103 | }); 104 | }; 105 | /** 106 | * @param {Request} request - Request from the frontend 107 | * @param {Response} response - Response that need to send to the client 108 | * @param {NextFunction} next - Next function 109 | * @returns {IWebinar} Get upcoming webinar document 110 | */ 111 | export const getUpcomingWebinar = async (request: Request, response: Response, next: NextFunction) => { 112 | await WebinarService.fetchUpcomingWebinar() 113 | .then((data) => { 114 | request.handleResponse.successRespond(response)(data); 115 | next(); 116 | }) 117 | .catch((error: any) => { 118 | request.handleResponse.errorRespond(response)(error.message); 119 | next(); 120 | }); 121 | }; 122 | /** 123 | * @param {Request} request - Request from the frontend 124 | * @param {Response} response - Response that need to send to the client 125 | * @param {NextFunction} next - Next function 126 | * @returns {IWebinar} Updated webinar document 127 | */ 128 | export const updateWebinar = async (request: Request, response: Response, next: NextFunction) => { 129 | if (request.file) { 130 | const bucketDirectoryName = "webinar-flyers"; 131 | 132 | const webinarFlyerPath = await ImageService.uploadImage(request.file, bucketDirectoryName); 133 | request.body.imageUrl = webinarFlyerPath; 134 | } 135 | 136 | const webinarId = request.params.webinarId; 137 | const updatedBy = request.user && request.user._id ? request.user._id : null; 138 | 139 | if (webinarId) { 140 | await WebinarService.updateWebinar(webinarId, request.body, updatedBy) 141 | .then((data) => { 142 | request.handleResponse.successRespond(response)(data); 143 | next(); 144 | }) 145 | .catch((error: any) => { 146 | request.handleResponse.errorRespond(response)(error.message); 147 | next(); 148 | }); 149 | } else { 150 | request.handleResponse.errorRespond(response)("WebinarId not found"); 151 | } 152 | }; 153 | /** 154 | * @param {Request} request - Request from the frontend 155 | * @param {Response} response - Response that need to send to the client 156 | * @param {NextFunction} next - Next function 157 | * @returns {IWebinar} Deleted webinar document 158 | */ 159 | export const deleteWebinar = async (request: Request, response: Response, next: NextFunction) => { 160 | const webinarId = request.params.webinarId; 161 | const deletedBy = request.user && request.user._id ? request.user._id : null; 162 | 163 | if (webinarId) { 164 | await WebinarService.removeWebinar(request.params.webinarId, deletedBy) 165 | .then((data) => { 166 | request.handleResponse.successRespond(response)(data); 167 | next(); 168 | }) 169 | .catch((error: any) => { 170 | request.handleResponse.errorRespond(response)(error.message); 171 | next(); 172 | }); 173 | } else { 174 | request.handleResponse.errorRespond(response)("WebinarId not found"); 175 | } 176 | }; 177 | 178 | export const webinarsForAdmin = async (request: Request, response: Response, next: NextFunction) => { 179 | await WebinarService.getAllWebinarsForAdmin() 180 | .then((data: any) => { 181 | request.handleResponse.successRespond(response)(data); 182 | next(); 183 | }) 184 | .catch((error: any) => { 185 | request.handleResponse.errorRespond(response)(error.message); 186 | next(); 187 | }); 188 | }; 189 | 190 | export const deletedWebinarsForAdmin = async (request: Request, response: Response, next: NextFunction) => { 191 | await WebinarService.getDeletedWebinarsForAdmin() 192 | .then((data: any) => { 193 | request.handleResponse.successRespond(response)(data); 194 | next(); 195 | }) 196 | .catch((error: any) => { 197 | request.handleResponse.errorRespond(response)(error.message); 198 | next(); 199 | }); 200 | }; 201 | 202 | export const recoverRemovedWebinar = async (request: Request, response: Response, next: NextFunction) => { 203 | const webinarId = request.params.webinarId; 204 | if (webinarId) { 205 | await WebinarService.recoverDeletedWebinar(webinarId) 206 | .then((data: any) => { 207 | request.handleResponse.successRespond(response)(data); 208 | next(); 209 | }) 210 | .catch((error: any) => { 211 | request.handleResponse.errorRespond(response)(error.message); 212 | next(); 213 | }); 214 | } else { 215 | request.handleResponse.errorRespond(response)("WebinarId not found"); 216 | } 217 | }; 218 | 219 | /** 220 | delete an webinar in the system 221 | * @param webinarId @type string 222 | */ 223 | export const deleteWebinarPermanently = async (request: Request, response: Response, next: NextFunction) => { 224 | const webinarId = request.params.webinarId; 225 | if (webinarId) { 226 | await WebinarService.deleteWebinarPermanently(webinarId) 227 | .then((data: any) => { 228 | request.handleResponse.successRespond(response)(data); 229 | next(); 230 | }) 231 | .catch((error: any) => { 232 | request.handleResponse.errorRespond(response)(error.message); 233 | next(); 234 | }); 235 | } else { 236 | request.handleResponse.errorRespond(response)("WebinarId not found"); 237 | } 238 | }; 239 | -------------------------------------------------------------------------------- /src/templates/Email-Template.html: -------------------------------------------------------------------------------- 1 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 |
34 |
35 | 53 | 54 | 55 | 221 | 222 | 223 |
56 | 57 | 58 | 59 | 69 | 70 | 71 | 212 | 213 | 214 | 217 | 218 | 219 |
60 | 67 |
68 |
72 |
73 | 74 | 75 | 76 | 86 | 87 | 88 | 91 | 92 | 93 | 96 | 97 | 98 | 99 | 207 | 208 | 209 |
77 | Hi {{name}} 78 | 79 | 84 | 85 |
89 | Email body message 90 |
94 | Thank you. 95 |
104 | 105 | 106 | 107 | 114 | 203 | 204 | 205 |
108 | 113 | 115 | 116 | 117 | 118 | 123 | 124 | 125 | 134 | 135 | 136 | 199 | 200 | 201 |
119 | Sri Lanka Institute Of Information Technology,
120 | Malabe,
121 | Sri Lanka. 122 |
126 | 131 | www.msclubsliit.org 132 | 133 |
137 | 138 | 139 | 140 | 154 | 168 | 182 | 195 | 196 | 197 |
198 |
202 |
206 |
210 |
211 |
215 | © 2021 MS Club of SLIIT All Rights Reserved. 216 |
220 |
224 |
225 |
226 |
227 |
228 | 229 | 230 | --------------------------------------------------------------------------------