├── .env.example ├── .github └── workflows │ ├── admin.deployment.yaml │ ├── auth.deployment.yaml │ ├── chat.deployment.yaml │ ├── client-app.deployment.yaml │ ├── deploy-manifest.yaml │ ├── job.deployment.yaml │ ├── payment.deployment.yaml │ └── profile.deployment.yaml ├── .gitignore ├── README.md ├── admin ├── .dockerignore ├── .prettierrc.json ├── Dockerfile.dev ├── Dockerfile.prod ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── src │ ├── config │ │ ├── appConfig.ts │ │ ├── db.connection.ts │ │ ├── dependencies.ts │ │ └── kafka.connection.ts │ ├── controllers │ │ ├── admin │ │ │ ├── dashboard.controller.ts │ │ │ ├── dashboardGraph.controller.ts │ │ │ └── index.ts │ │ ├── candidate │ │ │ ├── blockUnblock.controller.ts │ │ │ ├── getCandidates.controller.ts │ │ │ ├── index.ts │ │ │ └── viewProfile.controller.ts │ │ ├── index.ts │ │ ├── job │ │ │ ├── blockUnblock.controller.ts │ │ │ ├── getJob.controller.ts │ │ │ ├── getJobs.controller.ts │ │ │ └── index.ts │ │ ├── membership │ │ │ ├── blockUnblock.controller.ts │ │ │ ├── create.controller.ts │ │ │ ├── getMembership.controller.ts │ │ │ ├── getMemberships.controller.ts │ │ │ ├── index.ts │ │ │ └── update.controller.ts │ │ ├── payment │ │ │ ├── getPayments.controller.ts │ │ │ └── index.ts │ │ ├── recruiter │ │ │ ├── blockUnblock.controller.ts │ │ │ ├── getRecruiters.controller.ts │ │ │ ├── index.ts │ │ │ ├── search.controller.ts │ │ │ └── viewProfile.controller.ts │ │ └── search │ │ │ ├── index.ts │ │ │ └── search.controller.ts │ ├── entities │ │ ├── candidate.ts │ │ ├── index.ts │ │ ├── job.ts │ │ ├── membership-plan.ts │ │ ├── payment.ts │ │ └── recruiter.ts │ ├── frameworks │ │ ├── database │ │ │ ├── index.ts │ │ │ └── models │ │ │ │ ├── candidate.ts │ │ │ │ ├── index.ts │ │ │ │ ├── job.ts │ │ │ │ ├── membershipPlan.ts │ │ │ │ ├── payments.ts │ │ │ │ └── recruiter.ts │ │ ├── express │ │ │ ├── app.ts │ │ │ └── routes │ │ │ │ ├── candidate.ts │ │ │ │ ├── dashboard.ts │ │ │ │ ├── index.ts │ │ │ │ ├── job.ts │ │ │ │ ├── membershipplan.ts │ │ │ │ ├── payment.ts │ │ │ │ ├── recruiter.ts │ │ │ │ └── search.ts │ │ ├── repositories │ │ │ └── mongo │ │ │ │ ├── candidate.repository.ts │ │ │ │ ├── index.ts │ │ │ │ ├── job.repository.ts │ │ │ │ ├── membership.repository.ts │ │ │ │ ├── payment.repository.ts │ │ │ │ └── recruiter.repository.ts │ │ ├── types │ │ │ ├── dependency.ts │ │ │ ├── job.ts │ │ │ ├── membershipPlan.ts │ │ │ ├── payment.ts │ │ │ └── user.ts │ │ └── utils │ │ │ └── kafka-events │ │ │ ├── consumers │ │ │ ├── candidate-profile-updated-consumer.ts │ │ │ ├── job-created-consumer.ts │ │ │ ├── job-deleted-consumer.ts │ │ │ ├── job-updated-consumer.ts │ │ │ ├── payment-created-consumer.ts │ │ │ ├── recruiter-profile-updated-consumer.ts │ │ │ ├── user-created-consumer.ts │ │ │ └── user-updated-consumer.ts │ │ │ ├── handleMessage.ts │ │ │ └── publishers │ │ │ ├── job-updated-publisher.ts │ │ │ ├── membership-plan-created-publisher.ts │ │ │ ├── membership-plan-updated-publisher .ts │ │ │ └── user-updated-publisher.ts │ ├── index.ts │ └── useCases │ │ ├── candidate │ │ ├── blockunblock.ts │ │ ├── getCandidate.ts │ │ ├── getCandidates.ts │ │ └── index.ts │ │ ├── dashboard │ │ ├── getCardsDetails.ts │ │ ├── getGraphDetails.ts │ │ └── index.ts │ │ ├── index.ts │ │ ├── job │ │ ├── blockUnblock.ts │ │ ├── getJob.ts │ │ ├── getJobs.ts │ │ └── index.ts │ │ ├── membership │ │ ├── blockUnblock.ts │ │ ├── create.ts │ │ ├── getPlan.ts │ │ ├── getPlans.ts │ │ ├── index.ts │ │ └── updatePlan.ts │ │ ├── payment │ │ ├── getPayments.ts │ │ └── index.ts │ │ ├── recruiter │ │ ├── blockUnblock.ts │ │ ├── getRecruiter.ts │ │ ├── getRecruiters.ts │ │ └── index.ts │ │ └── search │ │ ├── index.ts │ │ └── search.ts └── tsconfig.json ├── auth ├── .dockerignore ├── .prettierrc.json ├── Dockerfile.dev ├── Dockerfile.prod ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── src │ ├── config │ │ ├── appConfig.ts │ │ ├── db.connection.ts │ │ ├── dependencies.ts │ │ └── kafka.connection.ts │ ├── controllers │ │ ├── auth │ │ │ ├── index.ts │ │ │ ├── signin.controller.ts │ │ │ ├── signout.controller.ts │ │ │ ├── signup.controller.ts │ │ │ └── signupEmailOtpVerification.controller.ts │ │ ├── index.ts │ │ ├── jwtRefresh │ │ │ ├── index.ts │ │ │ └── jwtRefresh.controller.ts │ │ ├── otp │ │ │ ├── index.ts │ │ │ ├── sendotp.controller.ts │ │ │ └── verifyotp.controller.ts │ │ └── password │ │ │ ├── index.ts │ │ │ └── update.controller.ts │ ├── entities │ │ ├── index.ts │ │ └── user.ts │ ├── frameworks │ │ ├── database │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ └── models │ │ │ │ ├── index.ts │ │ │ │ └── users.ts │ │ ├── express │ │ │ ├── app.ts │ │ │ └── routes │ │ │ │ ├── admin.ts │ │ │ │ ├── candidate.ts │ │ │ │ ├── index.ts │ │ │ │ ├── jwt-refresh.ts │ │ │ │ ├── otp.ts │ │ │ │ └── recruiter.ts │ │ ├── middlewares │ │ │ ├── signinValidation.ts │ │ │ └── signupValidation.ts │ │ ├── repositories │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ └── users.repository.ts │ │ ├── types │ │ │ ├── dependency.ts │ │ │ ├── otp.ts │ │ │ └── user.ts │ │ └── utils │ │ │ ├── constants.ts │ │ │ ├── jwtToken.ts │ │ │ ├── kafka-events │ │ │ ├── consumers │ │ │ │ └── user-updated-consumer.ts │ │ │ ├── handleMessage.ts │ │ │ └── publishers │ │ │ │ └── user-created-publisher.ts │ │ │ ├── password.ts │ │ │ ├── sendEmail.ts │ │ │ └── twilio.ts │ ├── index.ts │ └── useCases │ │ ├── auth │ │ ├── index.ts │ │ ├── refreshToken.ts │ │ ├── signin.ts │ │ ├── signup.ts │ │ └── updatePassword.ts │ │ ├── index.ts │ │ └── otp │ │ ├── authEmailVerificationOtp.ts │ │ ├── index.ts │ │ ├── sendOtpEmail.ts │ │ ├── sendOtpMobile.ts │ │ ├── verifyEmailOtp.ts │ │ └── verifyMobileOtp.ts └── tsconfig.json ├── chat ├── .dockerignore ├── .prettierrc.json ├── Dockerfile.dev ├── Dockerfile.prod ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── src │ ├── config │ │ ├── appConfig.ts │ │ ├── db.connection.ts │ │ ├── dependencies.ts │ │ └── kafka.connection.ts │ ├── controllers │ │ ├── chat │ │ │ ├── getChatRoom.controller.ts │ │ │ ├── getChatRooms.controller.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ └── notification │ │ │ ├── deleteNotifications.controller.ts │ │ │ ├── deleteNotificationsOfASender.controller.ts │ │ │ ├── getNotifications.controller.ts │ │ │ ├── getNotificationsCount.controller.ts │ │ │ ├── getUnreadMessagesCount.controller.ts │ │ │ └── index.ts │ ├── entities │ │ ├── chat-room.ts │ │ ├── notification.ts │ │ └── users.ts │ ├── frameworks │ │ ├── database │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ └── models │ │ │ │ ├── chatRoom.ts │ │ │ │ ├── index.ts │ │ │ │ ├── message.ts │ │ │ │ ├── notification.ts │ │ │ │ └── user.ts │ │ ├── express │ │ │ ├── app.ts │ │ │ └── routes │ │ │ │ ├── index.ts │ │ │ │ └── routes.ts │ │ ├── repositories │ │ │ └── mongo │ │ │ │ ├── chatRoom.repository.ts │ │ │ │ ├── index.ts │ │ │ │ ├── message.repository.ts │ │ │ │ ├── notifications.repository.ts │ │ │ │ └── user.repository.ts │ │ ├── types │ │ │ ├── chatRoom.ts │ │ │ ├── dependency.ts │ │ │ ├── message.ts │ │ │ ├── notification.ts │ │ │ └── user.ts │ │ ├── utils │ │ │ └── kafka-events │ │ │ │ ├── consumers │ │ │ │ ├── candidate-profile-updated-consumer.ts │ │ │ │ ├── user-created-consumer.ts │ │ │ │ └── user-updated-consumer.ts │ │ │ │ └── handleMessage.ts │ │ └── webSocket │ │ │ └── socket.ts │ ├── index.ts │ └── useCase │ │ ├── chat │ │ ├── getChatRoom.ts │ │ ├── getChatRooms.ts │ │ └── index.ts │ │ ├── index.ts │ │ └── notification │ │ ├── deleteNotifications.ts │ │ ├── deleteNotificationsBySenderId.ts │ │ ├── getNotifications.ts │ │ ├── getNotificationsCount.ts │ │ ├── getUnreadMessagesCount.ts │ │ └── index.ts └── tsconfig.json ├── client ├── .dockerignore ├── .eslintrc.cjs ├── Dockerfile.dev ├── Dockerfile.prod ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ └── favicon-32x32.png ├── src │ ├── App.tsx │ ├── assets │ │ ├── auth │ │ │ ├── candidate-login.svg │ │ │ └── recruiter-login.svg │ │ ├── chat │ │ │ └── double-chat-bubble-icon.svg │ │ ├── google │ │ │ └── google-icon.svg │ │ ├── jobs │ │ │ └── jobs-not-found.png │ │ ├── landingPage │ │ │ └── company-like.jpg │ │ ├── layoutItems │ │ │ ├── candidates.svg │ │ │ ├── companies.svg │ │ │ ├── dashboard.svg │ │ │ ├── favicon-32x32.png │ │ │ ├── finance.svg │ │ │ ├── left-arrow.svg │ │ │ ├── logout.svg │ │ │ └── membership.svg │ │ ├── payment │ │ │ └── wired-flat-37-approve-checked-simple (3).gif │ │ └── user │ │ │ └── user-not-found.png │ ├── axios │ │ ├── apiCalls.ts │ │ ├── apiMethods │ │ │ ├── admin-service │ │ │ │ ├── admin-dashboard.ts │ │ │ │ ├── candidates.ts │ │ │ │ ├── job.ts │ │ │ │ ├── recruiters.ts │ │ │ │ └── search.ts │ │ │ ├── auth-service │ │ │ │ ├── adminAuth.ts │ │ │ │ ├── candidateAuth.ts │ │ │ │ └── recruiterAuth.ts │ │ │ ├── chat-service │ │ │ │ ├── chat.ts │ │ │ │ └── notification.ts │ │ │ ├── jobs-service │ │ │ │ └── jobs.ts │ │ │ ├── payment-service │ │ │ │ ├── admin.ts │ │ │ │ └── candidate.ts │ │ │ ├── premium-plans-service │ │ │ │ ├── admin.ts │ │ │ │ └── candidate.ts │ │ │ └── profile-service │ │ │ │ ├── candidate.ts │ │ │ │ └── recruiter.ts │ │ ├── axiosInstance.ts │ │ └── refresh.ts │ ├── components │ │ ├── admin │ │ │ └── profile │ │ │ │ ├── CandidateProfile.tsx │ │ │ │ └── RecruiterProfile.tsx │ │ ├── cards │ │ │ ├── AdminViewJobDetailsCard.tsx │ │ │ ├── CandidateCard.tsx │ │ │ ├── DashBoardCard.tsx │ │ │ ├── JobAppliedCard.tsx │ │ │ ├── JobCard.tsx │ │ │ ├── JobCardAllJobs.tsx │ │ │ └── PaymentPlanCard.tsx │ │ ├── charts │ │ │ ├── ChartOne.tsx │ │ │ └── ChartThree.tsx │ │ ├── chat │ │ │ ├── ChatBoxTopBar.tsx │ │ │ ├── ChatInputBox.tsx │ │ │ ├── ChatRoomList.tsx │ │ │ └── Message.tsx │ │ ├── dropDown │ │ │ ├── DropDownSelect.tsx │ │ │ └── StatusChangeForm.tsx │ │ ├── filterSearch │ │ │ ├── FilterBar.tsx │ │ │ └── SearchBar.tsx │ │ ├── footer │ │ │ └── Footer.tsx │ │ ├── form │ │ │ ├── CreateJobForm.tsx │ │ │ ├── EditJob.tsx │ │ │ ├── EmailOrMobile.tsx │ │ │ ├── ForgotResetPasswordForm.tsx │ │ │ ├── auth │ │ │ │ ├── AdminSignin.tsx │ │ │ │ ├── CandidateAuth.tsx │ │ │ │ └── RecruiterAuth.tsx │ │ │ └── otpEnterForm.tsx │ │ ├── loading │ │ │ ├── BarLoading.tsx │ │ │ └── SpinnerLoading.tsx │ │ ├── navBar │ │ │ ├── LeftNavBarAdmin.tsx │ │ │ ├── LeftNavBarRecruiter.tsx │ │ │ ├── NavBarLanding.tsx │ │ │ └── TopNavBar.tsx │ │ ├── notification │ │ │ └── Notifications.tsx │ │ ├── pagination │ │ │ └── Paginate.tsx │ │ ├── recruiter │ │ │ ├── JobApplicationDetails.tsx │ │ │ └── JobDetails.tsx │ │ ├── shimmer │ │ │ ├── dashboard │ │ │ │ └── DashboardCardAdminShimmer.tsx │ │ │ ├── job │ │ │ │ ├── JobCardShimmer.tsx │ │ │ │ └── JobDetailsShimmer.tsx │ │ │ ├── payment │ │ │ │ └── PaymentPlanCardShimmer.tsx │ │ │ ├── recruiter │ │ │ │ └── CandidateCardShimmer.tsx │ │ │ └── table │ │ │ │ └── TableShimmer.tsx │ │ ├── table │ │ │ └── Table.tsx │ │ └── upload │ │ │ ├── FileUpload.tsx │ │ │ ├── ImageFileUpload.tsx │ │ │ └── ProfileResumeDisplay.tsx │ ├── config │ │ ├── apiUrlsConfig │ │ │ ├── adminServiceApiUrlConfig.ts │ │ │ ├── authApiUrlConfig.ts │ │ │ ├── chatApiUrlConfig.ts │ │ │ ├── jobApiUrlConfig.ts │ │ │ ├── notificationApiUrlConfig.ts │ │ │ ├── paymentApiUrlConfig.ts │ │ │ └── profileApiUrlConfig.ts │ │ ├── baseUrl.ts │ │ ├── firebase.ts │ │ └── socket.ts │ ├── context │ │ └── socketContext.tsx │ ├── index.css │ ├── main.tsx │ ├── pages │ │ ├── Error │ │ │ └── NotFound.tsx │ │ ├── admin │ │ │ └── UsersListPage.tsx │ │ ├── auth │ │ │ ├── EnterEmailOrMobilePage.tsx │ │ │ ├── OtpFormPage.tsx │ │ │ ├── UpdatePassword.tsx │ │ │ └── authUser │ │ │ │ ├── AdminSigninPage.tsx │ │ │ │ ├── AuthCandidate.tsx │ │ │ │ └── AuthRecruiter.tsx │ │ ├── chat │ │ │ └── ChatPage.tsx │ │ ├── dashboard │ │ │ ├── AdminDashBoard.tsx │ │ │ └── RecruiterDashBoardPage.tsx │ │ ├── job │ │ │ ├── JobDetailsPage.tsx │ │ │ ├── admin │ │ │ │ ├── JobsListPage.tsx │ │ │ │ └── ViewJobDetailsPage.tsx │ │ │ ├── candidate │ │ │ │ ├── AllJobsPage.tsx │ │ │ │ ├── AppliedJobsPage.tsx │ │ │ │ └── JobApplicationDetailsPage.tsx │ │ │ └── recruiter │ │ │ │ ├── AllAddedJobs.tsx │ │ │ │ ├── CreateJobPage.tsx │ │ │ │ ├── EditJobPage.tsx │ │ │ │ ├── JobApplicationDetailsPage.tsx │ │ │ │ └── JobApplicationsPage.tsx │ │ ├── landing │ │ │ └── LandingPage.tsx │ │ ├── layout │ │ │ ├── AdminLayout.tsx │ │ │ ├── CandidateLayout.tsx │ │ │ └── RecruiterLayout.tsx │ │ ├── payment │ │ │ ├── MembershipsListPage.tsx │ │ │ ├── PaymentFailed.tsx │ │ │ ├── PaymentPlans.tsx │ │ │ ├── PaymentSuccessFul.tsx │ │ │ └── PaymentsListPage.tsx │ │ ├── profile │ │ │ ├── admin │ │ │ │ └── ViewProfile.tsx │ │ │ ├── candidate │ │ │ │ ├── CandidateProfileEditPage.tsx │ │ │ │ └── CandidateProfilePage.tsx │ │ │ └── recruiter │ │ │ │ ├── RecruiterProfileEditPage.tsx │ │ │ │ └── RecruiterProfilePage.tsx │ │ └── recruiter │ │ │ └── ViewAllCandidatesPage.tsx │ ├── redux │ │ ├── reducer.ts │ │ ├── slice │ │ │ ├── chat.ts │ │ │ ├── isLoading.ts │ │ │ ├── job.ts │ │ │ ├── notification.ts │ │ │ └── user.ts │ │ └── store.ts │ ├── routes │ │ ├── AdminRoutes.tsx │ │ ├── CandidateRoutes.tsx │ │ └── RecruiterRouters.tsx │ ├── types │ │ ├── Job.ts │ │ ├── api.ts │ │ ├── chat.ts │ │ ├── otp.ts │ │ ├── payment.ts │ │ ├── profile.ts │ │ └── user.ts │ ├── utils │ │ ├── checkRole.ts │ │ ├── constants.ts │ │ ├── currency-format.ts │ │ ├── date-functions.ts │ │ ├── hotToastMessage.ts │ │ ├── localStorage.ts │ │ ├── swal.ts │ │ ├── toastMessage.ts │ │ └── validations │ │ │ ├── createJob.ts │ │ │ ├── otp.ts │ │ │ ├── signin.ts │ │ │ └── signup.ts │ └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── job ├── .dockerignore ├── .prettierrc.json ├── Dockerfile.dev ├── Dockerfile.prod ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── src │ ├── config │ │ ├── appConfig.ts │ │ ├── db.connection.ts │ │ ├── dependencies.ts │ │ └── kafka.connection.ts │ ├── controllers │ │ ├── candidate │ │ │ ├── appliedJobs.controller.ts │ │ │ ├── apply.controller.ts │ │ │ ├── checkApplied.controller.ts │ │ │ ├── getApplication.controller.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── jobs │ │ │ ├── filterJobs.controller.ts │ │ │ ├── getJob.controller.ts │ │ │ ├── getJobs.controller.ts │ │ │ ├── index.ts │ │ │ └── viewDistinctFieldValues.controller.ts │ │ ├── recruiter │ │ │ ├── changeApplicationStatus.controller.ts │ │ │ ├── closeJob.controller.ts │ │ │ ├── createJob.controller.ts │ │ │ ├── dashboardCardsData.controller.ts │ │ │ ├── dashboardGraphData.controller.ts │ │ │ ├── deleteJob.controller.ts │ │ │ ├── edit.controller.ts │ │ │ ├── getApplication.controller.ts │ │ │ ├── getApplications.controller.ts │ │ │ ├── getCreatedJobs.controller.ts │ │ │ └── index.ts │ │ └── search │ │ │ ├── index.ts │ │ │ └── search.controller.ts │ ├── entities │ │ ├── job.ts │ │ └── jobApplications.ts │ ├── frameworks │ │ ├── database │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ └── models │ │ │ │ ├── index.ts │ │ │ │ ├── job.ts │ │ │ │ ├── jobApplication.ts │ │ │ │ └── user.ts │ │ ├── express │ │ │ ├── app.ts │ │ │ └── routes │ │ │ │ ├── candidate.ts │ │ │ │ ├── index.ts │ │ │ │ └── recruiter.ts │ │ ├── repositories │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ ├── job.repository.ts │ │ │ │ ├── jobApplication.repository.ts │ │ │ │ └── user.repository.ts │ │ ├── types │ │ │ ├── dependency.ts │ │ │ ├── job.ts │ │ │ ├── jobApplication.ts │ │ │ └── user.ts │ │ └── utils │ │ │ ├── constants.ts │ │ │ └── kafka-events │ │ │ ├── consumers │ │ │ ├── jobUpdatedConsumer.ts │ │ │ ├── userCreatedConsumer.ts │ │ │ └── userUpdatedConsumer.ts │ │ │ ├── handleMessage.ts │ │ │ └── publishers │ │ │ ├── jobCreatedPublisher.ts │ │ │ ├── jobDeletedPublisher.ts │ │ │ └── jobUpdatedPublisher.ts │ ├── index.ts │ └── useCases │ │ ├── candidate │ │ ├── apply.ts │ │ ├── checkApplied.ts │ │ ├── getAppliedJobs.ts │ │ └── index.ts │ │ ├── index.ts │ │ ├── job │ │ ├── filterJob.ts │ │ ├── getApplication.ts │ │ ├── getJobById.ts │ │ ├── getJobFieldsDistinctValues.ts │ │ ├── getJobs.ts │ │ ├── index.ts │ │ └── search.ts │ │ └── recruiter │ │ ├── changeApplicationStatus.ts │ │ ├── changeClosejobStatus.ts │ │ ├── createJob.ts │ │ ├── dashboardCardsDetails.ts │ │ ├── dashboardGraphDetails.ts │ │ ├── deleteJob.ts │ │ ├── getApplications.ts │ │ ├── getCreatedJobs.ts │ │ ├── index.ts │ │ └── update.ts └── tsconfig.json ├── k8s ├── README.md ├── certificate │ ├── cert-issuer-prod.yaml │ └── cert-issuer-stagging.yaml ├── ingress │ ├── dev │ │ └── ingress-srv.yaml │ └── prod │ │ └── ingress-srv.yaml ├── stateful │ ├── admin-mongo-deployment.yaml │ ├── auth-mongo-deployment.yaml │ ├── job-mongo-deployment.yaml │ ├── kafka-deployment.yaml │ └── profile-mongo-deployment.yaml └── stateless │ ├── admin-deployment.yaml │ ├── auth-deployment.yaml │ ├── chat-deployment.yaml │ ├── client-app-deployment.yaml │ ├── job-deployment.yaml │ ├── payment-deployment.yaml │ └── profile-deployment.yaml ├── payment ├── .dockerignore ├── .prettierrc.json ├── Dockerfile.dev ├── Dockerfile.prod ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── src │ ├── config │ │ ├── appConfig.ts │ │ ├── db.connection.ts │ │ ├── dependencies.ts │ │ ├── kafka.connection.ts │ │ └── stripe.ts │ ├── controllers │ │ ├── index.ts │ │ ├── payment │ │ │ ├── createPayment.controller.ts │ │ │ └── index.ts │ │ └── premium │ │ │ ├── getPremiumPlans.controller.ts │ │ │ └── index.ts │ ├── entities │ │ ├── membershipPlan.ts │ │ └── payment.ts │ ├── frameworks │ │ ├── database │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ └── models │ │ │ │ ├── index.ts │ │ │ │ ├── membershipPlan.ts │ │ │ │ └── payment.ts │ │ ├── express │ │ │ ├── app.ts │ │ │ └── routes │ │ │ │ ├── index.ts │ │ │ │ ├── payment.ts │ │ │ │ └── premium.ts │ │ ├── repositories │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ ├── membershipPlan.repository.ts │ │ │ │ └── payment.repository.ts │ │ ├── types │ │ │ ├── dependency.ts │ │ │ ├── membershipPlan.ts │ │ │ └── payment.ts │ │ └── utils │ │ │ └── kafka-events │ │ │ ├── consumers │ │ │ └── premiumPlanCreatedConsumer.ts │ │ │ ├── handleMessage.ts │ │ │ └── publishers │ │ │ └── paymentDonePublisher.ts │ ├── index.ts │ └── useCase │ │ ├── index.ts │ │ ├── payment │ │ ├── createPayment.ts │ │ └── index.ts │ │ └── premium │ │ ├── getPremiumPlans.ts │ │ └── index.ts └── tsconfig.json ├── profile ├── .dockerignore ├── .prettierrc.json ├── Dockerfile.dev ├── Dockerfile.prod ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── src │ ├── config │ │ ├── appConfig.ts │ │ ├── cloudinary.ts │ │ ├── db.connection.ts │ │ ├── dependencies.ts │ │ ├── kafka.connection.ts │ │ └── multer.ts │ ├── controllers │ │ ├── candidate │ │ │ ├── deleteResume.controller.ts │ │ │ ├── index.ts │ │ │ ├── updatePreferredJobs.controller.ts │ │ │ ├── updateProfile.controller.ts │ │ │ ├── updateSkills.controller.ts │ │ │ ├── uploadProfilePic.controller.ts │ │ │ ├── uploadResume.controller.ts │ │ │ └── viewProfile.controller.ts │ │ ├── index.ts │ │ └── recruiter │ │ │ ├── getcandidates.controller.ts │ │ │ ├── index.ts │ │ │ ├── update.controller.ts │ │ │ └── viewProfile.controller.ts │ ├── entities │ │ ├── candidate-profile.ts │ │ ├── index.ts │ │ └── recruiter-profile.ts │ ├── frameworks │ │ ├── database │ │ │ └── mongo │ │ │ │ ├── index.ts │ │ │ │ └── models │ │ │ │ ├── candidate.ts │ │ │ │ ├── index.ts │ │ │ │ └── recruiter.ts │ │ ├── express │ │ │ ├── app.ts │ │ │ └── routes │ │ │ │ ├── candidate.ts │ │ │ │ ├── index.ts │ │ │ │ └── recruiter.ts │ │ ├── repository │ │ │ └── mongo │ │ │ │ ├── candidateProfile.repository.ts │ │ │ │ ├── index.ts │ │ │ │ └── recruiterProfile.repository.ts │ │ ├── types │ │ │ ├── candidate.ts │ │ │ ├── dependency.ts │ │ │ ├── recruiter.ts │ │ │ └── user.ts │ │ └── utils │ │ │ ├── kafka-events │ │ │ ├── consumers │ │ │ │ ├── payment-created-consumer.ts │ │ │ │ ├── user-created-consumer.ts │ │ │ │ └── user-updated-consumer.ts │ │ │ ├── handleMessage.ts │ │ │ └── publishers │ │ │ │ ├── candidate-profile-updated-publisher .ts │ │ │ │ ├── recruiter-profile-updated-publisher.ts │ │ │ │ └── user-updated-publisher.ts │ │ │ └── uploads.ts │ ├── index.ts │ └── useCases │ │ ├── candidate │ │ ├── deleteResume.ts │ │ ├── getProfileByEmail.ts │ │ ├── getProfileById.ts │ │ ├── index.ts │ │ ├── updatePreferredJobs.ts │ │ ├── updateProfile.ts │ │ ├── updateSkills.ts │ │ ├── uploadProfilePic.ts │ │ └── uploadResume.ts │ │ ├── index.ts │ │ └── recruiter │ │ ├── getCandidateProfiles.ts │ │ ├── getProfileById.ts │ │ ├── index.ts │ │ └── updateProfile.ts └── tsconfig.json └── skaffold.yaml /.github/workflows/deploy-manifest.yaml: -------------------------------------------------------------------------------- 1 | name: deploy-manifests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - 'k8s/**' 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | # # Commenting out Digital Ocean specific steps since we're not deploying there now 14 | # steps: 15 | # - uses: actions/checkout@v3 16 | # - uses: digitalocean/action-doctl@v2 17 | # with: 18 | # token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} 19 | # - run: doctl kubernetes cluster kubeconfig save devhive-hosted-clusterr 20 | # - run: kubectl apply -f k8s/stateless && kubectl apply -f k8s/ingress/prod 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | .log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | build 11 | dist 12 | 13 | .env 14 | 15 | node_modules 16 | dist 17 | dist-ssr 18 | .local 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | .DS_Store 25 | .suo 26 | .ntvs* 27 | .njsproj 28 | .sln 29 | .sw? 30 | -------------------------------------------------------------------------------- /admin/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /admin/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "printWidth": 110, 6 | "tabWidth": 4, 7 | "trailingComma": "all", 8 | "arrowParens": "always", 9 | "endOfLine": "auto" 10 | } -------------------------------------------------------------------------------- /admin/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | ENV NODE_ENV=development 14 | 15 | CMD [ "npm", "run", "dev" ] -------------------------------------------------------------------------------- /admin/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM node:20-alpine AS build 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json ./ 7 | 8 | # Install all dependencies (including dev) 9 | RUN npm ci 10 | 11 | COPY . . 12 | 13 | # Run the build command 14 | RUN npm run build 15 | 16 | 17 | # Production stage 18 | FROM node:20-alpine AS production 19 | 20 | WORKDIR /app 21 | 22 | COPY package*.json ./ 23 | 24 | # Install only production dependencies 25 | RUN npm ci --omit=dev 26 | 27 | # Copy built files from the build stage 28 | COPY --from=build /app/build ./build 29 | 30 | EXPOSE 3000 31 | 32 | ENV NODE_ENV=production 33 | 34 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /admin/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | 6 | export default [ 7 | {files: ["**/*.{js,mjs,cjs,ts}"]}, 8 | {files: ["**/*.js"], languageOptions: {sourceType: "script"}}, 9 | {languageOptions: { globals: globals.node }}, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | ]; -------------------------------------------------------------------------------- /admin/src/config/appConfig.ts: -------------------------------------------------------------------------------- 1 | interface IAppConfig { 2 | PORT: string | number; 3 | API_PREFIX: string; 4 | MONGO_URL_ADMIN: string; 5 | ENVIRONMENT: string; 6 | JWT_SECRET_KEY: string; 7 | JWT_REFRESH_SECRET_KEY: string; 8 | } 9 | 10 | const appConfig: Readonly = Object.freeze({ 11 | PORT: (process.env.PORT as string) || 3000, 12 | API_PREFIX: (process.env.API_PREFIX as string) || '/api/v1/admin', 13 | MONGO_URL_ADMIN: process.env.MONGO_URL_ADMIN as string, 14 | ENVIRONMENT: process.env.NODE_ENV as string, 15 | JWT_SECRET_KEY: process.env.JWT_SECRET_KEY as string, 16 | JWT_REFRESH_SECRET_KEY: process.env.JWT_REFRESH_SECRET_KEY as string, 17 | }); 18 | 19 | export { appConfig }; 20 | -------------------------------------------------------------------------------- /admin/src/config/db.connection.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnectionError } from '@abijobportal/common'; 2 | import mongoose from 'mongoose'; 3 | import { appConfig } from './appConfig'; 4 | 5 | const connectDB = async (): Promise => { 6 | try { 7 | await mongoose.connect(appConfig.MONGO_URL_ADMIN); 8 | console.log('admin service connected to mongodb...'); 9 | } catch (error) { 10 | console.error('admin service mongodb connection failed!!!!', error); 11 | throw new DatabaseConnectionError(); 12 | } 13 | }; 14 | 15 | export { connectDB }; 16 | -------------------------------------------------------------------------------- /admin/src/config/dependencies.ts: -------------------------------------------------------------------------------- 1 | import repositories from '../frameworks/repositories/mongo'; 2 | import * as useCases from '../useCases'; 3 | 4 | export default { repositories, useCases }; 5 | -------------------------------------------------------------------------------- /admin/src/config/kafka.connection.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | 3 | const kafkaClient = new Kafka({ 4 | clientId: 'admin-client', 5 | brokers: ['devhive-kafka:9092 '], 6 | }); 7 | 8 | export { kafkaClient }; 9 | -------------------------------------------------------------------------------- /admin/src/controllers/admin/dashboard.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllDashboardCardsDetailsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const dashboardCardsDetails = await getAllDashboardCardsDetailsUseCase(dependencies).execute(); 11 | 12 | res.status(200).json({ 13 | message: 'dashboard card details fetched successfully', 14 | data: dashboardCardsDetails, 15 | }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/controllers/admin/dashboardGraph.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getDashboardGraphDetailsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const dashboardGraphDetails = await getDashboardGraphDetailsUseCase(dependencies).execute(); 11 | 12 | res.status(200).json({ message: 'dashboard graph details', data: dashboardGraphDetails }); 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /admin/src/controllers/admin/index.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | import dashboardController from './dashboard.controller'; 4 | import getDashboardGraphDetailsController from './dashboardGraph.controller'; 5 | 6 | export = (dependencies: IDependency) => { 7 | return { 8 | dashboardCardsDataController: dashboardController(dependencies), 9 | getDashboardGraphDetailsController: getDashboardGraphDetailsController(dependencies), 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /admin/src/controllers/candidate/blockUnblock.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { blockUnblockCandidateUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.params; 11 | 12 | const isBlocked = await blockUnblockCandidateUseCase(dependencies).execute(userId); 13 | 14 | res.status(200).json({ 15 | message: `candidate ${isBlocked.isActive ? 'unBlocked' : 'Blocked'} successfully`, 16 | data: isBlocked, 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /admin/src/controllers/candidate/getCandidates.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllCandidatesUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { candidates, numberOfPages } = await getAllCandidatesUseCase(dependencies).execute( 11 | Number(req.params.page) || 1, 12 | Number(req.params.limit) || 4, 13 | ); 14 | 15 | res.status(200).json({ 16 | message: 'candidates fetched successfully', 17 | data: { candidates, numberOfPages }, 18 | }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /admin/src/controllers/candidate/index.ts: -------------------------------------------------------------------------------- 1 | import getAllCandidatesController from './getCandidates.controller'; 2 | import getCandidateByIdController from './viewProfile.controller'; 3 | import candidateBlockUnblockController from './blockUnblock.controller'; 4 | 5 | import { IDependency } from '../../frameworks/types/dependency'; 6 | 7 | export = (dependencies: IDependency) => { 8 | return { 9 | getAllCandidatesController: getAllCandidatesController(dependencies), 10 | getCandidateByIdController: getCandidateByIdController(dependencies), 11 | candidateBlockUnblockController: candidateBlockUnblockController(dependencies), 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /admin/src/controllers/candidate/viewProfile.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getCandidateProfileByuserIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.params; 11 | 12 | const candidate = await getCandidateProfileByuserIdUseCase(dependencies).execute(userId); 13 | 14 | res.status(200).json({ message: 'candidate profile fetched successfully', data: candidate }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /admin/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import candidateControllers from './candidate'; 2 | import jobControllers from './job'; 3 | import recruiterControllers from './recruiter'; 4 | import dashboardControllers from './admin'; 5 | import membershipControllers from './membership'; 6 | import paymentControllers from './payment'; 7 | import searchControllers from './search'; 8 | 9 | export { 10 | candidateControllers, 11 | jobControllers, 12 | recruiterControllers, 13 | dashboardControllers, 14 | membershipControllers, 15 | paymentControllers, 16 | searchControllers 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/controllers/job/blockUnblock.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { blockUnblockJobUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { jobId } = req.params; 11 | 12 | const isBlocked = await blockUnblockJobUseCase(dependencies).execute(jobId); 13 | res.status(200).json({ 14 | message: `job ${isBlocked.isActive ? 'unBlocked' : 'Blocked'} successfully`, 15 | data: isBlocked, 16 | }); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /admin/src/controllers/job/getJob.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getJobByIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { jobId } = req.params; 11 | const job = await getJobByIdUseCase(dependencies).execute(jobId); 12 | 13 | res.status(200).json({ message: 'job data', data: job }); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /admin/src/controllers/job/getJobs.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllJobsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { jobs, numberOfPages } = await getAllJobsUseCase(dependencies).execute( 11 | Number(req.params.page) || 1, 12 | Number(req.params.limit) || 4, 13 | ); 14 | 15 | res.status(200).json({ message: 'all jobs', data: { jobs, numberOfPages } }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/controllers/job/index.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | import blockUnblockJobController from './blockUnblock.controller'; 4 | import viewJobController from './getJob.controller'; 5 | import viewJobsController from './getJobs.controller'; 6 | 7 | export = (dependencies: IDependency) => { 8 | return { 9 | blockUnblockJobController: blockUnblockJobController(dependencies), 10 | viewJobController: viewJobController(dependencies), 11 | viewJobsController: viewJobsController(dependencies), 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /admin/src/controllers/membership/create.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IMembershipPlan } from '../../frameworks/types/membershipPlan'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { createMemberShipPlanUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const data = req.body as IMembershipPlan; 12 | 13 | const membershipPlan = await createMemberShipPlanUseCase(dependencies).execute(data); 14 | 15 | res.status(201).json({ message: 'MemberShipPlan created successfully', data: membershipPlan }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/controllers/membership/getMembership.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getMemberShipPlanByIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { memberShipPlanId } = req.params; 11 | 12 | const response = await getMemberShipPlanByIdUseCase(dependencies).execute(memberShipPlanId); 13 | 14 | res.status(200).json({ message: 'memberShipPlan get successfully', data: response }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /admin/src/controllers/membership/getMemberships.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllMembershipPlansUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { membershipPlans, numberOfPages } = await getAllMembershipPlansUseCase(dependencies).execute( 11 | Number(req.params.page) || 1, 12 | Number(req.params.limit) || 4, 13 | ); 14 | 15 | res.status(200).json({ message: 'memberShipPlans list', data: { membershipPlans, numberOfPages } }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/controllers/membership/update.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IMembershipPlan } from '../../frameworks/types/membershipPlan'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { updateMemberShipPlanUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const data = req.body as Partial; 12 | const { membershipId } = req.params; 13 | 14 | const updatedMemberShipPlan = await updateMemberShipPlanUseCase(dependencies).execute( 15 | membershipId, 16 | data, 17 | ); 18 | 19 | res.status(200).json({ message: 'MemberShipPlan updated successfully', data: updatedMemberShipPlan }); 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /admin/src/controllers/payment/getPayments.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllPaymentsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { payments, numberOfPages } = await getAllPaymentsUseCase(dependencies).execute( 11 | Number(req.params.page) || 1, 12 | Number(req.params.limit) || 4, 13 | ); 14 | res.status(200).json({ message: 'Payments are ', data: { payments, numberOfPages } }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /admin/src/controllers/payment/index.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | import getAllPaymentsController from './getPayments.controller'; 4 | 5 | export = (dependencies: IDependency) => { 6 | return { 7 | getAllPaymentsController: getAllPaymentsController(dependencies), 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /admin/src/controllers/recruiter/blockUnblock.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { blockUnblockRecruiterUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.params; 11 | 12 | const isBlocked = await blockUnblockRecruiterUseCase(dependencies).execute(userId); 13 | 14 | res.status(200).json({ 15 | message: `recruiter ${isBlocked.isActive ? 'unBlocked' : 'blocked'} successfully`, 16 | data: isBlocked, 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /admin/src/controllers/recruiter/getRecruiters.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllRecruitersUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { recruiters, numberOfPages } = await getAllRecruitersUseCase(dependencies).execute( 11 | Number(req.params.page) || 1, 12 | Number(req.params.limit) || 4, 13 | ); 14 | 15 | res.status(200).json({ message: 'all recruiters', data: { recruiters, numberOfPages } }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/controllers/recruiter/index.ts: -------------------------------------------------------------------------------- 1 | import getAllRecruitersController from './getRecruiters.controller'; 2 | import getRecruiterByIdController from './viewProfile.controller'; 3 | import recruiterBlockUnblockController from './blockUnblock.controller'; 4 | import searchRecruitersController from './search.controller'; 5 | 6 | import { IDependency } from '../../frameworks/types/dependency'; 7 | 8 | export = (dependencies: IDependency) => { 9 | return { 10 | getAllRecruitersController: getAllRecruitersController(dependencies), 11 | getRecruiterByIdController: getRecruiterByIdController(dependencies), 12 | recruiterBlockUnblockController: recruiterBlockUnblockController(dependencies), 13 | searchRecruitersController: searchRecruitersController(dependencies), 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /admin/src/controllers/recruiter/search.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { searchRecruitersUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { searchKey } = req.query; 11 | const { recruiters, numberOfPages } = await searchRecruitersUseCase(dependencies).execute( 12 | searchKey, 13 | Number(req.params.page) || 1, 14 | Number(req.params.limit) || 4, 15 | ); 16 | 17 | res.status(200).json({ message: 'all recruiters', data: { recruiters, numberOfPages } }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /admin/src/controllers/recruiter/viewProfile.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getRecruiterProfileByuserIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.params; 11 | 12 | const recruiter = await getRecruiterProfileByuserIdUseCase(dependencies).execute(userId); 13 | 14 | res.status(200).json({ message: 'recruiter data', data: recruiter }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /admin/src/controllers/search/index.ts: -------------------------------------------------------------------------------- 1 | import searchController from "./search.controller" 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | return { 6 | searchController: searchController(dependencies), 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /admin/src/controllers/search/search.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { searchUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { type: resourceType } = req.params; 11 | const { searchKey } = req.query; 12 | const { result, numberOfPages } = await searchUseCase(dependencies).execute( 13 | resourceType, 14 | searchKey as string, 15 | Number(req.params.page) || 1, 16 | Number(req.params.limit) || 4, 17 | ); 18 | 19 | res.status(200).json({ 20 | message: 'search results fetched successfully', 21 | data: { result, numberOfPages }, 22 | }); 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /admin/src/entities/candidate.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from '../frameworks/types/user'; 2 | 3 | export class CandidateProfile { 4 | userId: string; 5 | name: string; 6 | email: string; 7 | phone: number; 8 | role: string; 9 | isActive: boolean; 10 | constructor({ userId, name, email, phone, role, isActive }: IUser) { 11 | this.userId = userId; 12 | this.name = name; 13 | this.email = email; 14 | this.phone = phone; 15 | this.role = role; 16 | this.isActive = isActive; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /admin/src/entities/index.ts: -------------------------------------------------------------------------------- 1 | import { Job } from './job'; 2 | import { RecruiterProfile } from './recruiter'; 3 | 4 | export { Job, RecruiterProfile }; 5 | -------------------------------------------------------------------------------- /admin/src/entities/membership-plan.ts: -------------------------------------------------------------------------------- 1 | export interface IMembershipPlanData { 2 | name: string; 3 | features: Array; 4 | description: string; 5 | price: number; 6 | isActive?: boolean; 7 | } 8 | 9 | export class MembershipPlan { 10 | name: string; 11 | features: Array; 12 | description: string; 13 | price: number; 14 | 15 | constructor({ name, features, description, price }: IMembershipPlanData) { 16 | this.name = name; 17 | this.features = features; 18 | this.description = description; 19 | this.price = price; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /admin/src/entities/payment.ts: -------------------------------------------------------------------------------- 1 | import { IPaymentData } from '../frameworks/types/payment'; 2 | 3 | export class Payment { 4 | candidateId: string; 5 | membershipPlanId: string; 6 | 7 | constructor({ candidateId, membershipPlanId }: IPaymentData) { 8 | this.candidateId = candidateId; 9 | this.membershipPlanId = membershipPlanId; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /admin/src/entities/recruiter.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from '../frameworks/types/user'; 2 | 3 | export class RecruiterProfile { 4 | userId: string; 5 | name: string; 6 | email: string; 7 | phone: number; 8 | role: string; 9 | isActive: boolean; 10 | constructor({ userId, name, email, phone, role, isActive }: IUser) { 11 | this.userId = userId; 12 | this.name = name; 13 | this.email = email; 14 | this.phone = phone; 15 | this.role = role; 16 | this.isActive = isActive; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /admin/src/frameworks/database/index.ts: -------------------------------------------------------------------------------- 1 | export * from './models'; 2 | -------------------------------------------------------------------------------- /admin/src/frameworks/database/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './candidate'; 2 | export * from './recruiter'; 3 | export * from './job'; 4 | export * from './membershipPlan'; 5 | export * from './payments'; 6 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/app.ts: -------------------------------------------------------------------------------- 1 | import 'express-async-errors'; 2 | import express, { Express } from 'express'; 3 | import morgan from 'morgan'; 4 | import compression from 'compression'; 5 | import { NotFoundError, errorHandler } from '@abijobportal/common'; 6 | 7 | import { routes } from './routes'; 8 | import dependencies from '../../config/dependencies'; 9 | import { appConfig } from '../../config/appConfig'; 10 | 11 | const app: Express = express(); 12 | 13 | app.set('trust proxy', true); // trust first proxy 14 | 15 | // Middlewares 16 | app.use(morgan('dev')); 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: true })); 19 | app.use(compression()); 20 | // Routes 21 | app.use(appConfig.API_PREFIX, routes(dependencies)); 22 | 23 | app.all('*', async () => { 24 | throw new NotFoundError(); 25 | }); 26 | 27 | app.use(errorHandler); 28 | 29 | export { app }; 30 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/routes/candidate.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { candidateControllers } from '../../../controllers'; 3 | import { IDependency } from '../../types/dependency'; 4 | 5 | export const candidateRouter = (dependencies: IDependency) => { 6 | const router: Router = express.Router(); 7 | 8 | const candidateController = candidateControllers(dependencies); 9 | 10 | // candidate 11 | router.get('/candidates/:page/:limit', candidateController.getAllCandidatesController); 12 | router.get('/view-profile/:userId', candidateController.getCandidateByIdController); 13 | router.put('/block-unblock/:userId', candidateController.candidateBlockUnblockController); 14 | 15 | return router; 16 | }; 17 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/routes/dashboard.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { dashboardControllers } from '../../../controllers'; 3 | import { IDependency } from '../../types/dependency'; 4 | 5 | export const dashboardRouter = (dependencies: IDependency) => { 6 | const router: Router = express.Router(); 7 | 8 | const dashboardController = dashboardControllers(dependencies); 9 | 10 | // dashboard 11 | router.get('/cards-data', dashboardController.dashboardCardsDataController); 12 | 13 | router.get('/graph-data', dashboardController.getDashboardGraphDetailsController); 14 | 15 | return router; 16 | }; 17 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/routes/job.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { jobControllers } from '../../../controllers'; 3 | import { IDependency } from '../../types/dependency'; 4 | 5 | export const jobRouter = (dependencies: IDependency) => { 6 | const router: Router = express.Router(); 7 | 8 | const jobController = jobControllers(dependencies); 9 | 10 | router.get('/jobs/:page/:limit', jobController.viewJobsController); 11 | router.get('/:jobId', jobController.viewJobController); 12 | router.put('/block-unblock/:jobId', jobController.blockUnblockJobController); 13 | 14 | return router; 15 | }; 16 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/routes/membershipplan.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { IDependency } from '../../types/dependency'; 3 | import { membershipControllers } from '../../../controllers'; 4 | 5 | export const membershipPlanRouter = (dependencies: IDependency) => { 6 | const router: Router = express.Router(); 7 | 8 | const membershipController = membershipControllers(dependencies); 9 | 10 | router.post('/create', membershipController.createMembershipController); 11 | router.get('/plans/:page/:limit', membershipController.viewAllMembershipsController); 12 | router.get('/plan/:membershipPlanId', membershipController.viewMembershipController); 13 | router.put('/block-unblock/:membershipPlanId', membershipController.blockUnblockMembershipController); 14 | 15 | return router; 16 | }; 17 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/routes/payment.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { IDependency } from '../../types/dependency'; 3 | import { paymentControllers } from '../../../controllers'; 4 | 5 | export const paymentRouter = (dependencies: IDependency) => { 6 | const router: Router = express.Router(); 7 | 8 | const paymentController = paymentControllers(dependencies); 9 | 10 | router.get('/payments/:page/:limit', paymentController.getAllPaymentsController); 11 | 12 | return router; 13 | }; 14 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/routes/recruiter.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { recruiterControllers } from '../../../controllers'; 3 | import { IDependency } from '../../types/dependency'; 4 | 5 | export const recruiterRouter = (dependencies: IDependency) => { 6 | const router: Router = express.Router(); 7 | 8 | const recruiterController = recruiterControllers(dependencies); 9 | 10 | router.get('/recruiters/:page/:limit', recruiterController.getAllRecruitersController); 11 | router.get('/view-profile/:userId', recruiterController.getRecruiterByIdController); 12 | router.put('/block-unblock/:userId', recruiterController.recruiterBlockUnblockController); 13 | 14 | return router; 15 | }; 16 | -------------------------------------------------------------------------------- /admin/src/frameworks/express/routes/search.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { IDependency } from '../../types/dependency'; 3 | import { searchControllers } from '../../../controllers'; 4 | 5 | export const searchRouter = (dependencies: IDependency) => { 6 | const router: Router = express.Router(); 7 | 8 | const searchController = searchControllers(dependencies); 9 | 10 | router.get('/:type/:page/:limit/', searchController.searchController); 11 | 12 | return router; 13 | }; 14 | -------------------------------------------------------------------------------- /admin/src/frameworks/repositories/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import candidateRepository from './candidate.repository'; 2 | import recruiterRepository from './recruiter.repository'; 3 | import jobRepository from './job.repository'; 4 | import membershipRepository from './membership.repository'; 5 | import paymentRepository from './payment.repository'; 6 | 7 | export = { candidateRepository, recruiterRepository, jobRepository, membershipRepository, paymentRepository }; 8 | -------------------------------------------------------------------------------- /admin/src/frameworks/types/dependency.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface IDependency { 3 | useCases: any; 4 | repositories: any; 5 | } 6 | /* eslint-enable @typescript-eslint/no-explicit-any */ 7 | -------------------------------------------------------------------------------- /admin/src/frameworks/types/job.ts: -------------------------------------------------------------------------------- 1 | export interface IJob { 2 | jobId: string; 3 | title: string; 4 | recruiterId: string; 5 | companyName: string; 6 | companyLocation: string; 7 | jobDescription?: string; 8 | skills?: string[]; 9 | availablePosition?: number; 10 | experienceRequired?: number; 11 | educationRequired?: string; 12 | employmentType?: string; 13 | salaryMin?: number; 14 | salaryMax?: number; 15 | isActive?: boolean; 16 | deadline?: Date; 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/types/membershipPlan.ts: -------------------------------------------------------------------------------- 1 | export interface IMembershipPlan { 2 | name: string; 3 | features: string[]; 4 | description: string; 5 | price: number; 6 | } 7 | -------------------------------------------------------------------------------- /admin/src/frameworks/types/payment.ts: -------------------------------------------------------------------------------- 1 | export interface IPaymentData { 2 | candidateId: string; 3 | membershipPlanId: string; 4 | } 5 | -------------------------------------------------------------------------------- /admin/src/frameworks/types/user.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | userId: string; 3 | name: string; 4 | email: string; 5 | phone: number; 6 | role: string; 7 | isActive: boolean; 8 | } 9 | 10 | export interface IRecruiter extends IUser { 11 | gender?: string; 12 | companyName?: string; 13 | companyLocation?: string; 14 | companyState?: string; 15 | companyCountry?: string; 16 | profileImage?: string; 17 | about?: string; 18 | } 19 | 20 | export interface ICandidate extends IUser { 21 | gender?: string; 22 | currentLocation?: string; 23 | address?: object; 24 | skills?: string[]; 25 | profileImage?: string; 26 | about?: string; 27 | resume?: string; 28 | experience?: string; 29 | companyLocation?: string; 30 | bio?: string; 31 | } 32 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/candidate-profile-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, CANDIDATE_PROFILE_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class CandidateProfileUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.CANDIDATE_PROFILE_UPDATED_TOPIC = TOPICS.CANDIDATE_PROFILE_UPDATED_TOPIC; 7 | 8 | groupId: string = 'admin-1'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: CANDIDATE_PROFILE_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/job-created-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, JOB_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class JobCreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.JOB_CREATED_TOPIC = TOPICS.JOB_CREATED_TOPIC; 7 | 8 | groupId: string = 'admin-4'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: JOB_CREATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/job-deleted-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, JOB_DELETED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class JobDeletedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.JOB_DELETED_TOPIC = TOPICS.JOB_DELETED_TOPIC; 7 | 8 | groupId: string = 'admin-5'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: JOB_DELETED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/job-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, JOB_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class JobUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.JOB_UPDATED_TOPIC = TOPICS.JOB_UPDATED_TOPIC; 7 | 8 | groupId: string = 'admin-6'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: JOB_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/payment-created-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, PAYMENT_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class PaymentcreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.PAYMENT_CREATED_TOPIC = TOPICS.PAYMENT_CREATED_TOPIC; 7 | 8 | groupId: string = 'admin-10'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: PAYMENT_CREATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/recruiter-profile-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, RECRUITER_PROFILE_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class RecruiterProfileUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.RECRUITER_PROFILE_UPDATED_TOPIC = TOPICS.RECRUITER_PROFILE_UPDATED_TOPIC; 7 | 8 | groupId: string = 'admin-7'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: RECRUITER_PROFILE_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/user-created-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserCreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_CREATED_TOPIC = TOPICS.USER_CREATED_TOPIC; 7 | 8 | groupId: string = 'admin-8'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_CREATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/consumers/user-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_UPDATED_TOPIC = TOPICS.USER_UPDATED_TOPIC; 7 | 8 | groupId: string = 'admin-9'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_UPDATED_EVENT['data'], topic: string): Promise { 15 | // dont need to check role as every users are stored in one collection 16 | handleMessage(data, topic); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/publishers/job-updated-publisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, JOB_UPDATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class JobUpdatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.JOB_UPDATED_TOPIC = TOPICS.JOB_UPDATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/publishers/membership-plan-created-publisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, MEMBERSHIP_PLAN_CREATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class MemberShipPlanCreatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.MEMBERSHIP_PLAN_CREATED_TOPIC = TOPICS.MEMBERSHIP_PLAN_CREATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/publishers/membership-plan-updated-publisher .ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, MEMBERSHIP_PLAN_UPDATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class MemberShipPlanUpdatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.MEMBERSHIP_PLAN_UPDATED_TOPIC = TOPICS.MEMBERSHIP_PLAN_UPDATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /admin/src/frameworks/utils/kafka-events/publishers/user-updated-publisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, USER_UPDATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class UserUpdatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.USER_UPDATED_TOPIC = TOPICS.USER_UPDATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /admin/src/useCases/candidate/getCandidate.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { candidateRepository }, 6 | } = dependencies; 7 | 8 | if (!candidateRepository) { 9 | throw new Error('candidateRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (userId: string) => { 13 | const candidate = await candidateRepository.getById(userId); 14 | return candidate; 15 | }; 16 | 17 | return { execute }; 18 | }; 19 | -------------------------------------------------------------------------------- /admin/src/useCases/candidate/getCandidates.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { candidateRepository }, 6 | } = dependencies; 7 | 8 | if (!candidateRepository) { 9 | throw new Error('candidateRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (page: number, limit: number) => { 13 | // pagination 14 | const skip = (page - 1) * limit; 15 | 16 | const candidates = await candidateRepository.getAllCandidates(skip, limit); 17 | const candidatesCount = await candidateRepository.getCountOfCandidates(); 18 | const numberOfPages = Math.ceil(candidatesCount / limit); 19 | 20 | return { candidates, numberOfPages }; 21 | }; 22 | 23 | return { execute }; 24 | }; 25 | -------------------------------------------------------------------------------- /admin/src/useCases/candidate/index.ts: -------------------------------------------------------------------------------- 1 | import blockUnblockCandidateUseCase from './blockunblock'; 2 | import getCandidateProfileByuserIdUseCase from './getCandidate'; 3 | import getAllCandidatesUseCase from './getCandidates'; 4 | 5 | export { 6 | blockUnblockCandidateUseCase, 7 | getCandidateProfileByuserIdUseCase, 8 | getAllCandidatesUseCase, 9 | }; 10 | -------------------------------------------------------------------------------- /admin/src/useCases/dashboard/index.ts: -------------------------------------------------------------------------------- 1 | import getAllDashboardCardsDetailsUseCase from './getCardsDetails'; 2 | import getDashboardGraphDetailsUseCase from './getGraphDetails'; 3 | 4 | export { getAllDashboardCardsDetailsUseCase, getDashboardGraphDetailsUseCase }; 5 | -------------------------------------------------------------------------------- /admin/src/useCases/index.ts: -------------------------------------------------------------------------------- 1 | export * from './candidate'; 2 | 3 | export * from './recruiter'; 4 | 5 | export * from './job'; 6 | 7 | export * from './payment'; 8 | 9 | export * from './membership'; 10 | 11 | export * from './dashboard'; 12 | 13 | export * from './search'; 14 | -------------------------------------------------------------------------------- /admin/src/useCases/job/getJob.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { jobRepository }, 6 | } = dependencies; 7 | 8 | if (!jobRepository) { 9 | throw new Error('jobRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (jobId: string) => { 13 | return await jobRepository.getById(jobId); 14 | }; 15 | 16 | return { execute }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/useCases/job/getJobs.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { jobRepository }, 6 | } = dependencies; 7 | 8 | if (!jobRepository) { 9 | throw new Error('jobRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (page: number, limit: number) => { 13 | // pagination 14 | const skip = (page - 1) * limit; 15 | 16 | const jobs = await jobRepository.getAllJobs(skip, limit); 17 | const jobsCount = await jobRepository.getCountOfJobs(); 18 | const numberOfPages = Math.ceil(jobsCount / limit); 19 | 20 | return { jobs, numberOfPages }; 21 | }; 22 | 23 | return { execute }; 24 | }; 25 | -------------------------------------------------------------------------------- /admin/src/useCases/job/index.ts: -------------------------------------------------------------------------------- 1 | import blockUnblockJobUseCase from './blockUnblock'; 2 | import getJobByIdUseCase from './getJob'; 3 | import getAllJobsUseCase from './getJobs'; 4 | 5 | export { blockUnblockJobUseCase, getJobByIdUseCase, getAllJobsUseCase }; 6 | -------------------------------------------------------------------------------- /admin/src/useCases/membership/getPlan.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { membershipRepository }, 6 | } = dependencies; 7 | 8 | if (!membershipRepository) { 9 | throw new Error('membershipRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (jobId: string) => { 13 | return await membershipRepository.getById(jobId); 14 | }; 15 | 16 | return { execute }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/useCases/membership/getPlans.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { membershipRepository }, 6 | } = dependencies; 7 | 8 | if (!membershipRepository) { 9 | throw new Error('membershipRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (page: number, limit: number) => { 13 | // pagination 14 | const skip = (page - 1) * limit; 15 | 16 | const membershipPlans = await membershipRepository.getAllMembershipPlans(skip, limit); 17 | const paymentsCount = await membershipRepository.getCountOfMembershipPlans(); 18 | 19 | const numberOfPages = Math.ceil(paymentsCount / limit); 20 | 21 | return { membershipPlans, numberOfPages }; 22 | }; 23 | 24 | return { execute }; 25 | }; 26 | -------------------------------------------------------------------------------- /admin/src/useCases/membership/index.ts: -------------------------------------------------------------------------------- 1 | import blockUnblockMembershipPlanUseCase from './blockUnblock'; 2 | import createMemberShipPlanUseCase from './create'; 3 | import updateMembershipPlanUseCase from './updatePlan'; 4 | import getAllMembershipPlansUseCase from './getPlans'; 5 | import getMembershipPlanByIdUseCase from './getPlan'; 6 | 7 | export { 8 | blockUnblockMembershipPlanUseCase, 9 | createMemberShipPlanUseCase, 10 | updateMembershipPlanUseCase, 11 | getAllMembershipPlansUseCase, 12 | getMembershipPlanByIdUseCase, 13 | }; 14 | -------------------------------------------------------------------------------- /admin/src/useCases/payment/getPayments.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { paymentRepository }, 6 | } = dependencies; 7 | 8 | if (!paymentRepository) { 9 | throw new Error('paymentRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (page: number, limit: number) => { 13 | // pagination 14 | const skip = (page - 1) * limit; 15 | 16 | const payments = await paymentRepository.getAllPayments(skip, limit); 17 | const paymentsCount = await paymentRepository.getCountOfPayments(); 18 | 19 | const numberOfPages = Math.ceil(paymentsCount / limit); 20 | return { payments, numberOfPages }; 21 | }; 22 | 23 | return { execute }; 24 | }; 25 | -------------------------------------------------------------------------------- /admin/src/useCases/payment/index.ts: -------------------------------------------------------------------------------- 1 | import getAllPaymentsUseCase from './getPayments'; 2 | 3 | export { getAllPaymentsUseCase }; 4 | -------------------------------------------------------------------------------- /admin/src/useCases/recruiter/getRecruiter.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { recruiterRepository }, 6 | } = dependencies; 7 | 8 | if (!recruiterRepository) { 9 | throw new Error('recruiterRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (userId: string) => { 13 | return await recruiterRepository.getById(userId); 14 | }; 15 | 16 | return { execute }; 17 | }; 18 | -------------------------------------------------------------------------------- /admin/src/useCases/recruiter/getRecruiters.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { recruiterRepository }, 6 | } = dependencies; 7 | 8 | if (!recruiterRepository) { 9 | throw new Error('recruiterRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (page: number, limit: number) => { 13 | // pagination 14 | const skip = (page - 1) * limit; 15 | 16 | const recruiters = await recruiterRepository.getAllRecruiters(skip, limit); 17 | const recruitersCount = await recruiterRepository.getCountOfRecruiters(); 18 | const numberOfPages = Math.ceil(recruitersCount / limit); 19 | return { recruiters, numberOfPages }; 20 | }; 21 | 22 | return { execute }; 23 | }; 24 | -------------------------------------------------------------------------------- /admin/src/useCases/recruiter/index.ts: -------------------------------------------------------------------------------- 1 | import blockUnblockRecruiterUseCase from './blockUnblock'; 2 | import getRecruiterProfileByuserIdUseCase from './getRecruiter'; 3 | import getAllRecruitersUseCase from './getRecruiters'; 4 | 5 | export { 6 | blockUnblockRecruiterUseCase, 7 | getRecruiterProfileByuserIdUseCase, 8 | getAllRecruitersUseCase, 9 | }; 10 | -------------------------------------------------------------------------------- /admin/src/useCases/search/index.ts: -------------------------------------------------------------------------------- 1 | import searchUseCase from './search'; 2 | 3 | export { searchUseCase }; 4 | -------------------------------------------------------------------------------- /auth/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /auth/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "printWidth": 110, 6 | "tabWidth": 4, 7 | "trailingComma": "all", 8 | "arrowParens": "always", 9 | "endOfLine": "auto" 10 | } -------------------------------------------------------------------------------- /auth/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | ENV NODE_ENV=development 14 | 15 | CMD [ "npm", "run", "dev" ] -------------------------------------------------------------------------------- /auth/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM node:20-alpine AS build 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json ./ 7 | 8 | RUN npm ci 9 | 10 | COPY . . 11 | 12 | RUN npm run build 13 | 14 | 15 | # Production stage 16 | FROM node:20-alpine 17 | 18 | WORKDIR /app 19 | 20 | COPY package*.json ./ 21 | 22 | RUN npm ci --omit=dev 23 | 24 | COPY --from=build /app/build ./build 25 | 26 | EXPOSE 3000 27 | 28 | ENV NODE_ENV=production 29 | 30 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /auth/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import pluginJs from '@eslint/js'; 3 | import tseslint from 'typescript-eslint'; 4 | 5 | export default [ 6 | { files: ['**/*.{js,mjs,cjs,ts}'], }, 7 | { files: ['**/*.js'], languageOptions: { sourceType: 'script' } }, 8 | { languageOptions: { globals: globals.node } }, 9 | pluginJs.configs.recommended, 10 | ...tseslint.configs.recommended, 11 | ]; 12 | -------------------------------------------------------------------------------- /auth/src/config/db.connection.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnectionError } from '@abijobportal/common'; 2 | import mongoose from 'mongoose'; 3 | import { appConfig } from './appConfig'; 4 | 5 | const connectDB = async (): Promise => { 6 | try { 7 | await mongoose.connect(appConfig.MONGO_URL_AUTH); 8 | console.log('auth service connected to mongodb...'); 9 | } catch (error) { 10 | console.error('auth service mongodb connection failed!!!!', error); 11 | throw new DatabaseConnectionError(); 12 | } 13 | }; 14 | 15 | export { connectDB }; 16 | -------------------------------------------------------------------------------- /auth/src/config/dependencies.ts: -------------------------------------------------------------------------------- 1 | import repositories from '../frameworks/repositories/mongo'; 2 | import * as useCases from '../useCases'; 3 | 4 | export default { repositories, useCases }; 5 | -------------------------------------------------------------------------------- /auth/src/config/kafka.connection.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | 3 | const kafkaClient = new Kafka({ 4 | clientId: 'auth-client', 5 | brokers: ['devhive-kafka:9092'], 6 | }); 7 | 8 | export { kafkaClient }; 9 | -------------------------------------------------------------------------------- /auth/src/controllers/auth/index.ts: -------------------------------------------------------------------------------- 1 | import signupController from './signup.controller'; 2 | import signinController from './signin.controller'; 3 | import signoutController from './signout.controller'; 4 | import signupEmailOtpVerificationController from './signupEmailOtpVerification.controller'; 5 | 6 | import { IDependency } from '../../frameworks/types/dependency'; 7 | 8 | export = (dependencies: IDependency) => { 9 | return { 10 | signup: signupController(dependencies), 11 | signin: signinController(dependencies), 12 | signout: signoutController(), 13 | signupEmailOtpVerification: signupEmailOtpVerificationController(dependencies), 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /auth/src/controllers/auth/signin.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | import { IDependency } from '../../frameworks/types/dependency'; 4 | import { ISignin } from '../../frameworks/types/user'; 5 | 6 | export = (dependencies: IDependency) => { 7 | const { 8 | useCases: { signInUseCase }, 9 | } = dependencies; 10 | 11 | return async (req: Request, res: Response) => { 12 | const { email, password, role } = req.body as ISignin; 13 | 14 | const { user, accessToken, refreshToken } = await signInUseCase(dependencies).execute( 15 | email, 16 | password, 17 | role, 18 | ); 19 | 20 | res.status(200).json({ 21 | message: 'Login successful', 22 | data: { user, accessToken, refreshToken }, 23 | }); 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /auth/src/controllers/auth/signout.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | export = () => { 4 | return async (req: Request, res: Response) => { 5 | res.status(200).json({ message: `${req.currentUser.role} successfully logged out` }); 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /auth/src/controllers/auth/signup.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | import { IDependency } from '../../frameworks/types/dependency'; 4 | import { ISignup } from '../../frameworks/types/user'; 5 | 6 | export = (dependencies: IDependency) => { 7 | const { 8 | useCases: { signupUseCase }, 9 | } = dependencies; 10 | 11 | return async (req: Request, res: Response) => { 12 | const message: { message: string } = await signupUseCase(dependencies).execute(req.body as ISignup); 13 | 14 | return res.status(200).json(message); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /auth/src/controllers/auth/signupEmailOtpVerification.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IOtp } from '../../frameworks/types/otp'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { authEmailVerificationOtpUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | await authEmailVerificationOtpUseCase(dependencies).execute(req.body as IOtp); 12 | 13 | res.status(201).json({ 14 | message: 'user is registered successfully', 15 | }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /auth/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import authControllers from './auth'; 2 | import otpControllers from './otp'; 3 | import passwordControllers from './password'; 4 | import jwtRefreshControllers from './jwtRefresh'; 5 | 6 | export { authControllers, otpControllers, passwordControllers, jwtRefreshControllers }; 7 | -------------------------------------------------------------------------------- /auth/src/controllers/jwtRefresh/index.ts: -------------------------------------------------------------------------------- 1 | import jwtRefreshController from './jwtRefresh.controller'; 2 | 3 | import { IDependency } from '../../frameworks/types/dependency'; 4 | 5 | export = (dependencies: IDependency) => { 6 | return { 7 | jwtRefresh: jwtRefreshController(dependencies), 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /auth/src/controllers/jwtRefresh/jwtRefresh.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { refreshTokenUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const refreshData = await refreshTokenUseCase(dependencies).execute( 11 | req.headers.authorization as string, 12 | ); 13 | 14 | return res.status(200).json({ 15 | message: 'access token created', 16 | data: refreshData, 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /auth/src/controllers/otp/index.ts: -------------------------------------------------------------------------------- 1 | import sendOtpController from './sendotp.controller'; 2 | import verifyOtpController from './verifyotp.controller'; 3 | 4 | import { IDependency } from '../../frameworks/types/dependency'; 5 | 6 | export = (dependencies: IDependency) => { 7 | return { 8 | sendOtp: sendOtpController(dependencies), 9 | verifyOtp: verifyOtpController(dependencies), 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /auth/src/controllers/password/index.ts: -------------------------------------------------------------------------------- 1 | import updatePasswordController from './update.controller'; 2 | 3 | import { IDependency } from '../../frameworks/types/dependency'; 4 | 5 | export = (dependencies: IDependency) => { 6 | return { 7 | updatePassword: updatePasswordController(dependencies), 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /auth/src/controllers/password/update.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IUpdatePassword } from '../../frameworks/types/user'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { updatePasswordUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const { userId, password } = req.body as IUpdatePassword; 12 | 13 | const user = await updatePasswordUseCase(dependencies).execute({ 14 | userId, 15 | password, 16 | }); 17 | 18 | res.status(200).json({ message: 'password updated', data: user }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /auth/src/entities/index.ts: -------------------------------------------------------------------------------- 1 | import { User } from './user'; 2 | 3 | export { User }; 4 | -------------------------------------------------------------------------------- /auth/src/entities/user.ts: -------------------------------------------------------------------------------- 1 | import { ISignup } from '../frameworks/types/user'; 2 | 3 | export class User { 4 | name: string; 5 | email: string; 6 | phone: number; 7 | password: string; 8 | role: string; 9 | otp: number; 10 | 11 | constructor({ name, email, phone, password, role, otp }: Required) { 12 | this.name = name; 13 | this.email = email; 14 | this.phone = phone; 15 | this.password = password; 16 | this.role = role; 17 | this.otp = otp; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /auth/src/frameworks/database/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import Models from './models'; 2 | 3 | export default { Models }; 4 | -------------------------------------------------------------------------------- /auth/src/frameworks/database/mongo/models/index.ts: -------------------------------------------------------------------------------- 1 | import { UserModel } from './users'; 2 | 3 | export default { UserModel }; 4 | -------------------------------------------------------------------------------- /auth/src/frameworks/express/app.ts: -------------------------------------------------------------------------------- 1 | import 'express-async-errors'; 2 | import express, { Express } from 'express'; 3 | import morgan from 'morgan'; 4 | import compression from 'compression'; 5 | import { NotFoundError, errorHandler } from '@abijobportal/common'; 6 | 7 | import { routes } from './routes'; 8 | import dependencies from '../../config/dependencies'; 9 | import { appConfig } from '../../config/appConfig'; 10 | 11 | const app: Express = express(); 12 | 13 | app.set('trust proxy', true); // trust first proxy 14 | 15 | // Middlewares 16 | app.use(morgan('dev')); 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: true })); 19 | app.use(compression()); 20 | 21 | // Routes 22 | app.use(appConfig.API_PREFIX, routes(dependencies)); 23 | 24 | app.all('*', async () => { 25 | throw new NotFoundError(); 26 | }); 27 | 28 | app.use(errorHandler); 29 | 30 | export { app }; 31 | -------------------------------------------------------------------------------- /auth/src/frameworks/express/routes/admin.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { auth, ROLES } from '@abijobportal/common'; 3 | 4 | import { authControllers } from '../../../controllers'; 5 | import { signinRequestBodyValidatorMiddlewares } from '../../middlewares/signinValidation'; 6 | import { IDependency } from '../../types/dependency'; 7 | 8 | export const adminRouter = (dependencies: IDependency) => { 9 | const router = express.Router(); 10 | 11 | const authController = authControllers(dependencies); 12 | 13 | router.post('/signin', signinRequestBodyValidatorMiddlewares, authController.signin); 14 | router.post('/signout', auth(ROLES.ADMIN), authController.signout); 15 | 16 | return router; 17 | }; 18 | -------------------------------------------------------------------------------- /auth/src/frameworks/express/routes/jwt-refresh.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { jwtRefreshControllers } from '../../../controllers'; 4 | import { IDependency } from '../../types/dependency'; 5 | 6 | export const jwtRouter = (dependencies: IDependency) => { 7 | const router = express.Router(); 8 | 9 | const jwtController = jwtRefreshControllers(dependencies); 10 | 11 | router.post('/refreshToken', jwtController.jwtRefresh); 12 | 13 | return router; 14 | }; 15 | -------------------------------------------------------------------------------- /auth/src/frameworks/express/routes/otp.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { otpControllers } from '../../../controllers'; 4 | import { IDependency } from '../../types/dependency'; 5 | 6 | export const otpRouter = (dependencies: IDependency) => { 7 | const router = express.Router(); 8 | 9 | const otpController = otpControllers(dependencies); 10 | 11 | router.post('/sendOtp', otpController.sendOtp); 12 | 13 | router.post('/verify-forgotPassword-otp', otpController.verifyOtp); 14 | 15 | return router; 16 | }; 17 | -------------------------------------------------------------------------------- /auth/src/frameworks/middlewares/signinValidation.ts: -------------------------------------------------------------------------------- 1 | import { body } from 'express-validator'; 2 | import { ROLES, validateRequest } from '@abijobportal/common'; 3 | 4 | export const signinRequestBodyValidatorMiddlewares = [ 5 | body('email').isEmail().withMessage('Email must be valid').trim().escape(), 6 | body('password').notEmpty().withMessage('You must supply a password').trim().escape(), 7 | body('role') 8 | .notEmpty() 9 | .withMessage('Role is required') 10 | .isIn([ROLES.ADMIN, ROLES.CANDIDATE, ROLES.RECRUITER]) 11 | .withMessage(`Role must be one of ${ROLES.ADMIN}, ${ROLES.CANDIDATE}, or ${ROLES.RECRUITER}`) 12 | .trim() 13 | .escape(), 14 | validateRequest, //now errors contain an object if the above validation fails 15 | ]; 16 | -------------------------------------------------------------------------------- /auth/src/frameworks/repositories/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import usersRepository from './users.repository'; 2 | 3 | export = { usersRepository }; 4 | -------------------------------------------------------------------------------- /auth/src/frameworks/types/dependency.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface IDependency { 3 | useCases: any; 4 | repositories: any; 5 | } 6 | /* eslint-enable @typescript-eslint/no-explicit-any */ 7 | -------------------------------------------------------------------------------- /auth/src/frameworks/types/otp.ts: -------------------------------------------------------------------------------- 1 | export interface IOtp { 2 | otp: string; 3 | email: string; 4 | } 5 | 6 | export interface IMobileOtp extends IOtp { 7 | phone: number; 8 | } 9 | -------------------------------------------------------------------------------- /auth/src/frameworks/types/user.ts: -------------------------------------------------------------------------------- 1 | interface IUser { 2 | email: string; 3 | password: string; 4 | role: string; 5 | } 6 | 7 | export interface ISignup extends IUser { 8 | name: string; 9 | phone: number; 10 | otp?: number; 11 | } 12 | 13 | export type ISignin = IUser; 14 | 15 | export interface IUpdatePassword { 16 | userId: string; 17 | password: string; 18 | } 19 | -------------------------------------------------------------------------------- /auth/src/frameworks/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const AUTH_CONSTANTS = Object.freeze({ 2 | JWT_ACCESS_TOKEN_EXPIRY: '1d', 3 | JWT_REFRESH_TOKEN_EXPIRY: '30d', 4 | }); 5 | -------------------------------------------------------------------------------- /auth/src/frameworks/utils/kafka-events/consumers/user-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_UPDATED_TOPIC = TOPICS.USER_UPDATED_TOPIC; 7 | 8 | groupId: string = 'auth-1'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_UPDATED_EVENT['data'], topic: string): Promise { 15 | // dont need to check role as every users are stored in one collection 16 | // usersRepository.updateStatus(data); 17 | handleMessage(data, topic); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /auth/src/frameworks/utils/kafka-events/handleMessage.ts: -------------------------------------------------------------------------------- 1 | import usersRepository from '../../repositories/mongo/users.repository'; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 4 | export const handleMessage = (data: any, topic: string) => { 5 | switch (topic) { 6 | case 'USER-UPDATED-TOPIC': 7 | usersRepository.updateUser(data.userId, data); 8 | break; 9 | 10 | default: 11 | break; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /auth/src/frameworks/utils/kafka-events/publishers/user-created-publisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, USER_CREATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class UserCreatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.USER_CREATED_TOPIC = TOPICS.USER_CREATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /auth/src/frameworks/utils/password.ts: -------------------------------------------------------------------------------- 1 | import bcrypt from 'bcryptjs'; 2 | 3 | export const generateHashedPassword = async (password: string) => { 4 | const salt = await bcrypt.genSalt(10); 5 | const hashedPassword = await bcrypt.hash(password, salt); 6 | return hashedPassword; 7 | }; 8 | 9 | export const comparePassword = async (password: string, hashedPassword: string) => { 10 | const result = await bcrypt.compare(password, hashedPassword); 11 | return result; 12 | }; 13 | -------------------------------------------------------------------------------- /auth/src/useCases/auth/index.ts: -------------------------------------------------------------------------------- 1 | import refreshTokenUseCase from './refreshToken'; 2 | import signupUseCase from './signup'; 3 | import updatePasswordUseCase from './updatePassword'; 4 | import signInUseCase from './signin'; 5 | 6 | export { refreshTokenUseCase, signupUseCase, updatePasswordUseCase, signInUseCase }; 7 | -------------------------------------------------------------------------------- /auth/src/useCases/auth/updatePassword.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | import { IUpdatePassword } from '../../frameworks/types/user'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | repositories: { usersRepository }, 7 | } = dependencies; 8 | 9 | if (!usersRepository) throw new Error('usersRepository should exist in dependencies'); 10 | 11 | const execute = async ({ userId, password }: IUpdatePassword) => { 12 | return await usersRepository.updatePassword({ userId, password }); 13 | }; 14 | 15 | return { execute }; 16 | }; 17 | -------------------------------------------------------------------------------- /auth/src/useCases/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth'; 2 | export * from './otp'; 3 | -------------------------------------------------------------------------------- /auth/src/useCases/otp/index.ts: -------------------------------------------------------------------------------- 1 | import verifyMobileOtpUseCase from './verifyMobileOtp'; 2 | import verifyEmailOtpUseCase from './verifyEmailOtp'; 3 | import sendOtpEmailUseCase from './sendOtpEmail'; 4 | import sendOtpMobileUseCase from './sendOtpMobile'; 5 | import authEmailVerificationOtpUseCase from './authEmailVerificationOtp'; 6 | 7 | export { 8 | verifyMobileOtpUseCase, 9 | verifyEmailOtpUseCase, 10 | sendOtpEmailUseCase, 11 | sendOtpMobileUseCase, 12 | authEmailVerificationOtpUseCase, 13 | }; 14 | -------------------------------------------------------------------------------- /chat/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /chat/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "printWidth": 110, 6 | "tabWidth": 4, 7 | "trailingComma": "all", 8 | "arrowParens": "always", 9 | "endOfLine": "auto" 10 | } -------------------------------------------------------------------------------- /chat/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | ENV NODE_ENV=development 14 | 15 | CMD [ "npm", "run", "dev" ] -------------------------------------------------------------------------------- /chat/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM node:20-alpine AS build 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json ./ 7 | 8 | RUN npm ci 9 | 10 | COPY . . 11 | 12 | RUN npm run build 13 | 14 | 15 | # Production stage 16 | FROM node:20-alpine 17 | 18 | WORKDIR /app 19 | 20 | COPY package*.json ./ 21 | 22 | RUN npm ci --omit=dev 23 | 24 | COPY --from=build /app/build ./build 25 | 26 | EXPOSE 3000 27 | 28 | ENV NODE_ENV=production 29 | 30 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /chat/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | 6 | export default [ 7 | {files: ["**/*.{js,mjs,cjs,ts}"]}, 8 | {files: ["**/*.js"], languageOptions: {sourceType: "script"}}, 9 | {languageOptions: { globals: globals.node }}, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | ]; -------------------------------------------------------------------------------- /chat/src/config/appConfig.ts: -------------------------------------------------------------------------------- 1 | export interface IAppConfig { 2 | PORT: string | number; 3 | API_PREFIX: string; 4 | MONGO_URL_CHAT: string; 5 | ENVIRONMENT: string; 6 | JWT_SECRET_KEY: string; 7 | JWT_REFRESH_SECRET_KEY: string; 8 | } 9 | 10 | const appConfig: Readonly = Object.freeze({ 11 | PORT: (process.env.PORT as string) || 3000, 12 | API_PREFIX: (process.env.API_PREFIX as string) || '/api/v1/chat', 13 | MONGO_URL_CHAT: process.env.MONGO_URL_CHAT as string, 14 | ENVIRONMENT: process.env.NODE_ENV as string, 15 | JWT_SECRET_KEY: process.env.JWT_SECRET_KEY as string, 16 | JWT_REFRESH_SECRET_KEY: process.env.JWT_REFRESH_SECRET_KEY as string, 17 | }); 18 | 19 | export { appConfig }; 20 | -------------------------------------------------------------------------------- /chat/src/config/db.connection.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnectionError } from '@abijobportal/common'; 2 | import mongoose from 'mongoose'; 3 | import { appConfig } from './appConfig'; 4 | 5 | const connectDB = async () => { 6 | try { 7 | await mongoose.connect(appConfig.MONGO_URL_CHAT); 8 | console.log('chat service connected to mongodb...'); 9 | } catch (error) { 10 | console.error('chat service mongodb connection failed!!!!', error); 11 | throw new DatabaseConnectionError(); 12 | } 13 | }; 14 | 15 | export { connectDB }; 16 | -------------------------------------------------------------------------------- /chat/src/config/dependencies.ts: -------------------------------------------------------------------------------- 1 | import repositories from '../frameworks/repositories/mongo'; 2 | import * as useCases from '../useCase'; 3 | 4 | export default { repositories, useCases }; 5 | -------------------------------------------------------------------------------- /chat/src/config/kafka.connection.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | 3 | const kafkaClient = new Kafka({ 4 | clientId: 'chat-client', 5 | brokers: ['devhive-kafka:9092'], 6 | 7 | }); 8 | 9 | export { kafkaClient }; 10 | -------------------------------------------------------------------------------- /chat/src/controllers/chat/getChatRoom.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getConversationUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { chatRoomId } = req.params; 11 | const { userId } = req.currentUser; 12 | const conversation = await getConversationUseCase(dependencies).execute(userId, chatRoomId); 13 | 14 | res.status(200).json({ 15 | message: 'Conversations are ', 16 | data: conversation, 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /chat/src/controllers/chat/getChatRooms.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getChatRoomsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser; 11 | const chatRooms = await getChatRoomsUseCase(dependencies).execute(userId); 12 | 13 | res.status(200).json({ message: 'Chat rooms are ', data: chatRooms }); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /chat/src/controllers/chat/index.ts: -------------------------------------------------------------------------------- 1 | import getAllChatRoomsByUserIDController from './getChatRooms.controller'; 2 | import getConversationController from './getChatRoom.controller'; 3 | 4 | import { IDependency } from '../../frameworks/types/dependency'; 5 | 6 | export = (dependencies: IDependency) => { 7 | return { 8 | getAllChatRoomsByUserIDController: getAllChatRoomsByUserIDController(dependencies), 9 | getConversationController: getConversationController(dependencies), 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /chat/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import chatControllers from './chat'; 2 | import notificationControllers from './notification'; 3 | 4 | export { chatControllers, notificationControllers }; 5 | -------------------------------------------------------------------------------- /chat/src/controllers/notification/deleteNotifications.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { deleteAllNotificationsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | 12 | const response = await deleteAllNotificationsUseCase(dependencies).execute(userId); 13 | 14 | res.status(200).json({ message: 'response is ', data: response }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /chat/src/controllers/notification/deleteNotificationsOfASender.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { deleteAllNotificationsBySenderIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId: receiverId } = req.currentUser!; 11 | const { senderId } = req.params; 12 | 13 | const response = await deleteAllNotificationsBySenderIdUseCase(dependencies).execute( 14 | receiverId, 15 | senderId, 16 | ); 17 | 18 | res.status(200).json({ message: 'response is ', data: response }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /chat/src/controllers/notification/getNotifications.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllNotificationsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser; 11 | const notification = await getAllNotificationsUseCase(dependencies).execute(userId); 12 | 13 | res.status(200).json({ message: 'Notifications are ', data: notification }); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /chat/src/controllers/notification/getNotificationsCount.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllNotificationsCountUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | const notification = await getAllNotificationsCountUseCase(dependencies).execute(userId); 12 | 13 | res.status(200).json({ message: 'Notifications count are ', data: notification }); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /chat/src/controllers/notification/getUnreadMessagesCount.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getUnreadMessagesCountUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId: receiverId } = req.currentUser!; 11 | const { senderId } = req.params; 12 | const response = await getUnreadMessagesCountUseCase(dependencies).execute(receiverId, senderId); 13 | 14 | res.status(200).json({ message: 'response is ', data: response }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /chat/src/entities/chat-room.ts: -------------------------------------------------------------------------------- 1 | import { IChatRoom } from '../frameworks/types/chatRoom'; 2 | 3 | export class ChatRoom { 4 | users: string[]; 5 | 6 | constructor({ users }: IChatRoom) { 7 | this.users = users; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chat/src/entities/notification.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iam-abin/devHive/9abccf4342758b92b3c5032552fda938355a04c1/chat/src/entities/notification.ts -------------------------------------------------------------------------------- /chat/src/entities/users.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from '../frameworks/types/user'; 2 | 3 | export class User { 4 | userId: string; 5 | name: string; 6 | profileImage: string; 7 | role: string; 8 | 9 | constructor({ userId, name, profileImage, role }: IUser) { 10 | this.userId = userId; 11 | this.name = name; 12 | this.profileImage = profileImage; 13 | this.role = role; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /chat/src/frameworks/database/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import Models from './models'; 2 | 3 | export default { Models }; 4 | -------------------------------------------------------------------------------- /chat/src/frameworks/database/mongo/models/index.ts: -------------------------------------------------------------------------------- 1 | import { UserModel } from './user'; 2 | import { ChatRoomModel } from './chatRoom'; 3 | import { MessageModel } from './message'; 4 | import { NotificationModel } from './notification'; 5 | 6 | export default { UserModel, ChatRoomModel, MessageModel, NotificationModel }; 7 | -------------------------------------------------------------------------------- /chat/src/frameworks/express/routes/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { IDependency } from '../../types/dependency'; 4 | import { chatRouter } from './routes'; 5 | 6 | import { auth, checkCurrentUser, ROLES } from '@abijobportal/common'; 7 | 8 | export const routes = (dependencies: IDependency) => { 9 | const router = express.Router(); 10 | 11 | const routes = chatRouter(dependencies); 12 | 13 | router.use('/candidate', checkCurrentUser, auth(ROLES.CANDIDATE), routes); 14 | router.use('/recruiter', checkCurrentUser, auth(ROLES.RECRUITER), routes); 15 | 16 | return router; 17 | }; 18 | -------------------------------------------------------------------------------- /chat/src/frameworks/repositories/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import userRepository from './user.repository'; 2 | import messageRepository from './message.repository'; 3 | import chatRoomRepository from './chatRoom.repository'; 4 | import notificationsRepository from './notifications.repository'; 5 | 6 | export = { 7 | userRepository, 8 | messageRepository, 9 | chatRoomRepository, 10 | notificationsRepository, 11 | }; 12 | -------------------------------------------------------------------------------- /chat/src/frameworks/repositories/mongo/user.repository.ts: -------------------------------------------------------------------------------- 1 | import Models from '../../database/mongo/models'; 2 | import { IUserDocument } from '../../database/mongo/models/user'; 3 | import { IUser } from '../../types/user'; 4 | 5 | const { UserModel } = Models; 6 | 7 | export = { 8 | createUser: async (userData: IUser): Promise => { 9 | const newUser = UserModel.buildUser(userData); 10 | return await newUser.save(); 11 | }, 12 | 13 | updateUser: async (userId: string, data: Partial): Promise => { 14 | const user = await UserModel.findByIdAndUpdate(userId, { $set: data }, { new: true }); 15 | return user; 16 | }, 17 | 18 | getById: async (userId: string): Promise => { 19 | const user = await UserModel.findById(userId); 20 | return user; 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /chat/src/frameworks/types/chatRoom.ts: -------------------------------------------------------------------------------- 1 | export interface IChatRoom { 2 | users: string[]; 3 | } 4 | -------------------------------------------------------------------------------- /chat/src/frameworks/types/dependency.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface IDependency { 3 | useCases: any; 4 | repositories: any; 5 | } 6 | /* eslint-enable @typescript-eslint/no-explicit-any */ 7 | -------------------------------------------------------------------------------- /chat/src/frameworks/types/message.ts: -------------------------------------------------------------------------------- 1 | export interface IMessage { 2 | senderId: string; 3 | roomId: string; 4 | textMessage: string; 5 | } 6 | -------------------------------------------------------------------------------- /chat/src/frameworks/types/notification.ts: -------------------------------------------------------------------------------- 1 | export interface INotification { 2 | senderId: string; 3 | targetUserId: string; 4 | message: string; 5 | } 6 | -------------------------------------------------------------------------------- /chat/src/frameworks/types/user.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | userId: string; 3 | name: string; 4 | profileImage: string; 5 | role: string; 6 | } 7 | -------------------------------------------------------------------------------- /chat/src/frameworks/utils/kafka-events/consumers/candidate-profile-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, CANDIDATE_PROFILE_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class CandidateProfileUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.CANDIDATE_PROFILE_UPDATED_TOPIC = TOPICS.CANDIDATE_PROFILE_UPDATED_TOPIC; 7 | 8 | groupId: string = 'chat-3'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: CANDIDATE_PROFILE_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /chat/src/frameworks/utils/kafka-events/consumers/user-created-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserCreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_CREATED_TOPIC = TOPICS.USER_CREATED_TOPIC; 7 | 8 | groupId: string = 'chat-1'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_CREATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /chat/src/frameworks/utils/kafka-events/consumers/user-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_UPDATED_TOPIC = TOPICS.USER_UPDATED_TOPIC; 7 | 8 | groupId: string = 'chat-2'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /chat/src/useCase/chat/getChatRooms.ts: -------------------------------------------------------------------------------- 1 | import { NotFoundError } from '@abijobportal/common'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | repositories: { userRepository, chatRoomRepository }, 7 | } = dependencies; 8 | 9 | if (!chatRoomRepository) { 10 | throw new Error('chatRoomRepository should exist in dependencies'); 11 | } 12 | 13 | const execute = async (userId: string) => { 14 | const user = await userRepository.getById(userId); 15 | if (!user) throw new NotFoundError('user not found'); 16 | const chatRooms = await chatRoomRepository.getAllChatRoomsByUserId(userId); 17 | return chatRooms; 18 | }; 19 | 20 | return { execute }; 21 | }; 22 | -------------------------------------------------------------------------------- /chat/src/useCase/chat/index.ts: -------------------------------------------------------------------------------- 1 | import getChatRoomsUseCase from './getChatRooms'; 2 | import getConversationUseCase from './getChatRoom'; 3 | 4 | export { getChatRoomsUseCase, getConversationUseCase }; 5 | 6 | // chat room creation and chat cration are doing inside frameworks/weSocket/socket.ts file 7 | -------------------------------------------------------------------------------- /chat/src/useCase/index.ts: -------------------------------------------------------------------------------- 1 | export * from './chat'; 2 | 3 | export * from './notification'; 4 | -------------------------------------------------------------------------------- /chat/src/useCase/notification/deleteNotificationsBySenderId.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { notificationsRepository }, 6 | } = dependencies; 7 | 8 | if (!notificationsRepository) { 9 | throw new Error('jobApplicationRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (senderId: string, receiverId: string) => { 13 | return await notificationsRepository.clearAllNotificationsBySenderId(receiverId, senderId); 14 | }; 15 | 16 | return { execute }; 17 | }; 18 | -------------------------------------------------------------------------------- /chat/src/useCase/notification/getNotifications.ts: -------------------------------------------------------------------------------- 1 | import { NotFoundError } from '@abijobportal/common'; 2 | import { IUserDocument } from '../../frameworks/database/mongo/models/user'; 3 | import { IDependency } from '../../frameworks/types/dependency'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | repositories: { userRepository, notificationsRepository }, 8 | } = dependencies; 9 | 10 | if (!notificationsRepository) { 11 | throw new Error('notificationsRepository should exist in dependencies'); 12 | } 13 | 14 | const execute = async (userId: string) => { 15 | const user: IUserDocument | null = await userRepository.getById(userId); 16 | if (!user) throw new NotFoundError('user does not exist'); 17 | return await notificationsRepository.getAllNotificationsByUserId(userId); 18 | }; 19 | 20 | return { execute }; 21 | }; 22 | -------------------------------------------------------------------------------- /chat/src/useCase/notification/getUnreadMessagesCount.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { notificationsRepository }, 6 | } = dependencies; 7 | 8 | if (!notificationsRepository) { 9 | throw new Error('jobApplicationRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (senderId: string, receiverId: string) => { 13 | return await notificationsRepository.getUnreadMessagesCount(receiverId, senderId); 14 | }; 15 | 16 | return { execute }; 17 | }; 18 | -------------------------------------------------------------------------------- /chat/src/useCase/notification/index.ts: -------------------------------------------------------------------------------- 1 | import getAllNotificationsUseCase from './getNotifications'; 2 | import deleteAllNotificationsUseCase from './deleteNotifications'; 3 | import getAllNotificationsCountUseCase from './getNotificationsCount'; 4 | import deleteAllNotificationsBySenderIdUseCase from './deleteNotificationsBySenderId'; 5 | import getUnreadMessagesCountUseCase from './getUnreadMessagesCount'; 6 | 7 | export { 8 | getAllNotificationsUseCase, 9 | deleteAllNotificationsUseCase, 10 | getAllNotificationsCountUseCase, 11 | deleteAllNotificationsBySenderIdUseCase, 12 | getUnreadMessagesCountUseCase, 13 | }; 14 | -------------------------------------------------------------------------------- /client/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build -------------------------------------------------------------------------------- /client/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | '@typescript-eslint/no-explicit-any': 'warn' 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /client/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | ENV NODE_ENV=development 14 | 15 | CMD [ "npm", "run", "dev" ] -------------------------------------------------------------------------------- /client/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM node:20-alpine AS build 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json ./ 7 | 8 | # Install all dependencies (including dev) 9 | RUN npm ci 10 | 11 | COPY . . 12 | 13 | # Run the build command 14 | RUN npm run build 15 | 16 | 17 | # Production stage 18 | FROM node:20-alpine AS production 19 | 20 | WORKDIR /app 21 | 22 | COPY package*.json ./ 23 | 24 | # Install only production dependencies 25 | RUN npm ci --omit=dev 26 | 27 | # Copy built files from the build stage 28 | COPY --from=build /app/dist ./dist 29 | 30 | EXPOSE 3000 31 | 32 | ENV NODE_ENV=production 33 | 34 | CMD [ "npm","run","preview" ] -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DevHive 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iam-abin/devHive/9abccf4342758b92b3c5032552fda938355a04c1/client/public/favicon-32x32.png -------------------------------------------------------------------------------- /client/src/assets/jobs/jobs-not-found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iam-abin/devHive/9abccf4342758b92b3c5032552fda938355a04c1/client/src/assets/jobs/jobs-not-found.png -------------------------------------------------------------------------------- /client/src/assets/landingPage/company-like.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iam-abin/devHive/9abccf4342758b92b3c5032552fda938355a04c1/client/src/assets/landingPage/company-like.jpg -------------------------------------------------------------------------------- /client/src/assets/layoutItems/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iam-abin/devHive/9abccf4342758b92b3c5032552fda938355a04c1/client/src/assets/layoutItems/favicon-32x32.png -------------------------------------------------------------------------------- /client/src/assets/layoutItems/left-arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/assets/payment/wired-flat-37-approve-checked-simple (3).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iam-abin/devHive/9abccf4342758b92b3c5032552fda938355a04c1/client/src/assets/payment/wired-flat-37-approve-checked-simple (3).gif -------------------------------------------------------------------------------- /client/src/assets/user/user-not-found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iam-abin/devHive/9abccf4342758b92b3c5032552fda938355a04c1/client/src/assets/user/user-not-found.png -------------------------------------------------------------------------------- /client/src/axios/apiMethods/admin-service/admin-dashboard.ts: -------------------------------------------------------------------------------- 1 | 2 | import adminApiUrlConfig from "../../../config/apiUrlsConfig/adminServiceApiUrlConfig"; 3 | import { IResponse } from "../../../types/api"; 4 | import makeApiCall from "../../apiCalls"; 5 | 6 | 7 | export const getAllCardsDetailsApi = async (): Promise => { // for admin from job in admin service 8 | return await makeApiCall("get", adminApiUrlConfig.getAllCardsDetailsUrl); 9 | }; 10 | 11 | export const getGraphDataApi = async (): Promise => { // for admin from job in admin service 12 | return await makeApiCall("get", adminApiUrlConfig.getGraphDataUrl); 13 | }; -------------------------------------------------------------------------------- /client/src/axios/apiMethods/admin-service/job.ts: -------------------------------------------------------------------------------- 1 | 2 | import adminApiUrlConfig from "../../../config/apiUrlsConfig/adminServiceApiUrlConfig"; 3 | import { IResponse } from "../../../types/api"; 4 | import makeApiCall from "../../apiCalls"; 5 | 6 | export const getAllJobsAdminApi = async (page: number, limit: number): Promise => { // for admin from job in admin service 7 | return await makeApiCall("get", adminApiUrlConfig.getAllJobsUrl(page, limit)); 8 | }; 9 | 10 | export const blockUnblockJobApi = async (jobId: string): Promise => { 11 | return await makeApiCall("put", adminApiUrlConfig.blockUnblockJobUrl(jobId)); 12 | }; 13 | 14 | export const viewJobDetailsApi = async (jobId: string): Promise => { 15 | return await makeApiCall("get", adminApiUrlConfig.getAJobAdminUrl(jobId)); 16 | }; -------------------------------------------------------------------------------- /client/src/axios/apiMethods/admin-service/recruiters.ts: -------------------------------------------------------------------------------- 1 | import adminApiUrlConfig from "../../../config/apiUrlsConfig/adminServiceApiUrlConfig"; 2 | import { IResponse } from "../../../types/api"; 3 | import makeApiCall from "../../apiCalls"; 4 | 5 | export const getAllRecruitersApi = async (page: number, limit: number): Promise => { 6 | return await makeApiCall( 7 | "get", 8 | adminApiUrlConfig.getAllRecruitersUrl(page, limit) 9 | ); 10 | }; 11 | 12 | export const blockUnblockRecruiterApi = async ( 13 | userId: string 14 | ): Promise => { 15 | return await makeApiCall( 16 | "put", 17 | adminApiUrlConfig.blockUnblockRecruiterUrl(userId) 18 | ); 19 | }; 20 | 21 | export const viewRecruiterProfileDetailsApi = async ( 22 | userId: string 23 | ): Promise => { 24 | return await makeApiCall( 25 | "get", 26 | adminApiUrlConfig.viewRecruiterProfileDetailsUrl(userId) 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /client/src/axios/apiMethods/admin-service/search.ts: -------------------------------------------------------------------------------- 1 | 2 | import adminApiUrlConfig from "../../../config/apiUrlsConfig/adminServiceApiUrlConfig"; 3 | import { IResponse } from "../../../types/api"; 4 | import makeApiCall from "../../apiCalls"; 5 | 6 | 7 | export const searchApi = async (searchKey: string, resourceType: string ,page: number, limit: number): Promise => { 8 | return await makeApiCall("get", adminApiUrlConfig.getSearchResultsUrl(searchKey, resourceType, page, limit)); 9 | }; 10 | -------------------------------------------------------------------------------- /client/src/axios/apiMethods/auth-service/adminAuth.ts: -------------------------------------------------------------------------------- 1 | import authApiUrlConfig from "../../../config/apiUrlsConfig/authApiUrlConfig"; 2 | import { IResponse } from "../../../types/api"; 3 | import { ISignin } from "../../../types/user"; 4 | import makeApiCall from "../../apiCalls"; 5 | 6 | export const adminSigninApi = async (data: ISignin): Promise => { 7 | return await makeApiCall( 8 | "post", 9 | authApiUrlConfig.signinAdminUrl, 10 | data 11 | ); 12 | }; 13 | 14 | export const adminSignoutApi = async (): Promise => { 15 | return await makeApiCall( 16 | "post", 17 | authApiUrlConfig.signoutAdminUrl 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /client/src/axios/apiMethods/payment-service/admin.ts: -------------------------------------------------------------------------------- 1 | import adminApiUrlConfig from "../../../config/apiUrlsConfig/adminServiceApiUrlConfig"; 2 | import { IResponse } from "../../../types/api"; 3 | import makeApiCall from "../../apiCalls"; 4 | 5 | export const getAllPaymentsApi = async (page: number, limit: number): Promise => { 6 | return await makeApiCall("get", adminApiUrlConfig.getAllPaymentsUrl(page, limit)); 7 | }; 8 | -------------------------------------------------------------------------------- /client/src/axios/apiMethods/payment-service/candidate.ts: -------------------------------------------------------------------------------- 1 | import paymentApiUrlConfig from "../../../config/apiUrlsConfig/paymentApiUrlConfig"; 2 | import { IResponse } from "../../../types/api"; 3 | import makeApiCall from "../../apiCalls"; 4 | 5 | 6 | export const createPaymentApi = async (paymentData: any): Promise => { 7 | return await makeApiCall("post", paymentApiUrlConfig.createPaymentUrl, paymentData); 8 | }; 9 | -------------------------------------------------------------------------------- /client/src/axios/apiMethods/premium-plans-service/candidate.ts: -------------------------------------------------------------------------------- 1 | import makeApiCall from "../../apiCalls"; 2 | import paymentApiUrlConfig from "../../../config/apiUrlsConfig/paymentApiUrlConfig"; 3 | import { IResponse } from "../../../types/api"; 4 | 5 | 6 | export const getAllMembershipPlansByCandidateApi = async (): Promise => { 7 | return await makeApiCall("get", paymentApiUrlConfig.getAllMembershipPlansUrl); 8 | }; -------------------------------------------------------------------------------- /client/src/components/dropDown/DropDownSelect.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const DropDownSelect: React.FC<{ firstItem: string; jobs: any }> = ({ firstItem, jobs }) => { 4 | 5 | return ( 6 | <> 7 | 17 | 18 | ); 19 | }; 20 | 21 | export default DropDownSelect; 22 | -------------------------------------------------------------------------------- /client/src/components/loading/BarLoading.tsx: -------------------------------------------------------------------------------- 1 | const BarLoading = () => { 2 | return ( 3 |
4 | 5 | 6 | 7 | 8 |
9 | ); 10 | }; 11 | 12 | export default BarLoading; 13 | -------------------------------------------------------------------------------- /client/src/components/loading/SpinnerLoading.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const SpinnerLoading: React.FC = () => { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | 11 | export default SpinnerLoading 12 | -------------------------------------------------------------------------------- /client/src/components/shimmer/dashboard/DashboardCardAdminShimmer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const DashboardCardAdminShimmer: React.FC = () => { 4 | return ( 5 |
6 |
7 | {/* Placeholder for icon or children */} 8 |
9 | 10 |
11 |
12 |

13 | 14 |

15 |
16 |
17 | ); 18 | }; 19 | 20 | export default DashboardCardAdminShimmer; 21 | -------------------------------------------------------------------------------- /client/src/components/shimmer/table/TableShimmer.tsx: -------------------------------------------------------------------------------- 1 | const TableShimmer = ({ columnCount, rowCount }: { columnCount: number; rowCount: number }) => { 2 | 3 | return ( 4 | 5 | {Array.from({ length: columnCount }).map((_, rowIndex) => ( 6 | 7 | {Array.from({length:rowCount}).map((_: unknown, colIndex: number) => ( 8 | 9 |
10 | 11 | ))} 12 | 13 | ))} 14 | 15 | ); 16 | }; 17 | 18 | export default TableShimmer; 19 | -------------------------------------------------------------------------------- /client/src/config/apiUrlsConfig/chatApiUrlConfig.ts: -------------------------------------------------------------------------------- 1 | const CANDIDATE_CHAT_URL = `chat/candidate`; 2 | const RECRUITER_CHAT_URL = `chat/recruiter`; 3 | // const CHAT_URL = `chat`; 4 | 5 | 6 | 7 | 8 | const chatApiUrlConfig = { 9 | // Candidate 10 | getACandidateConversationUrl: (roomId: string) => `${CANDIDATE_CHAT_URL}/room/conversation/${roomId}`, 11 | getAllCandidateRoomsUrl: (userId: string) => `${CANDIDATE_CHAT_URL}/chat-rooms/${userId}`, 12 | 13 | // recruiter 14 | getARecruiterConversationUrl: (roomId: string) => `${RECRUITER_CHAT_URL}/room/conversation/${roomId}`, 15 | getAllRecruiterRoomsUrl: (userId: string) => `${RECRUITER_CHAT_URL}/chat-rooms/${userId}`, 16 | 17 | }; 18 | 19 | export default chatApiUrlConfig; 20 | -------------------------------------------------------------------------------- /client/src/config/apiUrlsConfig/paymentApiUrlConfig.ts: -------------------------------------------------------------------------------- 1 | // const Admin_PAYMENT_URL = `payment/payment-route`; 2 | const CANDIDATE_PAYMENT_URL = `payment/payment`; 3 | const CANDIDATE_MEMBERSHIP_PLANS_URL = `payment/premium/candidate`; 4 | 5 | 6 | const paymentApiUrlConfig = { 7 | // Candidate 8 | createPaymentUrl: `${CANDIDATE_PAYMENT_URL}/create`, 9 | 10 | // Premium plans 11 | getAllMembershipPlansUrl: `${CANDIDATE_MEMBERSHIP_PLANS_URL}/plans`, 12 | } 13 | 14 | export default paymentApiUrlConfig 15 | -------------------------------------------------------------------------------- /client/src/config/baseUrl.ts: -------------------------------------------------------------------------------- 1 | export const PRODUCTION_ORIGIN = "https://abinvarghese.online" 2 | export const DEVELOPMENT_ORIGIN = "https://devhive.dev" 3 | 4 | export const BASE_URL = `${DEVELOPMENT_ORIGIN}/api/v1` -------------------------------------------------------------------------------- /client/src/config/firebase.ts: -------------------------------------------------------------------------------- 1 | import { initializeApp } from "firebase/app"; 2 | import { getStorage } from "firebase/storage"; 3 | // import { getMessaging } from "firebase/messaging"; 4 | 5 | const firebaseConfig = { 6 | apiKey: import.meta.env.VITE_FIREBASE_API_KEY, 7 | authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, 8 | projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, 9 | storageBucket: "devhive-2230d.appspot.com", 10 | messagingSenderId: "534531593324", 11 | appId: "1:534531593324:web:c73cf5a3c14b34a3cc3449", 12 | measurementId: "G-J1Y3J654MY", 13 | }; 14 | 15 | // Initialize Firebase 16 | const firebaseApp = initializeApp(firebaseConfig); 17 | export const myFirebaseStorage = getStorage(firebaseApp); 18 | // export const myFirebaseMessaging = getMessaging(firebaseApp); 19 | -------------------------------------------------------------------------------- /client/src/config/socket.ts: -------------------------------------------------------------------------------- 1 | import { io, Socket } from "socket.io-client"; 2 | import { DEVELOPMENT_ORIGIN } from "./baseUrl"; 3 | import { getItemFromLocalStorage } from "../utils/localStorage"; 4 | import { LOCAL_STORAGE } from "../utils/constants"; 5 | 6 | const token: string | null = getItemFromLocalStorage(LOCAL_STORAGE.ACCESS_TOKEN); 7 | 8 | // const socket: Socket = io(PRODUCTION_ORIGIN, { 9 | // transports: ["websocket"], 10 | // path: "/api/v1/chat/socket.io", 11 | // withCredentials: true, 12 | // autoConnect: false, 13 | // auth: { token: `Bearer ${token}` } 14 | // }); 15 | 16 | 17 | 18 | const socket: Socket = io(DEVELOPMENT_ORIGIN, { 19 | transports: ["websocket"], 20 | path: "/api/v1/chat/socket.io", 21 | withCredentials: true, 22 | autoConnect: false, 23 | auth: { accessToken: `Bearer ${token}` } 24 | }); 25 | 26 | export default socket; 27 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body{ 6 | margin: 0; 7 | } -------------------------------------------------------------------------------- /client/src/main.tsx: -------------------------------------------------------------------------------- 1 | // import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import "./index.css"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | import { Provider } from "react-redux"; 7 | 8 | import { persistor, store } from "./redux/store.ts"; 9 | import { PersistGate } from "redux-persist/integration/react"; 10 | // import { ErrorBoundary } from "react-error-boundary"; 11 | // import Fallback from "./components/Error/ErrorBoundary.tsx"; 12 | 13 | ReactDOM.createRoot(document.getElementById("root")!).render( 14 | 15 | {/* */} 16 | 17 | 18 | {/* */} 19 | 20 | {/* */} 21 | 22 | 23 | {/* */} 24 | 25 | ); 26 | -------------------------------------------------------------------------------- /client/src/redux/reducer.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "@reduxjs/toolkit"; 2 | 3 | import apiCallLoadingSlice from "./slice/isLoading"; 4 | import chatSlice from "./slice/chat" 5 | import jobSlice from "./slice/job" 6 | import notificationSlice from "./slice/notification" 7 | import userSlice from "./slice/user" 8 | 9 | 10 | const rootRedcucer = combineReducers({ 11 | loading: apiCallLoadingSlice, 12 | chatReducer: chatSlice, 13 | jobReducer: jobSlice, 14 | notificationReducer: notificationSlice, 15 | userReducer: userSlice, 16 | }); 17 | 18 | export type RootState = ReturnType; 19 | export default rootRedcucer; 20 | -------------------------------------------------------------------------------- /client/src/redux/slice/isLoading.ts: -------------------------------------------------------------------------------- 1 | 2 | import { createSlice, } from '@reduxjs/toolkit'; 3 | // import { RootState } from '../../reducer/reducer'; 4 | 5 | interface ApiState { 6 | isLoading: boolean; 7 | } 8 | 9 | const initialState: ApiState = { 10 | isLoading: false, 11 | }; 12 | 13 | const apiCallLoadingSlice = createSlice({ 14 | name: 'api', 15 | initialState, 16 | reducers: { 17 | setLoading: (state) => { 18 | state.isLoading = true; 19 | }, 20 | setLoaded: (state) => { 21 | state.isLoading = false; 22 | }, 23 | }, 24 | }); 25 | 26 | // export const selectApiLoading = (state: RootState) => state.api.loading; 27 | 28 | export const { setLoading, setLoaded } = apiCallLoadingSlice.actions; 29 | export default apiCallLoadingSlice.reducer; 30 | -------------------------------------------------------------------------------- /client/src/types/api.ts: -------------------------------------------------------------------------------- 1 | export interface IResponse { 2 | message: string; 3 | data?: any; 4 | accessToken?: string; 5 | refreshToken?: string; 6 | } 7 | -------------------------------------------------------------------------------- /client/src/types/chat.ts: -------------------------------------------------------------------------------- 1 | export interface IChatRoom { 2 | _id: string, 3 | users: string[]; 4 | lastMessage: string 5 | createdAt: string 6 | } 7 | 8 | // export interface IChatRoomResponse { 9 | // _id: string, 10 | // users: string[]; 11 | // lastMessage: string 12 | // createdAt: string 13 | // } 14 | 15 | export interface IMessage { 16 | senderId: string; 17 | roomId: string; 18 | textMessage: string; 19 | read: boolean; 20 | 21 | } 22 | 23 | 24 | export interface INotification { 25 | senderId: string; 26 | targetUserId: string; 27 | message: string; 28 | } 29 | -------------------------------------------------------------------------------- /client/src/types/otp.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface IOtp { 3 | otp: string; 4 | } 5 | 6 | export interface IEmailOrMobile { 7 | email?: string; 8 | mobile?: string; 9 | } 10 | export interface IEmail { 11 | email: string; 12 | } 13 | 14 | export interface IMobile { 15 | mobile: string; 16 | } 17 | 18 | export interface IOtpFromProps { 19 | email?: string; 20 | phone?: string; 21 | handleSubmit: (otp: string) => void; 22 | } 23 | 24 | 25 | export interface IEmailOrMobileProps { 26 | initialValue: IEmailOrMobile; 27 | handleSubmit: (values: IEmailOrMobile) => void; 28 | } 29 | -------------------------------------------------------------------------------- /client/src/types/payment.ts: -------------------------------------------------------------------------------- 1 | export interface IMembershipPlan { 2 | membershipPlanId: string, 3 | name: string; 4 | features: string[]; 5 | description: string; 6 | price: number; 7 | isActive: boolean; 8 | } 9 | 10 | 11 | export interface IPayment { 12 | candidateId: string; 13 | membershipPlanId: string; 14 | stripeId: string; 15 | } -------------------------------------------------------------------------------- /client/src/utils/checkRole.ts: -------------------------------------------------------------------------------- 1 | import { IUserData } from "../types/user"; 2 | import { ROLES } from "./constants"; 3 | 4 | export const checkUserRole = (loggedinUser: IUserData | null) => { 5 | let isCandidate: boolean = false; 6 | let isRecruiter: boolean = false; 7 | let isAdmin: boolean = false; 8 | 9 | if (loggedinUser) { 10 | isCandidate = loggedinUser?.role === ROLES.CANDIDATE; 11 | isRecruiter = loggedinUser?.role === ROLES.RECRUITER; 12 | isAdmin = loggedinUser?.role === ROLES.ADMIN; 13 | } 14 | 15 | return { isCandidate, isRecruiter, isAdmin }; 16 | }; 17 | -------------------------------------------------------------------------------- /client/src/utils/currency-format.ts: -------------------------------------------------------------------------------- 1 | export const formatCurrency = (number: number): string => { 2 | const curr = number.toLocaleString("en-IN", { 3 | style: "currency", 4 | currency: "INR", 5 | }); 6 | return curr; 7 | }; 8 | -------------------------------------------------------------------------------- /client/src/utils/hotToastMessage.ts: -------------------------------------------------------------------------------- 1 | import toast from "react-hot-toast"; 2 | 3 | export const hotToastMessage = (msg: string, type: string): void => { 4 | switch (type) { 5 | case "success": 6 | toast.success(msg); 7 | break; 8 | 9 | case "error": 10 | toast.error(msg); 11 | break; 12 | case "warn": 13 | toast(msg, { 14 | icon: "⚠️", 15 | style: { 16 | border: "1px solid #FFA500", 17 | padding: "5px", 18 | color: "#00000", 19 | }, 20 | }); 21 | break; 22 | 23 | default: 24 | // Default to success for unknown types 25 | toast.success(msg); 26 | break; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /client/src/utils/localStorage.ts: -------------------------------------------------------------------------------- 1 | export const setItemToLocalStorage = (key: string, value: string): void => { 2 | localStorage.setItem(key, value); 3 | }; 4 | 5 | export const getItemFromLocalStorage = (key: string): string | null => { 6 | const token = localStorage.getItem(key); 7 | if (!token) return null; 8 | return JSON.parse(token); 9 | }; 10 | 11 | export const clearItemFromLocalStorage = (key: string): void => { 12 | localStorage.removeItem(key); 13 | }; 14 | 15 | export const clearLocalStorage = (): void => { 16 | localStorage.clear(); 17 | }; 18 | -------------------------------------------------------------------------------- /client/src/utils/swal.ts: -------------------------------------------------------------------------------- 1 | import Swal, { SweetAlertResult } from "sweetalert2"; 2 | 3 | export const swal = async ( 4 | title: string, 5 | confirmButtonText: string, 6 | avoidOptions = false 7 | ) => { 8 | const response: SweetAlertResult = await Swal.fire({ 9 | title, 10 | text: avoidOptions ? undefined : "Are you sure!", 11 | icon: avoidOptions ? undefined : "warning", 12 | showCancelButton: avoidOptions ? undefined : true, 13 | confirmButtonColor: avoidOptions ? undefined : "#3085d6", 14 | cancelButtonColor: avoidOptions ? undefined : "#d33", 15 | confirmButtonText, 16 | }); 17 | 18 | return response; 19 | }; 20 | -------------------------------------------------------------------------------- /client/src/utils/toastMessage.ts: -------------------------------------------------------------------------------- 1 | import { toast } from "react-toastify"; 2 | 3 | export const notify = (msg: string, type: string): void => { 4 | const options = { 5 | position: toast.POSITION.TOP_RIGHT, 6 | }; 7 | 8 | switch (type) { 9 | case "success": 10 | toast.success(msg, options); 11 | break; 12 | 13 | case "error": 14 | toast.error(msg, options); 15 | break; 16 | 17 | case "warning": 18 | toast.warning(msg, options); 19 | break; 20 | 21 | default: 22 | // Default to success for unknown types 23 | toast.success(msg, options); 24 | break; 25 | } 26 | }; -------------------------------------------------------------------------------- /client/src/utils/validations/signin.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from "yup"; 2 | import { IUser } from "../../types/user"; 3 | 4 | export const initialSigninValues: IUser = { 5 | email: "", 6 | password: "", 7 | 8 | }; 9 | 10 | export const signinSchema = Yup.object().shape({ 11 | email: Yup.string().email().required(), 12 | password: Yup.string().min(4).required(), 13 | }); 14 | 15 | export type SigninFormSchemaType = Yup.ObjectSchema; 16 | -------------------------------------------------------------------------------- /client/src/utils/validations/signup.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from "yup"; 2 | import { IAuth } from "../../types/user"; 3 | 4 | export const initialSignupValues: IAuth = { 5 | name: "", 6 | email: "", 7 | phone: "", 8 | password: "", 9 | }; 10 | 11 | export const signupSchema = Yup.object().shape({ 12 | name: Yup.string().matches(/^[^\s].*$/, 'Name cannot start with a space').min(3).required(), 13 | email: Yup.string().email().required(), 14 | phone: Yup.string().matches(/^\d{10}$/, 'Phone number must be 10 digits').required(), 15 | password: Yup.string().min(4).required(), 16 | }); 17 | 18 | export type SignupFormSchemaType = Yup.ObjectSchema; 19 | -------------------------------------------------------------------------------- /client/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./index.html", 5 | "./src/**/*.{js,ts,jsx,tsx}", 6 | ], 7 | theme: { 8 | extend: {} 9 | }, 10 | plugins: [require("daisyui")], 11 | daisyui: { 12 | themes: ["cupcake"], 13 | }, 14 | } -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src", "src/pages/auth/emailOrMobileEnter/EnterEmailOrMobilePage.tsx"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /client/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | server: { 8 | host: true, 9 | port: 3000, // This is the port which we will use in docker ,or (port on which the development server will run) 10 | watch: { 11 | usePolling: true 12 | } 13 | }, 14 | build: { 15 | chunkSizeWarningLimit: 1600 16 | }, 17 | preview: { 18 | port: 3000 19 | } 20 | }); -------------------------------------------------------------------------------- /job/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /job/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "printWidth": 110, 6 | "tabWidth": 4, 7 | "trailingComma": "all", 8 | "arrowParens": "always", 9 | "endOfLine": "auto" 10 | } -------------------------------------------------------------------------------- /job/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | ENV NODE_ENV=development 14 | 15 | CMD [ "npm", "run", "dev" ] -------------------------------------------------------------------------------- /job/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM node:20-alpine AS build 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json ./ 7 | 8 | RUN npm ci 9 | 10 | COPY . . 11 | 12 | RUN npm run build 13 | 14 | 15 | # Production stage 16 | FROM node:20-alpine 17 | 18 | WORKDIR /app 19 | 20 | COPY package*.json ./ 21 | 22 | RUN npm ci --omit=dev 23 | 24 | COPY --from=build /app/build ./build 25 | 26 | EXPOSE 3000 27 | 28 | ENV NODE_ENV=production 29 | 30 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /job/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | 6 | export default [ 7 | {files: ["**/*.{js,mjs,cjs,ts}"]}, 8 | {files: ["**/*.js"], languageOptions: {sourceType: "script"}}, 9 | {languageOptions: { globals: globals.node }}, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | ]; -------------------------------------------------------------------------------- /job/src/config/appConfig.ts: -------------------------------------------------------------------------------- 1 | export interface IAppConfig { 2 | PORT: string | number; 3 | API_PREFIX: string; 4 | MONGO_URL_JOB: string; 5 | ENVIRONMENT: string; 6 | JWT_SECRET_KEY: string; 7 | JWT_REFRESH_SECRET_KEY: string; 8 | } 9 | 10 | const appConfig: Readonly = Object.freeze({ 11 | PORT: (process.env.PORT as string) || 3000, 12 | API_PREFIX: (process.env.API_PREFIX as string) || '/api/v1/job', 13 | MONGO_URL_JOB: process.env.MONGO_URL_JOB as string, 14 | ENVIRONMENT: process.env.NODE_ENV as string, 15 | JWT_SECRET_KEY: process.env.JWT_SECRET_KEY as string, 16 | JWT_REFRESH_SECRET_KEY: process.env.JWT_REFRESH_SECRET_KEY as string, 17 | }); 18 | 19 | export { appConfig }; 20 | -------------------------------------------------------------------------------- /job/src/config/db.connection.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnectionError } from '@abijobportal/common'; 2 | import mongoose from 'mongoose'; 3 | import { appConfig } from './appConfig'; 4 | 5 | const connectDB = async () => { 6 | try { 7 | await mongoose.connect(appConfig.MONGO_URL_JOB); 8 | console.log('job service connected to mongodb...'); 9 | } catch (error) { 10 | console.error('job service mongodb connection failed!!!!', error); 11 | throw new DatabaseConnectionError(); 12 | } 13 | }; 14 | export { connectDB }; 15 | -------------------------------------------------------------------------------- /job/src/config/dependencies.ts: -------------------------------------------------------------------------------- 1 | import repositories from '../frameworks/repositories/mongo'; 2 | import * as useCases from '../useCases'; 3 | 4 | export default { repositories, useCases }; 5 | -------------------------------------------------------------------------------- /job/src/config/kafka.connection.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | 3 | const kafkaClient = new Kafka({ 4 | clientId: 'job-client', 5 | brokers: ['devhive-kafka:9092'], 6 | 7 | }); 8 | 9 | export { kafkaClient }; 10 | -------------------------------------------------------------------------------- /job/src/controllers/candidate/appliedJobs.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllAppliedJobsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | 12 | const { appliedJobs, numberOfPages } = await getAllAppliedJobsUseCase(dependencies).execute( 13 | userId, 14 | Number(req.params.page) || 1, 15 | Number(req.params.limit) || 2, 16 | ); 17 | 18 | res.status(200).json({ 19 | message: 'Applied Jobs are', 20 | data: { appliedJobs, numberOfPages }, 21 | }); 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /job/src/controllers/candidate/apply.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { applyJobUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; // candidateId 11 | const { jobId } = req.params; 12 | 13 | const applied = await applyJobUseCase(dependencies).execute(userId, jobId); 14 | 15 | res.status(200).json({ 16 | message: 'Job applied successfully', 17 | data: applied, 18 | }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /job/src/controllers/candidate/checkApplied.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { checkIsAppliedJobUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; // candidateId 11 | const { jobId } = req.params; 12 | 13 | const isApplied = await checkIsAppliedJobUseCase(dependencies).execute(userId, jobId); 14 | 15 | res.status(200).json({ 16 | message: 'Job applied or not fetched successfully', 17 | data: { isApplied }, 18 | }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /job/src/controllers/candidate/getApplication.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAJobApplicationUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { jobApplicationId } = req.params; 11 | const { userId } = req.currentUser!; 12 | 13 | const application = await getAJobApplicationUseCase(dependencies).execute({ 14 | jobApplicationId, 15 | candidateId: userId, 16 | }); 17 | 18 | res.status(200).json({ message: 'Job applications are ', data: application }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /job/src/controllers/candidate/index.ts: -------------------------------------------------------------------------------- 1 | import applyJobController from './apply.controller'; 2 | import appliedJobsController from './appliedJobs.controller'; 3 | import checkAppliedController from './checkApplied.controller'; 4 | import getAppliedJobApplicationController from './getApplication.controller'; 5 | 6 | import { IDependency } from '../../frameworks/types/dependency'; 7 | 8 | export = (dependencies: IDependency) => { 9 | return { 10 | applyJobController: applyJobController(dependencies), 11 | appliedJobsController: appliedJobsController(dependencies), 12 | checkAppliedController: checkAppliedController(dependencies), 13 | getAppliedJobApplicationController: getAppliedJobApplicationController(dependencies), 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /job/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import jobsControllers from './jobs'; 2 | import candidateJobControllers from './candidate'; 3 | import recruiterJobControllers from './recruiter'; 4 | import searchControllers from './search'; 5 | 6 | export { jobsControllers, candidateJobControllers, recruiterJobControllers, searchControllers }; 7 | -------------------------------------------------------------------------------- /job/src/controllers/jobs/filterJobs.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { filterJobUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const filterData = req.body; 11 | 12 | const { jobs, numberOfPages } = await filterJobUseCase(dependencies).execute( 13 | filterData, 14 | Number(req.params.page) || 1, 15 | Number(req.params.limit) || 2, 16 | ); 17 | 18 | res.status(200).json({ 19 | message: 'Job filtered successfully', 20 | data: { jobs, numberOfPages }, 21 | }); 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /job/src/controllers/jobs/getJob.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getJobByIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { id } = req.params; 11 | const job = await getJobByIdUseCase(dependencies).execute(id); 12 | res.status(200).json({ message: 'Job get successfully', data: job }); 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /job/src/controllers/jobs/getJobs.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllJobsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { jobs, numberOfPages } = await getAllJobsUseCase(dependencies).execute( 11 | Number(req.params.page) || 1, 12 | Number(req.params.limit) || 2, 13 | ); 14 | 15 | res.status(200).json({ 16 | message: 'Jobs list fetched successfully', 17 | data: { jobs, numberOfPages }, 18 | }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /job/src/controllers/jobs/index.ts: -------------------------------------------------------------------------------- 1 | import filterJobsController from './filterJobs.controller'; 2 | import viewAllJobsController from './getJobs.controller'; 3 | import viewJobByJobIdController from './getJob.controller'; 4 | import viewAllJobFieldsDistinctValuesController from './viewDistinctFieldValues.controller'; 5 | 6 | import { IDependency } from '../../frameworks/types/dependency'; 7 | 8 | export = (dependencies: IDependency) => { 9 | return { 10 | filterJobsController: filterJobsController(dependencies), 11 | viewAllJobsController: viewAllJobsController(dependencies), 12 | viewAllJobFieldsDistinctValuesController: viewAllJobFieldsDistinctValuesController(dependencies), 13 | viewJobByJobIdController: viewJobByJobIdController(dependencies), 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /job/src/controllers/jobs/viewDistinctFieldValues.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllJobFieldsDistinctValuesUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const jobFields = await getAllJobFieldsDistinctValuesUseCase(dependencies).execute(req.body); 11 | res.status(200).json({ message: 'Jobs list', data: jobFields }); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/changeApplicationStatus.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { changeJobApplicationStatusUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | const { jobApplicationId } = req.params; 12 | const { jobApplicationStatus } = req.body; 13 | 14 | const application = await changeJobApplicationStatusUseCase(dependencies).execute( 15 | jobApplicationId, 16 | jobApplicationStatus, 17 | userId, 18 | ); 19 | 20 | res.status(200).json({ message: `status updated to ${jobApplicationStatus}`, data: application }); 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/closeJob.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { changeClosejobStatusUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { jobId } = req.params; 11 | const { userId } = req.currentUser!; 12 | const job = await changeClosejobStatusUseCase(dependencies).execute(jobId, userId); 13 | 14 | res.status(200).json({ 15 | message: `close status updated to ${job?.isActive ? 'Open' : 'Close'}`, 16 | data: job, 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/createJob.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IJob } from '../../frameworks/types/job'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { createJobUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const { userId } = req.currentUser!; 12 | const newJob = await createJobUseCase(dependencies).execute(userId, req.body as IJob); 13 | res.status(201).json({ message: 'Job created successfully', data: newJob }); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/dashboardCardsData.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { recruiterDashboardCardsDetailsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | const dashboardCardsDetails = 12 | await recruiterDashboardCardsDetailsUseCase(dependencies).execute(userId); 13 | res.status(200).json({ 14 | message: 'dashboard card details fetched successfully', 15 | data: dashboardCardsDetails, 16 | }); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/dashboardGraphData.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { recruiterDashboardGraphDetailsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | const dashboardGraphDetails = 12 | await recruiterDashboardGraphDetailsUseCase(dependencies).execute(userId); 13 | 14 | res.status(200).json({ 15 | message: 'dashboard graph data fetched successfully', 16 | data: dashboardGraphDetails, 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/deleteJob.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { deleteJobUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { id: jobId } = req.params; 11 | const { userId } = req.currentUser!; 12 | 13 | const remainingJobs = await deleteJobUseCase(dependencies).execute(jobId, userId); 14 | 15 | return res.status(200).json({ message: 'Job deleted successfully', data: remainingJobs }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/edit.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IJob } from '../../frameworks/types/job'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { updateJobUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const { jobId } = req.params; 12 | const { userId } = req.currentUser!; 13 | const updatedJob = await updateJobUseCase(dependencies).execute( 14 | jobId, 15 | userId, 16 | req.body as Partial, 17 | ); 18 | 19 | res.status(200).json({ message: 'Job updated successfully', data: updatedJob }); 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/getApplication.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAJobApplicationUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { jobApplicationId } = req.params; 11 | const { userId } = req.currentUser!; 12 | const application = await getAJobApplicationUseCase(dependencies).execute({ 13 | jobApplicationId, 14 | recruiterId: userId, 15 | }); 16 | res.status(200).json({ message: 'Job applications are ', data: application }); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/getApplications.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllJobApplicationsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId: recruiterId } = req.currentUser!; 11 | 12 | const { applications, numberOfPages } = await getAllJobApplicationsUseCase(dependencies).execute( 13 | recruiterId, 14 | null, 15 | Number(req.params.page) || 1, 16 | Number(req.params.limit) || 4, 17 | ); 18 | 19 | res.status(200).json({ 20 | message: 'Job applications are ', 21 | data: { applications, numberOfPages }, 22 | }); 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /job/src/controllers/recruiter/getCreatedJobs.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getRecruiterCreatedJobsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | const { jobs, numberOfPages } = await getRecruiterCreatedJobsUseCase(dependencies).execute( 12 | userId, 13 | Number(req.params.page) || 1, 14 | Number(req.params.limit) || 4, 15 | ); 16 | 17 | res.status(201).json({ 18 | message: 'Jobs got successfully', 19 | data: { jobs, numberOfPages }, 20 | }); 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /job/src/controllers/search/index.ts: -------------------------------------------------------------------------------- 1 | import searchController from "./search.controller" 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | return { 6 | searchController: searchController(dependencies), 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /job/src/controllers/search/search.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { searchUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { type: resourceType } = req.params; 11 | const { searchKey } = req.query; 12 | 13 | const { jobs, numberOfPages } = await searchUseCase(dependencies).execute( 14 | resourceType, 15 | searchKey as string, 16 | Number(req.params.page) || 1, 17 | Number(req.params.limit) || 4, 18 | ); 19 | 20 | res.status(200).json({ 21 | message: 'search results fetched successfully ', 22 | data: { jobs, numberOfPages }, 23 | }); 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /job/src/entities/jobApplications.ts: -------------------------------------------------------------------------------- 1 | import { IJobApplication } from '../frameworks/types/jobApplication'; 2 | 3 | export class JobApplication { 4 | jobId: string; 5 | candidateId: string; 6 | recruiterId: string; 7 | applicationStatus: string; 8 | 9 | constructor({ jobId, candidateId, recruiterId, applicationStatus }: IJobApplication) { 10 | this.jobId = jobId; 11 | this.candidateId = candidateId; 12 | this.recruiterId = recruiterId; 13 | this.applicationStatus = applicationStatus; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /job/src/frameworks/database/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import Models from './models'; 2 | 3 | export default { Models }; 4 | -------------------------------------------------------------------------------- /job/src/frameworks/database/mongo/models/index.ts: -------------------------------------------------------------------------------- 1 | import { JobModel } from './job'; 2 | import { jobApplicationModel } from './jobApplication'; 3 | import { UserModel } from './user'; 4 | 5 | export default { JobModel, jobApplicationModel, UserModel }; 6 | -------------------------------------------------------------------------------- /job/src/frameworks/express/app.ts: -------------------------------------------------------------------------------- 1 | import 'express-async-errors'; 2 | import express, { Express } from 'express'; 3 | import morgan from 'morgan'; 4 | import { NotFoundError, errorHandler } from '@abijobportal/common'; 5 | import compression from 'compression'; 6 | 7 | import { routes } from './routes'; 8 | import dependencies from '../../config/dependencies'; 9 | import { appConfig } from '../../config/appConfig'; 10 | 11 | const app: Express = express(); 12 | 13 | app.set('trust proxy', true); // trust first proxy 14 | 15 | // Middlewares 16 | app.use(morgan('dev')); 17 | 18 | app.use(express.json()); 19 | app.use(express.urlencoded({ extended: true })); 20 | app.use(compression()); 21 | // Routes 22 | app.use(appConfig.API_PREFIX, routes(dependencies)); 23 | 24 | app.all('*', async () => { 25 | throw new NotFoundError(); 26 | }); 27 | 28 | app.use(errorHandler); 29 | 30 | export { app }; 31 | -------------------------------------------------------------------------------- /job/src/frameworks/express/routes/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { candidateRouter } from './candidate'; 4 | import { recruiterRouter } from './recruiter'; 5 | 6 | import { IDependency } from '../../types/dependency'; 7 | import { checkCurrentUser } from '@abijobportal/common'; 8 | 9 | export const routes = (dependencies: IDependency) => { 10 | const router = express.Router(); 11 | 12 | const candidate = candidateRouter(dependencies); 13 | const recruiter = recruiterRouter(dependencies); 14 | 15 | router.use('/candidate', checkCurrentUser, candidate); 16 | router.use('/recruiter', checkCurrentUser, recruiter); 17 | 18 | return router; 19 | }; 20 | -------------------------------------------------------------------------------- /job/src/frameworks/repositories/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import jobRepository from './job.repository'; 2 | import jobApplicationRepository from './jobApplication.repository'; 3 | 4 | export = { 5 | jobRepository, 6 | jobApplicationRepository, 7 | }; 8 | -------------------------------------------------------------------------------- /job/src/frameworks/types/dependency.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface IDependency { 3 | useCases: any; 4 | repositories: any; 5 | } 6 | /* eslint-enable @typescript-eslint/no-explicit-any */ 7 | -------------------------------------------------------------------------------- /job/src/frameworks/types/job.ts: -------------------------------------------------------------------------------- 1 | export interface IJob { 2 | title: string; 3 | recruiterId: string; 4 | companyName: string; 5 | companyLocation: string; 6 | 7 | jobDescription?: string; 8 | skills?: string[]; 9 | availablePosition?: number; 10 | experienceRequired?: number; 11 | educationRequired?: string; 12 | employmentType?: string; 13 | salaryMin?: number; 14 | salaryMax?: number; 15 | deadline?: Date; 16 | } 17 | 18 | export interface IFilter { 19 | title: string; 20 | companyLocation: string; 21 | employmentType: string; 22 | } 23 | -------------------------------------------------------------------------------- /job/src/frameworks/types/jobApplication.ts: -------------------------------------------------------------------------------- 1 | export interface IJobApplication { 2 | jobId: string; 3 | candidateId: string; 4 | recruiterId: string; 5 | applicationStatus: string; 6 | } 7 | -------------------------------------------------------------------------------- /job/src/frameworks/types/user.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | userId: string; 3 | email: string; 4 | name: string; 5 | role: string; 6 | phone: number; 7 | isActive: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /job/src/frameworks/utils/constants.ts: -------------------------------------------------------------------------------- 1 | type ApplicationStatus = { 2 | APPLIED: 'Applied'; 3 | SHORTLISTED: 'Shortlisted'; 4 | REJECTED: 'Rejected'; 5 | }; 6 | 7 | export const APPLICATION_STATUS: Readonly = Object.freeze({ 8 | APPLIED: 'Applied', 9 | SHORTLISTED: 'Shortlisted', 10 | REJECTED: 'Rejected', 11 | }); 12 | -------------------------------------------------------------------------------- /job/src/frameworks/utils/kafka-events/consumers/jobUpdatedConsumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, JOB_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class jobUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.JOB_UPDATED_TOPIC = TOPICS.JOB_UPDATED_TOPIC; 7 | 8 | groupId: string = 'job-1'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: JOB_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /job/src/frameworks/utils/kafka-events/consumers/userCreatedConsumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserCreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_CREATED_TOPIC = TOPICS.USER_CREATED_TOPIC; 7 | 8 | groupId: string = 'job-2'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_CREATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /job/src/frameworks/utils/kafka-events/consumers/userUpdatedConsumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_UPDATED_TOPIC = TOPICS.USER_UPDATED_TOPIC; 7 | 8 | groupId: string = 'job-3'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /job/src/frameworks/utils/kafka-events/publishers/jobCreatedPublisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, JOB_CREATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class JobCreatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.JOB_CREATED_TOPIC = TOPICS.JOB_CREATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /job/src/frameworks/utils/kafka-events/publishers/jobDeletedPublisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, JOB_DELETED_EVENT } from '@abijobportal/common'; 2 | 3 | export class JobDeletedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.JOB_DELETED_TOPIC = TOPICS.JOB_DELETED_TOPIC; 5 | } 6 | 7 | // if delete is soft delete, use job-update-publisher.ts 8 | // use this only if i am using JOB_DELETED_EVENT 9 | -------------------------------------------------------------------------------- /job/src/frameworks/utils/kafka-events/publishers/jobUpdatedPublisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, JOB_UPDATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class JobUpdatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.JOB_UPDATED_TOPIC = TOPICS.JOB_UPDATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /job/src/useCases/candidate/checkApplied.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { jobApplicationRepository }, 6 | } = dependencies; 7 | 8 | if (!jobApplicationRepository) { 9 | throw new Error('jobApplicationRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (candidateId: string, jobId: string) => { 13 | const isApplicationExist = await jobApplicationRepository.getAnAppliedJobByCandidate( 14 | candidateId, 15 | jobId, 16 | ); 17 | 18 | if (!isApplicationExist) { 19 | return false; 20 | } 21 | return true; 22 | }; 23 | 24 | return { execute }; 25 | }; 26 | -------------------------------------------------------------------------------- /job/src/useCases/candidate/index.ts: -------------------------------------------------------------------------------- 1 | import applyJobUseCase from './apply'; 2 | import checkIsAppliedJobUseCase from './checkApplied'; 3 | import getAllAppliedJobsUseCase from './getAppliedJobs'; 4 | 5 | export { applyJobUseCase, checkIsAppliedJobUseCase, getAllAppliedJobsUseCase }; 6 | -------------------------------------------------------------------------------- /job/src/useCases/index.ts: -------------------------------------------------------------------------------- 1 | // Re-export everything from usecase/job 2 | export * from './job'; 3 | 4 | // Re-export everything from usecase/candidate 5 | export * from './candidate'; 6 | 7 | // Re-export everything from usecase/recruiter 8 | export * from './recruiter'; 9 | -------------------------------------------------------------------------------- /job/src/useCases/job/getJobById.ts: -------------------------------------------------------------------------------- 1 | import { NotFoundError } from '@abijobportal/common'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | repositories: { jobRepository }, 7 | } = dependencies; 8 | 9 | if (!jobRepository) { 10 | throw new Error('jobRepository should exist in dependencies'); 11 | } 12 | 13 | const execute = async (jobId: string) => { 14 | const job = await jobRepository.getAJob(jobId); 15 | if (!job) throw new NotFoundError('job not found'); 16 | return job; 17 | }; 18 | 19 | return { execute }; 20 | }; 21 | -------------------------------------------------------------------------------- /job/src/useCases/job/getJobFieldsDistinctValues.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { jobRepository }, 6 | } = dependencies; 7 | 8 | if (!jobRepository) { 9 | throw new Error('jobRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (fields: Array) => { 13 | const jobFields = await jobRepository.getAllJobsDistinctValues(fields); 14 | return jobFields; 15 | }; 16 | 17 | return { execute }; 18 | }; 19 | -------------------------------------------------------------------------------- /job/src/useCases/job/getJobs.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { jobRepository }, 6 | } = dependencies; 7 | 8 | if (!jobRepository) throw new Error('jobRepository should exist in dependencies'); 9 | 10 | const execute = async (page: number, limit: number) => { 11 | // pagination 12 | const skip = (page - 1) * limit; 13 | 14 | const jobs = await jobRepository.getAllJobs(skip, limit); 15 | const jobCount = await jobRepository.getCountOfJobs(); 16 | 17 | const numberOfPages = Math.ceil(jobCount / limit); 18 | return { jobs, numberOfPages }; 19 | }; 20 | 21 | return { execute }; 22 | }; 23 | -------------------------------------------------------------------------------- /job/src/useCases/job/index.ts: -------------------------------------------------------------------------------- 1 | import getAllJobsUseCase from './getJobs'; 2 | import filterJobUseCase from './filterJob'; 3 | import getJobByIdUseCase from './getJobById'; 4 | import getAllJobFieldsDistinctValuesUseCase from './getJobFieldsDistinctValues'; 5 | import searchUseCase from './search'; 6 | 7 | export { 8 | getAllJobsUseCase, 9 | filterJobUseCase, 10 | getJobByIdUseCase, 11 | getAllJobFieldsDistinctValuesUseCase, 12 | searchUseCase, 13 | }; 14 | -------------------------------------------------------------------------------- /job/src/useCases/recruiter/changeClosejobStatus.ts: -------------------------------------------------------------------------------- 1 | import { ForbiddenError } from '@abijobportal/common'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | repositories: { jobRepository }, 7 | } = dependencies; 8 | 9 | if (!jobRepository) { 10 | throw new Error('jobRepository should exist in dependencies'); 11 | } 12 | 13 | const execute = async (jobId: string, recruiterId: string) => { 14 | const job = await jobRepository.getAJob(jobId); 15 | 16 | if (recruiterId !== job.recruiterId.id.toString()) { 17 | throw new ForbiddenError('You cannot modify others job'); 18 | } 19 | 20 | return await jobRepository.changeClosejobStatus(jobId); 21 | }; 22 | 23 | return { execute }; 24 | }; 25 | -------------------------------------------------------------------------------- /job/src/useCases/recruiter/dashboardCardsDetails.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { jobRepository, jobApplicationRepository }, 6 | } = dependencies; 7 | 8 | if (!jobRepository) throw new Error('jobRepository should exist in dependencies'); 9 | if (!jobApplicationRepository) throw new Error('jobApplicationRepository should exist in dependencies'); 10 | 11 | const execute = async (recruiterId: string) => { 12 | const [addedJobsCount, jobApplicationsCount]: [number, number] = await Promise.all([ 13 | jobRepository.getCountOfCreatedJobs(recruiterId), 14 | jobApplicationRepository.getCountOfApplications(recruiterId), 15 | ]); 16 | 17 | return { addedJobsCount, jobApplicationsCount }; 18 | }; 19 | 20 | return { execute }; 21 | }; 22 | -------------------------------------------------------------------------------- /job/src/useCases/recruiter/getCreatedJobs.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { jobRepository }, 6 | } = dependencies; 7 | 8 | if (!jobRepository) { 9 | throw new Error('jobRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async (recruiterId: string, page: number, limit: number) => { 13 | // pagination 14 | const skip = (page - 1) * limit; 15 | 16 | const jobs = await jobRepository.getAllJobsByRecruiterId(recruiterId, skip, limit); 17 | const jobsCount = await jobRepository.getCountOfCreatedJobs(recruiterId); 18 | const numberOfPages = Math.ceil(jobsCount / limit); 19 | 20 | return { jobs, numberOfPages }; 21 | }; 22 | 23 | return { execute }; 24 | }; 25 | -------------------------------------------------------------------------------- /k8s/certificate/cert-issuer-prod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt-prod 5 | namespace: cert-manager 6 | spec: 7 | acme: 8 | server: https://acme-v02.api.letsencrypt.org/directory 9 | email: helicopter1100@gmail.com 10 | privateKeySecretRef: 11 | name: letsencrypt-prod 12 | solvers: 13 | - http01: 14 | ingress: 15 | class: nginx -------------------------------------------------------------------------------- /k8s/certificate/cert-issuer-stagging.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt-staging 5 | namespace: cert-manager 6 | spec: 7 | acme: 8 | server: https://acme-staging-v02.api.letsencrypt.org/directory 9 | email: helicopter1100@gmail.com 10 | privateKeySecretRef: 11 | name: letsencrypt-staging 12 | solvers: 13 | - http01: 14 | ingress: 15 | class: nginxkubectl describe clusterissuer letsencrypt-staging --namespace cert-manager 16 | 17 | -------------------------------------------------------------------------------- /k8s/stateful/admin-mongo-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: admin-mongo-depl 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: admin-mongo 10 | template: 11 | metadata: 12 | labels: 13 | app: admin-mongo 14 | spec: 15 | containers: 16 | - name: admin-mongo 17 | image: mongo 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: admin-mongo-srv 23 | spec: 24 | selector: 25 | app: admin-mongo 26 | ports: 27 | - name: db 28 | protocol: TCP 29 | port: 27017 30 | targetPort: 27017 -------------------------------------------------------------------------------- /k8s/stateful/auth-mongo-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: auth-mongo-depl 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: auth-mongo 10 | template: 11 | metadata: 12 | labels: 13 | app: auth-mongo 14 | spec: 15 | containers: 16 | - name: auth-mongo 17 | image: mongo 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: auth-mongo-srv 23 | spec: 24 | selector: 25 | app: auth-mongo 26 | ports: 27 | - name: db 28 | protocol: TCP 29 | port: 27017 30 | targetPort: 27017 -------------------------------------------------------------------------------- /k8s/stateful/job-mongo-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: job-mongo-depl 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: job-mongo 10 | template: 11 | metadata: 12 | labels: 13 | app: job-mongo 14 | spec: 15 | containers: 16 | - name: job-mongo 17 | image: mongo 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: job-mongo-srv 23 | spec: 24 | selector: 25 | app: job-mongo 26 | ports: 27 | - name: db 28 | protocol: TCP 29 | port: 27017 30 | targetPort: 27017 -------------------------------------------------------------------------------- /k8s/stateful/profile-mongo-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: profile-mongo-depl 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: profile-mongo 10 | template: 11 | metadata: 12 | labels: 13 | app: profile-mongo 14 | spec: 15 | containers: 16 | - name: profile-mongo 17 | image: mongo 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: profile-mongo-srv 23 | spec: 24 | selector: 25 | app: profile-mongo 26 | ports: 27 | - name: db 28 | protocol: TCP 29 | port: 27017 30 | targetPort: 27017 -------------------------------------------------------------------------------- /payment/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /payment/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "printWidth": 110, 6 | "tabWidth": 4, 7 | "trailingComma": "all", 8 | "arrowParens": "always", 9 | "endOfLine": "auto" 10 | } -------------------------------------------------------------------------------- /payment/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | ENV NODE_ENV=development 14 | 15 | CMD [ "npm", "run", "dev" ] -------------------------------------------------------------------------------- /payment/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM node:20-alpine AS build 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json ./ 7 | 8 | RUN npm ci 9 | 10 | COPY . . 11 | 12 | RUN npm run build 13 | 14 | 15 | # Production stage 16 | FROM node:20-alpine 17 | 18 | WORKDIR /app 19 | 20 | COPY package*.json ./ 21 | 22 | RUN npm ci --omit=dev 23 | 24 | COPY --from=build /app/build ./build 25 | 26 | EXPOSE 3000 27 | 28 | ENV NODE_ENV=production 29 | 30 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /payment/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | 6 | export default [ 7 | {files: ["**/*.{js,mjs,cjs,ts}"]}, 8 | {files: ["**/*.js"], languageOptions: {sourceType: "script"}}, 9 | {languageOptions: { globals: globals.node }}, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | ]; -------------------------------------------------------------------------------- /payment/src/config/db.connection.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnectionError } from '@abijobportal/common'; 2 | import mongoose from 'mongoose'; 3 | import { appConfig } from './appConfig'; 4 | 5 | const connectDB = async (): Promise => { 6 | try { 7 | await mongoose.connect(appConfig.MONGO_URL_PAYMENT); 8 | console.log('payment service connected to mongodb...'); 9 | } catch (error) { 10 | console.error('payment service mongodb connection failed!!!!', error); 11 | throw new DatabaseConnectionError(); 12 | } 13 | }; 14 | 15 | export { connectDB }; 16 | -------------------------------------------------------------------------------- /payment/src/config/dependencies.ts: -------------------------------------------------------------------------------- 1 | import repositories from '../frameworks/repositories/mongo'; 2 | import * as useCases from '../useCase'; 3 | 4 | export default { repositories, useCases }; 5 | -------------------------------------------------------------------------------- /payment/src/config/kafka.connection.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | 3 | const kafkaClient: Kafka = new Kafka({ 4 | clientId: 'payment-client', 5 | brokers: ['devhive-kafka:9092'], 6 | 7 | }); 8 | 9 | export { kafkaClient }; 10 | -------------------------------------------------------------------------------- /payment/src/config/stripe.ts: -------------------------------------------------------------------------------- 1 | import Stripe from 'stripe'; 2 | import { appConfig } from './appConfig'; 3 | 4 | export const stripeInstance: Stripe = new Stripe(appConfig.STRIPE_SECRET_KEY); 5 | -------------------------------------------------------------------------------- /payment/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import premiumControllers from './premium'; 2 | import paymentControllers from './payment'; 3 | 4 | export { premiumControllers, paymentControllers }; 5 | -------------------------------------------------------------------------------- /payment/src/controllers/payment/index.ts: -------------------------------------------------------------------------------- 1 | import cratePaymentController from './createPayment.controller'; 2 | 3 | import { IDependency } from '../../frameworks/types/dependency'; 4 | 5 | export = (dependencies: IDependency) => { 6 | return { 7 | cratePaymentController: cratePaymentController(dependencies), 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /payment/src/controllers/premium/getPremiumPlans.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllPremiumPlansCandidateUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const plans = await getAllPremiumPlansCandidateUseCase(dependencies).execute(); 11 | 12 | res.status(200).json({ message: 'Premium plans are ', data: plans }); 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /payment/src/controllers/premium/index.ts: -------------------------------------------------------------------------------- 1 | import getAllPremiumPlansByCandidateController from './getPremiumPlans.controller'; 2 | 3 | import { IDependency } from '../../frameworks/types/dependency'; 4 | 5 | export = (dependencies: IDependency) => { 6 | return { 7 | getAllPremiumPlansByCandidateController: getAllPremiumPlansByCandidateController(dependencies), 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /payment/src/entities/membershipPlan.ts: -------------------------------------------------------------------------------- 1 | import { IMembershipPlan } from '../frameworks/types/membershipPlan'; 2 | 3 | export class MembershipPlan { 4 | membershipPlanId: string; 5 | name: string; 6 | features: string[]; 7 | description: string; 8 | price: number; 9 | isActive: boolean; 10 | 11 | constructor({ membershipPlanId, name, features, description, price, isActive }: IMembershipPlan) { 12 | this.membershipPlanId = membershipPlanId; 13 | this.name = name; 14 | this.features = features; 15 | this.description = description; 16 | this.price = price; 17 | this.isActive = isActive; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /payment/src/entities/payment.ts: -------------------------------------------------------------------------------- 1 | import { IPayment } from '../frameworks/types/payment'; 2 | 3 | export class Payment { 4 | candidateId: string; 5 | membershipPlanId: string; 6 | stripeId: string; 7 | 8 | constructor({ candidateId, membershipPlanId, stripeId }: IPayment) { 9 | this.candidateId = candidateId; 10 | this.membershipPlanId = membershipPlanId; 11 | this.stripeId = stripeId; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /payment/src/frameworks/database/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import Models from './models'; 2 | 3 | export default { Models }; 4 | -------------------------------------------------------------------------------- /payment/src/frameworks/database/mongo/models/index.ts: -------------------------------------------------------------------------------- 1 | import { MembershipPlanModel } from './membershipPlan'; 2 | import { PaymentModel } from './payment'; 3 | 4 | export default { MembershipPlanModel, PaymentModel }; 5 | -------------------------------------------------------------------------------- /payment/src/frameworks/express/app.ts: -------------------------------------------------------------------------------- 1 | import 'express-async-errors'; 2 | import express, { Express } from 'express'; 3 | import morgan from 'morgan'; 4 | import cors from 'cors'; 5 | import compression from 'compression'; 6 | import { NotFoundError, errorHandler } from '@abijobportal/common'; 7 | 8 | import { routes } from './routes'; 9 | import dependencies from '../../config/dependencies'; 10 | import { appConfig } from '../../config/appConfig'; 11 | 12 | const app: Express = express(); 13 | 14 | app.set('trust proxy', true); // trust first proxy 15 | app.use(cors()); 16 | 17 | // Middlewares 18 | app.use(morgan('dev')); 19 | 20 | app.use(express.json()); 21 | app.use(compression()); 22 | 23 | // Routes 24 | app.use(appConfig.API_PREFIX, routes(dependencies)); 25 | 26 | app.all('*', async () => { 27 | throw new NotFoundError(); 28 | }); 29 | 30 | app.use(errorHandler); 31 | 32 | export { app }; 33 | -------------------------------------------------------------------------------- /payment/src/frameworks/express/routes/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { IDependency } from '../../types/dependency'; 4 | import { premiumRouter } from './premium'; 5 | import { paymentRouter } from './payment'; 6 | import { auth, checkCurrentUser, ROLES } from '@abijobportal/common'; 7 | 8 | export const routes = (dependencies: IDependency) => { 9 | const router = express.Router(); 10 | 11 | const payment = paymentRouter(dependencies); 12 | const premium = premiumRouter(dependencies); 13 | 14 | router.use(checkCurrentUser); 15 | router.use(auth(ROLES.CANDIDATE)); 16 | 17 | router.use('/payment', payment); 18 | router.use('/premium/candidate', premium); 19 | 20 | return router; 21 | }; 22 | -------------------------------------------------------------------------------- /payment/src/frameworks/express/routes/payment.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { paymentControllers } from '../../../controllers'; 4 | import { IDependency } from '../../types/dependency'; 5 | 6 | export const paymentRouter = (dependencies: IDependency) => { 7 | const router = express.Router(); 8 | 9 | const paymentController = paymentControllers(dependencies); 10 | 11 | router.post('/create', paymentController.cratePaymentController); 12 | 13 | return router; 14 | }; 15 | -------------------------------------------------------------------------------- /payment/src/frameworks/express/routes/premium.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { premiumControllers } from '../../../controllers'; 3 | import { IDependency } from '../../types/dependency'; 4 | 5 | export const premiumRouter = (dependencies: IDependency) => { 6 | const router = express.Router(); 7 | 8 | const premiumController = premiumControllers(dependencies); 9 | 10 | router.get('/plans', premiumController.getAllPremiumPlansByCandidateController); 11 | 12 | return router; 13 | }; 14 | -------------------------------------------------------------------------------- /payment/src/frameworks/repositories/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import membershipRepository from './membershipPlan.repository'; 2 | import paymentRepository from './payment.repository'; 3 | 4 | export = { 5 | paymentRepository, 6 | membershipRepository, 7 | }; 8 | -------------------------------------------------------------------------------- /payment/src/frameworks/repositories/mongo/payment.repository.ts: -------------------------------------------------------------------------------- 1 | import Models from '../../database/mongo/models'; 2 | import { IPaymentDocument } from '../../database/mongo/models/payment'; 3 | 4 | const { PaymentModel } = Models; 5 | 6 | export = { 7 | createPayment: async ({ 8 | candidateId, 9 | stripeId, 10 | membershipPlanId, 11 | }: { 12 | candidateId: string; 13 | stripeId: string; 14 | membershipPlanId: string; 15 | }): Promise => { 16 | 17 | const payment = PaymentModel.buildPayment({ 18 | candidateId, 19 | membershipPlanId, 20 | stripeId, 21 | }); 22 | 23 | return await payment.save(); 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /payment/src/frameworks/types/dependency.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface IDependency { 3 | useCases: any; 4 | repositories: any; 5 | } 6 | /* eslint-enable @typescript-eslint/no-explicit-any */ 7 | -------------------------------------------------------------------------------- /payment/src/frameworks/types/membershipPlan.ts: -------------------------------------------------------------------------------- 1 | export interface IMembershipPlan { 2 | membershipPlanId: string; 3 | name: string; 4 | features: string[]; 5 | description: string; 6 | price: number; 7 | isActive: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /payment/src/frameworks/types/payment.ts: -------------------------------------------------------------------------------- 1 | export interface IPayment { 2 | candidateId: string; 3 | membershipPlanId: string; 4 | stripeId: string; 5 | } 6 | -------------------------------------------------------------------------------- /payment/src/frameworks/utils/kafka-events/consumers/premiumPlanCreatedConsumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, MEMBERSHIP_PLAN_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class MembershipPlanCreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.MEMBERSHIP_PLAN_CREATED_TOPIC = TOPICS.MEMBERSHIP_PLAN_CREATED_TOPIC; 7 | 8 | groupId: string = 'payment-1'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: MEMBERSHIP_PLAN_CREATED_EVENT['data'], topic: string): Promise { 15 | // dont need to check role as every users are stored in one collection 16 | handleMessage(data, topic); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /payment/src/frameworks/utils/kafka-events/publishers/paymentDonePublisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, PAYMENT_CREATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class PremiumPaymentDonePublisher extends KafkaPublisher { 4 | topic: TOPICS.PAYMENT_CREATED_TOPIC = TOPICS.PAYMENT_CREATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /payment/src/useCase/index.ts: -------------------------------------------------------------------------------- 1 | export * from './payment'; 2 | 3 | export * from './premium'; 4 | -------------------------------------------------------------------------------- /payment/src/useCase/payment/index.ts: -------------------------------------------------------------------------------- 1 | import createPaymentUseCase from './createPayment'; 2 | 3 | export { createPaymentUseCase }; 4 | -------------------------------------------------------------------------------- /payment/src/useCase/premium/getPremiumPlans.ts: -------------------------------------------------------------------------------- 1 | import { IDependency } from '../../frameworks/types/dependency'; 2 | 3 | export = (dependencies: IDependency) => { 4 | const { 5 | repositories: { membershipRepository }, 6 | } = dependencies; 7 | 8 | if (!membershipRepository) { 9 | throw new Error('membershipRepository should exist in dependencies'); 10 | } 11 | 12 | const execute = async () => { 13 | const membershipPlans = await membershipRepository.getAllMembershipPlans(); 14 | 15 | return membershipPlans; 16 | }; 17 | 18 | return { execute }; 19 | }; 20 | -------------------------------------------------------------------------------- /payment/src/useCase/premium/index.ts: -------------------------------------------------------------------------------- 1 | import getAllPremiumPlansCandidateUseCase from './getPremiumPlans'; 2 | 3 | export { getAllPremiumPlansCandidateUseCase }; 4 | -------------------------------------------------------------------------------- /profile/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /profile/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "printWidth": 110, 6 | "tabWidth": 4, 7 | "trailingComma": "all", 8 | "arrowParens": "always", 9 | "endOfLine": "auto" 10 | } -------------------------------------------------------------------------------- /profile/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | ENV NODE_ENV=development 14 | 15 | CMD [ "npm", "run", "dev" ] -------------------------------------------------------------------------------- /profile/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM node:20-alpine AS build 3 | 4 | WORKDIR /app 5 | 6 | COPY package*.json ./ 7 | 8 | RUN npm ci 9 | 10 | COPY . . 11 | 12 | RUN npm run build 13 | 14 | 15 | # Production stage 16 | FROM node:20-alpine 17 | 18 | WORKDIR /app 19 | 20 | COPY package*.json ./ 21 | 22 | RUN npm ci --omit=dev 23 | 24 | COPY --from=build /app/build ./build 25 | 26 | EXPOSE 3000 27 | 28 | ENV NODE_ENV=production 29 | 30 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /profile/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | 6 | export default [ 7 | {files: ["**/*.{js,mjs,cjs,ts}"]}, 8 | {files: ["**/*.js"], languageOptions: {sourceType: "script"}}, 9 | {languageOptions: { globals: globals.node }}, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | ]; -------------------------------------------------------------------------------- /profile/src/config/cloudinary.ts: -------------------------------------------------------------------------------- 1 | import { v2 as cloudinary } from 'cloudinary'; 2 | import { appConfig } from './appConfig'; 3 | 4 | cloudinary.config({ 5 | cloud_name: appConfig.CLOUDINARY_CLOUD_NAME, 6 | api_key: appConfig.CLOUDINARY_API_KEY, 7 | api_secret: appConfig.CLOUDINARY_API_SECRET, 8 | }); 9 | 10 | export { cloudinary }; 11 | -------------------------------------------------------------------------------- /profile/src/config/db.connection.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnectionError } from '@abijobportal/common'; 2 | import mongoose from 'mongoose'; 3 | import { appConfig } from './appConfig'; 4 | 5 | const connectDB = async (): Promise => { 6 | try { 7 | await mongoose.connect(appConfig.MONGO_URL_PROFILE); 8 | console.log('profile service connected to mongodb...'); 9 | } catch (error) { 10 | console.error('profile service mongodb connection failed!!!!', error); 11 | throw new DatabaseConnectionError(); 12 | } 13 | }; 14 | export { connectDB }; 15 | -------------------------------------------------------------------------------- /profile/src/config/dependencies.ts: -------------------------------------------------------------------------------- 1 | import repositories from '../frameworks/repository/mongo'; 2 | import * as useCases from '../useCases'; 3 | 4 | export default { repositories, useCases }; 5 | -------------------------------------------------------------------------------- /profile/src/config/kafka.connection.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | 3 | const kafkaClient = new Kafka({ 4 | clientId: 'profile-client', 5 | brokers: ['devhive-kafka:9092'], 6 | 7 | }); 8 | 9 | export { kafkaClient }; 10 | -------------------------------------------------------------------------------- /profile/src/config/multer.ts: -------------------------------------------------------------------------------- 1 | import { Request } from 'express'; 2 | import multer, { Multer } from 'multer'; 3 | import path from 'path'; 4 | 5 | const multerConfig: Multer = multer({ 6 | storage: multer.memoryStorage(), // Store in memory instead of public folder 7 | limits: { fileSize: 10 * 1024 * 1024 }, // 2 MB limit (adjust as needed) 8 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 9 | fileFilter: (req: Request, file: Express.Multer.File, cb: any) => { 10 | const extension = path.extname(file.originalname); 11 | if (!['.jpg', '.jpeg', '.png', '.pdf'].includes(extension.toLowerCase())) { 12 | cb(new Error(`${extension} is unsupported file type!`), false); 13 | return; 14 | } 15 | cb(null, true); 16 | }, 17 | }); 18 | 19 | export { multerConfig }; 20 | -------------------------------------------------------------------------------- /profile/src/controllers/candidate/deleteResume.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { deleteResumeUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser; 11 | const candidate = await deleteResumeUseCase(dependencies).execute(userId); 12 | 13 | res.status(201).json({ message: 'resume deleted', data: candidate }); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /profile/src/controllers/candidate/updatePreferredJobs.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { updatePreferredJobsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { preferredJobs } = req.body; 11 | const { userId } = req.currentUser; 12 | 13 | const candidate = await updatePreferredJobsUseCase(dependencies).execute(userId, preferredJobs); 14 | 15 | res.status(201).json({ message: 'skills updated', data: candidate }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /profile/src/controllers/candidate/updateProfile.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { updateCandidateProfileUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const updateData = req.body; 11 | const { userId } = req.currentUser; 12 | 13 | const candidate = await updateCandidateProfileUseCase(dependencies).execute(userId, updateData); 14 | 15 | res.status(200).json({ message: 'candidate updated successfully', data: candidate }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /profile/src/controllers/candidate/updateSkills.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { updateSkillsUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { skills } = req.body; 11 | const { userId } = req.currentUser; 12 | 13 | const candidate = await updateSkillsUseCase(dependencies).execute(userId, skills); 14 | res.status(201).json({ message: 'skills updated', data: candidate }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /profile/src/controllers/candidate/uploadProfilePic.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IFileData } from '../../frameworks/types/candidate'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { uploadCandidateProfilePicUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const { userId } = req.currentUser; 12 | 13 | const candidate = await uploadCandidateProfilePicUseCase(dependencies).execute( 14 | userId, 15 | req.file as IFileData, 16 | ); 17 | 18 | res.status(201).json({ message: 'profile image uploaded', data: candidate }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /profile/src/controllers/candidate/uploadResume.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IResume } from '../../frameworks/types/candidate'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { uploadResumeUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const { userId } = req.currentUser; 12 | 13 | const candidate = await uploadResumeUseCase(dependencies).execute(userId, req.body as IResume); 14 | 15 | res.status(201).json({ message: 'resume uploaded', data: candidate }); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /profile/src/controllers/candidate/viewProfile.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getCandidateProfileByIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | const { candidateId } = req.params!; 12 | 13 | const candidate = await getCandidateProfileByIdUseCase(dependencies).execute(candidateId ?? userId); 14 | res.status(200).json({ message: 'candidate profile feched successfully', data: candidate }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /profile/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import candidateProfileControllers from './candidate'; 2 | import recruiterProfileControllers from './recruiter'; 3 | 4 | export { candidateProfileControllers, recruiterProfileControllers }; 5 | -------------------------------------------------------------------------------- /profile/src/controllers/recruiter/getcandidates.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getAllCandidatesProfilesUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser; 11 | const { candidates, totalNumberOfPages } = await getAllCandidatesProfilesUseCase( 12 | dependencies, 13 | ).execute(Number(req.params.page) || 1, Number(req.params.limit) || 2, userId); 14 | 15 | res.status(200).json({ 16 | message: 'candidates data fetched successfully', 17 | data: { candidates, totalNumberOfPages }, 18 | }); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /profile/src/controllers/recruiter/index.ts: -------------------------------------------------------------------------------- 1 | import viewRecruiterProfileController from './viewProfile.controller'; 2 | import updateRecruiterProfileController from './update.controller'; 3 | import viewAllCandidatesProfilesController from './getcandidates.controller'; 4 | 5 | import { IDependency } from '../../frameworks/types/dependency'; 6 | 7 | export = (dependencies: IDependency) => { 8 | return { 9 | viewRecruiterProfileController: viewRecruiterProfileController(dependencies), 10 | updateRecruiterProfileController: updateRecruiterProfileController(dependencies), 11 | viewAllCandidatesProfilesController: viewAllCandidatesProfilesController(dependencies), 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /profile/src/controllers/recruiter/update.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | import { IRecruiterProfile } from '../../frameworks/types/recruiter'; 4 | 5 | export = (dependencies: IDependency) => { 6 | const { 7 | useCases: { updateRecruiterProfileUseCase }, 8 | } = dependencies; 9 | 10 | return async (req: Request, res: Response) => { 11 | const updatedData = req.body as Partial; 12 | const { userId } = req.currentUser!; 13 | 14 | const recruiter = await updateRecruiterProfileUseCase(dependencies).execute(userId, updatedData); 15 | 16 | res.status(200).json({ message: 'recruiter data', data: recruiter }); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /profile/src/controllers/recruiter/viewProfile.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | useCases: { getRecruiterProfileByIdUseCase }, 7 | } = dependencies; 8 | 9 | return async (req: Request, res: Response) => { 10 | const { userId } = req.currentUser!; 11 | const { recruiterId } = req.params!; 12 | const recruiter = await getRecruiterProfileByIdUseCase(dependencies).execute(recruiterId ?? userId); 13 | 14 | res.status(200).json({ message: 'recruiter data', data: recruiter }); 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /profile/src/entities/index.ts: -------------------------------------------------------------------------------- 1 | import { CandidateProfile } from './candidate-profile'; 2 | import { RecruiterProfile } from './recruiter-profile'; 3 | 4 | export { CandidateProfile, RecruiterProfile }; 5 | -------------------------------------------------------------------------------- /profile/src/entities/recruiter-profile.ts: -------------------------------------------------------------------------------- 1 | import { IRecruiterProfile } from '../frameworks/types/recruiter'; 2 | 3 | export class RecruiterProfile { 4 | name: string; 5 | email: string; 6 | phone: number; 7 | isActive: boolean; 8 | gender?: string; 9 | profileImage?: string; 10 | about?: string; 11 | userId?: string; 12 | constructor({ name, email, phone, isActive, gender, profileImage, about, userId }: IRecruiterProfile) { 13 | this.name = name; 14 | this.email = email; 15 | this.phone = phone; 16 | this.isActive = isActive; 17 | this.gender = gender; 18 | this.profileImage = profileImage; 19 | this.about = about; 20 | this.userId = userId; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /profile/src/frameworks/database/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import Models from './models'; 2 | 3 | export default { Models }; 4 | -------------------------------------------------------------------------------- /profile/src/frameworks/database/mongo/models/index.ts: -------------------------------------------------------------------------------- 1 | import { CandidateProfileModel } from './candidate'; 2 | import { RecruiterProfileModel } from './recruiter'; 3 | 4 | export default { 5 | CandidateProfileModel, 6 | RecruiterProfileModel, 7 | }; 8 | -------------------------------------------------------------------------------- /profile/src/frameworks/express/routes/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { IDependency } from '../../types/dependency'; 4 | import { candidateRouter } from './candidate'; 5 | import { recruiterRouter } from './recruiter'; 6 | import { checkCurrentUser } from '@abijobportal/common'; 7 | 8 | // import { checkCurrentUser } from "@abijobportal/common"; 9 | 10 | export const routes = (dependencies: IDependency) => { 11 | const router = express.Router(); 12 | 13 | const candidate = candidateRouter(dependencies); 14 | const recruiter = recruiterRouter(dependencies); 15 | 16 | router.use('/candidate', checkCurrentUser, candidate); 17 | router.use('/recruiter', checkCurrentUser, recruiter); 18 | 19 | return router; 20 | }; 21 | -------------------------------------------------------------------------------- /profile/src/frameworks/repository/mongo/index.ts: -------------------------------------------------------------------------------- 1 | import candidateProfileRepository from './candidateProfile.repository'; 2 | import recruiterProfileRepository from './recruiterProfile.repository'; 3 | 4 | export = { candidateProfileRepository, recruiterProfileRepository }; 5 | -------------------------------------------------------------------------------- /profile/src/frameworks/types/dependency.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface IDependency { 3 | useCases: any; 4 | repositories: any; 5 | } 6 | /* eslint-enable @typescript-eslint/no-explicit-any */ 7 | -------------------------------------------------------------------------------- /profile/src/frameworks/types/recruiter.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from './user'; 2 | 3 | export interface IRecruiterProfile extends IUser { 4 | gender: string; 5 | profileImage: string; 6 | about: string; 7 | 8 | companyName: string; 9 | companyWebsite: string; 10 | companyLocation: string; 11 | companyState: string; 12 | companyCountry: string; 13 | } 14 | -------------------------------------------------------------------------------- /profile/src/frameworks/types/user.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | userId: string; 3 | email: string; 4 | name: string; 5 | phone: number; 6 | role: string; 7 | isActive: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /profile/src/frameworks/utils/kafka-events/consumers/payment-created-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, PAYMENT_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class paymentCreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.PAYMENT_CREATED_TOPIC = TOPICS.PAYMENT_CREATED_TOPIC; 7 | 8 | groupId: string = 'profile-4'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: PAYMENT_CREATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /profile/src/frameworks/utils/kafka-events/consumers/user-created-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_CREATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserCreatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_CREATED_TOPIC = TOPICS.USER_CREATED_TOPIC; 7 | 8 | groupId: string = 'profile-2'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_CREATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /profile/src/frameworks/utils/kafka-events/consumers/user-updated-consumer.ts: -------------------------------------------------------------------------------- 1 | import { Kafka } from 'kafkajs'; 2 | import { KafkaConsumer, TOPICS, USER_UPDATED_EVENT } from '@abijobportal/common'; 3 | import { handleMessage } from '../handleMessage'; 4 | 5 | export class UserUpdatedEventConsumer extends KafkaConsumer { 6 | topic: TOPICS.USER_UPDATED_TOPIC = TOPICS.USER_UPDATED_TOPIC; 7 | 8 | groupId: string = 'profile-3'; 9 | 10 | constructor(client: Kafka) { 11 | super(client); 12 | } 13 | 14 | async onMessage(data: USER_UPDATED_EVENT['data'], topic: string): Promise { 15 | handleMessage(data, topic); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /profile/src/frameworks/utils/kafka-events/publishers/candidate-profile-updated-publisher .ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, CANDIDATE_PROFILE_UPDATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class CandidateProfileUpdatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.CANDIDATE_PROFILE_UPDATED_TOPIC = TOPICS.CANDIDATE_PROFILE_UPDATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /profile/src/frameworks/utils/kafka-events/publishers/recruiter-profile-updated-publisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, RECRUITER_PROFILE_UPDATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class RecruiterProfileUpdatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.RECRUITER_PROFILE_UPDATED_TOPIC = TOPICS.RECRUITER_PROFILE_UPDATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /profile/src/frameworks/utils/kafka-events/publishers/user-updated-publisher.ts: -------------------------------------------------------------------------------- 1 | import { KafkaPublisher, TOPICS, USER_UPDATED_EVENT } from '@abijobportal/common'; 2 | 3 | export class UserUpdatedEventPublisher extends KafkaPublisher { 4 | topic: TOPICS.USER_UPDATED_TOPIC = TOPICS.USER_UPDATED_TOPIC; 5 | } 6 | -------------------------------------------------------------------------------- /profile/src/useCases/candidate/getProfileByEmail.ts: -------------------------------------------------------------------------------- 1 | import { NotFoundError } from '@abijobportal/common'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | repositories: { candidateProfileRepository }, 7 | } = dependencies; 8 | 9 | if (!candidateProfileRepository) { 10 | throw new Error('candidateProfileRepository should exist in dependencies'); 11 | } 12 | 13 | const execute = async (email: string) => { 14 | const profile = await candidateProfileRepository.getProfileByEmail(email); 15 | if (!profile) throw new NotFoundError('Profile not found'); 16 | return profile; 17 | }; 18 | 19 | return { execute }; 20 | }; 21 | -------------------------------------------------------------------------------- /profile/src/useCases/candidate/getProfileById.ts: -------------------------------------------------------------------------------- 1 | import { NotFoundError } from '@abijobportal/common'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | repositories: { candidateProfileRepository }, 7 | } = dependencies; 8 | 9 | if (!candidateProfileRepository) 10 | throw new Error('candidateProfileRepository should exist in dependencies'); 11 | 12 | const execute = async (candidateId: string) => { 13 | 14 | const profile = await candidateProfileRepository.getProfileByUserId(candidateId); 15 | if (!profile) throw new NotFoundError('Profile not found'); 16 | return profile; 17 | }; 18 | 19 | return { execute }; 20 | }; 21 | -------------------------------------------------------------------------------- /profile/src/useCases/candidate/index.ts: -------------------------------------------------------------------------------- 1 | import updateCandidateProfileUseCase from './updateProfile'; 2 | import uploadResumeUseCase from './uploadResume'; 3 | import getCandidateProfileByIdUseCase from './getProfileById'; 4 | import getCandidateProfileByEmailUseCase from './getProfileByEmail'; 5 | import uploadCandidateProfilePicUseCase from './uploadProfilePic'; 6 | import updateSkillsUseCase from './updateSkills'; 7 | import deleteResumeUseCase from './deleteResume'; 8 | import updatePreferredJobsUseCase from './updatePreferredJobs'; 9 | 10 | export { 11 | updateCandidateProfileUseCase, 12 | uploadResumeUseCase, 13 | deleteResumeUseCase, 14 | getCandidateProfileByIdUseCase, 15 | getCandidateProfileByEmailUseCase, 16 | uploadCandidateProfilePicUseCase, 17 | updateSkillsUseCase, 18 | updatePreferredJobsUseCase, 19 | }; 20 | -------------------------------------------------------------------------------- /profile/src/useCases/index.ts: -------------------------------------------------------------------------------- 1 | export * from './candidate'; 2 | export * from './recruiter'; 3 | -------------------------------------------------------------------------------- /profile/src/useCases/recruiter/getProfileById.ts: -------------------------------------------------------------------------------- 1 | import { NotFoundError } from '@abijobportal/common'; 2 | import { IDependency } from '../../frameworks/types/dependency'; 3 | 4 | export = (dependencies: IDependency) => { 5 | const { 6 | repositories: { recruiterProfileRepository }, 7 | } = dependencies; 8 | 9 | if (!recruiterProfileRepository) 10 | throw new Error('recruiterProfileRepository should exist in dependencies'); 11 | 12 | const execute = async (recruiterId: string) => { 13 | const profile = await recruiterProfileRepository.getProfileByUserId(recruiterId); 14 | if (!profile) throw new NotFoundError('profile not found'); 15 | return profile; 16 | }; 17 | 18 | return { execute }; 19 | }; 20 | -------------------------------------------------------------------------------- /profile/src/useCases/recruiter/index.ts: -------------------------------------------------------------------------------- 1 | import updateRecruiterProfileUseCase from './updateProfile'; 2 | import getRecruiterProfileByIdUseCase from './getProfileById'; 3 | import getAllCandidatesProfilesUseCase from './getCandidateProfiles'; 4 | 5 | export { updateRecruiterProfileUseCase, getRecruiterProfileByIdUseCase, getAllCandidatesProfilesUseCase }; 6 | --------------------------------------------------------------------------------