├── .github ├── scripts │ ├── deploy.sh │ ├── java_dev_app.service │ ├── java_prod_app.service │ └── java_staging_app.service └── workflows │ ├── cd.dev.txt │ ├── cd.prod.txt │ ├── cd.staging.txt │ ├── ci.yaml │ ├── create-application-properties │ └── action.yml │ ├── create-flyway-conf │ └── action.yml │ ├── latest-dev.yaml │ ├── latest-prod.yaml │ ├── latest-staging.yaml │ ├── postman-api-tests.yaml │ ├── pr-deploy.txt │ └── staging-cd.txt ├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── Contributors.md ├── Dockerfile ├── LICENSE ├── README.md ├── compose.override.yaml ├── compose.yaml ├── compose ├── compose.dev.yaml ├── compose.prod.yaml └── compose.staging.yaml ├── mvnw ├── mvnw.cmd ├── nginx.conf ├── nginx └── nginx.conf ├── pom.xml ├── profile_photos ├── 6e6bbd89-a83d-44a8-994f-9949c48b0f59_oskar-smethurst-B1GtwanCbiw-unsplash.jpg ├── 9f947e39-fe38-44dd-85d3-927253847576_oskar-smethurst-B1GtwanCbiw-unsplash.jpg ├── c3ae0d7a-fecb-4145-9992-be8b8c4f1e2a_oskar-smethurst-B1GtwanCbiw-unsplash.jpg ├── c72cde4e-e8a6-46a8-a010-5b8a8d185bc9_test.jpg ├── d53189d2-ae88-4bee-a4ac-d71d1994bfe1_oskar-smethurst-B1GtwanCbiw-unsplash.jpg ├── f28a3ff5-7685-4feb-acae-aaafa0946da2_oskar-smethurst-B1GtwanCbiw-unsplash.jpg └── ff8588cb-395e-4168-a069-8c108280fe6b_test.jpg ├── qa_tests ├── regression │ └── Spartacus_Java_Boilerplate_Copy.postman_collection.json ├── test_newman.ps1 └── test_newman.sh └── src ├── main ├── java │ └── hng_java_boilerplate │ │ ├── Program.java │ │ ├── aboutpage │ │ ├── controller │ │ │ └── AboutPageController.java │ │ ├── dto │ │ │ ├── AboutPageContentDto.java │ │ │ └── ApiResponse.java │ │ ├── entity │ │ │ └── AboutPageContent.java │ │ ├── repository │ │ │ └── AboutPageRepository.java │ │ └── service │ │ │ └── AboutPageService.java │ │ ├── activitylog │ │ ├── controller │ │ │ └── ActivityLogController.java │ │ ├── dto │ │ │ ├── ActivityLogResponseDto.java │ │ │ ├── ApiResponseDto.java │ │ │ └── ErrorResponseDto.java │ │ ├── model │ │ │ └── ActivityLog.java │ │ ├── repository │ │ │ └── ActivityLogRepository.java │ │ └── service │ │ │ └── ActivityLogService.java │ │ ├── categories │ │ ├── controller │ │ │ └── CategoryController.java │ │ ├── dto │ │ │ ├── CategoryDto.java │ │ │ ├── CategoryRequest.java │ │ │ └── CustomResponse.java │ │ ├── entity │ │ │ └── Category.java │ │ ├── repository │ │ │ └── CategoryRepository.java │ │ └── service │ │ │ ├── CategoryService.java │ │ │ └── CategoryServiceImpl.java │ │ ├── comment │ │ ├── controller │ │ │ └── CommentController.java │ │ ├── dto │ │ │ ├── CommentDataDto.java │ │ │ ├── ErrorResponse.java │ │ │ ├── RequestDto.java │ │ │ └── ResponseDto.java │ │ ├── entity │ │ │ └── Comment.java │ │ ├── repository │ │ │ └── CommentRepository.java │ │ └── service │ │ │ └── CommentService.java │ │ ├── config │ │ ├── AwsConfig.java │ │ ├── DatabaseSeeder.java │ │ ├── FilterConfig.java │ │ ├── MetricsConfig.java │ │ ├── RabbitMQConfig.java │ │ ├── SwaggerConfig.java │ │ ├── TwoFactorConfig.java │ │ └── WebSecurityConfig.java │ │ ├── dashboard │ │ ├── controller │ │ │ └── DashboardController.java │ │ └── service │ │ │ └── DashboardService.java │ │ ├── email │ │ ├── EmailServices │ │ │ ├── EmailConsumerService.java │ │ │ ├── EmailProducerService.java │ │ │ └── EmailTemplateService.java │ │ ├── controller │ │ │ └── EmailTemplateController.java │ │ ├── dto │ │ │ ├── EmailTemplateRequestDto.java │ │ │ ├── EmailTemplateResponse.java │ │ │ └── EmailTemplateUpdate.java │ │ ├── entity │ │ │ ├── EmailMessage.java │ │ │ └── EmailTemplate.java │ │ ├── enums │ │ │ └── EmailTemplateStatus.java │ │ └── repository │ │ │ └── EmailTemplateRepository.java │ │ ├── exception │ │ ├── BadRequestException.java │ │ ├── ConflictException.java │ │ ├── CustomError.java │ │ ├── ErrorResponseDto.java │ │ ├── GlobalExceptionHandler.java │ │ ├── NotFoundException.java │ │ ├── ProfilePictureUploadException.java │ │ ├── UnAuthorizedException.java │ │ └── ValidationError.java │ │ ├── helpCenter │ │ ├── contactUs │ │ │ ├── controller │ │ │ │ └── ContactUsController.java │ │ │ ├── dto │ │ │ │ ├── request │ │ │ │ │ └── ContactUsRequest.java │ │ │ │ └── response │ │ │ │ │ ├── ContactUsResponse.java │ │ │ │ │ └── CustomResponse.java │ │ │ ├── entity │ │ │ │ └── Contact.java │ │ │ ├── repository │ │ │ │ └── ContactUsRepository.java │ │ │ ├── service │ │ │ │ └── ContactUsService.java │ │ │ └── serviceImpl │ │ │ │ └── ContactUsServiceImpl.java │ │ ├── faq │ │ │ ├── controller │ │ │ │ └── FaqController.java │ │ │ ├── dto │ │ │ │ ├── request │ │ │ │ │ └── FaqRequest.java │ │ │ │ └── response │ │ │ │ │ ├── CustomResponse.java │ │ │ │ │ └── FaqResponse.java │ │ │ ├── entity │ │ │ │ └── FAQ.java │ │ │ ├── repository │ │ │ │ └── FaqRepository.java │ │ │ ├── service │ │ │ │ └── FaqService.java │ │ │ └── serviceImpl │ │ │ │ └── FaqServiceImpl.java │ │ └── topic │ │ │ ├── controller │ │ │ └── TopicController.java │ │ │ ├── entity │ │ │ └── Topic.java │ │ │ ├── repository │ │ │ └── TopicRepository.java │ │ │ └── service │ │ │ └── TopicService.java │ │ ├── jobs │ │ ├── controller │ │ │ └── JobListingController.java │ │ ├── dto │ │ │ └── ApiResponse.java │ │ ├── entity │ │ │ └── JobListing.java │ │ ├── repository │ │ │ └── JobListingRepository.java │ │ └── service │ │ │ ├── JobListingService.java │ │ │ └── JobListingServiceImpl.java │ │ ├── metrics │ │ └── MetricsController.java │ │ ├── newsletter │ │ ├── controller │ │ │ └── NewsletterController.java │ │ ├── dto │ │ │ ├── DeleteRequest.java │ │ │ ├── SubscribeRequest.java │ │ │ ├── SubscribeResponse.java │ │ │ ├── SubscribersDto.java │ │ │ └── SubscribersResponse.java │ │ ├── entity │ │ │ └── Newsletter.java │ │ ├── repository │ │ │ └── NewsletterRepository.java │ │ └── service │ │ │ └── NewsletterService.java │ │ ├── notification │ │ ├── controllers │ │ │ ├── NotificationController.java │ │ │ └── NotificationSettingsController.java │ │ ├── dto │ │ │ ├── request │ │ │ │ ├── MarkRead.java │ │ │ │ ├── NotificationRequest.java │ │ │ │ └── NotificationSettingsRequestDTO.java │ │ │ └── response │ │ │ │ ├── ApiResponseDTO.java │ │ │ │ ├── NotificationData.java │ │ │ │ ├── NotificationDto.java │ │ │ │ ├── NotificationDtoRes.java │ │ │ │ ├── NotificationResponse.java │ │ │ │ └── NotificationSettingsResponseDTO.java │ │ ├── models │ │ │ ├── Notification.java │ │ │ └── NotificationSettings.java │ │ ├── repositories │ │ │ ├── NotificationRepository.java │ │ │ └── NotificationSettingsRepository.java │ │ └── services │ │ │ ├── NotificationService.java │ │ │ └── NotificationSettingsService.java │ │ ├── organisation │ │ ├── controller │ │ │ └── OrganisationController.java │ │ ├── dto │ │ │ ├── AddUserRequestDTO.java │ │ │ ├── AddUserResponseDTO.java │ │ │ ├── CreateOrganisationRequestDto.java │ │ │ ├── CreateOrganisationResponseDto.java │ │ │ └── DataDto.java │ │ ├── entity │ │ │ └── Organisation.java │ │ ├── exception │ │ │ ├── AuthErrorResponse.java │ │ │ ├── ExceptionResponse.java │ │ │ ├── OrgGlobalExceptionHandler.java │ │ │ ├── OrganisationNameAlreadyExistsException.java │ │ │ └── ValidationError.java │ │ ├── interfaces │ │ │ └── AddUserResponse.java │ │ ├── repository │ │ │ └── OrganisationRepository.java │ │ └── service │ │ │ ├── AddUsersToOrganisationService.java │ │ │ └── OrganisationService.java │ │ ├── payment │ │ ├── controller │ │ │ └── PaymentController.java │ │ ├── dtos │ │ │ ├── PaymentRequestBody.java │ │ │ └── SessionResponse.java │ │ ├── entity │ │ │ └── Payment.java │ │ ├── enums │ │ │ └── PaymentStatus.java │ │ ├── repository │ │ │ └── PaymentRepository.java │ │ ├── service │ │ │ └── PaymentService.java │ │ └── utils │ │ │ ├── CustomerUtils.java │ │ │ └── FulfillCheckout.java │ │ ├── plans │ │ ├── controller │ │ │ └── PlanController.java │ │ ├── dtos │ │ │ ├── CreatePlanDto.java │ │ │ └── PlanResponse.java │ │ ├── entity │ │ │ └── Plan.java │ │ ├── repository │ │ │ └── PlanRepository.java │ │ ├── service │ │ │ └── PlanService.java │ │ ├── serviceImpl │ │ │ └── PlanServiceImpl.java │ │ └── util │ │ │ ├── ListOfStringsValidator.java │ │ │ ├── StringListConverter.java │ │ │ └── ValidList.java │ │ ├── privacy_policy │ │ ├── controller │ │ │ └── PrivacyPolicyController.java │ │ ├── dto │ │ │ └── ApiResponse.java │ │ ├── entity │ │ │ └── PrivacyPolicy.java │ │ ├── repository │ │ │ └── PrivacyPolicyRepository.java │ │ └── service │ │ │ ├── PrivacyPolicyService.java │ │ │ └── PrivacyPolicyServiceImpl.java │ │ ├── product │ │ ├── controller │ │ │ └── ProductController.java │ │ ├── documentation │ │ │ └── product.md │ │ ├── dto │ │ │ ├── ErrorDTO.java │ │ │ ├── GetProductsDTO.java │ │ │ ├── ProductCountDto.java │ │ │ ├── ProductDTO.java │ │ │ ├── ProductErrorDTO.java │ │ │ └── ProductSearchDTO.java │ │ ├── entity │ │ │ └── Product.java │ │ ├── product_mapper │ │ │ └── ProductMapper.java │ │ ├── repository │ │ │ └── ProductRepository.java │ │ └── service │ │ │ ├── ProductService.java │ │ │ └── ProductServiceImpl.java │ │ ├── profile │ │ ├── controller │ │ │ └── ProfileController.java │ │ ├── dto │ │ │ ├── request │ │ │ │ ├── DeactivateUserRequest.java │ │ │ │ └── UpdateUserProfileDto.java │ │ │ └── response │ │ │ │ ├── DeactivateUserResponse.java │ │ │ │ ├── ProfileDto.java │ │ │ │ ├── ProfileErrorResponseDto.java │ │ │ │ ├── ProfilePictureResponse.java │ │ │ │ ├── ProfileResponse.java │ │ │ │ └── ProfileUpdateResponseDto.java │ │ ├── entity │ │ │ └── Profile.java │ │ ├── repository │ │ │ └── ProfileRepository.java │ │ ├── service │ │ │ └── ProfileService.java │ │ └── serviceImpl │ │ │ └── ProfileServiceImpl.java │ │ ├── region │ │ ├── controller │ │ │ └── RegionController.java │ │ ├── dto │ │ │ ├── request │ │ │ │ ├── CreateRegion.java │ │ │ │ └── UpdateRequest.java │ │ │ └── response │ │ │ │ ├── GetAllRegion.java │ │ │ │ └── GetResponse.java │ │ ├── entity │ │ │ └── Region.java │ │ ├── repository │ │ │ └── RegionRepository.java │ │ ├── service │ │ │ └── RegionService.java │ │ └── serviceImpl │ │ │ └── RegionServiceImpl.java │ │ ├── resources │ │ ├── controller │ │ │ └── ResourcesController.java │ │ ├── dto │ │ │ ├── ResourceRequestDto.java │ │ │ └── ResourceResponseDto.java │ │ ├── entity │ │ │ └── Resources.java │ │ ├── repository │ │ │ └── ResourceRepository.java │ │ └── service │ │ │ ├── ResourceService.java │ │ │ └── ResourceServiceImpl.java │ │ ├── squeeze │ │ ├── controller │ │ │ ├── SqueezeConfigController.java │ │ │ └── SqueezeRequestController.java │ │ ├── dto │ │ │ ├── ResponseMessageDto.java │ │ │ └── ValidationErrorResponseDto.java │ │ ├── entity │ │ │ ├── SqueezeConfig.java │ │ │ └── SqueezeRequest.java │ │ ├── repository │ │ │ ├── SqueezeConfigRepository.java │ │ │ └── SqueezeRequestRepository.java │ │ ├── service │ │ │ ├── SqueezeConfigService.java │ │ │ └── SqueezeRequestService.java │ │ └── util │ │ │ └── StringListConverter.java │ │ ├── statusPage │ │ ├── controller │ │ │ └── StatusPageController.java │ │ ├── entity │ │ │ └── StatusPage.java │ │ ├── repository │ │ │ └── StatusPageRepository.java │ │ └── service │ │ │ ├── StatusPageService.java │ │ │ └── StatusPageServiceImpl.java │ │ ├── testimonials │ │ ├── controller │ │ │ └── TestimonialController.java │ │ ├── dto │ │ │ ├── TestimonialDataDto.java │ │ │ ├── TestimonialRequestDto.java │ │ │ ├── TestimonialResponseDto.java │ │ │ └── UpdateTestimonialRequestDto.java │ │ ├── entity │ │ │ └── Testimonial.java │ │ ├── repository │ │ │ └── TestimonialRepository.java │ │ └── service │ │ │ └── TestimonialService.java │ │ ├── twilio │ │ ├── CallLogs │ │ │ └── Entity.java │ │ ├── Controller │ │ │ └── TwillioCallController.java │ │ ├── Repository │ │ │ └── TwilioCallRepo.java │ │ ├── RequestAndResponse │ │ │ ├── CallRequest.java │ │ │ └── CallResponse.java │ │ └── Service │ │ │ └── TwilioCallService.java │ │ ├── twofactor │ │ ├── controller │ │ │ └── TwoFactorController.java │ │ ├── dtos │ │ │ ├── EnableTwoFactorRequest.java │ │ │ ├── TotpRequest.java │ │ │ └── TwoFactorResponse.java │ │ └── service │ │ │ ├── TotpService.java │ │ │ └── TwoFactorService.java │ │ ├── user │ │ ├── controller │ │ │ ├── AuthController.java │ │ │ └── UserController.java │ │ ├── documentation │ │ │ └── User.md │ │ ├── dto │ │ │ ├── request │ │ │ │ ├── DeleteUserRequest.java │ │ │ │ ├── EmailSenderDto.java │ │ │ │ ├── FacebookDto.java │ │ │ │ ├── GetUserDto.java │ │ │ │ ├── GoogleOAuthDto.java │ │ │ │ ├── LoginDto.java │ │ │ │ ├── MagicLinkRequest.java │ │ │ │ ├── ResetPasswordDto.java │ │ │ │ ├── SignupDto.java │ │ │ │ └── VerificationTokenDto.java │ │ │ └── response │ │ │ │ ├── ApiResponse.java │ │ │ │ ├── ErrorResponse.java │ │ │ │ ├── MembersResponse.java │ │ │ │ ├── OAuthBaseResponse.java │ │ │ │ ├── OAuthLastUserResponse.java │ │ │ │ ├── OAuthResponse.java │ │ │ │ ├── OAuthUserResponse.java │ │ │ │ ├── Response.java │ │ │ │ ├── ResponseData.java │ │ │ │ ├── UserOAuthDetails.java │ │ │ │ └── UserResponse.java │ │ ├── entity │ │ │ ├── MagicLinkToken.java │ │ │ ├── PasswordResetToken.java │ │ │ ├── User.java │ │ │ └── VerificationToken.java │ │ ├── enums │ │ │ └── Role.java │ │ ├── repository │ │ │ ├── MagicLinkTokenRepository.java │ │ │ ├── PasswordResetTokenRepository.java │ │ │ ├── UserRepository.java │ │ │ └── VerificationTokenRepository.java │ │ ├── service │ │ │ └── UserService.java │ │ └── serviceImpl │ │ │ ├── EmailServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ ├── util │ │ ├── FacebookJwtUtils.java │ │ ├── GoogleJwtUtils.java │ │ ├── JwtAuthenticationFilter.java │ │ ├── JwtUtils.java │ │ ├── PaginationUtils.java │ │ ├── PasswordValidator.java │ │ ├── UUIDGenarator.java │ │ ├── ValidPassword.java │ │ └── ratelimit │ │ │ ├── filter │ │ │ └── RateLimitFilter.java │ │ │ └── service │ │ │ └── RateLimitService.java │ │ ├── waitlist │ │ ├── controller │ │ │ ├── WaitlistController.java │ │ │ └── WaitlistPageController.java │ │ ├── entity │ │ │ ├── Waitlist.java │ │ │ └── WaitlistPage.java │ │ ├── repository │ │ │ ├── WaitlistPageRepository.java │ │ │ └── WaitlistRepository.java │ │ └── service │ │ │ ├── EmailService.java │ │ │ ├── WaitlistPageService.java │ │ │ └── WaitlistService.java │ │ └── welcome │ │ └── WelcomeController.java └── resources │ ├── application-example.properties │ └── db │ └── migration │ ├── V10__create_squeeze_request_table.sql │ ├── V11__alter_plan_table.sql │ ├── V12__update_squeeze_table.sql │ ├── V14__create_faq_and_contact_table.sql │ ├── V15__create_org_roles_and_permissions.sql │ ├── V16__create_topics_table.sql │ ├── V17__alter_waitlist_table.sql │ ├── V18__create_notification_table.sql │ ├── V19__create_waitlist_pages_table.sql │ ├── V1__init_database.sql │ ├── V20__create_squeeze_config_page_table.sql │ ├── V21__alter_profile_table.sql │ ├── V22__create_regions_table.sql │ ├── V23__create_email_template_table.sql │ ├── V24__create_testimonials_table.sql │ ├── V25__Video_table.sql │ ├── V26__update_email_template_table.sql │ ├── V27__alter_Video_table.sql │ ├── V28__alter_user_table.sql │ ├── V29__create_job_listings_table.sql │ ├── V2__alter_products_table_schema.sql │ ├── V30__create_activity_log_table.sql │ ├── V31__comment_db.sql │ ├── V32__create_about_page_table.sql │ ├── V33__alter_job_listings_table.sql │ ├── V34__create_verification_token_table.sql │ ├── V35__create_payment_table.sql │ ├── V36__alter_video_table.sql │ ├── V37__update_payment.sql │ ├── V38__resources_table.sql │ ├── V39__drop_video_suite.sql │ ├── V3__update_users_and_related_tables.sql │ ├── V40__create_privacy_policy_table.sql │ ├── V41__create_newsletter_table.sql │ ├── V42__alter_organisation_table.sql │ ├── V43__create_category_table.sql │ ├── V44__create_api_status_table.sql │ ├── V45__add_user_payments_table.sql │ ├── V46__create_reset_password_token_table.sql │ ├── V47__magic_link_table.sql │ ├── V48__alter_product_table.sql │ ├── V49__alter_newsletter_product_table.sql │ ├── V4__alter_user_table.sql │ ├── V50__alter_newsletter_table.sql │ ├── V51__create_subscription_table.sql │ ├── V5__alter_user_data.sql │ ├── V6__alter_user_password.sql │ ├── V7__create_plan_table.sql │ ├── V8__create_waitlist_table.sql │ └── V9__update_organisations.sql └── test └── java └── hng_java_boilerplate ├── ProgramTest.java ├── aboutpage └── AboutPageServiceTest.java ├── activitylog └── ActivityLogControllerTest.java ├── categories ├── controller │ └── CategoryControllerTest.java └── service │ └── CategoryServiceTest.java ├── comment_unit_test └── CommentServiceTest.java ├── helpCenter ├── contactUs │ ├── controller │ │ └── ContactUsControllerTest.java │ └── serviceImpl │ │ └── ContactUsServiceImplTest.java ├── faq │ ├── controller │ │ └── FaqControllerTest.java │ └── serviceImpl │ │ └── FaqServiceImplTest.java └── topic │ └── service │ └── TopicServiceTest.java ├── jobs ├── controller │ └── JobListingControllerTest.java └── service │ └── JobListingServiceTest.java ├── newsletter ├── service │ └── NewsletterServiceTest.java └── unit_test │ └── NewsletterTest.java ├── notification ├── NotificationControllerTest.java ├── NotificationSettingsControllerTest.java └── services │ └── NotificationServiceTest.java ├── organisation └── service │ ├── AddUsersToOrganisationServiceTest.java │ ├── OrganisationServiceTest.java │ └── e2e.java ├── plan_test └── PlanServiceTest.java ├── privacy-policy ├── controller │ └── PrivacyPolicyControllerTest.java └── service │ └── PrivacyPolicyServiceTest.java ├── product └── service │ └── ProductServiceImplTest.java ├── product_test └── unit_test │ ├── CreateProductTest.java │ ├── DeleteProductTest.java │ ├── EditProductTest.java │ └── ProductSearchTest.java ├── profile ├── controller │ └── ProfileControllerTest.java └── serviceImpl │ ├── ProfileServiceImplTest.java │ └── UserProfileUnitTest.java ├── region └── serviceImpl │ └── RegionServiceImplTest.java ├── resources └── ServiceTest.java ├── squeeze ├── controller │ ├── SqueezeConfigControllerTest.java │ └── SqueezeRequestControllerTest.java └── service │ └── SqueezeRequestServiceTest.java ├── statusPage ├── controller │ └── StatusPageControllerTest.java └── service │ └── StatusPageServiceTest.java ├── testimonials ├── controller │ └── TestimonialControllerTest.java └── service │ └── TestimonialServiceTest.java ├── user ├── crud_operations_test │ ├── DeleteUserTest.java │ ├── GetAllUsersTest.java │ └── GetSingleUserTest.java ├── login_unit_test │ └── UserLoginTest.java ├── magic_link_test │ └── UserServiceImplTest.java ├── signup_unit_test │ └── UserServiceImplTest.java └── verify_oto_test │ └── UserServiceImplTest.java ├── utils ├── FacebookJwtUtilsTest.java ├── GoogleJwtUtilsTest.java └── TestDataUtil.java ├── waitlist └── controller │ ├── WaitlistControllerTest.java │ └── WaitlistPageControllerTest.java └── welcome └── WelcomeControllerTest.java /.github/scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ~ 4 | # ./stop-app.sh 5 | # systemctl stop java_app.service 6 | # cp application.properties ~/hng_boilerplate_java_web/src/main/resources/ 7 | # cp flyway.conf ~/hng_boilerplate_java_web/ 8 | cd ~/hng_boilerplate_java_web/ 9 | mvn dependency:resolve 10 | ./mvnw clean install 11 | flyway 12 | # nohup ./mvnw spring-boot:run > app.log 2>&1 & 13 | # systemctl start java_app.service -------------------------------------------------------------------------------- /.github/scripts/java_dev_app.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Java Boilerplate Development 3 | After=network.target 4 | 5 | [Service] 6 | User=teamjava 7 | Group=teamjava 8 | WorkingDirectory=/home/teamjava/hng_boilerplate_java_web 9 | ExecStart=/usr/bin/java -XX:ParallelGCThreads=2 -XX:ConcGCThreads=2 -XX:ActiveProcessorCount=2 -Xmx512m -Xms256m -jar /home/teamjava/hng_boilerplate_java_web/target/hng-java-boilerplate-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev 10 | 11 | # System-level Controls 12 | CPUQuota=15% 13 | TasksMax=256 14 | MemoryLimit=256M 15 | LimitNPROC=256 16 | LimitNOFILE=512 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /.github/scripts/java_prod_app.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Java Boilerplate Production 3 | After=network.target 4 | 5 | [Service] 6 | User=teamjava 7 | Group=teamjava 8 | WorkingDirectory=/home/teamjava/hng_boilerplate_java_web 9 | ExecStart=/usr/bin/java -XX:ParallelGCThreads=2 -XX:ConcGCThreads=2 -XX:ActiveProcessorCount=2 -Xmx256m -Xms256m -jar /home/teamjava/hng_boilerplate_java_web/target/hng-java-boilerplate-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod 10 | 11 | # System-level Controls 12 | CPUQuota=25% 13 | TasksMax=256 14 | MemoryLimit=256M 15 | LimitNPROC=256 16 | LimitNOFILE=512 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /.github/scripts/java_staging_app.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Java Boilerplate Staging 3 | After=network.target 4 | 5 | [Service] 6 | User=teamjava 7 | Group=teamjava 8 | WorkingDirectory=/home/teamjava/hng_boilerplate_java_web 9 | ExecStart=/usr/bin/java -XX:ParallelGCThreads=2 -XX:ConcGCThreads=2 -XX:ActiveProcessorCount=2 -Xmx512m -Xms256m -jar /home/teamjava/hng_boilerplate_java_web/target/hng-java-boilerplate-0.0.1-SNAPSHOT.jar --spring.profiles.active=staging 10 | 11 | # System-level Controls 12 | CPUQuota=25% 13 | TasksMax=256 14 | MemoryLimit=512M 15 | LimitNPROC=256 16 | LimitNOFILE=1024 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /.github/workflows/create-flyway-conf/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Create flyway.conf' 2 | description: 'Creates the flyway.conf file' 3 | runs: 4 | using: "composite" 5 | steps: 6 | - run: | 7 | source .env 8 | cat << EOF > ~/flyway.conf 9 | flyway.url=jdbc:postgresql://localhost:5432/${DB_NAME} 10 | flyway.user=${DB_USERNAME} 11 | flyway.password=${DB_PASSWORD} 12 | flyway.locations=classpath:db/migration 13 | flyway.baselineOnMigrate=true 14 | flyway.baselineVersion=1 15 | shell: bash -------------------------------------------------------------------------------- /.github/workflows/pr-deploy.txt: -------------------------------------------------------------------------------- 1 | name: PR-Deploy 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened, closed] 6 | 7 | jobs: 8 | deploy-pr: 9 | environment: 10 | name: preview 11 | url: ${{ steps.deploy.outputs.preview-url }} 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout Code 15 | uses: actions/checkout@v4 16 | - name: Copy .env.sample to app.props 17 | run: cp .env.sample ./src/main/resources/application.properties 18 | 19 | - id: deploy 20 | name: Deploy Pull Request 21 | uses: hngprojects/pr-deploy@main 22 | with: 23 | server_host: ${{ secrets.SSH_HOST }} 24 | server_username: ${{ secrets.DEV_SSH_USERNAME }} 25 | server_password: ${{ secrets.DEV_SSH_PASSWORD }} 26 | # server_port: ${{ secrets.SERVER_PORT }} 27 | comment: true 28 | context: '.' 29 | dockerfile: 'Dockerfile' 30 | exposed_port: '8080' 31 | # host_volume_path: '/var/' 32 | # container_volume_path: '/var/' 33 | github_token: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | - name: Display Preview URL 36 | run: | 37 | echo "Preview URL: ${{ steps.deploy.outputs.preview-url }}" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | # Ignore all application.properties 36 | application*.properties 37 | !application-example.properties 38 | 39 | .env* -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip 20 | -------------------------------------------------------------------------------- /Contributors.md: -------------------------------------------------------------------------------- 1 | 2 | Tachtwitch 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Stage 1 2 | # Use a base image with Maven and JDK 17 3 | FROM maven:3.8.1-openjdk-17 AS stage1 4 | # Speed up Maven JVM a bit 5 | ENV MAVEN_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1" 6 | 7 | # Set working directory 8 | WORKDIR /app 9 | # Copy just pom.xml 10 | COPY .mvn/ .mvn 11 | COPY mvnw pom.xml ./ 12 | 13 | # Go-offline using the pom.xml 14 | RUN mvn dependency:go-offline 15 | 16 | # Copy your other files 17 | COPY ./src ./src 18 | # Compile the source code and package it in a jar file 19 | RUN mvn package -Dmaven.test.skip=true 20 | 21 | # Stage 2 22 | # Use a base image with Eclipse Temurin JRE 17 23 | FROM eclipse-temurin:17-jre-alpine 24 | # Set deployment directory 25 | WORKDIR /app 26 | # Copy over the built artifact from the maven image 27 | COPY --from=stage1 /app/target/*.jar app.jar 28 | 29 | # Create a non-root user and group 30 | RUN addgroup -S appgroup && adduser -S appuser -G appgroup 31 | 32 | # Change ownership of the application files to the non-root user 33 | RUN chown -R appuser:appgroup /app 34 | 35 | # Switch to the non-root user 36 | USER appuser 37 | 38 | # Expose the application port 39 | EXPOSE 8080 40 | 41 | # Run the application 42 | CMD ["java", "-jar", "app.jar"] -------------------------------------------------------------------------------- /compose.override.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | env_file: 4 | - .env 5 | 6 | rabbitmq: 7 | env_file: 8 | - .env 9 | 10 | nginx: 11 | ports: 12 | - 8000:80 13 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | name: java 2 | 3 | services: 4 | backend: 5 | image: ${COMPOSE_PROJECT_NAME} 6 | build: . 7 | healthcheck: 8 | test: "wget -qO- http://localhost:8080" 9 | start_period: 20s 10 | interval: 30s 11 | timeout: 10s 12 | retries: 5 13 | depends_on: 14 | rabbitmq: 15 | condition: service_healthy 16 | postgres: 17 | condition: service_healthy 18 | restart: unless-stopped 19 | 20 | postgres: 21 | image: postgres:16-alpine 22 | volumes: 23 | - postgres:/var/lib/postgresql/data 24 | healthcheck: 25 | test: "pg_isready" 26 | interval: 10s 27 | timeout: 10s 28 | retries: 5 29 | restart: unless-stopped 30 | 31 | rabbitmq: 32 | image: rabbitmq:3-management-alpine 33 | volumes: 34 | - rabbitmq:/var/lib/rabbitmq 35 | healthcheck: 36 | test: "rabbitmq-diagnostics check_running" 37 | interval: 10s 38 | timeout: 10s 39 | retries: 5 40 | restart: unless-stopped 41 | 42 | nginx: 43 | image: nginx:alpine 44 | volumes: 45 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf 46 | depends_on: 47 | backend: 48 | condition: service_healthy 49 | healthcheck: 50 | test: "wget -qO- http://nginx:80" 51 | interval: 10s 52 | timeout: 10s 53 | retries: 5 54 | restart: unless-stopped 55 | 56 | volumes: 57 | postgres: 58 | rabbitmq: 59 | -------------------------------------------------------------------------------- /compose/compose.dev.yaml: -------------------------------------------------------------------------------- 1 | name: java_dev 2 | 3 | services: 4 | postgres: 5 | env_file: 6 | - .env.dev 7 | 8 | rabbitmq: 9 | env_file: 10 | - .env.dev 11 | 12 | nginx: 13 | ports: 14 | - 8000:80 -------------------------------------------------------------------------------- /compose/compose.prod.yaml: -------------------------------------------------------------------------------- 1 | name: java_prod 2 | 3 | services: 4 | postgres: 5 | env_file: 6 | - .env.prod 7 | 8 | rabbitmq: 9 | env_file: 10 | - .env.prod 11 | 12 | nginx: 13 | ports: 14 | - 8002:80 -------------------------------------------------------------------------------- /compose/compose.staging.yaml: -------------------------------------------------------------------------------- 1 | name: java_staging 2 | 3 | services: 4 | postgres: 5 | env_file: 6 | - .env.staging 7 | 8 | rabbitmq: 9 | env_file: 10 | - .env.staging 11 | 12 | nginx: 13 | ports: 14 | - 8001:80 -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | 5 | http { 6 | include mime.types; 7 | default_type application/octet-stream; 8 | 9 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE 10 | ssl_prefer_server_ciphers on; 11 | 12 | upstream backend { 13 | server backend:8080; 14 | } 15 | 16 | sendfile on; 17 | keepalive_timeout 100; 18 | 19 | server { 20 | listen 80; 21 | 22 | location / { 23 | proxy_pass http://backend; 24 | proxy_set_header Host $host; 25 | proxy_set_header X-Real-IP $remote_addr; 26 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 27 | proxy_set_header X-Forwarded-Proto $scheme; 28 | } 29 | error_log /var/log/nginx/error.log; 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { worker_connections 1024; } 4 | 5 | http { 6 | 7 | resolver 127.0.0.11 valid=1s; 8 | 9 | server { 10 | listen 80; 11 | 12 | location / { 13 | proxy_pass http://backend:8080; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /profile_photos/6e6bbd89-a83d-44a8-994f-9949c48b0f59_oskar-smethurst-B1GtwanCbiw-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hngprojects/hng_boilerplate_java_web/a2e428a6aeec1f0fd98c6a5a5081f5ff892692d7/profile_photos/6e6bbd89-a83d-44a8-994f-9949c48b0f59_oskar-smethurst-B1GtwanCbiw-unsplash.jpg -------------------------------------------------------------------------------- /profile_photos/9f947e39-fe38-44dd-85d3-927253847576_oskar-smethurst-B1GtwanCbiw-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hngprojects/hng_boilerplate_java_web/a2e428a6aeec1f0fd98c6a5a5081f5ff892692d7/profile_photos/9f947e39-fe38-44dd-85d3-927253847576_oskar-smethurst-B1GtwanCbiw-unsplash.jpg -------------------------------------------------------------------------------- /profile_photos/c3ae0d7a-fecb-4145-9992-be8b8c4f1e2a_oskar-smethurst-B1GtwanCbiw-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hngprojects/hng_boilerplate_java_web/a2e428a6aeec1f0fd98c6a5a5081f5ff892692d7/profile_photos/c3ae0d7a-fecb-4145-9992-be8b8c4f1e2a_oskar-smethurst-B1GtwanCbiw-unsplash.jpg -------------------------------------------------------------------------------- /profile_photos/c72cde4e-e8a6-46a8-a010-5b8a8d185bc9_test.jpg: -------------------------------------------------------------------------------- 1 | dummy image content -------------------------------------------------------------------------------- /profile_photos/d53189d2-ae88-4bee-a4ac-d71d1994bfe1_oskar-smethurst-B1GtwanCbiw-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hngprojects/hng_boilerplate_java_web/a2e428a6aeec1f0fd98c6a5a5081f5ff892692d7/profile_photos/d53189d2-ae88-4bee-a4ac-d71d1994bfe1_oskar-smethurst-B1GtwanCbiw-unsplash.jpg -------------------------------------------------------------------------------- /profile_photos/f28a3ff5-7685-4feb-acae-aaafa0946da2_oskar-smethurst-B1GtwanCbiw-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hngprojects/hng_boilerplate_java_web/a2e428a6aeec1f0fd98c6a5a5081f5ff892692d7/profile_photos/f28a3ff5-7685-4feb-acae-aaafa0946da2_oskar-smethurst-B1GtwanCbiw-unsplash.jpg -------------------------------------------------------------------------------- /profile_photos/ff8588cb-395e-4168-a069-8c108280fe6b_test.jpg: -------------------------------------------------------------------------------- 1 | dummy image content -------------------------------------------------------------------------------- /qa_tests/test_newman.ps1: -------------------------------------------------------------------------------- 1 | # Define the output directory and file name 2 | $outputDir = "newman-reports" 3 | $timestamp = Get-Date -Format "yyyyMMddHHmmss" 4 | $outputFile = "$outputDir\report-$timestamp.html" 5 | # Create the directory if it doesn't exist 6 | if (-Not (Test-Path -Path $outputDir)) { 7 | New-Item -ItemType Directory -Path $outputDir 8 | } 9 | # Run Newman with HTML reporter 10 | newman run hng_boilerplate_java_web/qa_tests/regression/Spartacus_Java_Boilerplate_Copy.postman_collection.json ` 11 | -r html ` 12 | --reporter-html-export $outputFile -------------------------------------------------------------------------------- /qa_tests/test_newman.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define the output directory and file name 4 | # OUTPUT_DIR="newman-reports" 5 | # OUTPUT_FILE="$OUTPUT_DIR/report-$(date +%Y%m%d%H%M%S).html" 6 | 7 | # Create the directory if it doesn't exist 8 | # mkdir -p $OUTPUT_DIR 9 | 10 | # Run Newman with HTML reporter 11 | newman run hng_boilerplate_java_web/qa_tests/regression/Spartacus_Java_Boilerplate_Copy.postman_collection.json -r htmlextra 12 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/Program.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | @SpringBootApplication 8 | @EnableScheduling 9 | public class Program { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Program.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/aboutpage/controller/AboutPageController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.aboutpage.controller; 2 | 3 | import hng_java_boilerplate.aboutpage.dto.AboutPageContentDto; 4 | import hng_java_boilerplate.aboutpage.dto.ApiResponse; 5 | import hng_java_boilerplate.aboutpage.service.AboutPageService; 6 | import io.swagger.v3.oas.annotations.Operation; 7 | import io.swagger.v3.oas.annotations.tags.Tag; 8 | import jakarta.validation.Valid; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.security.access.prepost.PreAuthorize; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | @RestController 16 | @RequestMapping("/api/v1/content") 17 | @RequiredArgsConstructor 18 | @Tag(name="About Page", description = "controller for about page") 19 | public class AboutPageController { 20 | private final AboutPageService aboutPageService; 21 | 22 | @PutMapping("/about") 23 | @PreAuthorize("hasRole('SUPER_ADMIN')") 24 | @Operation(summary = "Updates the about page content") 25 | public ResponseEntity updateAboutPageContent(@Valid @RequestBody AboutPageContentDto contentDto) { 26 | aboutPageService.updateAboutPageContent(contentDto); 27 | return ResponseEntity.ok(new ApiResponse("About page content updated successfully.", 200)); 28 | } 29 | 30 | @DeleteMapping("/about") 31 | @PreAuthorize("hasRole('SUPER_ADMIN')") 32 | @Operation(summary = "Deletes about page content") 33 | public ResponseEntity deleteAboutPageContent() { 34 | aboutPageService.deleteAboutPageContent(); 35 | return ResponseEntity.ok(new ApiResponse("About page content deleted successfully.", 200)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/aboutpage/dto/AboutPageContentDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.aboutpage.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotNull; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import lombok.Setter; 10 | 11 | import java.util.Map; 12 | 13 | @Getter 14 | @Setter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class AboutPageContentDto { 18 | @NotBlank 19 | private String title; 20 | 21 | @NotBlank 22 | private String introduction; 23 | 24 | @NotNull 25 | @JsonProperty("custom_sections") 26 | private Map customSections; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/aboutpage/dto/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.aboutpage.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | public class ApiResponse { 11 | @JsonProperty("message") 12 | private String message; 13 | 14 | @JsonProperty("status_code") 15 | private int statusCode; 16 | 17 | public ApiResponse(String message, int statusCode) { 18 | this.message = message; 19 | this.statusCode = statusCode; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/aboutpage/entity/AboutPageContent.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.aboutpage.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Entity 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Getter 13 | @Setter 14 | public class AboutPageContent { 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | 19 | private String title; 20 | 21 | private String introduction; 22 | 23 | private Integer yearsInBusiness; 24 | private Integer customers; 25 | private Integer monthlyBlogReaders; 26 | private Integer socialFollowers; 27 | 28 | private String servicesTitle; 29 | private String servicesDescription; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/aboutpage/repository/AboutPageRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.aboutpage.repository; 2 | 3 | import hng_java_boilerplate.aboutpage.entity.AboutPageContent; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface AboutPageRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/activitylog/dto/ActivityLogResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.activitylog.dto; 2 | 3 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 4 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 9 | @Getter 10 | @Setter 11 | public class ActivityLogResponseDto { 12 | private String activity; 13 | private String timestamp; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/activitylog/dto/ApiResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.activitylog.dto; 2 | 3 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 4 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 5 | import lombok.*; 6 | 7 | import java.util.List; 8 | 9 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | public class ApiResponseDto { 14 | private String message; 15 | private boolean success; 16 | private int statusCode; 17 | private List data; 18 | 19 | public ApiResponseDto(String message, boolean success, int statusCode, List data) { 20 | this.message = message; 21 | this.success = success; 22 | this.statusCode = statusCode; 23 | this.data = data; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/activitylog/dto/ErrorResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.activitylog.dto; 2 | 3 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 4 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | 10 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Getter 14 | @Setter 15 | public class ErrorResponseDto { 16 | private int statusCode; 17 | private String message; 18 | private String error; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/activitylog/model/ActivityLog.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.activitylog.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.time.Instant; 10 | import java.util.UUID; 11 | 12 | @Entity 13 | @Table(name = "activity_logs") 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Getter 17 | @Setter 18 | public class ActivityLog { 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | 23 | @Column(name = "org_id", nullable = false) 24 | private String orgId; 25 | 26 | @Column(name = "user_id", nullable = false) 27 | private String userId; 28 | 29 | @Column(name = "activity", nullable = false) 30 | private String activity; 31 | 32 | @Column(name = "timestamp", nullable = false) 33 | private Instant timestamp; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/activitylog/repository/ActivityLogRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.activitylog.repository; 2 | 3 | import hng_java_boilerplate.activitylog.model.ActivityLog; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | public interface ActivityLogRepository extends JpaRepository { 10 | List findByOrgIdAndUserId(String orgId, String userId); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/activitylog/service/ActivityLogService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.activitylog.service; 2 | 3 | import hng_java_boilerplate.activitylog.model.ActivityLog; 4 | import hng_java_boilerplate.activitylog.repository.ActivityLogRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | import java.time.Instant; 10 | import java.util.List; 11 | import java.util.UUID; 12 | 13 | @Service 14 | public class ActivityLogService { 15 | private final ActivityLogRepository activityLogRepository; 16 | 17 | 18 | public ActivityLogService(ActivityLogRepository activityLogRepository) { 19 | this.activityLogRepository = activityLogRepository; 20 | } 21 | @Transactional 22 | public void logActivity(String orgId, String userId, String activity) { 23 | ActivityLog log = new ActivityLog(); 24 | log.setOrgId(orgId); 25 | log.setUserId(userId); 26 | log.setActivity(activity); 27 | log.setTimestamp(Instant.now()); 28 | System.out.println("Attempting to save log: " + log); 29 | activityLogRepository.save(log); 30 | } 31 | 32 | public List getActivityLogs(String orgId, String userId) { 33 | return activityLogRepository.findByOrgIdAndUserId(orgId, userId); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/categories/dto/CategoryDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.categories.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.UUID; 11 | 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class CategoryDto { 17 | private UUID category_id; 18 | private String name; 19 | private String description; 20 | private String slug; 21 | private LocalDateTime created_at; 22 | private LocalDateTime updated_at; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/categories/dto/CategoryRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.categories.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.Data; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Data 9 | public class CategoryRequest { 10 | @NotBlank(message = "name is required") 11 | private String name; 12 | @NotBlank(message = "description is required") 13 | private String description; 14 | @NotBlank(message = "slug is required") 15 | private String slug; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/categories/dto/CustomResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.categories.dto; 2 | 3 | public record CustomResponse(int status_code, String message) { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/categories/entity/Category.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.categories.entity; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.UUID; 5 | import jakarta.persistence.Entity; 6 | import jakarta.persistence.GeneratedValue; 7 | import jakarta.persistence.GenerationType; 8 | import jakarta.persistence.Id; 9 | import jakarta.persistence.Table; 10 | import jakarta.validation.constraints.NotBlank; 11 | import lombok.AllArgsConstructor; 12 | import lombok.Getter; 13 | import lombok.NoArgsConstructor; 14 | import lombok.Setter; 15 | 16 | @Entity 17 | @Getter 18 | @Setter 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @Table(name = "categories") 22 | public class Category { 23 | 24 | @Id 25 | @GeneratedValue(strategy = GenerationType.AUTO) 26 | private UUID id; 27 | 28 | @NotBlank(message = "Name is mandatory") 29 | private String name; 30 | 31 | private String description; 32 | 33 | @NotBlank(message = "Slug is mandatory") 34 | private String slug; 35 | 36 | private LocalDateTime createdAt; 37 | private LocalDateTime updatedAt; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/categories/repository/CategoryRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.categories.repository; 2 | 3 | import hng_java_boilerplate.categories.entity.Category; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | import java.util.UUID; 7 | 8 | @Repository 9 | public interface CategoryRepository extends JpaRepository { 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/categories/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.categories.service; 2 | 3 | import hng_java_boilerplate.categories.dto.CategoryDto; 4 | import hng_java_boilerplate.categories.dto.CategoryRequest; 5 | import hng_java_boilerplate.categories.dto.CustomResponse; 6 | import hng_java_boilerplate.categories.entity.Category; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | import java.util.UUID; 11 | 12 | public interface CategoryService { 13 | CategoryDto createCategory(CategoryRequest category); 14 | List getAllCategories(); 15 | CategoryDto getCategoryById(UUID category_id); 16 | CustomResponse deleteCategory(UUID category_id); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/comment/dto/CommentDataDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.comment.dto; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | 12 | public class CommentDataDto { 13 | private String user_id; 14 | private String userName; 15 | 16 | private String comment; 17 | private String createdAt; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/comment/dto/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.comment.dto; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @Setter 9 | @Getter 10 | @AllArgsConstructor 11 | public class ErrorResponse { 12 | private String message; 13 | private String error; 14 | private int status_code; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/comment/dto/RequestDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.comment.dto; 2 | 3 | 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.Size; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | 10 | @Getter 11 | @Setter 12 | @AllArgsConstructor 13 | public class RequestDto { 14 | 15 | @NotBlank(message = "input comment") 16 | @Size(max = 1000, message = "comment should not exceed 1000 characters") 17 | private String comment; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/comment/dto/ResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.comment.dto; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | 9 | @Getter 10 | @Setter 11 | @AllArgsConstructor 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class ResponseDto { 14 | private String status; 15 | private String message; 16 | private CommentDataDto data; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/comment/entity/Comment.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.comment.entity; 2 | 3 | 4 | import hng_java_boilerplate.user.entity.User; 5 | import jakarta.persistence.*; 6 | import jakarta.validation.constraints.NotBlank; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | import lombok.Setter; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | @Entity 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Getter 18 | @Setter 19 | @Table(name = "comments") 20 | 21 | public class Comment { 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.UUID) 24 | private String commentId; 25 | 26 | @JoinColumn(name = "user_id", nullable = false) 27 | @ManyToOne 28 | private User user; 29 | 30 | @Column(nullable = false) 31 | private Boolean deleted = false; 32 | 33 | @NotBlank(message = "input comment") 34 | @Column(nullable = false, length = 1000) 35 | private String comment; 36 | 37 | @Column(name = "created_at", nullable = false) 38 | private LocalDateTime createdAt; 39 | 40 | @Column(name = "updated_at", nullable = false) 41 | private LocalDateTime updatedAt; 42 | 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/comment/repository/CommentRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.comment.repository; 2 | 3 | 4 | import hng_java_boilerplate.comment.entity.Comment; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.Optional; 9 | 10 | 11 | @Repository 12 | public interface CommentRepository extends JpaRepository { 13 | Optional findByCommentIdAndDeletedFalse(String commentId); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/config/AwsConfig.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.config; 2 | 3 | import com.amazonaws.auth.AWSStaticCredentialsProvider; 4 | import com.amazonaws.auth.BasicAWSCredentials; 5 | import com.amazonaws.services.s3.AmazonS3; 6 | import com.amazonaws.services.s3.AmazonS3ClientBuilder; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @Configuration 12 | public class AwsConfig { 13 | 14 | @Value("${aws.s3.access-key}") 15 | private String accessKey; 16 | 17 | @Value("${aws.s3.secret-key}") 18 | private String secretKey; 19 | 20 | @Value("${aws.s3.region}") 21 | private String region; 22 | 23 | @Bean 24 | public AmazonS3 amazonS3() { 25 | BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey); 26 | return AmazonS3ClientBuilder.standard() 27 | .withRegion(region) 28 | .withCredentials(new AWSStaticCredentialsProvider(awsCreds)) 29 | .build(); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/config/FilterConfig.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.config; 2 | 3 | import hng_java_boilerplate.util.ratelimit.filter.RateLimitFilter; 4 | import hng_java_boilerplate.util.ratelimit.service.RateLimitService; 5 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class FilterConfig { 11 | 12 | @Bean 13 | public RateLimitFilter rateLimitFilter(RateLimitService rateLimitService) { 14 | return new RateLimitFilter(rateLimitService); 15 | } 16 | 17 | @Bean 18 | public FilterRegistrationBean rateLimitFilterRegistrationBean(RateLimitFilter rateLimitFilter) { 19 | FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); 20 | registrationBean.setFilter(rateLimitFilter); 21 | registrationBean.addUrlPatterns("/api/v1/waitlist/*"); 22 | return registrationBean; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/config/MetricsConfig.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.config; 2 | 3 | import io.micrometer.prometheus.PrometheusMeterRegistry; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class MetricsConfig { 9 | 10 | @Bean 11 | public PrometheusMeterRegistry prometheusMeterRegistry() { 12 | return new PrometheusMeterRegistry(io.micrometer.prometheus.PrometheusConfig.DEFAULT); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/config/RabbitMQConfig.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.config; 2 | 3 | import org.springframework.amqp.core.Queue; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class RabbitMQConfig { 10 | 11 | @Value("${rabbitmq.queue.email}") 12 | private String emailQueueName; 13 | 14 | @Bean 15 | public Queue emailQueue() { 16 | return new Queue(emailQueueName, true); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/config/TwoFactorConfig.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.config; 2 | 3 | import dev.samstevens.totp.code.*; 4 | import dev.samstevens.totp.qr.QrGenerator; 5 | import dev.samstevens.totp.qr.ZxingPngQrGenerator; 6 | import dev.samstevens.totp.secret.DefaultSecretGenerator; 7 | import dev.samstevens.totp.secret.SecretGenerator; 8 | import dev.samstevens.totp.time.SystemTimeProvider; 9 | import dev.samstevens.totp.time.TimeProvider; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | @Configuration 14 | public class TwoFactorConfig { 15 | 16 | @Bean 17 | public SecretGenerator secretGenerator(){ 18 | return new DefaultSecretGenerator(); 19 | } 20 | 21 | @Bean 22 | public QrGenerator qrGenerator(){ 23 | return new ZxingPngQrGenerator(); 24 | } 25 | 26 | @Bean 27 | public CodeVerifier codeVerifier() { 28 | TimeProvider timeProvider = new SystemTimeProvider(); 29 | CodeGenerator codeGenerator = new DefaultCodeGenerator(HashingAlgorithm.SHA512, 6); 30 | DefaultCodeVerifier codeVerifier = new DefaultCodeVerifier(codeGenerator, timeProvider); 31 | codeVerifier.setTimePeriod(30); 32 | codeVerifier.setAllowedTimePeriodDiscrepancy(2); 33 | return codeVerifier; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/dashboard/controller/DashboardController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.dashboard.controller; 2 | 3 | import hng_java_boilerplate.dashboard.service.DashboardService; 4 | import hng_java_boilerplate.product.dto.GetProductsDTO; 5 | import hng_java_boilerplate.product.dto.ProductCountDto; 6 | import hng_java_boilerplate.product.dto.ProductDTO; 7 | import io.swagger.v3.oas.annotations.Operation; 8 | import io.swagger.v3.oas.annotations.tags.Tag; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | @RestController 15 | @RequiredArgsConstructor 16 | @PreAuthorize("hasRole('SUPER_ADMIN')") 17 | @RequestMapping("/api/v1/dashboards") 18 | @Tag(name = "dashboard", description = "admin dashboard") 19 | public class DashboardController { 20 | private final DashboardService dashboardService; 21 | 22 | @GetMapping("/products") 23 | @Operation(summary = "get all the products in the database") 24 | public ResponseEntity getProducts() { 25 | return ResponseEntity.ok(dashboardService.getAllProducts()); 26 | } 27 | 28 | @GetMapping("/products/count") 29 | @Operation(summary = "get the total counts of products") 30 | public ResponseEntity getProductCount() { 31 | return ResponseEntity.ok(dashboardService.getProductCount()); 32 | } 33 | 34 | @GetMapping("/products/{productId}") 35 | @Operation(summary = "get a product by the product id") 36 | public ResponseEntity getProductById(@PathVariable String productId) { 37 | return ResponseEntity.ok(dashboardService.getProductById(productId)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/dashboard/service/DashboardService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.dashboard.service; 2 | 3 | import hng_java_boilerplate.product.dto.GetProductsDTO; 4 | import hng_java_boilerplate.product.dto.ProductCountDto; 5 | import hng_java_boilerplate.product.dto.ProductDTO; 6 | import hng_java_boilerplate.product.service.ProductService; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | @RequiredArgsConstructor 12 | public class DashboardService { 13 | private final ProductService productService; 14 | 15 | public ProductCountDto getProductCount() { 16 | return productService.getProductsCount(); 17 | } 18 | 19 | public GetProductsDTO getAllProducts() { 20 | return productService.getProducts(); 21 | } 22 | 23 | public ProductDTO getProductById(String productId) { 24 | return productService.getProductById(productId); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/EmailServices/EmailConsumerService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.EmailServices; 2 | 3 | import hng_java_boilerplate.email.entity.EmailMessage; 4 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.mail.SimpleMailMessage; 8 | import org.springframework.mail.javamail.JavaMailSender; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | public class EmailConsumerService { 13 | private final JavaMailSender emailSender; 14 | 15 | @Value("${rabbitmq.queue.email}") 16 | private String emailQueue; 17 | 18 | @Autowired 19 | public EmailConsumerService(JavaMailSender emailSender) { 20 | this.emailSender = emailSender; 21 | } 22 | 23 | @RabbitListener(queues = "${rabbitmq.queue.email}") 24 | public void receiveMessage(EmailMessage emailMessage) { 25 | SimpleMailMessage message = new SimpleMailMessage(); 26 | message.setTo(emailMessage.getTo()); 27 | message.setSubject(emailMessage.getSubject()); 28 | message.setText(emailMessage.getText()); 29 | emailSender.send(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/EmailServices/EmailProducerService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.EmailServices; 2 | 3 | import hng_java_boilerplate.email.entity.EmailMessage; 4 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class EmailProducerService { 11 | private final RabbitTemplate rabbitTemplate; 12 | 13 | @Value("${rabbitmq.queue.email}") 14 | private String emailQueue; 15 | 16 | @Autowired 17 | public EmailProducerService(RabbitTemplate rabbitTemplate) { 18 | this.rabbitTemplate = rabbitTemplate; 19 | } 20 | 21 | public void sendEmailMessage(String to, String subject, String text) { 22 | EmailMessage emailMessage = new EmailMessage(to, subject, text); 23 | rabbitTemplate.convertAndSend(emailQueue, emailMessage); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/dto/EmailTemplateRequestDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | 5 | public record EmailTemplateRequestDto( 6 | @NotBlank(message = "Title can not be blank") 7 | String title, 8 | 9 | @NotBlank(message = "Template can not be blank") 10 | String template, 11 | 12 | @NotBlank(message = "Type can not be blank") 13 | String type 14 | 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/dto/EmailTemplateResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import hng_java_boilerplate.email.entity.EmailTemplate; 5 | 6 | public record EmailTemplateResponse( 7 | String message, 8 | 9 | @JsonProperty("status_code") 10 | Integer statusCode, 11 | EmailTemplate data 12 | 13 | ) { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/dto/EmailTemplateUpdate.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | 5 | public record EmailTemplateUpdate( 6 | 7 | @NotBlank(message = "Name can not be blank.") 8 | String name, 9 | 10 | @NotBlank(message = "Content can not be blank") 11 | String content 12 | ) { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/entity/EmailMessage.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.entity; 2 | 3 | import java.io.Serializable; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @Setter 10 | @Getter 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class EmailMessage implements Serializable { 14 | private static final long serialVersionUID = 1L; 15 | 16 | private String to; 17 | private String subject; 18 | private String text; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/entity/EmailTemplate.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.entity; 2 | 3 | 4 | import hng_java_boilerplate.email.enums.EmailTemplateStatus; 5 | import jakarta.persistence.*; 6 | import lombok.*; 7 | import org.hibernate.annotations.CreationTimestamp; 8 | import org.hibernate.annotations.UpdateTimestamp; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Entity 17 | @Builder 18 | @Table(name = "email_templates") 19 | public class EmailTemplate { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.UUID) 23 | private String id; 24 | 25 | @Column(nullable = false, unique = true) 26 | private String title; 27 | 28 | @Column(nullable = false) 29 | private String template; 30 | 31 | @Column(nullable = false) 32 | private String type; 33 | 34 | @Enumerated(EnumType.STRING) 35 | private EmailTemplateStatus status; 36 | 37 | @CreationTimestamp 38 | @Column(name = "created_at", updatable = false) 39 | private LocalDateTime createdAt; 40 | 41 | @UpdateTimestamp 42 | @Column(name = "updated_at") 43 | private LocalDateTime updatedAt; 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/enums/EmailTemplateStatus.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.enums; 2 | 3 | public enum EmailTemplateStatus { 4 | ONLINE, OFFLINE 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/email/repository/EmailTemplateRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.email.repository; 2 | 3 | import hng_java_boilerplate.email.entity.EmailTemplate; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.Optional; 8 | 9 | @Repository 10 | public interface EmailTemplateRepository extends JpaRepository { 11 | boolean existsByTitle(String title); 12 | Optional findByTitle(String title); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/BadRequestException.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | public class BadRequestException extends RuntimeException { 4 | public BadRequestException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/ConflictException.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | public class ConflictException extends RuntimeException{ 4 | 5 | public ConflictException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/CustomError.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | public record CustomError(int status_code, String error) { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/ErrorResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | @Data 11 | public class ErrorResponseDto { 12 | 13 | private String message; 14 | private String error; 15 | private int status_code; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | public class NotFoundException extends RuntimeException{ 4 | public NotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/ProfilePictureUploadException.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | public class ProfilePictureUploadException extends RuntimeException { 4 | public ProfilePictureUploadException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/UnAuthorizedException.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | public class UnAuthorizedException extends RuntimeException{ 4 | public UnAuthorizedException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/exception/ValidationError.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.exception; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class ValidationError { 13 | private int status_code; 14 | private String error; 15 | private Map detail; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/contactUs/dto/request/ContactUsRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.contactUs.dto.request; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class ContactUsRequest { 9 | @NotBlank(message = "name is required") 10 | private String name; 11 | 12 | @NotBlank(message = "email is required") 13 | @Email(message = "email must be valid") 14 | private String email; 15 | 16 | @NotBlank(message = "phone_number is required") 17 | private String phone_number; 18 | 19 | @NotBlank(message = "message is required") 20 | private String message; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/contactUs/dto/response/ContactUsResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.contactUs.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class ContactUsResponse { 13 | private String name; 14 | private String email; 15 | private String phone; 16 | private String message; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/contactUs/dto/response/CustomResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.contactUs.dto.response; 2 | 3 | public record CustomResponse(String status, String message) {} 4 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/contactUs/entity/Contact.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.contactUs.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.util.UUID; 10 | 11 | @Entity 12 | @Getter 13 | @Setter 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Table(name = "contacts") 17 | public class Contact { 18 | @Id 19 | private String id; 20 | @Column(nullable = false) 21 | private String name; 22 | @Column(nullable = false) 23 | private String email; 24 | @Column(nullable = false) 25 | private String phone; 26 | @Column(nullable = false) 27 | private String message; 28 | 29 | @PrePersist 30 | private void prePersist() { 31 | if (id == null) { 32 | id = UUID.randomUUID().toString(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/contactUs/repository/ContactUsRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.contactUs.repository; 2 | 3 | import hng_java_boilerplate.helpCenter.contactUs.entity.Contact; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface ContactUsRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/contactUs/service/ContactUsService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.contactUs.service; 2 | 3 | import hng_java_boilerplate.helpCenter.contactUs.dto.request.ContactUsRequest; 4 | import hng_java_boilerplate.helpCenter.contactUs.dto.response.ContactUsResponse; 5 | import hng_java_boilerplate.helpCenter.contactUs.dto.response.CustomResponse; 6 | 7 | import java.util.List; 8 | 9 | public interface ContactUsService { 10 | CustomResponse processContactMessage(ContactUsRequest request); 11 | List getAllContacts(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/faq/dto/request/FaqRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.faq.dto.request; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class FaqRequest { 8 | @NotBlank(message = "question is required") 9 | private String question; 10 | @NotBlank(message = "answer is required") 11 | private String answer; 12 | private String category; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/faq/dto/response/CustomResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.faq.dto.response; 2 | 3 | public record CustomResponse(String status, String message) { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/faq/dto/response/FaqResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.faq.dto.response; 2 | 3 | public record FaqResponse(String status, String id, String question, String answer, String category) {} 4 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/faq/entity/FAQ.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.faq.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.util.UUID; 10 | 11 | @Entity 12 | @Getter 13 | @Setter 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Table(name = "faqs") 17 | public class FAQ { 18 | @Id 19 | private String id; 20 | @Column(nullable = false) 21 | private String question; 22 | @Column(nullable = false) 23 | private String answer; 24 | @Column 25 | private String category; 26 | 27 | @PrePersist 28 | public void prePersist() { 29 | if (id == null) { 30 | id = UUID.randomUUID().toString(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/faq/repository/FaqRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.faq.repository; 2 | 3 | import hng_java_boilerplate.helpCenter.faq.entity.FAQ; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface FaqRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/faq/service/FaqService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.faq.service; 2 | 3 | import hng_java_boilerplate.helpCenter.faq.dto.request.FaqRequest; 4 | import hng_java_boilerplate.helpCenter.faq.dto.response.CustomResponse; 5 | import hng_java_boilerplate.helpCenter.faq.dto.response.FaqResponse; 6 | 7 | import java.util.List; 8 | 9 | public interface FaqService { 10 | FaqResponse createFaq(FaqRequest request); 11 | List getFaqs(); 12 | CustomResponse updateFaq(FaqRequest request, String faqId); 13 | CustomResponse deleteFaq(String faqId); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/topic/entity/Topic.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.topic.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.UUID; 11 | 12 | @Entity 13 | @Table(name = "help_center_topics") 14 | @Getter 15 | @Setter 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class Topic { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.AUTO) 22 | private UUID id; 23 | 24 | @Column(nullable = false, unique = true) 25 | private String title; 26 | 27 | @Column(nullable = false, columnDefinition = "TEXT") 28 | private String content; 29 | 30 | @Column(nullable = false) 31 | private String author; 32 | 33 | @Column(nullable = false) 34 | private LocalDateTime createdAt; 35 | 36 | @Column(nullable = false) 37 | private LocalDateTime updatedAt; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/helpCenter/topic/repository/TopicRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.helpCenter.topic.repository; 2 | 3 | import hng_java_boilerplate.helpCenter.topic.entity.Topic; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | public interface TopicRepository extends JpaRepository { 10 | List findByTitleContainingIgnoreCase(String title); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/jobs/dto/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.jobs.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class ApiResponse { 13 | private String message; 14 | private int statusCode; 15 | private T data; 16 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/jobs/entity/JobListing.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.jobs.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Entity 10 | @Getter 11 | @Setter 12 | @Table(name = "job_listings") 13 | public class JobListing { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | 18 | @Column(nullable = false) 19 | private String title; 20 | 21 | @Column(nullable = false, length = 1000) 22 | private String description; 23 | 24 | @Column(nullable = false) 25 | private String location; 26 | 27 | @Column(nullable = false) 28 | private String salary; 29 | 30 | @Column(name = "job_type", nullable = false) 31 | private String jobType; 32 | 33 | @Column(name = "company_name", nullable = false) 34 | private String companyName; 35 | 36 | @Column(name = "created_at", nullable = false) 37 | private LocalDateTime createdAt; 38 | 39 | @Column(name = "updated_at") 40 | private LocalDateTime updatedAt; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/jobs/repository/JobListingRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.jobs.repository; 2 | 3 | import hng_java_boilerplate.jobs.entity.JobListing; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface JobListingRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/jobs/service/JobListingService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.jobs.service; 2 | 3 | import hng_java_boilerplate.jobs.entity.JobListing; 4 | 5 | import java.util.List; 6 | 7 | public interface JobListingService { 8 | JobListing createJobListing(JobListing jobListing); 9 | JobListing getJobListingById(Long id); 10 | List getAllJobListings(); 11 | JobListing updateJobListing(Long id, JobListing updatedJobListing); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/metrics/MetricsController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.metrics; 2 | 3 | import io.micrometer.prometheus.PrometheusMeterRegistry; 4 | import io.swagger.v3.oas.annotations.Hidden; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | @RequiredArgsConstructor 14 | @RequestMapping("/metrics") 15 | @Hidden 16 | public class MetricsController { 17 | 18 | private final PrometheusMeterRegistry prometheusMeterRegistry; 19 | 20 | @GetMapping(produces = MediaType.TEXT_PLAIN_VALUE) 21 | public ResponseEntity getMetrics() { 22 | String metrics = prometheusMeterRegistry.scrape(); 23 | return ResponseEntity.ok(metrics); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.newsletter.dto; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class DeleteRequest { 9 | private String user_id; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/newsletter/dto/SubscribeRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.newsletter.dto; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class SubscribeRequest { 9 | @NotBlank(message = "email is required") 10 | @Email(message = "email must be valid") 11 | private String email; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/newsletter/dto/SubscribeResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.newsletter.dto; 2 | 3 | public record SubscribeResponse(int status_code, String message) { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/newsletter/dto/SubscribersDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.newsletter.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Getter 11 | @Setter 12 | @Builder 13 | @AllArgsConstructor 14 | public class SubscribersDto { 15 | private String id; 16 | private String email; 17 | private LocalDateTime subscribedAt; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/newsletter/dto/SubscribersResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.newsletter.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.List; 9 | 10 | @Getter 11 | @Setter 12 | @Builder 13 | @AllArgsConstructor 14 | public class SubscribersResponse { 15 | private List subscribers; 16 | private int page; 17 | private int size; 18 | private long totalElements; 19 | private int totalPages; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.newsletter.entity; 2 | 3 | import hng_java_boilerplate.user.entity.User; 4 | import jakarta.persistence.*; 5 | import lombok.*; 6 | import org.hibernate.annotations.CreationTimestamp; 7 | import org.hibernate.annotations.UpdateTimestamp; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | @Getter 12 | @Setter 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Builder 16 | @Entity 17 | @Table(name = "newsletters") 18 | public class Newsletter { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.UUID) 22 | private String id; 23 | 24 | @ManyToOne 25 | @JoinColumn(name = "user_id") 26 | private User user; 27 | 28 | @Column(nullable = false) 29 | private String title; 30 | 31 | @Column(nullable = false) 32 | private String content; 33 | 34 | @CreationTimestamp 35 | @Column(name = "created_at",nullable = false) 36 | private LocalDateTime createdAt; 37 | 38 | @Column(name = "updated_at") 39 | @UpdateTimestamp 40 | private LocalDateTime updatedAt; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.newsletter.repository; 2 | 3 | 4 | import hng_java_boilerplate.newsletter.entity.Newsletter; 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.jpa.repository.JpaRepository; 8 | import org.springframework.data.jpa.repository.Query; 9 | import org.springframework.data.repository.query.Param; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | public interface NewsletterRepository extends JpaRepository { 14 | 15 | Page findByUser_Id(String userId, Pageable page); 16 | 17 | @Query("SELECT n FROM Newsletter n WHERE n.createdAt > :date") 18 | Page findNewsletterByCreatedAtAfter(@Param("date") LocalDateTime date, Pageable page); 19 | 20 | void deleteByUser_Id(String userId); 21 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/request/MarkRead.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.request; 2 | 3 | import jakarta.validation.constraints.NotNull; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class MarkRead { 8 | @NotNull(message = "is_read is required and should be a boolean") 9 | private boolean is_read; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/request/NotificationRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.request; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class NotificationRequest { 8 | @NotBlank(message = "message is required") 9 | private String message; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/request/NotificationSettingsRequestDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class NotificationSettingsRequestDTO { 8 | @JsonProperty("mobile_push_notifications") 9 | private boolean mobilePushNotifications; 10 | 11 | @JsonProperty("email_notification_activity_in_workspace") 12 | private boolean emailNotificationActivityInWorkspace; 13 | 14 | @JsonProperty("email_notification_always_send_email_notifications") 15 | private boolean emailNotificationAlwaysSendEmailNotifications; 16 | 17 | @JsonProperty("email_notification_email_digest") 18 | private boolean emailNotificationEmailDigest; 19 | 20 | @JsonProperty("email_notification_announcement_and_update_emails") 21 | private boolean emailNotificationAnnouncementAndUpdateEmails; 22 | 23 | @JsonProperty("slack_notifications_activity_on_your_workspace") 24 | private boolean slackNotificationsActivityOnYourWorkspace; 25 | 26 | @JsonProperty("slack_notifications_always_send_email_notifications") 27 | private boolean slackNotificationsAlwaysSendEmailNotifications; 28 | 29 | @JsonProperty("slack_notifications_announcement_and_update_emails") 30 | private boolean slackNotificationsAnnouncementAndUpdateEmails; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/response/ApiResponseDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.response; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ApiResponseDTO { 7 | private String status; 8 | private String message; 9 | private int statusCode; 10 | private T data; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/response/NotificationData.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.response; 2 | 3 | import lombok.Builder; 4 | 5 | import java.util.List; 6 | 7 | @Builder 8 | public record NotificationData( 9 | int total_notification_count, 10 | int total_unread_notification_count, 11 | List notifications) {} 12 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/response/NotificationDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.response; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.util.UUID; 7 | 8 | @Data 9 | @Builder 10 | public class NotificationDto { 11 | private UUID notification_id; 12 | private boolean is_read; 13 | private String message; 14 | private String created_at; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/response/NotificationDtoRes.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.response; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public record NotificationDtoRes(String status, String message, int status_code, NotificationDto data) { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/response/NotificationResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.response; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public record NotificationResponse(String status, String message, int status_code, NotificationData data) { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/dto/response/NotificationSettingsResponseDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class NotificationSettingsResponseDTO { 8 | @JsonProperty("mobile_push_notifications") 9 | private boolean mobilePushNotifications; 10 | 11 | @JsonProperty("email_notification_activity_in_workspace") 12 | private boolean emailNotificationActivityInWorkspace; 13 | 14 | @JsonProperty("email_notification_always_send_email_notifications") 15 | private boolean emailNotificationAlwaysSendEmailNotifications; 16 | 17 | @JsonProperty("email_notification_email_digest") 18 | private boolean emailNotificationEmailDigest; 19 | 20 | @JsonProperty("email_notification_announcement_and_update_emails") 21 | private boolean emailNotificationAnnouncementAndUpdateEmails; 22 | 23 | @JsonProperty("slack_notifications_activity_on_your_workspace") 24 | private boolean slackNotificationsActivityOnYourWorkspace; 25 | 26 | @JsonProperty("slack_notifications_always_send_email_notifications") 27 | private boolean slackNotificationsAlwaysSendEmailNotifications; 28 | 29 | @JsonProperty("slack_notifications_announcement_and_update_emails") 30 | private boolean slackNotificationsAnnouncementAndUpdateEmails; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/models/Notification.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import jakarta.persistence.*; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | 10 | import java.time.LocalDateTime; 11 | import java.util.UUID; 12 | 13 | @AllArgsConstructor 14 | @Getter 15 | @Entity 16 | @Setter 17 | @NoArgsConstructor 18 | @Table(name = "notifications") 19 | public class Notification { 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.UUID) 22 | private UUID notificationId; 23 | 24 | private String userId; 25 | private String message; 26 | private Boolean isRead; 27 | private LocalDateTime createdAt; 28 | 29 | @JsonIgnore 30 | @OneToOne(cascade = CascadeType.ALL) 31 | @JoinColumn(name = "notification_settings_id", referencedColumnName = "id") 32 | private NotificationSettings notificationSettings; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/models/NotificationSettings.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.models; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Getter 12 | @Setter 13 | @Entity 14 | @Table(name = "notification_settings") 15 | public class NotificationSettings { 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private Long id; 19 | 20 | private String userId; 21 | 22 | private Boolean mobilePushNotifications = false; 23 | private Boolean emailNotificationActivityInWorkspace = false; 24 | private Boolean emailNotificationAlwaysSendEmailNotifications = false; 25 | private Boolean emailNotificationEmailDigest = false; 26 | private Boolean emailNotificationAnnouncementAndUpdateEmails = false; 27 | private Boolean slackNotificationsActivityOnYourWorkspace = false; 28 | private Boolean slackNotificationsAlwaysSendEmailNotifications = false; 29 | private Boolean slackNotificationsAnnouncementAndUpdateEmails = false; 30 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/repositories/NotificationRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.repositories; 2 | 3 | import hng_java_boilerplate.notification.models.Notification; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | public interface NotificationRepository extends JpaRepository { 10 | List findByUserIdAndIsRead(String userId, Boolean isRead); 11 | 12 | int countByIsRead(boolean b); 13 | List findByIsRead(Boolean isRead); 14 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/notification/repositories/NotificationSettingsRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.notification.repositories; 2 | 3 | import hng_java_boilerplate.notification.models.NotificationSettings; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | public interface NotificationSettingsRepository extends JpaRepository { 8 | NotificationSettings findByUserId(String userId); 9 | @Query("SELECT ns FROM NotificationSettings ns") // Assuming there's only one record 10 | NotificationSettings findFirst(); 11 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/dto/AddUserRequestDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.dto; 2 | 3 | import jakarta.validation.constraints.NotEmpty; 4 | import jakarta.validation.constraints.Size; 5 | import lombok.Builder; 6 | 7 | import java.util.List; 8 | 9 | @Builder 10 | public record AddUserRequestDTO( 11 | @NotEmpty(message = "User ID cannot be empty") 12 | @Size(min = 1, message = "Provide a valid user ID") 13 | List user_ids 14 | ) { 15 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/dto/AddUserResponseDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.dto; 2 | 3 | import hng_java_boilerplate.organisation.interfaces.AddUserResponse; 4 | import lombok.Builder; 5 | 6 | import java.util.List; 7 | 8 | @Builder 9 | public record AddUserResponseDTO( 10 | String status, 11 | String message, 12 | String organization_id, 13 | List users_added_to_organisation, 14 | Integer status_code 15 | ) implements AddUserResponse { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/dto/CreateOrganisationRequestDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.dto; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | import lombok.Builder; 6 | 7 | @Builder 8 | public record CreateOrganisationRequestDto ( 9 | @NotBlank(message = "Organisation Name is required") 10 | String name, 11 | 12 | @NotBlank(message = "Organisation Description is required") 13 | String description, 14 | 15 | @Email(message = "Organisation Email is not properly formatted") 16 | @NotBlank(message = "Organisation Email is required") 17 | String email, 18 | 19 | @NotBlank(message = "Organisation Industry is required") 20 | String industry, 21 | 22 | @NotBlank(message = "Organisation Type is required") 23 | String type, 24 | 25 | @NotBlank(message = "Organisation Country is required") 26 | String country, 27 | 28 | @NotBlank(message = "Organisation Address is required") 29 | String address, 30 | 31 | @NotBlank(message = "Organisation State is required") 32 | String state 33 | ){ 34 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/dto/CreateOrganisationResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.dto; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public record CreateOrganisationResponseDto ( 7 | String status, 8 | String message, 9 | DataDto data, 10 | Integer status_code 11 | ) { 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/dto/DataDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.dto; 2 | 3 | import lombok.Builder; 4 | 5 | import java.time.LocalDateTime; 6 | 7 | @Builder 8 | public record DataDto( 9 | String id, 10 | String name, 11 | String description, 12 | String owner_id, 13 | String slug, 14 | String email, 15 | String industry, 16 | String type, 17 | String country, 18 | String address, 19 | String state, 20 | LocalDateTime created_at, 21 | LocalDateTime updated_at 22 | ) { 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/exception/AuthErrorResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.exception; 2 | 3 | public record AuthErrorResponse ( 4 | String status, 5 | String message, 6 | Integer status_code 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/exception/ExceptionResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.exception; 2 | 3 | import java.util.List; 4 | 5 | public record ExceptionResponse( 6 | List errors 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/exception/OrganisationNameAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.exception; 2 | 3 | public class OrganisationNameAlreadyExistsException extends RuntimeException { 4 | public OrganisationNameAlreadyExistsException(String msg){ 5 | super(msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/exception/ValidationError.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.exception; 2 | 3 | public record ValidationError( 4 | String field, 5 | String message 6 | ) { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/interfaces/AddUserResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.interfaces; 2 | 3 | public interface AddUserResponse { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/organisation/repository/OrganisationRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.organisation.repository; 2 | 3 | import hng_java_boilerplate.organisation.entity.Organisation; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface OrganisationRepository extends JpaRepository { 9 | Optional findByName(String name); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/payment/dtos/PaymentRequestBody.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.payment.dtos; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record PaymentRequestBody( 7 | @NotBlank(message = "Interval can not be blank") 8 | String interval, 9 | 10 | @JsonProperty("plan_id") 11 | @NotBlank(message = "Plan id can not be blank") 12 | String planId 13 | ) { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/payment/dtos/SessionResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.payment.dtos; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 5 | 6 | import java.util.Map; 7 | 8 | @JsonPropertyOrder({"status_code", "message", "data"}) 9 | public record SessionResponse ( 10 | 11 | String message, 12 | 13 | @JsonProperty("status_code") 14 | int StatusCode, 15 | 16 | Map data 17 | ){ 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/payment/entity/Payment.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.payment.entity; 2 | 3 | 4 | import hng_java_boilerplate.payment.enums.PaymentStatus; 5 | import hng_java_boilerplate.user.entity.User; 6 | import jakarta.persistence.*; 7 | import lombok.*; 8 | import org.hibernate.annotations.CreationTimestamp; 9 | import org.hibernate.annotations.UpdateTimestamp; 10 | 11 | import java.time.LocalDateTime; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | @Setter 16 | @Getter 17 | @Entity 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @Builder() 21 | @Table(name = "payments") 22 | public class Payment { 23 | 24 | @Id 25 | @GeneratedValue(strategy = GenerationType.UUID) 26 | private String id; 27 | 28 | @Column(nullable = false, name = "amount") 29 | private Double amount; 30 | 31 | @ManyToMany(fetch = FetchType.LAZY) 32 | @JoinTable( 33 | name = "user_payments", 34 | joinColumns = @JoinColumn(name = "payment_id"), 35 | inverseJoinColumns = @JoinColumn(name = "user_id") 36 | ) 37 | private Set user = new HashSet<>(); 38 | 39 | @Enumerated(EnumType.STRING) 40 | private PaymentStatus status; 41 | 42 | @CreationTimestamp 43 | @Column(name = "created_at", updatable = false) 44 | private LocalDateTime createdAt; 45 | 46 | @UpdateTimestamp 47 | @Column(name = "updated_at") 48 | private LocalDateTime updatedAt; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/payment/enums/PaymentStatus.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.payment.enums; 2 | 3 | public enum PaymentStatus { 4 | SUCCESS, FAILED, PENDING, CANCELLED 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/payment/repository/PaymentRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.payment.repository; 2 | 3 | import hng_java_boilerplate.payment.entity.Payment; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | 8 | @Repository 9 | public interface PaymentRepository extends JpaRepository { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/payment/utils/CustomerUtils.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.payment.utils; 2 | 3 | import com.stripe.exception.StripeException; 4 | import com.stripe.model.Customer; 5 | import com.stripe.model.CustomerSearchResult; 6 | import com.stripe.net.RequestOptions; 7 | import com.stripe.param.CustomerCreateParams; 8 | import com.stripe.param.CustomerSearchParams; 9 | 10 | import java.util.UUID; 11 | 12 | public class CustomerUtils { 13 | public static Customer findOrCreateCustomer(String email, String name) throws StripeException { 14 | CustomerSearchParams params = 15 | CustomerSearchParams 16 | .builder() 17 | .setQuery("email:'" + email + "'") 18 | .build(); 19 | 20 | CustomerSearchResult result = Customer.search(params); 21 | Customer customer; 22 | 23 | if (result.getData().isEmpty()) { 24 | CustomerCreateParams customerCreateParams = CustomerCreateParams.builder() 25 | .setName(name) 26 | .setEmail(email) 27 | .build(); 28 | RequestOptions options = new RequestOptions.RequestOptionsBuilder().setIdempotencyKey(UUID.randomUUID().toString()).build(); 29 | customer = Customer.create(customerCreateParams, options); 30 | } else { 31 | customer = result.getData().get(0); 32 | } 33 | return customer; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/controller/PlanController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.controller; 2 | 3 | import hng_java_boilerplate.plans.dtos.CreatePlanDto; 4 | import hng_java_boilerplate.plans.dtos.PlanResponse; 5 | import hng_java_boilerplate.plans.entity.Plan; 6 | import hng_java_boilerplate.plans.service.PlanService; 7 | import io.swagger.v3.oas.annotations.tags.Tag; 8 | import jakarta.validation.Valid; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.List; 15 | 16 | @RestController 17 | @CrossOrigin 18 | @RequestMapping("api/v1/payment/plans") 19 | @RequiredArgsConstructor 20 | @Tag(name = "Plans") 21 | public class PlanController { 22 | 23 | private final PlanService planService; 24 | 25 | @PostMapping 26 | @PreAuthorize("hasRole('ROLE_ADMIN')") 27 | public ResponseEntity createPlan(@RequestBody @Valid CreatePlanDto createPlanDto) { 28 | return planService.create(createPlanDto); 29 | } 30 | 31 | @GetMapping 32 | public ResponseEntity> getAll() { 33 | return planService.findAll(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/dtos/CreatePlanDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.dtos; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import hng_java_boilerplate.plans.util.ValidList; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotNull; 8 | 9 | import java.util.List; 10 | 11 | public record CreatePlanDto( 12 | @NotBlank(message = "Name can not be blank") 13 | String name, 14 | @NotBlank(message = "Description can not be blank") 15 | String description, 16 | @Min(value = 1, message = "Price must be up to 1") 17 | double price, 18 | @Min(value = 1, message = "Duration must be up to 1") 19 | int duration, 20 | @JsonProperty("duration_unit") 21 | @NotBlank(message = "Duration unit can not be blank") 22 | String durationUnit, 23 | @ValidList 24 | @NotNull 25 | List features 26 | ) { 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/dtos/PlanResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.dtos; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import hng_java_boilerplate.plans.entity.Plan; 5 | 6 | public record PlanResponse( 7 | Plan data, 8 | @JsonProperty("status_code") 9 | int statusCode, 10 | String message 11 | ) { 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/entity/Plan.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.entity; 2 | 3 | import hng_java_boilerplate.plans.util.StringListConverter; 4 | import jakarta.persistence.*; 5 | import lombok.*; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | 11 | @Setter 12 | @Getter 13 | @Entity 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Builder() 17 | @Table(name = "plans") 18 | public class Plan { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.UUID) 22 | private String id; 23 | 24 | @Column(unique = true, nullable = false) 25 | private String name; 26 | 27 | @Column(nullable = false) 28 | private String description; 29 | 30 | @Column(nullable = false) 31 | private Double price; 32 | 33 | @Convert(converter = StringListConverter.class) 34 | @Column(name = "features", nullable = false) 35 | private List features = new ArrayList<>(); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/repository/PlanRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.repository; 2 | 3 | import hng_java_boilerplate.plans.entity.Plan; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface PlanRepository extends JpaRepository { 7 | boolean existsByName(String name); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/service/PlanService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.service; 2 | 3 | import hng_java_boilerplate.plans.dtos.CreatePlanDto; 4 | import hng_java_boilerplate.plans.dtos.PlanResponse; 5 | import hng_java_boilerplate.plans.entity.Plan; 6 | import org.springframework.http.ResponseEntity; 7 | 8 | import java.util.List; 9 | 10 | 11 | public interface PlanService { 12 | ResponseEntity create(CreatePlanDto createPlanDto); 13 | 14 | ResponseEntity> findAll(); 15 | 16 | Plan findOne(String id); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/util/ListOfStringsValidator.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.util; 2 | 3 | import jakarta.validation.ConstraintValidator; 4 | import jakarta.validation.ConstraintValidatorContext; 5 | 6 | import java.util.List; 7 | 8 | public class ListOfStringsValidator implements ConstraintValidator> { 9 | @Override 10 | public boolean isValid(List value, ConstraintValidatorContext context) { 11 | if (value == null) { 12 | return true; 13 | } 14 | for (String str : value) { 15 | if (str == null || str.trim().isEmpty()) { 16 | return false; 17 | } 18 | } 19 | return true; 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/util/StringListConverter.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.util; 2 | 3 | import jakarta.persistence.AttributeConverter; 4 | import jakarta.persistence.Converter; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static java.util.Collections.emptyList; 10 | 11 | @Converter 12 | public class StringListConverter implements AttributeConverter, String> { 13 | private static final String SPLIT_CHAR = ";"; 14 | 15 | @Override 16 | public String convertToDatabaseColumn(List stringList) { 17 | return stringList != null ? String.join(SPLIT_CHAR, stringList) : ""; 18 | } 19 | 20 | @Override 21 | public List convertToEntityAttribute(String string) { 22 | return string != null ? Arrays.asList(string.split(SPLIT_CHAR)) : emptyList(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/plans/util/ValidList.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.plans.util; 2 | 3 | import jakarta.validation.Constraint; 4 | import jakarta.validation.Payload; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Constraint(validatedBy = {ListOfStringsValidator.class}) 14 | public @interface ValidList { 15 | String message() default "Invalid list of strings"; 16 | 17 | Class[] groups() default {}; 18 | 19 | Class[] payload() default {}; 20 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/privacy_policy/dto/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.privacy_policy.dto; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class ApiResponse { 9 | private String status; 10 | private String message; 11 | private Object data; 12 | 13 | public ApiResponse(String status, String message, Object data) { 14 | this.status = status; 15 | this.message = message; 16 | this.data = data; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/privacy_policy/entity/PrivacyPolicy.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.privacy_policy.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.UUID; 9 | 10 | @Entity 11 | @Getter 12 | @Setter 13 | @Table(name = "privacy_policies") 14 | public class PrivacyPolicy { 15 | 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.AUTO) 18 | private UUID id; 19 | 20 | @Column(nullable = false) 21 | private String content; 22 | 23 | @Column(name = "created_at", nullable = false, updatable = false) 24 | private LocalDateTime createdAt; 25 | 26 | @Column(name = "updated_at") 27 | private LocalDateTime updatedAt; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/privacy_policy/repository/PrivacyPolicyRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.privacy_policy.repository; 2 | 3 | import hng_java_boilerplate.privacy_policy.entity.PrivacyPolicy; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.UUID; 7 | 8 | public interface PrivacyPolicyRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/privacy_policy/service/PrivacyPolicyService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.privacy_policy.service; 2 | 3 | import hng_java_boilerplate.privacy_policy.entity.PrivacyPolicy; 4 | import jakarta.transaction.Transactional; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.util.List; 8 | import java.util.UUID; 9 | 10 | @Service 11 | public interface PrivacyPolicyService { 12 | 13 | PrivacyPolicy createPrivacyPolicy(PrivacyPolicy newPolicy); 14 | 15 | List getAllPrivacyPolicies(); 16 | 17 | PrivacyPolicy getPrivacyPolicyById(UUID id); 18 | 19 | PrivacyPolicy updatePrivacyPolicy(UUID id, PrivacyPolicy updatedPolicy); 20 | 21 | @Transactional 22 | void deletePrivacyPolicy(UUID id); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/dto/ErrorDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Data 12 | public class ErrorDTO { 13 | private String parameter; 14 | private String message; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/dto/GetProductsDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.dto; 2 | 3 | import lombok.Builder; 4 | 5 | import java.util.List; 6 | 7 | @Builder 8 | public record GetProductsDTO(int status_code, String status, List data) { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/dto/ProductCountDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.dto; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public record ProductCountDto(int status_code, String status, CountData data) { 7 | 8 | @Builder 9 | public record CountData(int count) {} 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/dto/ProductDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import jakarta.validation.constraints.NotNull; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.sql.Timestamp; 11 | import java.time.LocalDateTime; 12 | 13 | @Data 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class ProductDTO { 18 | 19 | private String id; 20 | @NotNull(message = "Name is required") 21 | @NotBlank(message = "Name is required") 22 | private String name; 23 | 24 | @NotNull(message = "Name is required") 25 | @NotBlank(message = "Name is required") 26 | private String description; 27 | 28 | @NotNull(message = "Name is required") 29 | @NotBlank(message = "Name is required") 30 | private String category; 31 | 32 | @NotNull(message = "Name is required") 33 | @NotBlank(message = "Name is required") 34 | private Double price; 35 | 36 | @NotNull(message = "Name is required") 37 | @NotBlank(message = "Name is required") 38 | private String image_url; 39 | 40 | private LocalDateTime created_at; 41 | private LocalDateTime updated_at; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/dto/ProductErrorDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Data 12 | public class ProductErrorDTO { 13 | private boolean success; 14 | private String message; 15 | private int status_code; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/dto/ProductSearchDTO.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.dto; 2 | 3 | import hng_java_boilerplate.product.entity.Product; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ProductSearchDTO { 14 | private boolean success; 15 | private int status_code; 16 | private List products; 17 | private int total; 18 | private int page; 19 | private int limit; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/entity/Product.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import hng_java_boilerplate.organisation.entity.Organisation; 5 | import hng_java_boilerplate.user.entity.User; 6 | import jakarta.persistence.*; 7 | import lombok.*; 8 | import org.hibernate.annotations.CreationTimestamp; 9 | import org.hibernate.annotations.UpdateTimestamp; 10 | 11 | import java.time.LocalDateTime; 12 | import java.util.UUID; 13 | 14 | @Entity 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @EqualsAndHashCode(callSuper = false) 18 | @Data 19 | @Table(name = "products") 20 | @Builder 21 | public class Product { 22 | @Id 23 | private String id; 24 | 25 | private String name; 26 | private String description; 27 | private String category; 28 | private double price; 29 | @Column(name = "image_url") 30 | private String imageUrl; 31 | 32 | @ManyToOne 33 | @JsonIgnore 34 | @JoinColumn(name = "user_id", nullable = false) 35 | private User user; 36 | 37 | @ManyToOne 38 | @JsonIgnore 39 | @JoinColumn(name = "org_id", nullable = false) 40 | private Organisation organisation; 41 | 42 | @CreationTimestamp 43 | @Column(name = "created_at", updatable = false) 44 | private LocalDateTime createdAt; 45 | 46 | @UpdateTimestamp 47 | @Column(name = "updated_at") 48 | private LocalDateTime updatedAt; 49 | 50 | @PrePersist 51 | public void prePersist() { 52 | if (this.id == null) { 53 | this.id = UUID.randomUUID().toString(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/product_mapper/ProductMapper.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.product_mapper; 2 | 3 | import hng_java_boilerplate.product.dto.ProductDTO; 4 | import hng_java_boilerplate.product.entity.Product; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.Mapping; 7 | import org.mapstruct.factory.Mappers; 8 | import org.springframework.data.domain.Page; 9 | 10 | import java.util.List; 11 | 12 | @Mapper 13 | public interface ProductMapper { 14 | ProductMapper INSTANCE = Mappers.getMapper(ProductMapper.class); 15 | 16 | @Mapping(source = "id", target = "id") 17 | @Mapping(source = "name", target = "name") 18 | @Mapping(source = "category", target = "category") 19 | @Mapping(source = "description", target = "description") 20 | @Mapping(source = "imageUrl", target = "image_url") 21 | ProductDTO toDTO(Product product); 22 | 23 | default Page toDTOList(Page products) { 24 | return products.map(this::toDTO); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/repository/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.repository; 2 | 3 | import hng_java_boilerplate.product.entity.Product; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.query.Param; 9 | import java.util.List; 10 | 11 | public interface ProductRepository extends JpaRepository { 12 | 13 | @Query("SELECT p FROM Product p WHERE " + 14 | "(:name IS NULL OR p.name ILIKE %:name%) AND " + 15 | "(:category IS NULL OR p.category ILIKE %:category%) AND " + 16 | "(:minPrice IS NULL OR p.price >= :minPrice) AND " + 17 | "(:maxPrice IS NULL OR p.price <= :maxPrice)") 18 | Page searchProducts(@Param("name") String name, 19 | @Param("category") String category, 20 | @Param("minPrice") Double minPrice, 21 | @Param("maxPrice") Double maxPrice, 22 | Pageable pageable); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/product/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product.service; 2 | 3 | import hng_java_boilerplate.organisation.entity.Organisation; 4 | import hng_java_boilerplate.product.dto.GetProductsDTO; 5 | import hng_java_boilerplate.product.dto.ProductCountDto; 6 | import hng_java_boilerplate.product.dto.ProductDTO; 7 | import hng_java_boilerplate.product.entity.Product; 8 | import hng_java_boilerplate.user.entity.User; 9 | import org.springframework.data.domain.Page; 10 | import org.springframework.data.domain.Pageable; 11 | 12 | import java.util.List; 13 | 14 | public interface ProductService { 15 | 16 | Page productsSearch(String name, String category, Double minPrice, Double maxPrice, Pageable pageable); 17 | ProductCountDto getProductsCount(); 18 | GetProductsDTO getProducts(); 19 | ProductDTO getProductById(String productId); 20 | ProductDTO createProduct(User user, Organisation orgId, ProductDTO productDTO); 21 | void deleteProduct(Product product); 22 | ProductDTO editProduct(Product product, ProductDTO productDTO); 23 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/request/DeactivateUserRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.request; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Data 8 | @Builder 9 | public class DeactivateUserRequest { 10 | private String reason; 11 | @NotBlank(message = "Deactivation confirmation is required") 12 | private String confirmation; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/request/UpdateUserProfileDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.request; 2 | import com.fasterxml.jackson.annotation.JsonProperty; 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class UpdateUserProfileDto { 13 | 14 | @JsonProperty("first_name") 15 | private String firstName; 16 | 17 | @JsonProperty("last_name") 18 | private String lastName; 19 | 20 | @JsonProperty("pronouns") 21 | private String pronouns; 22 | 23 | @JsonProperty("job_title") 24 | private String jobTitle; 25 | 26 | @JsonProperty("department") 27 | private String department; 28 | 29 | @JsonProperty("social") 30 | private String social; 31 | 32 | @JsonProperty("bio") 33 | private String bio; 34 | 35 | @JsonProperty("phone_number") 36 | private String phoneNumber; 37 | 38 | @JsonProperty("avatar_url") 39 | private String avatarUrl; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/response/DeactivateUserResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.response; 2 | 3 | public record DeactivateUserResponse(int status_code, String message) {} 4 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/response/ProfileDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.response; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class ProfileDto { 9 | private String id; 10 | private String first_name; 11 | private String last_name; 12 | private String phone_number; 13 | private String avatar_url; 14 | private String pronouns; 15 | private String job_title; 16 | private String department; 17 | private String social; 18 | private String bio; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/response/ProfileErrorResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.response; 2 | import com.fasterxml.jackson.annotation.JsonProperty; 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class ProfileErrorResponseDto { 13 | 14 | @JsonProperty("message") 15 | private String message; 16 | 17 | @JsonProperty("error") 18 | private String error; 19 | 20 | @JsonProperty("status_code") 21 | private int statusCode; 22 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/response/ProfilePictureResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | 9 | public class ProfilePictureResponse { 10 | private boolean success; 11 | private String message; 12 | private String imageUrl; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/response/ProfileResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.response; 2 | 3 | import hng_java_boilerplate.profile.entity.Profile; 4 | 5 | public record ProfileResponse(int status_code, String message, ProfileDto data) { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/dto/response/ProfileUpdateResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import hng_java_boilerplate.profile.entity.Profile; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @Data 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class ProfileUpdateResponseDto { 15 | @JsonProperty("message") 16 | private String message; 17 | 18 | @JsonProperty("status_code") 19 | private int statusCode; 20 | 21 | @JsonProperty("data") 22 | private Profile data; 23 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/repository/ProfileRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.repository; 2 | 3 | import hng_java_boilerplate.profile.entity.Profile; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface ProfileRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/profile/service/ProfileService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.profile.service; 2 | 3 | import hng_java_boilerplate.profile.dto.request.DeactivateUserRequest; 4 | import hng_java_boilerplate.profile.dto.request.UpdateUserProfileDto; 5 | import hng_java_boilerplate.profile.dto.response.DeactivateUserResponse; 6 | import hng_java_boilerplate.profile.dto.response.ProfileResponse; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.web.multipart.MultipartFile; 10 | 11 | import java.io.IOException; 12 | import java.util.Optional; 13 | 14 | public interface ProfileService { 15 | DeactivateUserResponse deactivateUser(DeactivateUserRequest request); 16 | Optional updateUserProfile(String userId, UpdateUserProfileDto updateUserProfileDto); 17 | ProfileResponse getUserProfile(String userId); 18 | ResponseEntity uploadProfileImage(MultipartFile file) throws IOException; 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/region/dto/request/CreateRegion.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.region.dto.request; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Data 8 | @Builder 9 | public class CreateRegion { 10 | @NotBlank(message = "region is required") 11 | private String region; 12 | @NotBlank(message = "language is required") 13 | private String language; 14 | @NotBlank(message = "timezone is required") 15 | private String timezone; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/region/dto/request/UpdateRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.region.dto.request; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Data 8 | @Builder 9 | public class UpdateRequest { 10 | @NotBlank(message = "region is required") 11 | private String region; 12 | @NotBlank(message = "language is required") 13 | private String language; 14 | @NotBlank(message = "timezone is required") 15 | private String timezone; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/region/dto/response/GetAllRegion.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.region.dto.response; 2 | 3 | import java.util.List; 4 | 5 | public record GetAllRegion(int status, String message, List data) { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/region/dto/response/GetResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.region.dto.response; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class GetResponse { 9 | private String id; 10 | private String user_id; 11 | private String region; 12 | private String language; 13 | private String timezone; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/region/entity/Region.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.region.entity; 2 | 3 | import hng_java_boilerplate.user.entity.User; 4 | import jakarta.persistence.*; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | 10 | import java.util.UUID; 11 | 12 | @Getter 13 | @Setter 14 | @Entity 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Table(name = "regions") 18 | public class Region { 19 | @Id 20 | private String id; 21 | @Column(nullable = false) 22 | private String region; 23 | @Column(nullable = false) 24 | private String language; 25 | @Column(nullable = false) 26 | private String timezone; 27 | @OneToOne(cascade = CascadeType.ALL) 28 | @JoinColumn(name = "user_id", referencedColumnName = "id") 29 | private User user; 30 | 31 | @PrePersist 32 | void prePersist() { 33 | if (id == null) { 34 | id = UUID.randomUUID().toString(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/region/repository/RegionRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.region.repository; 2 | 3 | import hng_java_boilerplate.region.entity.Region; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface RegionRepository extends JpaRepository { 9 | Optional findByUserId(String userId); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/region/service/RegionService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.region.service; 2 | 3 | import hng_java_boilerplate.region.dto.request.CreateRegion; 4 | import hng_java_boilerplate.region.dto.request.UpdateRequest; 5 | import hng_java_boilerplate.region.dto.response.GetAllRegion; 6 | import hng_java_boilerplate.region.dto.response.GetResponse; 7 | 8 | public interface RegionService { 9 | GetResponse getRegionByUserId(String userId); 10 | GetResponse createRegion(CreateRegion request); 11 | GetAllRegion getAllRegions(); 12 | GetResponse updateRegion(UpdateRequest request, String id); 13 | void deleteRegion(String regionId); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/resources/dto/ResourceRequestDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.resources.dto; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class ResourceRequestDto { 12 | 13 | private String id; 14 | private String title; 15 | private String description; 16 | private String image; 17 | private Boolean status; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/resources/dto/ResourceResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.resources.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import hng_java_boilerplate.resources.entity.Resources; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | 16 | public class ResourceResponseDto { 17 | 18 | private String message; 19 | private List data; 20 | private Resources resourceData; 21 | private Integer totalPages; 22 | private Integer currentPage; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/resources/entity/Resources.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.resources.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.annotations.CreationTimestamp; 9 | 10 | import java.sql.Date; 11 | 12 | @Entity 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @EqualsAndHashCode(callSuper = false) 16 | @Data 17 | @Table(name = "resources") 18 | public class Resources { 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.UUID) 21 | private String id; 22 | 23 | @Column(name = "title") 24 | private String title; 25 | 26 | @Column(name = "description") 27 | private String description; 28 | 29 | @Column(name = "image") 30 | private String image; 31 | 32 | @Column(nullable = false, updatable = false) 33 | @CreationTimestamp 34 | private Date date; 35 | 36 | @Column(name = "published") 37 | private Boolean published; 38 | 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/resources/repository/ResourceRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.resources.repository; 2 | 3 | import hng_java_boilerplate.resources.entity.Resources; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.query.Param; 9 | import org.springframework.stereotype.Repository; 10 | 11 | import java.util.List; 12 | 13 | @Repository 14 | public interface ResourceRepository extends JpaRepository { 15 | 16 | @Query("SELECT r FROM Resources r WHERE r.published = true AND " + 17 | "(:query IS NULL OR LOWER(r.title) LIKE LOWER(CONCAT('%', :query, '%')) " + 18 | "OR LOWER(r.description) LIKE LOWER(CONCAT('%', :query, '%')))") 19 | Page search(@Param("query") String query, Pageable pageable); 20 | 21 | @Query("SELECT r FROM Resources r WHERE r.published = true") 22 | Page searchAllPublishedArticles(Pageable pageable); 23 | @Query("SELECT r FROM Resources r WHERE :query IS NULL OR LOWER(r.title) " + 24 | "LIKE LOWER(CONCAT('%', :query, '%')) OR LOWER(r.description) LIKE LOWER(CONCAT('%', :query, '%'))") 25 | 26 | List searchAllResourcesForAdmin( 27 | @Param("query") String query); 28 | 29 | @Query("SELECT r FROM Resources r WHERE r.published = true") 30 | List getAllPublishedResources(); 31 | 32 | @Query("SELECT r FROM Resources r WHERE r.published = false") 33 | List getAllUnPublishedResources(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/resources/service/ResourceService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.resources.service; 2 | 3 | import hng_java_boilerplate.resources.dto.ResourceRequestDto; 4 | import hng_java_boilerplate.resources.dto.ResourceResponseDto; 5 | import org.springframework.data.domain.Pageable; 6 | 7 | public interface ResourceService { 8 | 9 | ResourceResponseDto findByTitleAndDescriptionForUser(String query, Pageable pageable); 10 | ResourceResponseDto getAllResources(Pageable pageable); 11 | ResourceResponseDto addResources(ResourceRequestDto resourceRequestDto); 12 | ResourceResponseDto deleteResources(String Id); 13 | ResourceResponseDto editResources(ResourceRequestDto resourceRequestDto); 14 | ResourceResponseDto getResourceById(String Id); 15 | ResourceResponseDto unpublishResource(String Id); 16 | ResourceResponseDto publishResource(String Id); 17 | ResourceResponseDto getAllPublishedResource(); 18 | ResourceResponseDto findByTitleAndDescriptionForAdmin(String query); 19 | ResourceResponseDto getAllUnPublishedResource(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/squeeze/dto/ResponseMessageDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.squeeze.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | @AllArgsConstructor 10 | public class ResponseMessageDto { 11 | private String message; 12 | private int status_code; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/squeeze/dto/ValidationErrorResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.squeeze.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.util.List; 8 | 9 | @Setter 10 | @Getter 11 | @AllArgsConstructor 12 | public class ValidationErrorResponseDto { 13 | private String message; 14 | private List errors; 15 | 16 | @Getter 17 | @Setter 18 | @AllArgsConstructor 19 | public static class FieldErrorDto { 20 | private String field; 21 | private String error; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/squeeze/entity/SqueezeConfig.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.squeeze.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.UUID; 11 | 12 | @Entity 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class SqueezeConfig { 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.UUID) 20 | private UUID id; 21 | 22 | @Column(nullable = false) 23 | private String pageTitle; 24 | 25 | @Column(nullable = false, unique = true) 26 | private String urlSlug; 27 | 28 | @Column(nullable = false) 29 | private String headlineText; 30 | 31 | @Column(nullable = false) 32 | private String subheadlineText; 33 | 34 | @Column(nullable = false, columnDefinition = "TEXT") 35 | private String bodyText; 36 | 37 | private boolean active; 38 | 39 | @Column(name = "created_date") 40 | private LocalDateTime createdDate; 41 | 42 | @PrePersist 43 | protected void onCreate() { 44 | createdDate = LocalDateTime.now(); 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/squeeze/entity/SqueezeRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.squeeze.entity; 2 | 3 | import hng_java_boilerplate.squeeze.util.StringListConverter; 4 | import jakarta.persistence.*; 5 | import jakarta.validation.constraints.Email; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotEmpty; 8 | import lombok.*; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | @Builder 14 | @Entity 15 | @Getter 16 | @Setter 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @Table(name = "squeeze_request") 20 | public class SqueezeRequest { 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.UUID) 23 | private String id; 24 | 25 | @Email 26 | @NotBlank 27 | @Column(unique = true) 28 | private String email; 29 | 30 | @NotBlank 31 | private String first_name; 32 | 33 | @NotBlank 34 | private String last_name; 35 | 36 | @NotBlank 37 | private String phone; 38 | 39 | @NotBlank 40 | private String location; 41 | 42 | @NotBlank 43 | private String job_title; 44 | 45 | @NotBlank 46 | private String company; 47 | 48 | @NotEmpty 49 | @Convert(converter = StringListConverter.class) 50 | private List interests = new ArrayList<>(); 51 | 52 | @NotBlank 53 | private String referral_source; 54 | 55 | private boolean updated = false; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/squeeze/repository/SqueezeConfigRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.squeeze.repository; 2 | 3 | import hng_java_boilerplate.squeeze.entity.SqueezeConfig; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.UUID; 9 | 10 | @Repository 11 | public interface SqueezeConfigRepository extends JpaRepository, JpaSpecificationExecutor { 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/squeeze/repository/SqueezeRequestRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.squeeze.repository; 2 | 3 | import hng_java_boilerplate.squeeze.entity.SqueezeRequest; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | import java.util.UUID; 8 | 9 | public interface SqueezeRequestRepository extends JpaRepository { 10 | boolean existsByEmail(String email); 11 | Optional findByEmail(String email); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/squeeze/util/StringListConverter.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.squeeze.util; 2 | 3 | import jakarta.persistence.AttributeConverter; 4 | import jakarta.persistence.Converter; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | @Converter 10 | public class StringListConverter implements AttributeConverter, String> { 11 | 12 | @Override 13 | public String convertToDatabaseColumn(List attribute) { 14 | if (attribute == null || attribute.isEmpty()) { 15 | return ""; 16 | } 17 | return attribute.stream().collect(Collectors.joining(",")); 18 | } 19 | 20 | @Override 21 | public List convertToEntityAttribute(String dbData) { 22 | if (dbData == null || dbData.isEmpty()) { 23 | return List.of(); 24 | } 25 | return Arrays.asList(dbData.split(",")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/statusPage/controller/StatusPageController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.statusPage.controller; 2 | 3 | import hng_java_boilerplate.statusPage.entity.StatusPage; 4 | import hng_java_boilerplate.statusPage.service.StatusPageService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.List; 12 | 13 | @RestController 14 | @RequiredArgsConstructor 15 | @RequestMapping("/api/v1/api-status") 16 | public class StatusPageController { 17 | 18 | private final StatusPageService statusPageService; 19 | 20 | @GetMapping 21 | public List getAllApiStatuses() { 22 | return statusPageService.getAllApiStatuses(); 23 | } 24 | 25 | @PostMapping 26 | public void updateApiStatus() { 27 | statusPageService.updateApiStatus(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/statusPage/entity/StatusPage.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.statusPage.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Entity 10 | @Getter 11 | @Setter 12 | @Table(name = "api_status") 13 | public class StatusPage { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | 18 | @Column(nullable = false) 19 | private String apiGroup; 20 | 21 | @Enumerated(EnumType.STRING) 22 | @Column(nullable = false) 23 | private Status status; 24 | 25 | @Column(nullable = false) 26 | private LocalDateTime lastChecked; 27 | 28 | private Integer responseTime; 29 | 30 | private String details; 31 | 32 | public enum Status { 33 | OPERATIONAL, DEGRADED, DOWN 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/statusPage/repository/StatusPageRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.statusPage.repository; 2 | 3 | import hng_java_boilerplate.statusPage.entity.StatusPage; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface StatusPageRepository extends JpaRepository { 7 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/statusPage/service/StatusPageService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.statusPage.service; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import hng_java_boilerplate.statusPage.entity.StatusPage; 5 | 6 | import java.util.List; 7 | 8 | public interface StatusPageService { 9 | void updateApiStatus(); 10 | void processJsonData(JsonNode jsonNode); 11 | List getAllApiStatuses(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/testimonials/dto/TestimonialDataDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.testimonials.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.time.LocalDate; 9 | 10 | @AllArgsConstructor 11 | @Getter 12 | @Setter 13 | public class TestimonialDataDto { 14 | @JsonProperty("user_id") 15 | private String user_id; 16 | private String name; 17 | private String content; 18 | @JsonProperty("created_at") 19 | private LocalDate created_at; 20 | @JsonProperty("updated_at") 21 | private LocalDate updated_at; 22 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/testimonials/dto/TestimonialRequestDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.testimonials.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import jakarta.validation.constraints.Size; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | public class TestimonialRequestDto { 11 | 12 | @NotBlank(message = "Name is required") 13 | @Size(max = 100, message = "Name must be at most 100 characters") 14 | private String name; 15 | 16 | @NotBlank(message = "Content is required") 17 | @Size(max = 1000, message = "Content must be at most 1000 characters") 18 | private String content; 19 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/testimonials/dto/TestimonialResponseDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.testimonials.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @AllArgsConstructor 8 | @Getter 9 | @Setter 10 | public class TestimonialResponseDto { 11 | private String status; 12 | private String message; 13 | private TestimonialDataDto data; 14 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/testimonials/dto/UpdateTestimonialRequestDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.testimonials.dto; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import jakarta.validation.constraints.Size; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | public class UpdateTestimonialRequestDto { 11 | 12 | @NotBlank(message = "Content is required") 13 | @Size(max = 1000, message = "Content must be at most 1000 characters") 14 | private String content; 15 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/testimonials/entity/Testimonial.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.testimonials.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.time.LocalDate; 10 | 11 | @Entity 12 | @Table(name = "testimonials") 13 | @Getter 14 | @Setter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class Testimonial { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.UUID) 21 | private String id; 22 | 23 | @Column(nullable = false) 24 | private String user_id; 25 | 26 | @Column(nullable = false) 27 | private String name; 28 | 29 | @Column(nullable = false, length = 1000) 30 | private String content; 31 | 32 | @Column(nullable = false) 33 | private LocalDate created_at; 34 | 35 | @Column(nullable = false) 36 | private LocalDate updated_at; 37 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/testimonials/repository/TestimonialRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.testimonials.repository; 2 | 3 | import hng_java_boilerplate.testimonials.entity.Testimonial; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface TestimonialRepository extends JpaRepository { 7 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twilio/CallLogs/Entity.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twilio.CallLogs; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | @jakarta.persistence.Entity 12 | @Table(name = "call_logs") 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Builder 17 | public class Entity { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | 23 | private String toNumber; 24 | private String fromNumber; 25 | private String callSid; 26 | private LocalDateTime timestamp; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twilio/Controller/TwillioCallController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twilio.Controller; 2 | 3 | import com.twilio.exception.TwilioException; 4 | import hng_java_boilerplate.comment.dto.ErrorResponse; 5 | import hng_java_boilerplate.exception.BadRequestException; 6 | import hng_java_boilerplate.exception.CustomError; 7 | import hng_java_boilerplate.exception.NotFoundException; 8 | import hng_java_boilerplate.twilio.RequestAndResponse.CallRequest; 9 | import hng_java_boilerplate.twilio.RequestAndResponse.CallResponse; 10 | import hng_java_boilerplate.twilio.Service.TwilioCallService; 11 | import lombok.RequiredArgsConstructor; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | @RestController 20 | @RequestMapping("/api") 21 | @RequiredArgsConstructor 22 | public class TwillioCallController { 23 | 24 | private final TwilioCallService twilioCallService; 25 | 26 | @PostMapping("/call") 27 | public CallResponse makecall(@RequestBody CallRequest callRequest) { 28 | if (callRequest == null || callRequest.getToNumber() == null || callRequest.getFromNumber() == null) { 29 | throw new BadRequestException("Invalid request: Phone number is required"); 30 | } 31 | 32 | CallResponse response = twilioCallService.makeCall(callRequest); 33 | 34 | if (response == null) { 35 | throw new NotFoundException("Twilio service error: Service not found/available"); 36 | } 37 | return response; 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twilio/Repository/TwilioCallRepo.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twilio.Repository; 2 | 3 | import hng_java_boilerplate.twilio.CallLogs.Entity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface TwilioCallRepo extends JpaRepository { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twilio/RequestAndResponse/CallRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twilio.RequestAndResponse; 2 | 3 | public class CallRequest { 4 | private String toNumber; 5 | private String fromNumber; 6 | private String messageUrl; 7 | 8 | public String getFromNumber() { 9 | return fromNumber; 10 | } 11 | 12 | public void setFromNumber(String fromNumber) { 13 | this.fromNumber = fromNumber; 14 | } 15 | 16 | public String getToNumber() { 17 | return toNumber; 18 | } 19 | 20 | public void setToNumber(String toNumber) { 21 | this.toNumber = toNumber; 22 | } 23 | 24 | public String getMessageUrl() { 25 | return messageUrl; 26 | } 27 | 28 | public void setMessageUrl(String messageUrl) { 29 | this.messageUrl = messageUrl; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twilio/RequestAndResponse/CallResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twilio.RequestAndResponse; 2 | 3 | public class CallResponse { 4 | private String message; 5 | private String callSid; 6 | 7 | public CallResponse(String message, String callSid){ 8 | this.message = message; 9 | this.callSid = callSid; 10 | } 11 | 12 | public String getMessage() { 13 | return message; 14 | } 15 | 16 | public void setMessage(String message) { 17 | this.message = message; 18 | } 19 | 20 | public String getCallSid() { 21 | return callSid; 22 | } 23 | 24 | public void setCallSid(String callSid) { 25 | this.callSid = callSid; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twofactor/controller/TwoFactorController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twofactor.controller; 2 | 3 | import dev.samstevens.totp.exceptions.QrGenerationException; 4 | import hng_java_boilerplate.twofactor.dtos.EnableTwoFactorRequest; 5 | import hng_java_boilerplate.twofactor.dtos.TotpRequest; 6 | import hng_java_boilerplate.twofactor.dtos.TwoFactorResponse; 7 | import hng_java_boilerplate.twofactor.service.TwoFactorService; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RequiredArgsConstructor 16 | @RestController 17 | @RequestMapping("api/v1/auth/2fa") 18 | public class TwoFactorController { 19 | 20 | private final TwoFactorService twoFactorService; 21 | 22 | @PostMapping("enable") 23 | public ResponseEntity enable2fa(@RequestBody EnableTwoFactorRequest request) throws QrGenerationException { 24 | return twoFactorService.enableTwoFactor(); 25 | } 26 | 27 | @PostMapping("verify") 28 | public ResponseEntity verify(@RequestBody TotpRequest totp) { 29 | return twoFactorService.verifyTotp(totp); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twofactor/dtos/EnableTwoFactorRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twofactor.dtos; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | 5 | public record EnableTwoFactorRequest( 6 | 7 | @NotBlank(message = "Password can not be blank") 8 | String password 9 | ) { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twofactor/dtos/TotpRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twofactor.dtos; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record TotpRequest( 7 | @JsonProperty("totp_code") 8 | @NotBlank(message = "Totp code can not be blank") 9 | String totp 10 | ) { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twofactor/dtos/TwoFactorResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twofactor.dtos; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | @JsonInclude(JsonInclude.Include.NON_NULL) 7 | public record TwoFactorResponse( 8 | @JsonProperty("status_code") 9 | int statusCode, 10 | String message, 11 | Object data 12 | ) { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/twofactor/service/TotpService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.twofactor.service; 2 | 3 | import dev.samstevens.totp.code.CodeVerifier; 4 | import dev.samstevens.totp.code.HashingAlgorithm; 5 | import dev.samstevens.totp.exceptions.QrGenerationException; 6 | import dev.samstevens.totp.qr.QrData; 7 | import dev.samstevens.totp.qr.QrGenerator; 8 | import dev.samstevens.totp.secret.SecretGenerator; 9 | import dev.samstevens.totp.util.Utils; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.stereotype.Service; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class TotpService { 16 | 17 | private final SecretGenerator secretGenerator; 18 | private final QrGenerator qrGenerator; 19 | private final CodeVerifier codeVerifier; 20 | 21 | public String generateSecretKey() { 22 | return secretGenerator.generate(); 23 | } 24 | 25 | public String getQRCode(String secret, String username) throws QrGenerationException { 26 | QrData qrData = new QrData.Builder().label(username) 27 | .issuer("2FA boilerplate") 28 | .secret(secret) 29 | .digits(6) 30 | .period(30) 31 | .algorithm(HashingAlgorithm.SHA512) 32 | .build(); 33 | return Utils.getDataUriForImage(qrGenerator.generate(qrData), qrGenerator.getImageMimeType()); 34 | } 35 | 36 | public boolean verifyTotp(String code, String secret) { 37 | return codeVerifier.isValidCode(secret, code); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/DeleteUserRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | public class DeleteUserRequest { 10 | 11 | @NotBlank(message = "Email is required and cannot be blank.") 12 | private String email; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/EmailSenderDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class EmailSenderDto { 13 | @Email(message = "Email should be valid") 14 | @NotBlank(message = "Email is required") 15 | private String email; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/FacebookDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class FacebookDto { 12 | @JsonProperty("access_token") 13 | private String accessToken; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/GetUserDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class GetUserDto { 15 | private String name; 16 | private String id; 17 | private String email; 18 | private ProfileDto profile; 19 | private List organisations; 20 | private List products; 21 | 22 | @Data 23 | @Builder 24 | public static class ProfileDto { 25 | private String first_name; 26 | private String last_name; 27 | private String phone; 28 | private String avatar_url; 29 | } 30 | 31 | @Data 32 | @Builder 33 | public static class OrganisationDto { 34 | private String org_id; 35 | private String name; 36 | private String description; 37 | } 38 | 39 | @Data 40 | @Builder 41 | public static class ProductDto { 42 | private String product_id; 43 | private String name; 44 | private String description; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/GoogleOAuthDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class GoogleOAuthDto { 12 | @JsonProperty("id_token") 13 | private String idToken; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/LoginDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import hng_java_boilerplate.util.ValidPassword; 4 | import jakarta.validation.constraints.Email; 5 | import jakarta.validation.constraints.NotBlank; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class LoginDto { 14 | @Email(message = "Invalid email or password") 15 | @NotBlank(message = "Invalid email or password") 16 | private String email; 17 | 18 | @ValidPassword(message = "Invalid email or password") 19 | @NotBlank(message = "Invalid email or password") 20 | private String password; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/MagicLinkRequest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class MagicLinkRequest { 11 | private String email; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/ResetPasswordDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class ResetPasswordDto { 11 | private String new_password; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/SignupDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import hng_java_boilerplate.util.ValidPassword; 5 | import jakarta.validation.constraints.Email; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class SignupDto { 16 | @JsonProperty("first_name") 17 | @NotBlank(message = "First name is required") 18 | private String firstName; 19 | 20 | @JsonProperty("last_name") 21 | @NotBlank(message = "Last name is required") 22 | private String lastName; 23 | 24 | @Email(message = "Email should be valid") 25 | @NotBlank(message = "Email is required") 26 | private String email; 27 | 28 | @NotBlank(message = "Password is required") 29 | @ValidPassword 30 | private String password; 31 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/request/VerificationTokenDto.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class VerificationTokenDto { 11 | private String token; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class ApiResponse { 11 | private int status_code; 12 | private String message; 13 | private String access_token; 14 | private T data; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class ErrorResponse { 11 | private String message; 12 | private String error; 13 | private int status_code; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/MembersResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import lombok.*; 7 | 8 | @Builder 9 | @AllArgsConstructor 10 | @RequiredArgsConstructor 11 | @Setter 12 | @Getter 13 | public class MembersResponse { 14 | 15 | private String fullName; 16 | private String email; 17 | 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | private String phone; 20 | 21 | @JsonInclude(JsonInclude.Include.NON_NULL) 22 | private String status; 23 | 24 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 25 | private String createdAt; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/OAuthBaseResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class OAuthBaseResponse { 11 | private int status_code; 12 | private String message; 13 | private String access_token; 14 | private OAuthLastUserResponse data; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/OAuthLastUserResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class OAuthLastUserResponse { 13 | private UserOAuthDetails user; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/OAuthResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class OAuthResponse { 13 | private String first_name; 14 | private String last_name; 15 | private String email; 16 | private String password; 17 | private String access_token; 18 | private String img_url; 19 | private boolean is_active; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/OAuthUserResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class OAuthUserResponse { 13 | private String id; 14 | private String email; 15 | private String first_name; 16 | private String last_name; 17 | private String fullname; 18 | private String role; 19 | private String access_token; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/Response.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.*; 4 | 5 | @Builder 6 | @AllArgsConstructor 7 | @RequiredArgsConstructor 8 | @Setter 9 | @Getter 10 | public class Response { 11 | 12 | private String status_code; 13 | private String message; 14 | private T data; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/ResponseData.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class ResponseData { 11 | private UserResponse user; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/UserOAuthDetails.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Builder 12 | public class UserOAuthDetails { 13 | private String id; 14 | private String email; 15 | private String first_name; 16 | private String last_name; 17 | private String fullname; 18 | private String role; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/dto/response/UserResponse.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.dto.response; 2 | 3 | import hng_java_boilerplate.organisation.entity.Organisation; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.List; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class UserResponse { 17 | private String id; 18 | private String first_name; 19 | private String last_name; 20 | private String email; 21 | private String role; 22 | private String imr_url; 23 | private List organisations; 24 | private LocalDateTime created_at; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/entity/MagicLinkToken.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | import java.util.UUID; 10 | 11 | @Data 12 | @Entity 13 | @NoArgsConstructor 14 | public class MagicLinkToken { 15 | private static final int EXPIRATION_TIME = 60; 16 | 17 | @Id 18 | private String id; 19 | 20 | private String token; 21 | 22 | private Date expirationTime; 23 | 24 | @OneToOne(fetch = FetchType.EAGER) 25 | @JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "FK_USER_VERIFY_TOKEN")) 26 | private User user; 27 | 28 | public MagicLinkToken(User user, String token){ 29 | super(); 30 | this.id = UUID.randomUUID().toString(); 31 | this.token = token; 32 | this.user = user; 33 | this.expirationTime = calculateExpirationDate(EXPIRATION_TIME); 34 | } 35 | public MagicLinkToken(String token){ 36 | super(); 37 | this.token = token; 38 | this.expirationTime = calculateExpirationDate(EXPIRATION_TIME); 39 | } 40 | 41 | private Date calculateExpirationDate(int expirationTime) { 42 | Calendar calender = Calendar.getInstance(); 43 | calender.setTimeInMillis(new Date().getTime()); 44 | calender.add(Calendar.MINUTE, expirationTime); 45 | return new Date(calender.getTime().getTime()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/entity/PasswordResetToken.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | import java.util.UUID; 10 | 11 | @Data 12 | @Entity 13 | @NoArgsConstructor 14 | public class PasswordResetToken { 15 | 16 | private static final int EXPIRATION_TIME = 20; 17 | 18 | @Id 19 | private String id; 20 | 21 | private String token; 22 | 23 | private Date expirationTime; 24 | 25 | @OneToOne(fetch = FetchType.EAGER) 26 | @JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "FK_USER_PASSWORD_TOKEN")) 27 | private User user; 28 | 29 | public PasswordResetToken(User user, String token) { 30 | super(); 31 | this.id = UUID.randomUUID().toString(); 32 | this.token = token; 33 | this.user = user; 34 | this.expirationTime = calculateExpirationDate(EXPIRATION_TIME); 35 | } 36 | 37 | public PasswordResetToken(String token) { 38 | super(); 39 | this.id = UUID.randomUUID().toString(); 40 | this.token = token; 41 | this.expirationTime = calculateExpirationDate(EXPIRATION_TIME); 42 | } 43 | 44 | private Date calculateExpirationDate(int expirationTime) { 45 | Calendar calendar = Calendar.getInstance(); 46 | calendar.setTimeInMillis(new Date().getTime()); 47 | calendar.add(Calendar.MINUTE, expirationTime); 48 | return new Date(calendar.getTime().getTime()); 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/entity/VerificationToken.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | import java.util.UUID; 10 | 11 | @Data 12 | @Entity 13 | @NoArgsConstructor 14 | public class VerificationToken { 15 | private static final int EXPIRATION_TIME = 60; 16 | 17 | @Id 18 | private String id; 19 | 20 | private String token; 21 | 22 | private Date expirationTime; 23 | 24 | @OneToOne(fetch = FetchType.EAGER) 25 | @JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "FK_USER_VERIFY_TOKEN")) 26 | private User user; 27 | 28 | public VerificationToken(User user, String token){ 29 | super(); 30 | this.id = UUID.randomUUID().toString(); 31 | this.token = token; 32 | this.user = user; 33 | this.expirationTime = calculateExpirationDate(EXPIRATION_TIME); 34 | } 35 | public VerificationToken(String token){ 36 | super(); 37 | this.token = token; 38 | this.expirationTime = calculateExpirationDate(EXPIRATION_TIME); 39 | } 40 | 41 | private Date calculateExpirationDate(int expirationTime) { 42 | Calendar calender = Calendar.getInstance(); 43 | calender.setTimeInMillis(new Date().getTime()); 44 | calender.add(Calendar.MINUTE, expirationTime); 45 | return new Date(calender.getTime().getTime()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/enums/Role.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.enums; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_SUPER_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/repository/MagicLinkTokenRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.repository; 2 | 3 | import hng_java_boilerplate.user.entity.MagicLinkToken; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface MagicLinkTokenRepository extends JpaRepository { 7 | MagicLinkToken findByToken(String token); 8 | MagicLinkToken findByUserId(String id); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/repository/PasswordResetTokenRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.repository; 2 | 3 | 4 | import hng_java_boilerplate.user.entity.PasswordResetToken; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface PasswordResetTokenRepository extends JpaRepository { 8 | PasswordResetToken findByToken(String token); 9 | PasswordResetToken findByUserId(String userId); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.repository; 2 | 3 | import hng_java_boilerplate.user.entity.User; 4 | import hng_java_boilerplate.user.enums.Role; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public interface UserRepository extends JpaRepository { 11 | Optional findByEmail(String username); 12 | 13 | Optional findById(String id); 14 | 15 | boolean existsByEmail(String email); 16 | 17 | void deleteByEmail(String mail); 18 | 19 | List findUserByUserRole(Role role); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/repository/VerificationTokenRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.repository; 2 | import hng_java_boilerplate.user.entity.User; 3 | import hng_java_boilerplate.user.entity.VerificationToken; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface VerificationTokenRepository extends JpaRepository { 7 | VerificationToken findByToken(String token); 8 | VerificationToken findByUser(User user); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/user/service/UserService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.user.service; 2 | 3 | import hng_java_boilerplate.user.dto.request.*; 4 | import hng_java_boilerplate.user.dto.response.ApiResponse; 5 | import hng_java_boilerplate.user.dto.response.ResponseData; 6 | import hng_java_boilerplate.user.dto.response.MembersResponse; 7 | import hng_java_boilerplate.user.dto.response.Response; 8 | import hng_java_boilerplate.user.entity.User; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.core.Authentication; 12 | 13 | import java.util.List; 14 | 15 | 16 | 17 | public interface UserService { 18 | 19 | GetUserDto getUserWithDetails(String userId); 20 | ResponseEntity> registerUser(SignupDto signupDto); 21 | ResponseEntity verifyOtp(String email, String token, HttpServletRequest request); 22 | User getLoggedInUser(); 23 | ResponseEntity> loginUser(LoginDto loginDto); 24 | User save(User user); 25 | User findUser(String id); 26 | void forgotPassword(EmailSenderDto passwordDto, HttpServletRequest request); 27 | ResponseEntity resetPassword(String token, ResetPasswordDto passwordDto); 28 | void requestToken(EmailSenderDto emailSenderDto, HttpServletRequest request); 29 | void sendMagicLink(String email, HttpServletRequest request); 30 | List getAllUsers(int page, Authentication authentication); 31 | Response deleteUserByEmail(DeleteUserRequest request, Authentication authentication); 32 | Response getUserById(String userId, Authentication authentication); 33 | ResponseEntity magicLinkLogin(String token); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/util/PaginationUtils.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.util; 2 | 3 | import hng_java_boilerplate.exception.BadRequestException; 4 | import hng_java_boilerplate.user.entity.User; 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.PageImpl; 7 | import org.springframework.data.domain.PageRequest; 8 | import org.springframework.data.domain.Pageable; 9 | 10 | import java.util.List; 11 | 12 | public class PaginationUtils { 13 | 14 | public static final int ONE = 1; 15 | public static final int ZERO = 0; 16 | public static final int DEFAULT_PAGE_NUMBER = 1; 17 | private static final int DEFAULT_PAGE_LIMIT = 5; 18 | 19 | 20 | public static Pageable buildPageRequest(int page, int items){ 21 | if (page<=ZERO) page=DEFAULT_PAGE_NUMBER; 22 | if (items<=ZERO) items = DEFAULT_PAGE_LIMIT; 23 | page-=ONE; 24 | return PageRequest.of(page, items); 25 | } 26 | 27 | public static Page getPaginatedUsers(int page, List allUser) { 28 | Pageable pageable = buildPageRequest(page, 0); 29 | int start = (int) pageable.getOffset(); 30 | int end = Math.min((start + pageable.getPageSize()), allUser.size()); 31 | return new PageImpl<>(allUser.subList(start, end), pageable, allUser.size()); 32 | } 33 | 34 | public static void validatePageNumber(int page, List allUser) throws BadRequestException { 35 | int pageSize = 5; 36 | int totalUsers = allUser.size(); 37 | int totalPages = (int) Math.ceil((double) totalUsers / pageSize); 38 | 39 | if (page < 1 || page > totalPages) { 40 | throw new BadRequestException("Invalid page number requested: " + page); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/util/PasswordValidator.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.util; 2 | 3 | 4 | import jakarta.validation.ConstraintValidator; 5 | import jakarta.validation.ConstraintValidatorContext; 6 | 7 | public class PasswordValidator implements ConstraintValidator { 8 | 9 | @Override 10 | public void initialize(ValidPassword constraintAnnotation) { 11 | } 12 | 13 | @Override 14 | public boolean isValid(String password, ConstraintValidatorContext context) { 15 | if (password == null) { 16 | return false; 17 | } 18 | return password.length() >= 8 && password.matches(".*\\d.*") && password.matches(".*[a-zA-Z].*"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/util/UUIDGenarator.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.util; 2 | 3 | import hng_java_boilerplate.organisation.entity.Organisation; 4 | import hng_java_boilerplate.product.entity.Product; 5 | import hng_java_boilerplate.profile.entity.Profile; 6 | import hng_java_boilerplate.user.entity.User; 7 | import jakarta.persistence.PrePersist; 8 | 9 | import java.util.UUID; 10 | 11 | public class UUIDGenarator { 12 | 13 | @PrePersist 14 | public void generateUUID(Object entity) { 15 | if (entity instanceof User user) { 16 | if (user.getId() == null) { 17 | user.setId(UUID.randomUUID().toString()); 18 | } 19 | } else if (entity instanceof Profile profile) { 20 | if (profile.getId() == null) { 21 | profile.setId(UUID.randomUUID().toString()); 22 | } 23 | } else if (entity instanceof Product product) { 24 | if (product.getId() == null) { 25 | product.setId(UUID.randomUUID().toString()); 26 | } 27 | } else if (entity instanceof Organisation organisation) { 28 | if (organisation.getId() == null) { 29 | organisation.setId(UUID.randomUUID().toString()); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/util/ValidPassword.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.util; 2 | 3 | 4 | import jakarta.validation.Constraint; 5 | import jakarta.validation.Payload; 6 | 7 | import java.lang.annotation.*; 8 | 9 | @Documented 10 | @Constraint(validatedBy = PasswordValidator.class) 11 | @Target({ ElementType.METHOD, ElementType.FIELD }) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ValidPassword { 14 | String message() default "Password must be at least 8 characters long and contain alphanumeric characters"; 15 | Class[] groups() default {}; 16 | Class[] payload() default {}; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/util/ratelimit/filter/RateLimitFilter.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.util.ratelimit.filter; 2 | 3 | import hng_java_boilerplate.util.ratelimit.service.RateLimitService; 4 | import jakarta.servlet.*; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletResponse; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.io.IOException; 12 | 13 | 14 | public class RateLimitFilter implements Filter { 15 | private final RateLimitService rateLimitService; 16 | 17 | @Autowired 18 | public RateLimitFilter(RateLimitService rateLimitService) { 19 | this.rateLimitService = rateLimitService; 20 | } 21 | 22 | @Override 23 | public void init(FilterConfig filterConfig) throws ServletException { 24 | Filter.super.init(filterConfig); 25 | } 26 | 27 | @Override 28 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 29 | HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; 30 | HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; 31 | 32 | String clientIp = httpRequest.getRemoteAddr(); 33 | 34 | if (rateLimitService.isAllowed(clientIp)) { 35 | filterChain.doFilter(servletRequest, servletResponse); 36 | } else { 37 | httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); 38 | httpResponse.getWriter().write("Rate limit exceeded. Try again later."); 39 | } 40 | } 41 | 42 | @Override 43 | public void destroy() { 44 | Filter.super.destroy(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/util/ratelimit/service/RateLimitService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.util.ratelimit.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | @Service 10 | public class RateLimitService { 11 | private static final long TIME_WINDOW = 60000; // 1 minute 12 | private static final int MAX_REQUESTS = 10; // max requests per time window 13 | 14 | private final Map requestCounts = new ConcurrentHashMap<>(); 15 | 16 | public boolean isAllowed(String clientIp) { 17 | long currentTime = System.currentTimeMillis(); 18 | requestCounts.putIfAbsent(clientIp, new RequestInfo(0, currentTime)); 19 | 20 | RequestInfo requestInfo = requestCounts.get(clientIp); 21 | synchronized (requestInfo) { 22 | if (currentTime - requestInfo.startTime > TIME_WINDOW) { 23 | requestInfo.startTime = currentTime; 24 | requestInfo.count.set(0); 25 | } 26 | 27 | if (requestInfo.count.incrementAndGet() > MAX_REQUESTS) { 28 | return false; 29 | } 30 | } 31 | return true; 32 | } 33 | 34 | private static class RequestInfo { 35 | AtomicInteger count; 36 | long startTime; 37 | 38 | RequestInfo(int count, long startTime) { 39 | this.count = new AtomicInteger(count); 40 | this.startTime = startTime; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/waitlist/entity/Waitlist.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.waitlist.entity; 2 | 3 | import jakarta.persistence.*; 4 | import jakarta.validation.constraints.Email; 5 | import jakarta.validation.constraints.NotBlank; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import lombok.Setter; 10 | 11 | import java.time.LocalDateTime; 12 | import java.util.UUID; 13 | 14 | 15 | @Entity 16 | @Getter 17 | @Setter 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class Waitlist { 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.UUID) 23 | private UUID id; 24 | @NotBlank(message = "fullName is required") 25 | private String fullName; 26 | @Email(message = "Email should be valid") 27 | @NotBlank(message = "Email is required") 28 | private String email; 29 | @Column(name = "signup_date") 30 | private LocalDateTime signupDate; 31 | 32 | @PrePersist 33 | protected void onCreate() { 34 | signupDate = LocalDateTime.now(); 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/waitlist/entity/WaitlistPage.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.waitlist.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import java.time.LocalDateTime; 10 | import java.util.UUID; 11 | 12 | @Entity 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class WaitlistPage { 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.UUID) 20 | private UUID id; 21 | 22 | @Column(nullable = false) 23 | private String pageTitle; 24 | 25 | @Column(nullable = false, unique = true) 26 | private String urlSlug; 27 | 28 | @Column(nullable = false) 29 | private String headlineText; 30 | 31 | @Column(nullable = false) 32 | private String subheadlineText; 33 | 34 | @Column(nullable = false, columnDefinition = "TEXT") 35 | private String bodyText; 36 | 37 | private boolean active; 38 | 39 | @Column(name = "created_date") 40 | private LocalDateTime createdDate; 41 | 42 | @PrePersist 43 | protected void onCreate() { 44 | createdDate = LocalDateTime.now(); 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/waitlist/repository/WaitlistPageRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.waitlist.repository; 2 | 3 | import hng_java_boilerplate.waitlist.entity.WaitlistPage; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.UUID; 9 | 10 | @Repository 11 | public interface WaitlistPageRepository extends JpaRepository, JpaSpecificationExecutor { 12 | } -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/waitlist/repository/WaitlistRepository.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.waitlist.repository; 2 | 3 | import hng_java_boilerplate.waitlist.entity.Waitlist; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import java.util.UUID; 10 | 11 | @Repository 12 | public interface WaitlistRepository extends JpaRepository { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/waitlist/service/EmailService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.waitlist.service; 2 | 3 | import org.springframework.mail.SimpleMailMessage; 4 | import org.springframework.mail.javamail.JavaMailSender; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class EmailService { 10 | 11 | private final JavaMailSender emailSender; 12 | 13 | @Autowired 14 | public EmailService(JavaMailSender emailSender) { 15 | this.emailSender = emailSender; 16 | } 17 | 18 | public void sendConfirmationEmail(String to, String subject, String text) { 19 | SimpleMailMessage message = new SimpleMailMessage(); 20 | message.setTo(to); 21 | message.setSubject(subject); 22 | message.setText(text); 23 | emailSender.send(message); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/waitlist/service/WaitlistService.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.waitlist.service; 2 | 3 | import hng_java_boilerplate.waitlist.entity.Waitlist; 4 | import hng_java_boilerplate.waitlist.repository.WaitlistRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.PageRequest; 8 | import org.springframework.data.domain.Pageable; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | public class WaitlistService { 13 | 14 | private final WaitlistRepository waitlistRepository; 15 | 16 | public WaitlistService(WaitlistRepository waitlistRepository) { 17 | this.waitlistRepository = waitlistRepository; 18 | } 19 | 20 | public Waitlist saveWaitlist(Waitlist waitlist) { 21 | return waitlistRepository.save(waitlist); 22 | } 23 | 24 | public Page getWaitlistUsers(Pageable pageable) { 25 | return waitlistRepository.findAll(pageable); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/hng_java_boilerplate/welcome/WelcomeController.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.welcome; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class WelcomeController { 8 | 9 | @GetMapping 10 | public String HelloWorld() { 11 | return "Hello world"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V10__create_squeeze_request_table.sql: -------------------------------------------------------------------------------- 1 | -- V3__Create_squeeze_request_table.sql 2 | CREATE TABLE squeeze_request ( 3 | id VARCHAR(255) PRIMARY KEY, 4 | email VARCHAR(255) NOT NULL UNIQUE, 5 | first_name VARCHAR(255) NOT NULL, 6 | last_name VARCHAR(255) NOT NULL, 7 | phone VARCHAR(20) NOT NULL, 8 | location VARCHAR(255) NOT NULL, 9 | job_title VARCHAR(255) NOT NULL, 10 | company VARCHAR(255) NOT NULL, 11 | interests VARCHAR(255) NOT NULL, 12 | referral_source VARCHAR(255) NOT NULL, 13 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 14 | ); 15 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V11__alter_plan_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE plans 2 | ADD features VARCHAR(255); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V12__update_squeeze_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE squeeze_request ADD COLUMN updated BOOLEAN DEFAULT FALSE; 2 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V14__create_faq_and_contact_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS faqs ( 2 | id VARCHAR(255) PRIMARY KEY, 3 | question VARCHAR(255) NOT NULL, 4 | category VARCHAR(150), 5 | answer VARCHAR(255) NOT NULL 6 | ); 7 | 8 | CREATE TABLE IF NOT EXISTS contacts ( 9 | id VARCHAR(100) PRIMARY KEY, 10 | name VARCHAR(100) NOT NULL, 11 | email VARCHAR(100) NOT NULL, 12 | phone VARCHAR(100) NOT NULL, 13 | message VARCHAR(255) NOT NULL 14 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V16__create_topics_table.sql: -------------------------------------------------------------------------------- 1 | -- Create the table for help center topics 2 | CREATE TABLE help_center_topics ( 3 | id UUID PRIMARY KEY, 4 | title TEXT NOT NULL UNIQUE, 5 | content TEXT NOT NULL, 6 | author TEXT NOT NULL, 7 | created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, 8 | updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL 9 | ); 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V17__alter_waitlist_table.sql: -------------------------------------------------------------------------------- 1 | -- V2__Add_signup_date_to_waitlist.sql 2 | ALTER TABLE waitlist ADD COLUMN signup_date TIMESTAMP; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V18__create_notification_table.sql: -------------------------------------------------------------------------------- 1 | -- V18__Create_notification_and_notification_settings_tables.sql 2 | 3 | -- Creating the notification_settings table 4 | CREATE TABLE notification_settings ( 5 | id BIGSERIAL PRIMARY KEY, 6 | user_id VARCHAR(255) NOT NULL, 7 | mobile_push_notifications BOOLEAN DEFAULT FALSE, 8 | email_notification_activity_in_workspace BOOLEAN DEFAULT FALSE, 9 | email_notification_always_send_email_notifications BOOLEAN DEFAULT FALSE, 10 | email_notification_email_digest BOOLEAN DEFAULT FALSE, 11 | email_notification_announcement_and_update_emails BOOLEAN DEFAULT FALSE, 12 | slack_notifications_activity_on_your_workspace BOOLEAN DEFAULT FALSE, 13 | slack_notifications_always_send_email_notifications BOOLEAN DEFAULT FALSE, 14 | slack_notifications_announcement_and_update_emails BOOLEAN DEFAULT FALSE 15 | ); 16 | 17 | -- Creating the notifications table with a foreign key reference to notification_settings 18 | CREATE TABLE notifications ( 19 | notification_id UUID PRIMARY KEY, 20 | user_id VARCHAR(255) NOT NULL, 21 | message VARCHAR(255) NOT NULL, 22 | is_read BOOLEAN NOT NULL, 23 | created_at TIMESTAMP NOT NULL, 24 | notification_settings_id BIGINT UNIQUE, 25 | CONSTRAINT fk_notification_settings 26 | FOREIGN KEY(notification_settings_id) 27 | REFERENCES notification_settings(id) 28 | ON DELETE CASCADE 29 | ); 30 | 31 | -- Ensure the uuid-ossp extension is enabled for UUID generation if not already done 32 | --CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V19__create_waitlist_pages_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE waitlist_page ( 2 | id UUID PRIMARY KEY, 3 | page_title VARCHAR(255) NOT NULL, 4 | url_slug VARCHAR(255) NOT NULL UNIQUE, 5 | headline_text VARCHAR(255) NOT NULL, 6 | subheadline_text VARCHAR(255) NOT NULL, 7 | body_text TEXT NOT NULL, 8 | active BOOLEAN NOT NULL DEFAULT false, 9 | created_date TIMESTAMP NOT NULL 10 | ); 11 | 12 | -- Create an index on url_slug for faster lookups 13 | CREATE INDEX idx_waitlist_page_url_slug ON waitlist_page(url_slug); 14 | 15 | -- Create an index on created_date for sorting 16 | CREATE INDEX idx_waitlist_page_created_date ON waitlist_page(created_date); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1__init_database.sql: -------------------------------------------------------------------------------- 1 | -- V1__Create_profiles_table.sql 2 | CREATE TABLE profiles ( 3 | id VARCHAR(36) PRIMARY KEY, 4 | first_name VARCHAR(255), 5 | last_name VARCHAR(255), 6 | phone VARCHAR(255), 7 | avatar_url TEXT 8 | ); 9 | 10 | 11 | -- V1__Create_users_table.sql 12 | CREATE TABLE users ( 13 | id VARCHAR(36) PRIMARY KEY, 14 | name VARCHAR(255) NOT NULL, 15 | email VARCHAR(255) NOT NULL, 16 | profile_id VARCHAR(36) UNIQUE, 17 | FOREIGN KEY (profile_id) REFERENCES profiles(id) 18 | ); 19 | 20 | 21 | 22 | -- V1__Create_organisations_table.sql 23 | CREATE TABLE organisations ( 24 | id VARCHAR(36) PRIMARY KEY, 25 | name VARCHAR(255) NOT NULL, 26 | description TEXT 27 | ); 28 | 29 | -- V1__Create_products_table.sql 30 | CREATE TABLE products ( 31 | id VARCHAR(36) PRIMARY KEY, 32 | name VARCHAR(255) NOT NULL, 33 | description TEXT, 34 | user_id VARCHAR(36) NOT NULL, 35 | FOREIGN KEY (user_id) REFERENCES users(id) 36 | ); 37 | 38 | -- V1__Create_user_organisation_table.sql 39 | CREATE TABLE user_organisation ( 40 | user_id VARCHAR(36), 41 | organisation_id VARCHAR(36), 42 | PRIMARY KEY (user_id, organisation_id), 43 | FOREIGN KEY (user_id) REFERENCES users(id), 44 | FOREIGN KEY (organisation_id) REFERENCES organisations(id) 45 | ); 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V20__create_squeeze_config_page_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE squeeze_config ( 2 | id UUID PRIMARY KEY, 3 | page_title VARCHAR(255) NOT NULL, 4 | url_slug VARCHAR(255) NOT NULL UNIQUE, 5 | headline_text VARCHAR(255) NOT NULL, 6 | subheadline_text VARCHAR(255) NOT NULL, 7 | body_text TEXT NOT NULL, 8 | active BOOLEAN NOT NULL DEFAULT false, 9 | created_date TIMESTAMP NOT NULL 10 | ); 11 | 12 | -- Create an index on url_slug for faster lookups 13 | CREATE INDEX idx_squeeze_page_url_slug ON squeeze_config(url_slug); 14 | 15 | -- Create an index on created_date for sorting 16 | CREATE INDEX idx_squeeze_page_created_date ON squeeze_config(created_date); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V21__alter_profile_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE profiles 2 | ADD COLUMN pronouns VARCHAR(255), 3 | ADD COLUMN job_title VARCHAR(255), 4 | ADD COLUMN department VARCHAR(255), 5 | ADD COLUMN social TEXT, 6 | ADD COLUMN bio TEXT -------------------------------------------------------------------------------- /src/main/resources/db/migration/V22__create_regions_table.sql: -------------------------------------------------------------------------------- 1 | -- Create regions table 2 | CREATE TABLE regions ( 3 | id VARCHAR(100) PRIMARY KEY, 4 | region VARCHAR(255) NOT NULL, 5 | language VARCHAR(255) NOT NULL, 6 | timezone VARCHAR(255) NOT NULL, 7 | user_id VARCHAR(100) UNIQUE NOT NULL, 8 | FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE 9 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V23__create_email_template_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE email_templates ( 2 | id VARCHAR(36) PRIMARY KEY, 3 | title VARCHAR(255) NOT NULL UNIQUE, 4 | template TEXT NOT NULL, 5 | status VARCHAR(255), 6 | created_at TIMESTAMP DEFAULT current_timestamp NOT NULL, 7 | updated_at TIMESTAMP DEFAULT current_timestamp NOT NULL 8 | ); 9 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V24__create_testimonials_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE testimonials ( 2 | id VARCHAR(255) PRIMARY KEY, 3 | user_id VARCHAR(255) NOT NULL, 4 | name VARCHAR(255) NOT NULL, 5 | content TEXT NOT NULL, 6 | created_at DATE NOT NULL, 7 | updated_at DATE NOT NULL 8 | ); 9 | 10 | CREATE OR REPLACE FUNCTION update_timestamp() 11 | RETURNS TRIGGER AS $$ 12 | BEGIN 13 | NEW.updated_at = CURRENT_TIMESTAMP; 14 | RETURN NEW; 15 | END; 16 | $$ language 'plpgsql'; 17 | 18 | CREATE TRIGGER update_testimonials_timestamp 19 | BEFORE UPDATE ON testimonials 20 | FOR EACH ROW 21 | EXECUTE FUNCTION update_timestamp(); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V25__Video_table.sql: -------------------------------------------------------------------------------- 1 | -- Create the video_suite table 2 | CREATE TABLE video_suite ( 3 | job_id VARCHAR(36) PRIMARY KEY, 4 | status VARCHAR(10) NOT NULL, 5 | job_type VARCHAR(50) NOT NULL, 6 | progress INTEGER, 7 | output_video_url TEXT, 8 | message TEXT 9 | ); 10 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V26__update_email_template_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE email_templates 2 | ADD type VARCHAR(255); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V27__alter_Video_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE video_suite 2 | ADD COLUMN current_process VARCHAR(50); 3 | 4 | ALTER TABLE video_suite 5 | RENAME COLUMN output_video_url TO filename; 6 | 7 | ALTER TABLE video_suite 8 | ALTER COLUMN filename TYPE VARCHAR(50); 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V28__alter_user_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE users 2 | ADD COLUMN secret_key VARCHAR(255), 3 | ADD COLUMN two_factor_enabled BOOLEAN DEFAULT FALSE; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V29__create_job_listings_table.sql: -------------------------------------------------------------------------------- 1 | -- V29__create_job_listings_table.sql 2 | DO $$ 3 | BEGIN 4 | IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'job_listings') THEN 5 | CREATE TABLE job_listings ( 6 | id BIGSERIAL PRIMARY KEY, 7 | title VARCHAR(255) NOT NULL, 8 | description TEXT NOT NULL, 9 | location VARCHAR(255) NOT NULL, 10 | salary VARCHAR(255) NOT NULL, 11 | job_type VARCHAR(255) NOT NULL, 12 | company_name VARCHAR(255) NOT NULL, 13 | created_at TIMESTAMP NOT NULL 14 | ); 15 | END IF; 16 | END $$; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V2__alter_products_table_schema.sql: -------------------------------------------------------------------------------- 1 | --Added needed columns 2 | ALTER TABLE products 3 | ADD COLUMN category VARCHAR(255), 4 | ADD COLUMN price DOUBLE PRECISION, 5 | ADD COLUMN image_url VARCHAR(255); 6 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V30__create_activity_log_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE activity_logs ( 2 | id BIGSERIAL PRIMARY KEY, 3 | org_id VARCHAR(255) NOT NULL, 4 | user_id VARCHAR(255) NOT NULL, 5 | activity TEXT NOT NULL, 6 | timestamp TIMESTAMP WITH TIME ZONE NOT NULL 7 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V31__comment_db.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE comments( 2 | comment_id VARCHAR(36) PRIMARY KEY, 3 | user_id VARCHAR(36) NOT NULL, 4 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 5 | updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 6 | comment VARCHAR(1000) NOT NULL, 7 | deleted BOOLEAN NOT NULL, 8 | FOREIGN KEY (user_id) REFERENCES users(id) 9 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V32__create_about_page_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE about_page_content ( 2 | id BIGSERIAL PRIMARY KEY, 3 | title VARCHAR(255) NOT NULL, 4 | introduction TEXT NOT NULL, 5 | years_in_business INTEGER, 6 | customers INTEGER, 7 | monthly_blog_readers INTEGER, 8 | social_followers INTEGER, 9 | services_title VARCHAR(255), 10 | services_description TEXT 11 | ); 12 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V33__alter_job_listings_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE job_listings 2 | ADD COLUMN updated_at TIMESTAMP NOT NULL 3 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V34__create_verification_token_table.sql: -------------------------------------------------------------------------------- 1 | -- V28__create_verification_token_table.sql 2 | CREATE TABLE verification_token ( 3 | id VARCHAR(36) PRIMARY KEY, 4 | token VARCHAR(255) NOT NULL, 5 | expiration_time TIMESTAMP NOT NULL, 6 | user_id VARCHAR(36) NOT NULL, 7 | FOREIGN KEY (user_id) REFERENCES users(id) 8 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V35__create_payment_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE users 2 | ADD COLUMN plan_id VARCHAR(255); 3 | 4 | ALTER TABLE users 5 | ADD CONSTRAINT fk_plan 6 | FOREIGN KEY (plan_id) 7 | REFERENCES plans(id); 8 | 9 | 10 | 11 | ALTER TABLE plans 12 | DROP COLUMN duration, 13 | DROP COLUMN duration_unit; 14 | 15 | INSERT INTO plans (id, name, description,price, features) 16 | VALUES 17 | ('1', 'Free', 'To try out our platform and see if it fits your needs.', 0.00, 'Ai-powered transcription, subtitles and translation.;10 minutes for a single file;File export not available.'), 18 | ('2', 'Pro', 'For advanced creators and teams with recurring language needs.', 10.00, 'Ai-powered transcription, subtitles and translation.;300 minutes per month;Export transcriptions in Word, TXT and PDF.;Export subtitles in SRT, MP4, VTT, STL, HTML, XML, TXT, DOCX and more.'), 19 | ('3', 'Enterprise', 'For large organizations in need of recurring language needs.', 300.00, '600 minutes for a single file;Expand up to 100 hours per month.;3 users seats included.;Manage workspace roles and permissions.;Create unlimited style guides and glossaries.;5% discount on human-made services.;Premium support.'); 20 | 21 | 22 | CREATE TABLE payments ( 23 | id VARCHAR(36) PRIMARY KEY, 24 | amount FLOAT NOT NULL, 25 | status VARCHAR(255) NOT NULL, 26 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 27 | updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 28 | ); 29 | 30 | 31 | CREATE TABLE user_payments ( 32 | user_id VARCHAR(36), 33 | payment_id VARCHAR(36), 34 | PRIMARY KEY (user_id, payment_id), 35 | FOREIGN KEY (user_id) REFERENCES users(id), 36 | FOREIGN KEY (payment_id) REFERENCES payments(id) 37 | ); 38 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V36__alter_video_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE video_suite 2 | ADD COLUMN media_format VARCHAR(10), 3 | ADD COLUMN expected_format VARCHAR(10); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V37__update_payment.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE user_payments; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V38__resources_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE resources ( 2 | id VARCHAR(255) PRIMARY KEY, 3 | title VARCHAR(255), 4 | description TEXT, 5 | image VARCHAR(255), 6 | date DATE, 7 | published BOOLEAN DEFAULT FALSE 8 | ); 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V39__drop_video_suite.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE video_suite; 2 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V3__update_users_and_related_tables.sql: -------------------------------------------------------------------------------- 1 | -- Add new columns to users table 2 | ALTER TABLE users 3 | ADD COLUMN password VARCHAR(255) NULL, 4 | ADD COLUMN user_role VARCHAR(255), 5 | ADD COLUMN is_enabled BOOLEAN DEFAULT FALSE, 6 | ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | ADD COLUMN updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V40__create_privacy_policy_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE privacy_policies ( 2 | id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 3 | content TEXT NOT NULL, 4 | created_at TIMESTAMP NOT NULL DEFAULT NOW(), 5 | updated_at TIMESTAMP NULL 6 | ); 7 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V41__create_newsletter_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS newsletters ( 2 | id VARCHAR(50) PRIMARY KEY, 3 | user_id VARCHAR(50) NOT NULL, 4 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 5 | updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 6 | ) -------------------------------------------------------------------------------- /src/main/resources/db/migration/V42__alter_organisation_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE organisations 2 | DROP CONSTRAINT IF EXISTS organisations_owner_id_fkey, -- Drop the foreign key constraint if it exists 3 | ALTER COLUMN owner_id TYPE VARCHAR(255); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V43__create_category_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE categories ( 2 | id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 3 | name VARCHAR(255) NOT NULL, 4 | description TEXT, 5 | slug VARCHAR(255) NOT NULL UNIQUE, 6 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 7 | updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 8 | ); 9 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V44__create_api_status_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE api_status ( 2 | id BIGSERIAL PRIMARY KEY, 3 | api_group VARCHAR(255) NOT NULL, 4 | status VARCHAR(50) NOT NULL, 5 | last_checked TIMESTAMP NOT NULL, 6 | response_time INTEGER, 7 | details TEXT 8 | ); 9 | 10 | -- Create an index on the api_group column for faster lookups 11 | CREATE INDEX idx_api_status_api_group ON api_status(api_group); 12 | 13 | -- Add a constraint to ensure status is one of the allowed values 14 | ALTER TABLE api_status 15 | ADD CONSTRAINT chk_status CHECK (status IN ('OPERATIONAL', 'DEGRADED', 'DOWN')); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V45__add_user_payments_table.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE IF NOT EXISTS user_payments ( 3 | user_id VARCHAR(36), 4 | payment_id VARCHAR(36), 5 | PRIMARY KEY (user_id, payment_id), 6 | FOREIGN KEY (user_id) REFERENCES users(id), 7 | FOREIGN KEY (payment_id) REFERENCES payments(id) 8 | ); 9 | 10 | 11 | INSERT INTO plans (id, name, description,price, features) 12 | VALUES 13 | ('4', 'Premium', 'Designed for power users and maximum functionalities.', 100.00, '250 Projects.;Up to 10000 subscribers.;Advanced analytics.;24-hour support response time.;Marketing advisor.'); 14 | 15 | UPDATE plans 16 | SET name = 'Advanced', description = 'Perfect for users who want more features.', price = 50.00, features = '200 Projects.;Up to 100 subscribers.;Advanced analytics.;24-hour support response time.;Marketing advisor.' 17 | WHERE id = '3'; 18 | 19 | UPDATE plans 20 | SET name = 'Basic', description = 'Ideal for growing needs who want more features.', price = 20.00, features = '100 Projects.;Up to 50 subscribers.;Advanced analytics.;24-hour support response time.' 21 | WHERE id = '2'; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V46__create_reset_password_token_table.sql: -------------------------------------------------------------------------------- 1 | -- V46__create_password_reset_token_table.sql 2 | CREATE TABLE password_reset_token ( 3 | id VARCHAR(36) PRIMARY KEY, 4 | token VARCHAR(255) NOT NULL, 5 | expiration_time TIMESTAMP NOT NULL, 6 | user_id VARCHAR(36) NOT NULL, 7 | FOREIGN KEY (user_id) REFERENCES users(id) 8 | ); 9 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V47__magic_link_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE magic_link_token ( 2 | id VARCHAR(36) PRIMARY KEY, 3 | token VARCHAR(255) NOT NULL, 4 | expiration_time TIMESTAMP NOT NULL, 5 | user_id VARCHAR(36) NOT NULL, 6 | FOREIGN KEY (user_id) REFERENCES users(id) 7 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V48__alter_product_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE products 2 | ADD COLUMN created_at TIMESTAMP, 3 | ADD COLUMN updated_at TIMESTAMP, 4 | ADD COLUMN org_id VARCHAR(36); 5 | 6 | ALTER TABLE products 7 | ADD CONSTRAINT fk_org_id 8 | FOREIGN KEY (org_id) 9 | REFERENCES organisations(id); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V49__alter_newsletter_product_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE newsletters 2 | ADD COLUMN title VARCHAR(255), 3 | ADD COLUMN content TEXT -------------------------------------------------------------------------------- /src/main/resources/db/migration/V4__alter_user_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE users 2 | ADD COLUMN is_deactivated BOOLEAN DEFAULT FALSE; -------------------------------------------------------------------------------- /src/main/resources/db/migration/V50__alter_newsletter_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE newsletters 2 | DROP COLUMN user_id 3 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V51__create_subscription_table.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE newsletters 2 | ADD COLUMN user_id VARCHAR(50), 3 | ADD CONSTRAINT fk_user_id 4 | FOREIGN KEY (user_id) 5 | REFERENCES users(id) 6 | ON DELETE CASCADE 7 | ON UPDATE CASCADE; 8 | 9 | CREATE TABLE subscribers( 10 | user_id VARCHAR(50), 11 | newsletter_id VARCHAR(50), 12 | PRIMARY KEY (user_id, newsletter_id), 13 | FOREIGN KEY (user_id) REFERENCES users(id), 14 | FOREIGN KEY (newsletter_id) REFERENCES newsletters(id), 15 | subscribed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V5__alter_user_data.sql: -------------------------------------------------------------------------------- 1 | UPDATE users 2 | SET password = 'default_password' 3 | WHERE password IS NULL; 4 | 5 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V6__alter_user_password.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE users 2 | ALTER COLUMN password SET NOT NULL; 3 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V7__create_plan_table.sql: -------------------------------------------------------------------------------- 1 | -- V7__create_plan_table.sql 2 | CREATE TABLE plans 3 | ( 4 | id VARCHAR(255) PRIMARY KEY, 5 | name VARCHAR(255) NOT NULL UNIQUE, 6 | description VARCHAR(255) NOT NULL, 7 | price FLOAT NOT NULL, 8 | duration INTEGER NOT NULL, 9 | duration_unit VARCHAR(255) NOT NULL 10 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V8__create_waitlist_table.sql: -------------------------------------------------------------------------------- 1 | -- V1__Create_waitlist_table.sql 2 | CREATE TABLE waitlist ( 3 | id UUID PRIMARY KEY, 4 | full_name VARCHAR(255) NOT NULL, 5 | email VARCHAR(255) NOT NULL 6 | ); -------------------------------------------------------------------------------- /src/main/resources/db/migration/V9__update_organisations.sql: -------------------------------------------------------------------------------- 1 | -- Add new columns to organisations table 2 | ALTER TABLE organisations 3 | ADD COLUMN slug VARCHAR(255) UNIQUE, 4 | ADD COLUMN owner_id VARCHAR(255) REFERENCES users(id), 5 | ADD COLUMN email VARCHAR(255), 6 | ADD COLUMN industry VARCHAR(255), 7 | ADD COLUMN type VARCHAR(255), 8 | ADD COLUMN country VARCHAR(255), 9 | ADD COLUMN address VARCHAR(255), 10 | ADD COLUMN state VARCHAR(255), 11 | ADD COLUMN created_at TIMESTAMP, 12 | ADD COLUMN updated_at TIMESTAMP; -------------------------------------------------------------------------------- /src/test/java/hng_java_boilerplate/ProgramTest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; 4 | 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | @SpringBootTest 9 | public class ProgramTest { 10 | 11 | @Test 12 | public void ItShouldPassForNoReason() { 13 | assertTrue(true); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/hng_java_boilerplate/product_test/unit_test/DeleteProductTest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.product_test.unit_test; 2 | 3 | import static org.mockito.Mockito.*; 4 | import static org.junit.jupiter.api.Assertions.*; 5 | 6 | import hng_java_boilerplate.product.entity.Product; 7 | import hng_java_boilerplate.product.repository.ProductRepository; 8 | import hng_java_boilerplate.product.service.ProductServiceImpl; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.mockito.InjectMocks; 12 | import org.mockito.Mock; 13 | import org.mockito.MockitoAnnotations; 14 | 15 | public class DeleteProductTest { 16 | 17 | @Mock 18 | private ProductRepository productRepository; 19 | 20 | @InjectMocks 21 | private ProductServiceImpl productService; 22 | 23 | private Product product; 24 | 25 | @BeforeEach 26 | public void setUp() { 27 | MockitoAnnotations.openMocks(this); 28 | 29 | product = new Product(); 30 | product.setId("prod-123"); 31 | product.setName("Test Product"); 32 | } 33 | 34 | @Test 35 | public void testDeleteProduct() { 36 | 37 | productService.deleteProduct(product); 38 | 39 | verify(productRepository, times(1)).delete(product); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/test/java/hng_java_boilerplate/welcome/WelcomeControllerTest.java: -------------------------------------------------------------------------------- 1 | package hng_java_boilerplate.welcome; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | @SpringBootTest 9 | public class WelcomeControllerTest { 10 | @Test 11 | public void HelloWorldShouldReturnHelloWorld() { 12 | var controller = new WelcomeController(); 13 | 14 | assertEquals(controller.HelloWorld(), "Hello world"); 15 | } 16 | } 17 | --------------------------------------------------------------------------------