├── .idea ├── .gitignore ├── compiler.xml ├── dataSources.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── itsdone.uk.iml ├── jarRepositories.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── auth-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── safalifter │ │ │ └── authservice │ │ │ ├── AuthServiceApplication.java │ │ │ ├── client │ │ │ ├── CustomErrorDecoder.java │ │ │ └── UserServiceClient.java │ │ │ ├── config │ │ │ ├── AuthConfig.java │ │ │ ├── FeignConfig.java │ │ │ └── OpenApiConfig.java │ │ │ ├── controller │ │ │ └── AuthController.java │ │ │ ├── dto │ │ │ ├── RegisterDto.java │ │ │ ├── TokenDto.java │ │ │ └── UserDto.java │ │ │ ├── enums │ │ │ └── Role.java │ │ │ ├── exc │ │ │ ├── GeneralExceptionHandler.java │ │ │ ├── GenericErrorResponse.java │ │ │ ├── ValidationException.java │ │ │ └── WrongCredentialsException.java │ │ │ ├── request │ │ │ ├── LoginRequest.java │ │ │ └── RegisterRequest.java │ │ │ └── service │ │ │ ├── AuthService.java │ │ │ ├── CustomUserDetails.java │ │ │ ├── CustomUserDetailsService.java │ │ │ └── JwtService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── safalifter │ └── authservice │ └── AuthServiceApplicationTests.java ├── config-server ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── safalifter │ │ │ └── configserver │ │ │ └── ConfigServerApplication.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── safalifter │ └── configserver │ └── ConfigServerApplicationTests.java ├── config └── application.properties ├── docker-compose.yml ├── eureka-server ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── safalifter │ │ │ └── eurekaserver │ │ │ └── EurekaServerApplication.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── safalifter │ └── eurekaserver │ └── EurekaServerApplicationTests.java ├── file-storage ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── safalifter │ │ │ └── filestorage │ │ │ ├── FileStorageApplication.java │ │ │ ├── controller │ │ │ └── StorageController.java │ │ │ ├── exc │ │ │ ├── GeneralExceptionHandler.java │ │ │ └── GenericErrorResponse.java │ │ │ ├── model │ │ │ └── File.java │ │ │ ├── repository │ │ │ └── FileRepository.java │ │ │ └── service │ │ │ └── StorageService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── safalifter │ └── filestorage │ └── FileStorageApplicationTests.java ├── gateway ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── safalifter │ │ │ └── gateway │ │ │ ├── GatewayApplication.java │ │ │ ├── config │ │ │ └── GatewayConfig.java │ │ │ ├── filter │ │ │ └── JwtAuthenticationFilter.java │ │ │ └── util │ │ │ └── JwtUtil.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── safalifter │ └── gateway │ └── GatewayApplicationTests.java ├── job-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── safalifter │ │ │ └── jobservice │ │ │ ├── JobServiceApplication.java │ │ │ ├── client │ │ │ ├── CustomErrorDecoder.java │ │ │ ├── FileStorageClient.java │ │ │ └── UserServiceClient.java │ │ │ ├── config │ │ │ ├── BeanConfig.java │ │ │ ├── FeignConfig.java │ │ │ ├── KafkaConfig.java │ │ │ ├── OpenApiConfig.java │ │ │ └── SecurityConfig.java │ │ │ ├── controller │ │ │ ├── AdvertController.java │ │ │ ├── CategoryController.java │ │ │ ├── JobController.java │ │ │ └── OfferController.java │ │ │ ├── dto │ │ │ ├── AdvertDto.java │ │ │ ├── CategoryDto.java │ │ │ ├── JobDto.java │ │ │ ├── OfferDto.java │ │ │ └── UserDto.java │ │ │ ├── enums │ │ │ ├── AdvertStatus.java │ │ │ ├── Advertiser.java │ │ │ └── OfferStatus.java │ │ │ ├── exc │ │ │ ├── GeneralExceptionHandler.java │ │ │ ├── GenericErrorResponse.java │ │ │ ├── NotFoundException.java │ │ │ └── UnauthorizedException.java │ │ │ ├── jwt │ │ │ ├── JwtAuthenticationFilter.java │ │ │ └── JwtUtil.java │ │ │ ├── model │ │ │ ├── Advert.java │ │ │ ├── BaseEntity.java │ │ │ ├── Category.java │ │ │ ├── Job.java │ │ │ └── Offer.java │ │ │ ├── repository │ │ │ ├── AdvertRepository.java │ │ │ ├── CategoryRepository.java │ │ │ ├── JobRepository.java │ │ │ └── OfferRepository.java │ │ │ ├── request │ │ │ ├── advert │ │ │ │ ├── AdvertCreateRequest.java │ │ │ │ └── AdvertUpdateRequest.java │ │ │ ├── category │ │ │ │ ├── CategoryCreateRequest.java │ │ │ │ └── CategoryUpdateRequest.java │ │ │ ├── job │ │ │ │ ├── JobCreateRequest.java │ │ │ │ └── JobUpdateRequest.java │ │ │ ├── notification │ │ │ │ └── SendNotificationRequest.java │ │ │ └── offer │ │ │ │ ├── MakeAnOfferRequest.java │ │ │ │ └── OfferUpdateRequest.java │ │ │ └── service │ │ │ ├── AdvertService.java │ │ │ ├── CategoryService.java │ │ │ ├── JobService.java │ │ │ └── OfferService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── safalifter │ └── jobservice │ └── JobServiceApplicationTests.java ├── notification-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── safalifter │ │ │ └── notificationservice │ │ │ ├── NotificationServiceApplication.java │ │ │ ├── config │ │ │ └── KafkaConfig.java │ │ │ ├── controller │ │ │ └── NotificationController.java │ │ │ ├── listeners │ │ │ └── NotificationListener.java │ │ │ ├── model │ │ │ └── Notification.java │ │ │ ├── repository │ │ │ └── NotificationRepository.java │ │ │ ├── request │ │ │ └── SendNotificationRequest.java │ │ │ └── service │ │ │ └── NotificationService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── safalifter │ └── notificationservice │ └── NotificationServiceApplicationTests.java ├── screenshots ├── auth.png ├── category-advert.png ├── eureka.png ├── file-download.png ├── file-upload.png ├── kafka-ui.png ├── offer-job.png └── user.png └── user-service ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── safalifter │ │ └── userservice │ │ ├── UserServiceApplication.java │ │ ├── client │ │ ├── CustomErrorDecoder.java │ │ └── FileStorageClient.java │ │ ├── config │ │ ├── BeanConfig.java │ │ ├── FeignConfig.java │ │ ├── OpenApiConfig.java │ │ └── SecurityConfig.java │ │ ├── controller │ │ └── UserController.java │ │ ├── dto │ │ ├── AuthUserDto.java │ │ └── UserDto.java │ │ ├── enums │ │ ├── Active.java │ │ └── Role.java │ │ ├── exc │ │ ├── GeneralExceptionHandler.java │ │ ├── GenericErrorResponse.java │ │ ├── NotFoundException.java │ │ └── UnauthorizedException.java │ │ ├── jwt │ │ ├── JwtAuthenticationFilter.java │ │ └── JwtUtil.java │ │ ├── model │ │ ├── BaseEntity.java │ │ ├── User.java │ │ └── UserDetails.java │ │ ├── repository │ │ └── UserRepository.java │ │ ├── request │ │ ├── RegisterRequest.java │ │ └── UserUpdateRequest.java │ │ └── service │ │ └── UserService.java └── resources │ └── application.properties └── test └── java └── com └── safalifter └── userservice └── UserServiceApplicationTests.java /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 41 | 42 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | postgresql 6 | true 7 | org.postgresql.Driver 8 | jdbc:postgresql://localhost:5432/microservice 9 | $ProjectFileDir$ 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.idea/itsdone.uk.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /auth-service/.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 | -------------------------------------------------------------------------------- /auth-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/auth-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /auth-service/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /auth-service/HELP.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/auth-service/HELP.md -------------------------------------------------------------------------------- /auth-service/mvnw.cmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/auth-service/mvnw.cmd -------------------------------------------------------------------------------- /auth-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | auth-service 13 | 0.0.1-SNAPSHOT 14 | auth-service 15 | Demo project for Spring Boot 16 | 17 | 17 18 | 2021.0.8 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-security 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-netflix-eureka-client 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-openfeign 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-config 40 | 41 | 42 | org.springdoc 43 | springdoc-openapi-ui 44 | 1.6.15 45 | 46 | 47 | org.projectlombok 48 | lombok 49 | true 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-test 54 | test 55 | 56 | 57 | org.springframework.security 58 | spring-security-test 59 | test 60 | 61 | 62 | io.jsonwebtoken 63 | jjwt-api 64 | 0.11.5 65 | 66 | 67 | io.jsonwebtoken 68 | jjwt-impl 69 | 0.11.5 70 | 71 | 72 | io.jsonwebtoken 73 | jjwt-jackson 74 | 0.11.5 75 | 76 | 77 | 78 | 79 | 80 | org.springframework.cloud 81 | spring-cloud-dependencies 82 | ${spring-cloud.version} 83 | pom 84 | import 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-maven-plugin 94 | 95 | 96 | 97 | org.projectlombok 98 | lombok 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/AuthServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | import org.springframework.cloud.openfeign.EnableFeignClients; 7 | 8 | @SpringBootApplication 9 | @EnableFeignClients 10 | @EnableEurekaClient 11 | public class AuthServiceApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(AuthServiceApplication.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/client/CustomErrorDecoder.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.client; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.safalifter.authservice.exc.GenericErrorResponse; 5 | import com.safalifter.authservice.exc.ValidationException; 6 | import feign.Response; 7 | import feign.codec.ErrorDecoder; 8 | import org.apache.commons.io.IOUtils; 9 | import org.springframework.http.HttpStatus; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.Map; 15 | public class CustomErrorDecoder implements ErrorDecoder { 16 | private final ObjectMapper mapper = new ObjectMapper(); 17 | 18 | @Override 19 | public Exception decode(String methodKey, Response response) { 20 | try (InputStream body = response.body().asInputStream()) { 21 | Map errors = 22 | mapper.readValue(IOUtils.toString(body, StandardCharsets.UTF_8), Map.class); 23 | if (response.status() == 400) { 24 | return ValidationException.builder() 25 | .validationErrors(errors).build(); 26 | } else 27 | return GenericErrorResponse 28 | .builder() 29 | .httpStatus(HttpStatus.valueOf(response.status())) 30 | .message(errors.get("error")) 31 | .build(); 32 | 33 | } catch (IOException exception) { 34 | throw GenericErrorResponse.builder() 35 | .httpStatus(HttpStatus.valueOf(response.status())) 36 | .message(exception.getMessage()) 37 | .build(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/client/UserServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.client; 2 | 3 | import com.safalifter.authservice.dto.RegisterDto; 4 | import com.safalifter.authservice.dto.UserDto; 5 | import com.safalifter.authservice.request.RegisterRequest; 6 | import org.springframework.cloud.openfeign.FeignClient; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | 13 | @FeignClient(name = "user-service", path = "/v1/user") 14 | public interface UserServiceClient { 15 | @PostMapping("/save") 16 | ResponseEntity save(@RequestBody RegisterRequest request); 17 | 18 | @GetMapping("/getUserByUsername/{username}") 19 | ResponseEntity getUserByUsername(@PathVariable String username); 20 | } 21 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/config/AuthConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.lang.NonNull; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.security.crypto.password.PasswordEncoder; 13 | import org.springframework.security.web.SecurityFilterChain; 14 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 15 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 16 | 17 | @Configuration 18 | @EnableWebSecurity 19 | public class AuthConfig { 20 | @Bean 21 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 22 | return http.csrf().disable() 23 | .authorizeRequests() 24 | .antMatchers("/v1/auth/**").permitAll() 25 | .and() 26 | .build(); 27 | } 28 | 29 | @Bean 30 | public AuthenticationManager authenticationManager(final AuthenticationConfiguration authenticationConfiguration) throws Exception { 31 | return authenticationConfiguration.getAuthenticationManager(); 32 | } 33 | 34 | @Bean 35 | public PasswordEncoder passwordEncoder() { 36 | return new BCryptPasswordEncoder(); 37 | } 38 | 39 | @Bean 40 | public WebSecurityCustomizer webSecurityCustomizer() { 41 | return (web) -> web.ignoring().antMatchers( 42 | "/v1/auth/**", 43 | "/swagger-resources/**", 44 | "/swagger-ui.html/**", 45 | "/swagger-resources/**", 46 | "/swagger-ui/**", 47 | "/v3/api-docs/**"); 48 | } 49 | 50 | @Bean 51 | public WebMvcConfigurer corsConfigurer() { 52 | return new WebMvcConfigurer() { 53 | @Override 54 | public void addCorsMappings(@NonNull CorsRegistry registry) { 55 | registry.addMapping("/**") 56 | .allowedMethods("*"); 57 | } 58 | }; 59 | } 60 | } -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/config/FeignConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.config; 2 | 3 | import com.safalifter.authservice.client.CustomErrorDecoder; 4 | import feign.codec.ErrorDecoder; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class FeignConfig { 10 | @Bean 11 | public ErrorDecoder errorDecoder() { 12 | return new CustomErrorDecoder(); 13 | } 14 | } -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/config/OpenApiConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.config; 2 | 3 | import io.swagger.v3.oas.models.Components; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import io.swagger.v3.oas.models.info.License; 7 | import io.swagger.v3.oas.models.security.SecurityRequirement; 8 | import io.swagger.v3.oas.models.security.SecurityScheme; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | @Configuration 14 | public class OpenApiConfig { 15 | @Bean 16 | public OpenAPI openAPI(@Value("${application-title}") String title, 17 | @Value("${application-description}") String description, 18 | @Value("${application-version}") String version, 19 | @Value("${application-license}") String license) { 20 | return new OpenAPI().addSecurityItem(new SecurityRequirement() 21 | .addList("Bearer Authentication")) 22 | .components(new Components().addSecuritySchemes 23 | ("Bearer Authentication", createAPIKeyScheme())) 24 | .info(new Info() 25 | .title(title) 26 | .description(description) 27 | .version(version) 28 | .license(new License().name(license))); 29 | } 30 | 31 | private SecurityScheme createAPIKeyScheme() { 32 | return new SecurityScheme().type(SecurityScheme.Type.HTTP) 33 | .bearerFormat("JWT") 34 | .scheme("bearer"); 35 | } 36 | } -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/controller/AuthController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.controller; 2 | 3 | import com.safalifter.authservice.dto.RegisterDto; 4 | import com.safalifter.authservice.dto.TokenDto; 5 | import com.safalifter.authservice.request.LoginRequest; 6 | import com.safalifter.authservice.request.RegisterRequest; 7 | import com.safalifter.authservice.service.AuthService; 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 | @RestController 16 | @RequestMapping("/v1/auth") 17 | @RequiredArgsConstructor 18 | public class AuthController { 19 | private final AuthService authService; 20 | 21 | @PostMapping("/login") 22 | public ResponseEntity login(@RequestBody LoginRequest request) { 23 | return ResponseEntity.ok(authService.login(request)); 24 | } 25 | 26 | @PostMapping("/register") 27 | public ResponseEntity register(@RequestBody RegisterRequest request) { 28 | return ResponseEntity.ok(authService.register(request)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/dto/RegisterDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.dto; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class RegisterDto { 9 | private String id; 10 | private String username; 11 | private String email; 12 | } 13 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/dto/TokenDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.dto; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class TokenDto { 9 | private String token; 10 | } 11 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.dto; 2 | 3 | import com.safalifter.authservice.enums.Role; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class UserDto { 8 | private String id; 9 | private String username; 10 | private String password; 11 | private Role role; 12 | } 13 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/enums/Role.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.enums; 2 | 3 | public enum Role { 4 | ADMIN, USER 5 | } 6 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/exc/GeneralExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.exc; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.ExceptionHandler; 6 | import org.springframework.web.bind.annotation.RestControllerAdvice; 7 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | @RestControllerAdvice 13 | public class GeneralExceptionHandler extends ResponseEntityExceptionHandler { 14 | @ExceptionHandler(Exception.class) 15 | public final ResponseEntity handleAllException(Exception ex) { 16 | Map errors = new HashMap<>(); 17 | errors.put("error", ex.getMessage()); 18 | return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); 19 | } 20 | 21 | @ExceptionHandler(GenericErrorResponse.class) 22 | public ResponseEntity genericError(GenericErrorResponse exception) { 23 | Map errors = new HashMap<>(); 24 | errors.put("error", exception.getMessage()); 25 | return new ResponseEntity<>(errors, exception.getHttpStatus()); 26 | } 27 | 28 | @ExceptionHandler(WrongCredentialsException.class) 29 | public ResponseEntity usernameOrPasswordInvalidException(WrongCredentialsException exception) { 30 | Map errors = new HashMap<>(); 31 | errors.put("error", exception.getMessage()); 32 | return new ResponseEntity<>(errors, HttpStatus.UNAUTHORIZED); 33 | } 34 | 35 | @ExceptionHandler(ValidationException.class) 36 | public ResponseEntity validationException(ValidationException exception) { 37 | return ResponseEntity.badRequest().body(exception.getValidationErrors()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/exc/GenericErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.exc; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | @Builder 8 | @Getter 9 | public class GenericErrorResponse extends RuntimeException { 10 | private final String message; 11 | private final HttpStatus httpStatus; 12 | 13 | public GenericErrorResponse(String message, HttpStatus httpStatus) { 14 | super(message); 15 | this.message = message; 16 | this.httpStatus = httpStatus; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/exc/ValidationException.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.exc; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | import java.util.Map; 7 | 8 | @Builder 9 | @Getter 10 | public class ValidationException extends RuntimeException { 11 | private Map validationErrors; 12 | } -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/exc/WrongCredentialsException.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.exc; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 7 | public class WrongCredentialsException extends RuntimeException { 8 | public WrongCredentialsException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/request/LoginRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.request; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class LoginRequest { 7 | private String username; 8 | private String password; 9 | } 10 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/request/RegisterRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.request; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class RegisterRequest { 7 | private String username; 8 | private String password; 9 | private String email; 10 | } 11 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/service/AuthService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.service; 2 | 3 | import com.safalifter.authservice.client.UserServiceClient; 4 | import com.safalifter.authservice.dto.RegisterDto; 5 | import com.safalifter.authservice.dto.TokenDto; 6 | import com.safalifter.authservice.exc.WrongCredentialsException; 7 | import com.safalifter.authservice.request.LoginRequest; 8 | import com.safalifter.authservice.request.RegisterRequest; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.security.authentication.AuthenticationManager; 11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.stereotype.Service; 14 | 15 | @Service 16 | @RequiredArgsConstructor 17 | public class AuthService { 18 | private final AuthenticationManager authenticationManager; 19 | private final UserServiceClient userServiceClient; 20 | private final JwtService jwtService; 21 | 22 | public TokenDto login(LoginRequest request) { 23 | Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())); 24 | if (authenticate.isAuthenticated()) 25 | return TokenDto 26 | .builder() 27 | .token(jwtService.generateToken(request.getUsername())) 28 | .build(); 29 | else throw new WrongCredentialsException("Wrong credentials"); 30 | } 31 | 32 | public RegisterDto register(RegisterRequest request) { 33 | return userServiceClient.save(request).getBody(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/service/CustomUserDetails.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.service; 2 | 3 | import com.safalifter.authservice.dto.UserDto; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.security.core.GrantedAuthority; 6 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | 9 | import java.util.Collection; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | @RequiredArgsConstructor 14 | 15 | public class CustomUserDetails implements UserDetails { 16 | private final UserDto user; 17 | 18 | @Override 19 | public Collection getAuthorities() { 20 | return Stream.of(user.getRole()) 21 | .map(x -> new SimpleGrantedAuthority("ROLE_" + x.name())) 22 | .collect(Collectors.toList()); 23 | } 24 | 25 | @Override 26 | public String getPassword() { 27 | return user.getPassword(); 28 | } 29 | 30 | @Override 31 | public String getUsername() { 32 | return user.getUsername(); 33 | } 34 | 35 | @Override 36 | public boolean isAccountNonExpired() { 37 | return true; 38 | } 39 | 40 | @Override 41 | public boolean isAccountNonLocked() { 42 | return true; 43 | } 44 | 45 | @Override 46 | public boolean isCredentialsNonExpired() { 47 | return true; 48 | } 49 | 50 | @Override 51 | public boolean isEnabled() { 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/service/CustomUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.service; 2 | 3 | 4 | import com.safalifter.authservice.client.UserServiceClient; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | import org.springframework.security.core.userdetails.UserDetailsService; 7 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | public class CustomUserDetailsService implements UserDetailsService { 12 | private final UserServiceClient userServiceClient; 13 | 14 | public CustomUserDetailsService(UserServiceClient userServiceClient) { 15 | this.userServiceClient = userServiceClient; 16 | } 17 | 18 | @Override 19 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 20 | var user = userServiceClient.getUserByUsername(username).getBody(); 21 | assert user != null; 22 | return new CustomUserDetails(user); 23 | } 24 | } -------------------------------------------------------------------------------- /auth-service/src/main/java/com/safalifter/authservice/service/JwtService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice.service; 2 | 3 | import io.jsonwebtoken.Jwts; 4 | import io.jsonwebtoken.SignatureAlgorithm; 5 | import io.jsonwebtoken.io.Decoders; 6 | import io.jsonwebtoken.security.Keys; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.security.Key; 12 | import java.util.Date; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | @Component 17 | @RequiredArgsConstructor 18 | public class JwtService { 19 | private final CustomUserDetailsService customUserDetailsService; 20 | public static final String SECRET = "5367566B59703373367639792F423F4528482B4D6251655468576D5A71347437"; 21 | 22 | public String generateToken(String username) { 23 | UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); 24 | Map claims = new HashMap<>(); 25 | return createToken(claims, userDetails); 26 | } 27 | 28 | private String createToken(Map claims, UserDetails userDetails) { 29 | return Jwts.builder() 30 | .setClaims(claims) 31 | .setSubject(userDetails.getUsername()) 32 | .setIssuer(userDetails.getAuthorities().iterator().next().getAuthority()) 33 | .setIssuedAt(new Date(System.currentTimeMillis())) 34 | .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 hour 35 | .signWith(getSignKey(), SignatureAlgorithm.HS256).compact(); 36 | } 37 | 38 | private Key getSignKey() { 39 | byte[] keyBytes = Decoders.BASE64.decode(SECRET); 40 | return Keys.hmacShaKeyFor(keyBytes); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /auth-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=0 2 | 3 | application-title=Auth Service 4 | 5 | spring.application.name=auth-service 6 | 7 | spring.config.import=configserver:http://localhost:8888/ -------------------------------------------------------------------------------- /auth-service/src/test/java/com/safalifter/authservice/AuthServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.authservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AuthServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /config-server/.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 | -------------------------------------------------------------------------------- /config-server/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/config-server/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /config-server/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /config-server/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /config-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | config-server 13 | 0.0.1-SNAPSHOT 14 | config-server 15 | config-server 16 | 17 | 17 18 | 2021.0.8 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-config-server 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-dependencies 36 | ${spring-cloud.version} 37 | pom 38 | import 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-maven-plugin 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /config-server/src/main/java/com/safalifter/configserver/ConfigServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.configserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.config.server.EnableConfigServer; 6 | 7 | @SpringBootApplication 8 | @EnableConfigServer 9 | public class ConfigServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ConfigServerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /config-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=config-server 2 | server.port=8888 3 | 4 | spring.profiles.active=git 5 | spring.cloud.config.server.git.uri=https://github.com/devsyx/spring-boot-microservices 6 | spring.cloud.config.server.git.search-paths=config -------------------------------------------------------------------------------- /config-server/src/test/java/com/safalifter/configserver/ConfigServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.configserver; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ConfigServerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /config/application.properties: -------------------------------------------------------------------------------- 1 | spring.jpa.hibernate.ddl-auto=update 2 | spring.jpa.database=postgresql 3 | spring.datasource.url=jdbc:postgresql://localhost:5432/microservice 4 | spring.datasource.username=${POSTGRES_USER:postgres} 5 | spring.datasource.password=${POSTGRES_PASSWORD:55} 6 | 7 | eureka.instance.prefer-ip-address=true 8 | eureka.client.service-url.default-zone=${EUREKA_URI:http://localhost:8761/eureka} 9 | 10 | application-description=Its Done UK Application 11 | application-license=API Licence 12 | application-version=1.0 -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | postgres: 5 | container_name: postgres 6 | image: postgres:latest 7 | environment: 8 | POSTGRES_USER: postgres 9 | POSTGRES_PASSWORD: 55 10 | POSTGRES_DB: microservice 11 | volumes: 12 | - postgres_data:/var/lib/postgresql/data 13 | ports: 14 | - "5432:5432" 15 | 16 | zookeeper: 17 | container_name: zookeeper 18 | image: "docker.io/bitnami/zookeeper:3" 19 | ports: 20 | - "2181:2181" 21 | volumes: 22 | - "zookeeper_data:/bitnami" 23 | environment: 24 | - ALLOW_ANONYMOUS_LOGIN=yes 25 | 26 | kafka: 27 | container_name: kafka 28 | image: "docker.io/bitnami/kafka:2-debian-10" 29 | ports: 30 | - "9092:9092" 31 | expose: 32 | - "9093" 33 | volumes: 34 | - "kafka_data:/bitnami" 35 | environment: 36 | - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 37 | - ALLOW_PLAINTEXT_LISTENER=yes 38 | - KAFKA_ADVERTISED_LISTENERS=INSIDE://kafka:9093,OUTSIDE://localhost:9092 39 | - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT 40 | - KAFKA_LISTENERS=INSIDE://0.0.0.0:9093,OUTSIDE://0.0.0.0:9092 41 | - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE 42 | - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 43 | depends_on: 44 | - zookeeper 45 | 46 | kafka-ui: 47 | container_name: kafka-ui 48 | image: provectuslabs/kafka-ui 49 | ports: 50 | - "9090:8080" 51 | restart: always 52 | environment: 53 | - KAFKA_CLUSTERS_0_NAME=local 54 | - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9093 55 | - KAFKA_CLUSTERS_0_ZOOKEEPER=localhost:2181 56 | 57 | volumes: 58 | zookeeper_data: 59 | driver: local 60 | kafka_data: 61 | driver: local 62 | postgres_data: 63 | driver: local -------------------------------------------------------------------------------- /eureka-server/.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 | -------------------------------------------------------------------------------- /eureka-server/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/eureka-server/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /eureka-server/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /eureka-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | eureka-server 13 | 0.0.1-SNAPSHOT 14 | eureka-server 15 | Demo project for Spring Boot 16 | 17 | 17 18 | 2021.0.8 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-netflix-eureka-server 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | test 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-dependencies 37 | ${spring-cloud.version} 38 | pom 39 | import 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-maven-plugin 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /eureka-server/src/main/java/com/safalifter/eurekaserver/EurekaServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.eurekaserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class EurekaServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(EurekaServerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /eureka-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8761 2 | eureka.client.register-with-eureka=false 3 | eureka.client.fetch-registry=false 4 | -------------------------------------------------------------------------------- /eureka-server/src/test/java/com/safalifter/eurekaserver/EurekaServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.eurekaserver; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class EurekaServerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /file-storage/.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 | -------------------------------------------------------------------------------- /file-storage/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/file-storage/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /file-storage/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /file-storage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | file-storage 13 | 0.0.1-SNAPSHOT 14 | file-storage 15 | file-storage 16 | 17 | 17 18 | 2021.0.8 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.postgresql 31 | postgresql 32 | runtime 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-starter-netflix-eureka-client 37 | 38 | 39 | org.springframework.cloud 40 | spring-cloud-starter-config 41 | 42 | 43 | org.projectlombok 44 | lombok 45 | true 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-test 50 | test 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.springframework.cloud 58 | spring-cloud-dependencies 59 | ${spring-cloud.version} 60 | pom 61 | import 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-maven-plugin 71 | 72 | 73 | 74 | org.projectlombok 75 | lombok 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /file-storage/src/main/java/com/safalifter/filestorage/FileStorageApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaClient 9 | public class FileStorageApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(FileStorageApplication.class, args); 13 | } 14 | } -------------------------------------------------------------------------------- /file-storage/src/main/java/com/safalifter/filestorage/controller/StorageController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage.controller; 2 | 3 | import com.safalifter.filestorage.service.StorageService; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.http.MediaType; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.*; 8 | import org.springframework.web.multipart.MultipartFile; 9 | 10 | @RestController 11 | @RequestMapping("/v1/file-storage") 12 | @RequiredArgsConstructor 13 | public class StorageController { 14 | private final StorageService storageService; 15 | 16 | @PostMapping("/upload") 17 | public ResponseEntity uploadImageToFIleSystem(@RequestPart("image") MultipartFile file) { 18 | return ResponseEntity.ok().body(storageService.uploadImageToFileSystem(file)); 19 | } 20 | 21 | @GetMapping("/download/{id}") 22 | public ResponseEntity downloadImageFromFileSystem(@PathVariable String id) { 23 | return ResponseEntity.ok() 24 | .contentType(MediaType.valueOf("image/png")) 25 | .body(storageService.downloadImageFromFileSystem(id)); 26 | } 27 | 28 | @DeleteMapping("/delete/{id}") 29 | public ResponseEntity deleteImageFromFileSystem(@PathVariable String id) { 30 | storageService.deleteImageFromFileSystem(id); 31 | return ResponseEntity.ok().build(); 32 | } 33 | } -------------------------------------------------------------------------------- /file-storage/src/main/java/com/safalifter/filestorage/exc/GeneralExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage.exc; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.ExceptionHandler; 6 | import org.springframework.web.bind.annotation.RestControllerAdvice; 7 | import org.springframework.web.multipart.MaxUploadSizeExceededException; 8 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | @RestControllerAdvice 14 | public class GeneralExceptionHandler extends ResponseEntityExceptionHandler { 15 | 16 | @ExceptionHandler(Exception.class) 17 | public final ResponseEntity handleAllException(Exception ex) { 18 | Map errors = new HashMap<>(); 19 | errors.put("error", ex.getMessage()); 20 | return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); 21 | } 22 | 23 | @ExceptionHandler(GenericErrorResponse.class) 24 | public ResponseEntity genericError(GenericErrorResponse exception) { 25 | Map errors = new HashMap<>(); 26 | errors.put("error", exception.getMessage()); 27 | return new ResponseEntity<>(errors, exception.getHttpStatus()); 28 | } 29 | 30 | @ExceptionHandler(MaxUploadSizeExceededException.class) 31 | public ResponseEntity handleMaxSizeException(MaxUploadSizeExceededException exc) { 32 | return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED) 33 | .body("File too large!"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /file-storage/src/main/java/com/safalifter/filestorage/exc/GenericErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage.exc; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | @Builder 8 | @Getter 9 | public class GenericErrorResponse extends RuntimeException { 10 | private final String message; 11 | private final HttpStatus httpStatus; 12 | 13 | public GenericErrorResponse(String message, HttpStatus httpStatus) { 14 | super(message); 15 | this.message = message; 16 | this.httpStatus = httpStatus; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /file-storage/src/main/java/com/safalifter/filestorage/model/File.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage.model; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.Id; 7 | 8 | @Entity(name = "files") 9 | @Builder 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Getter 13 | @Setter 14 | public class File { 15 | @Id 16 | private String id; 17 | private String type; 18 | private String filePath; 19 | } -------------------------------------------------------------------------------- /file-storage/src/main/java/com/safalifter/filestorage/repository/FileRepository.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage.repository; 2 | 3 | import com.safalifter.filestorage.model.File; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface FileRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /file-storage/src/main/java/com/safalifter/filestorage/service/StorageService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage.service; 2 | 3 | import com.safalifter.filestorage.exc.GenericErrorResponse; 4 | import com.safalifter.filestorage.model.File; 5 | import com.safalifter.filestorage.repository.FileRepository; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.web.multipart.MultipartFile; 10 | 11 | import javax.annotation.PostConstruct; 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.util.UUID; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class StorageService { 19 | private final FileRepository fileRepository; 20 | private String FOLDER_PATH; 21 | 22 | @PostConstruct 23 | public void init() { 24 | String currentWorkingDirectory = System.getProperty("user.dir"); 25 | 26 | FOLDER_PATH = currentWorkingDirectory + "/file-storage/src/main/resources/attachments"; 27 | 28 | java.io.File targetFolder = new java.io.File(FOLDER_PATH); 29 | 30 | if (!targetFolder.exists()) { 31 | boolean directoriesCreated = targetFolder.mkdirs(); 32 | if (!directoriesCreated) { 33 | throw GenericErrorResponse.builder() 34 | .message("Unable to create directories") 35 | .httpStatus(HttpStatus.INTERNAL_SERVER_ERROR) 36 | .build(); 37 | } 38 | } 39 | } 40 | 41 | public String uploadImageToFileSystem(MultipartFile file) { 42 | String uuid = UUID.randomUUID().toString(); 43 | String filePath = FOLDER_PATH + "/" + uuid; 44 | 45 | try { 46 | file.transferTo(new java.io.File(filePath)); 47 | } catch (IOException e) { 48 | throw GenericErrorResponse.builder() 49 | .message("Unable to save file to storage") 50 | .httpStatus(HttpStatus.INTERNAL_SERVER_ERROR) 51 | .build(); 52 | } 53 | 54 | fileRepository.save(File.builder() 55 | .id(uuid) 56 | .type(file.getContentType()) 57 | .filePath(filePath).build()); 58 | return uuid; 59 | } 60 | 61 | public byte[] downloadImageFromFileSystem(String id) { 62 | try { 63 | return Files.readAllBytes(new java.io.File(findFileById(id) 64 | .getFilePath()).toPath()); 65 | } catch (IOException e) { 66 | throw GenericErrorResponse.builder() 67 | .message("Unable to read file from storage") 68 | .httpStatus(HttpStatus.INTERNAL_SERVER_ERROR) 69 | .build(); 70 | } 71 | } 72 | 73 | public void deleteImageFromFileSystem(String id) { 74 | java.io.File file = new java.io.File(findFileById(id).getFilePath()); 75 | 76 | boolean deletionResult = file.delete(); 77 | 78 | if (deletionResult) fileRepository.deleteById(id); 79 | 80 | else throw GenericErrorResponse.builder() 81 | .message("Unable to delete file from storage") 82 | .httpStatus(HttpStatus.INTERNAL_SERVER_ERROR) 83 | .build(); 84 | } 85 | 86 | 87 | protected File findFileById(String id) { 88 | return fileRepository.findById(id) 89 | .orElseThrow(() -> GenericErrorResponse.builder() 90 | .message("File not found") 91 | .httpStatus(HttpStatus.NOT_FOUND) 92 | .build()); 93 | } 94 | } -------------------------------------------------------------------------------- /file-storage/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=0 2 | 3 | spring.servlet.multipart.max-file-size=5MB 4 | spring.servlet.multipart.max-request-size=5MB 5 | 6 | spring.application.name=file-storage 7 | 8 | spring.config.import=configserver:http://localhost:8888/ -------------------------------------------------------------------------------- /file-storage/src/test/java/com/safalifter/filestorage/FileStorageApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.filestorage; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class FileStorageApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /gateway/.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 | -------------------------------------------------------------------------------- /gateway/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/gateway/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /gateway/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /gateway/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | gateway 13 | 0.0.1-SNAPSHOT 14 | gateway 15 | gateway 16 | 17 | 17 18 | 2021.0.4 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-webflux 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-actuator 29 | 30 | 31 | org.springframework.cloud 32 | spring-cloud-starter-gateway 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-starter-netflix-eureka-client 37 | 38 | 39 | io.jsonwebtoken 40 | jjwt-api 41 | 0.11.5 42 | 43 | 44 | io.jsonwebtoken 45 | jjwt-impl 46 | 0.11.5 47 | 48 | 49 | io.jsonwebtoken 50 | jjwt-jackson 51 | 0.11.5 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-test 56 | test 57 | 58 | 59 | org.projectlombok 60 | lombok 61 | true 62 | 63 | 64 | org.springframework.data 65 | spring-data-commons 66 | 67 | 68 | 69 | 70 | 71 | org.springframework.cloud 72 | spring-cloud-dependencies 73 | ${spring-cloud.version} 74 | pom 75 | import 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-maven-plugin 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/safalifter/gateway/GatewayApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.gateway; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GatewayApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GatewayApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/safalifter/gateway/config/GatewayConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.gateway.config; 2 | 3 | import com.safalifter.gateway.filter.JwtAuthenticationFilter; 4 | import org.springframework.cloud.gateway.route.RouteLocator; 5 | import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class GatewayConfig { 11 | private final JwtAuthenticationFilter filter; 12 | 13 | public GatewayConfig(JwtAuthenticationFilter filter) { 14 | this.filter = filter; 15 | } 16 | 17 | @Bean 18 | public RouteLocator routes(RouteLocatorBuilder builder) { 19 | return builder.routes() 20 | .route("user-service", r -> r.path("/v1/user/**") 21 | .filters(f -> f.filter(filter)) 22 | .uri("lb://user-service")) 23 | 24 | .route("job-service", r -> r.path("/v1/job-service/**") 25 | .filters(f -> f.filter(filter)) 26 | .uri("lb://job-service")) 27 | 28 | .route("notification-service", r -> r.path("/v1/notification/**") 29 | .filters(f -> f.filter(filter)) 30 | .uri("lb://notification-service")) 31 | 32 | .route("auth-service", r -> r.path("/v1/auth/**") 33 | .uri("lb://auth-service")) 34 | 35 | .route("file-storage", r -> r.path("/v1/file-storage/**") 36 | .filters(f -> f.filter(filter)) 37 | .uri("lb://file-storage")) 38 | .build(); 39 | } 40 | } -------------------------------------------------------------------------------- /gateway/src/main/java/com/safalifter/gateway/filter/JwtAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.gateway.filter; 2 | 3 | import com.safalifter.gateway.util.JwtUtil; 4 | import org.springframework.cloud.gateway.filter.GatewayFilter; 5 | import org.springframework.cloud.gateway.filter.GatewayFilterChain; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.server.reactive.ServerHttpRequest; 8 | import org.springframework.http.server.reactive.ServerHttpResponse; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.web.server.ServerWebExchange; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.List; 14 | import java.util.function.Predicate; 15 | 16 | @Component 17 | public class JwtAuthenticationFilter implements GatewayFilter { 18 | private final JwtUtil jwtUtil; 19 | 20 | public JwtAuthenticationFilter(JwtUtil jwtUtil) { 21 | this.jwtUtil = jwtUtil; 22 | } 23 | 24 | @Override 25 | public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { 26 | ServerHttpRequest request = exchange.getRequest(); 27 | 28 | final List apiEndpoints = List.of("/v1/auth/login", "/v1/auth/register", "/eureka"); 29 | 30 | Predicate isApiSecured = r -> apiEndpoints.stream() 31 | .noneMatch(uri -> r.getURI().getPath().contains(uri)); 32 | 33 | if (isApiSecured.test(request)) { 34 | if (authMissing(request)) return onError(exchange); 35 | 36 | String token = request.getHeaders().getOrEmpty("Authorization").get(0); 37 | 38 | if (token != null && token.startsWith("Bearer ")) token = token.substring(7); 39 | 40 | try { 41 | jwtUtil.validateToken(token); 42 | } catch (Exception e) { 43 | return onError(exchange); 44 | } 45 | } 46 | return chain.filter(exchange); 47 | } 48 | 49 | private Mono onError(ServerWebExchange exchange) { 50 | ServerHttpResponse response = exchange.getResponse(); 51 | response.setStatusCode(HttpStatus.UNAUTHORIZED); 52 | return response.setComplete(); 53 | } 54 | 55 | private boolean authMissing(ServerHttpRequest request) { 56 | return !request.getHeaders().containsKey("Authorization"); 57 | } 58 | } -------------------------------------------------------------------------------- /gateway/src/main/java/com/safalifter/gateway/util/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.gateway.util; 2 | 3 | import io.jsonwebtoken.Jwts; 4 | import io.jsonwebtoken.io.Decoders; 5 | import io.jsonwebtoken.security.Keys; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.security.Key; 9 | 10 | @Component 11 | public class JwtUtil { 12 | public static final String SECRET = "5367566B59703373367639792F423F4528482B4D6251655468576D5A71347437"; 13 | 14 | public void validateToken(final String token) { 15 | Jwts.parserBuilder().setSigningKey(getSignKey()).build().parseClaimsJws(token); 16 | } 17 | 18 | private Key getSignKey() { 19 | byte[] keyBytes = Decoders.BASE64.decode(SECRET); 20 | return Keys.hmacShaKeyFor(keyBytes); 21 | } 22 | } -------------------------------------------------------------------------------- /gateway/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | spring: 5 | application: 6 | name: api-gateway 7 | cloud: 8 | gateway: 9 | discovery: 10 | locator: 11 | enabled: true 12 | httpclient: 13 | connect-timeout: 60000 14 | response-timeout: 60s -------------------------------------------------------------------------------- /gateway/src/test/java/com/safalifter/gateway/GatewayApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.gateway; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class GatewayApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /job-service/.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 | -------------------------------------------------------------------------------- /job-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/job-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /job-service/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /job-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | job-service 13 | 0.0.1-SNAPSHOT 14 | job-service 15 | Demo project for Spring Boot 16 | 17 | 17 18 | 2021.0.8 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-validation 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-netflix-eureka-client 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-openfeign 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-config 44 | 45 | 46 | org.springframework.kafka 47 | spring-kafka 48 | 49 | 50 | org.springframework.kafka 51 | spring-kafka-test 52 | test 53 | 54 | 55 | org.springdoc 56 | springdoc-openapi-ui 57 | 1.6.15 58 | 59 | 60 | org.postgresql 61 | postgresql 62 | runtime 63 | 64 | 65 | org.projectlombok 66 | lombok 67 | true 68 | 69 | 70 | 71 | org.modelmapper 72 | modelmapper 73 | 3.1.1 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-security 79 | 80 | 81 | io.jsonwebtoken 82 | jjwt-api 83 | 0.11.5 84 | 85 | 86 | io.jsonwebtoken 87 | jjwt-impl 88 | 0.11.5 89 | 90 | 91 | io.jsonwebtoken 92 | jjwt-jackson 93 | 0.11.5 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-starter-test 99 | test 100 | 101 | 102 | 103 | 104 | 105 | org.springframework.cloud 106 | spring-cloud-dependencies 107 | ${spring-cloud.version} 108 | pom 109 | import 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.springframework.boot 118 | spring-boot-maven-plugin 119 | 120 | 121 | 122 | org.projectlombok 123 | lombok 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/JobServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.openfeign.EnableFeignClients; 6 | 7 | @SpringBootApplication 8 | @EnableFeignClients 9 | public class JobServiceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(JobServiceApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/client/CustomErrorDecoder.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.client; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.safalifter.jobservice.exc.GenericErrorResponse; 5 | import feign.Response; 6 | import feign.codec.ErrorDecoder; 7 | import org.apache.commons.io.IOUtils; 8 | import org.springframework.http.HttpStatus; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.Map; 14 | 15 | public class CustomErrorDecoder implements ErrorDecoder { 16 | private final ObjectMapper mapper = new ObjectMapper(); 17 | 18 | @Override 19 | public Exception decode(String methodKey, Response response) { 20 | try (InputStream body = response.body().asInputStream()) { 21 | Map errors = 22 | mapper.readValue(IOUtils.toString(body, StandardCharsets.UTF_8), Map.class); 23 | return GenericErrorResponse 24 | .builder() 25 | .httpStatus(HttpStatus.valueOf(response.status())) 26 | .message(errors.get("error")) 27 | .build(); 28 | 29 | } catch (IOException exception) { 30 | throw GenericErrorResponse.builder() 31 | .httpStatus(HttpStatus.valueOf(response.status())) 32 | .message(exception.getMessage()) 33 | .build(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/client/FileStorageClient.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.client; 2 | 3 | import org.springframework.cloud.openfeign.FeignClient; 4 | import org.springframework.http.MediaType; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.DeleteMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestPart; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | @FeignClient(name = "file-storage", path = "/v1/file-storage") 13 | public interface FileStorageClient { 14 | @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) 15 | ResponseEntity uploadImageToFIleSystem(@RequestPart("image") MultipartFile file); 16 | 17 | @DeleteMapping("/delete/{id}") 18 | ResponseEntity deleteImageFromFileSystem(@PathVariable String id); 19 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/client/UserServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.client; 2 | 3 | import com.safalifter.jobservice.dto.UserDto; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | 9 | @FeignClient(name = "user-service", path = "/v1/user") 10 | public interface UserServiceClient { 11 | @GetMapping("/getUserById/{id}") 12 | ResponseEntity getUserById(@PathVariable String id); 13 | } 14 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/config/BeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.config; 2 | 3 | 4 | import org.modelmapper.Conditions; 5 | import org.modelmapper.ModelMapper; 6 | import org.modelmapper.convention.MatchingStrategies; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class BeanConfig { 12 | @Bean 13 | public ModelMapper getModelMapper() { 14 | ModelMapper modelMapper = new ModelMapper(); 15 | modelMapper.getConfiguration() 16 | .setMatchingStrategy(MatchingStrategies.LOOSE) 17 | .setPropertyCondition(Conditions.isNotNull()); 18 | return modelMapper; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/config/FeignConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.config; 2 | 3 | import com.safalifter.jobservice.client.CustomErrorDecoder; 4 | import feign.codec.ErrorDecoder; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class FeignConfig { 10 | @Bean 11 | public ErrorDecoder errorDecoder() { 12 | return new CustomErrorDecoder(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/config/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.config; 2 | 3 | import org.apache.kafka.clients.admin.NewTopic; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.kafka.config.TopicBuilder; 8 | 9 | @Configuration 10 | public class KafkaConfig { 11 | @Value("${spring.kafka.topic.name}") 12 | private String topicName; 13 | 14 | @Bean 15 | public NewTopic topic() { 16 | return TopicBuilder.name(topicName) 17 | .build(); 18 | } 19 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/config/OpenApiConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.config; 2 | 3 | import io.swagger.v3.oas.models.Components; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import io.swagger.v3.oas.models.info.License; 7 | import io.swagger.v3.oas.models.security.SecurityRequirement; 8 | import io.swagger.v3.oas.models.security.SecurityScheme; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | @Configuration 14 | public class OpenApiConfig { 15 | @Bean 16 | public OpenAPI openAPI(@Value("${application-title}") String title, 17 | @Value("${application-description}") String description, 18 | @Value("${application-version}") String version, 19 | @Value("${application-license}") String license) { 20 | return new OpenAPI().addSecurityItem(new SecurityRequirement() 21 | .addList("Bearer Authentication")) 22 | .components(new Components().addSecuritySchemes 23 | ("Bearer Authentication", createAPIKeyScheme())) 24 | .info(new Info() 25 | .title(title) 26 | .description(description) 27 | .version(version) 28 | .license(new License().name(license))); 29 | } 30 | 31 | private SecurityScheme createAPIKeyScheme() { 32 | return new SecurityScheme().type(SecurityScheme.Type.HTTP) 33 | .bearerFormat("JWT") 34 | .scheme("bearer"); 35 | } 36 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.config; 2 | 3 | import com.safalifter.jobservice.jwt.JwtAuthenticationFilter; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.lang.NonNull; 8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; 12 | import org.springframework.security.web.SecurityFilterChain; 13 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 14 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 15 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 16 | 17 | @Configuration 18 | @EnableWebSecurity 19 | @EnableGlobalMethodSecurity(prePostEnabled = true) 20 | @RequiredArgsConstructor 21 | public class SecurityConfig { 22 | private final JwtAuthenticationFilter jwtAuthenticationFilter; 23 | 24 | @Bean 25 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 26 | return http.csrf().disable() 27 | .authorizeRequests() 28 | .anyRequest().permitAll() 29 | .and() 30 | .formLogin().disable() 31 | .httpBasic().disable() 32 | .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) 33 | .build(); 34 | } 35 | 36 | @Bean 37 | public WebSecurityCustomizer webSecurityCustomizer() { 38 | return (web) -> web.ignoring().antMatchers( 39 | "/swagger-resources/**", 40 | "/swagger-ui.html/**", 41 | "/swagger-resources/**", 42 | "/swagger-ui/**", 43 | "/v3/api-docs/**"); 44 | } 45 | 46 | @Bean 47 | public WebMvcConfigurer corsConfigurer() { 48 | return new WebMvcConfigurer() { 49 | @Override 50 | public void addCorsMappings(@NonNull CorsRegistry registry) { 51 | registry.addMapping("/**") 52 | .allowedMethods("*"); 53 | } 54 | }; 55 | } 56 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/controller/AdvertController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.controller; 2 | 3 | import com.safalifter.jobservice.dto.AdvertDto; 4 | import com.safalifter.jobservice.enums.Advertiser; 5 | import com.safalifter.jobservice.request.advert.AdvertCreateRequest; 6 | import com.safalifter.jobservice.request.advert.AdvertUpdateRequest; 7 | import com.safalifter.jobservice.service.AdvertService; 8 | import lombok.RequiredArgsConstructor; 9 | import org.modelmapper.ModelMapper; 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 | import org.springframework.web.multipart.MultipartFile; 15 | 16 | import javax.validation.Valid; 17 | import java.util.List; 18 | 19 | @RestController 20 | @RequestMapping("/v1/job-service/advert") 21 | @RequiredArgsConstructor 22 | public class AdvertController { 23 | private final AdvertService advertService; 24 | private final ModelMapper modelMapper; 25 | 26 | @PostMapping("/create") 27 | public ResponseEntity createAdvert(@Valid @RequestPart AdvertCreateRequest request, 28 | @RequestPart(required = false) MultipartFile file) { 29 | return ResponseEntity.status(HttpStatus.CREATED) 30 | .body(modelMapper.map(advertService.createAdvert(request, file), AdvertDto.class)); 31 | } 32 | 33 | @GetMapping("/getAll") 34 | public ResponseEntity> getAll() { 35 | return ResponseEntity.ok(advertService.getAll().stream() 36 | .map(advert -> modelMapper.map(advert, AdvertDto.class)).toList()); 37 | } 38 | 39 | @GetMapping("/getAdvertById/{id}") 40 | public ResponseEntity getAdvertById(@PathVariable String id) { 41 | return ResponseEntity.ok(modelMapper.map(advertService.getAdvertById(id), AdvertDto.class)); 42 | } 43 | 44 | @GetMapping("/getAdvertsByUserId/{id}") 45 | public ResponseEntity> getAdvertsByUserId(@PathVariable String id, 46 | @RequestParam Advertiser type) { 47 | return ResponseEntity.ok(advertService.getAdvertsByUserId(id, type).stream() 48 | .map(advert -> modelMapper.map(advert, AdvertDto.class)).toList()); 49 | } 50 | 51 | @PutMapping("/update") 52 | @PreAuthorize("hasRole('ADMIN') or @advertService.authorizeCheck(#request.id, principal)") 53 | public ResponseEntity updateAdvertById(@Valid @RequestPart AdvertUpdateRequest request, 54 | @RequestPart(required = false) MultipartFile file) { 55 | return ResponseEntity.ok(modelMapper.map(advertService.updateAdvertById(request, file), AdvertDto.class)); 56 | } 57 | 58 | @DeleteMapping("/deleteAdvertById/{id}") 59 | @PreAuthorize("hasRole('ADMIN') or @advertService.authorizeCheck(#id, principal)") 60 | public ResponseEntity deleteAdvertById(@PathVariable String id) { 61 | advertService.deleteAdvertById(id); 62 | return ResponseEntity.ok().build(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/controller/CategoryController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.controller; 2 | 3 | import com.safalifter.jobservice.dto.CategoryDto; 4 | import com.safalifter.jobservice.dto.JobDto; 5 | import com.safalifter.jobservice.request.category.CategoryCreateRequest; 6 | import com.safalifter.jobservice.request.category.CategoryUpdateRequest; 7 | import com.safalifter.jobservice.service.CategoryService; 8 | import lombok.RequiredArgsConstructor; 9 | import org.modelmapper.ModelMapper; 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 | import org.springframework.web.multipart.MultipartFile; 15 | 16 | import javax.validation.Valid; 17 | import java.util.List; 18 | 19 | @RestController 20 | @RequestMapping("/v1/job-service/category") 21 | @RequiredArgsConstructor 22 | public class CategoryController { 23 | private final CategoryService categoryService; 24 | private final ModelMapper modelMapper; 25 | 26 | @PostMapping("/create") 27 | @PreAuthorize("hasRole('ADMIN')") 28 | ResponseEntity createCategory(@Valid @RequestPart CategoryCreateRequest request, 29 | @RequestPart(required = false) MultipartFile file) { 30 | return ResponseEntity.status(HttpStatus.CREATED) 31 | .body(modelMapper.map(categoryService.createCategory(request,file), CategoryDto.class)); 32 | } 33 | 34 | @GetMapping("/getAll") 35 | ResponseEntity> getAll() { 36 | return ResponseEntity.ok(categoryService.getAll().stream() 37 | .map(category -> modelMapper.map(category, CategoryDto.class)).toList()); 38 | } 39 | 40 | @GetMapping("/getCategoryById/{id}") 41 | ResponseEntity getCategoryById(@PathVariable String id) { 42 | return ResponseEntity.ok(modelMapper.map(categoryService.getCategoryById(id), CategoryDto.class)); 43 | } 44 | 45 | @PutMapping("/update") 46 | @PreAuthorize("hasRole('ADMIN')") 47 | ResponseEntity updateCategoryById(@Valid @RequestPart CategoryUpdateRequest request, 48 | @RequestPart(required = false) MultipartFile file) { 49 | return ResponseEntity.ok(modelMapper.map(categoryService.updateCategoryById(request,file), JobDto.class)); 50 | } 51 | 52 | @DeleteMapping("/deleteCategoryById/{id}") 53 | @PreAuthorize("hasRole('ADMIN')") 54 | ResponseEntity deleteCategoryById(@PathVariable String id) { 55 | categoryService.deleteCategoryById(id); 56 | return ResponseEntity.ok().build(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/controller/JobController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.controller; 2 | 3 | import com.safalifter.jobservice.dto.JobDto; 4 | import com.safalifter.jobservice.request.job.JobCreateRequest; 5 | import com.safalifter.jobservice.request.job.JobUpdateRequest; 6 | import com.safalifter.jobservice.service.JobService; 7 | import lombok.RequiredArgsConstructor; 8 | import org.modelmapper.ModelMapper; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | import org.springframework.web.multipart.MultipartFile; 14 | 15 | import javax.validation.Valid; 16 | import java.util.List; 17 | 18 | @RestController 19 | @RequestMapping("/v1/job-service/job") 20 | @RequiredArgsConstructor 21 | public class JobController { 22 | private final JobService jobService; 23 | private final ModelMapper modelMapper; 24 | 25 | @PostMapping("/create") 26 | @PreAuthorize("hasRole('ADMIN')") 27 | ResponseEntity createJob(@Valid @RequestPart JobCreateRequest request, 28 | @RequestPart(required = false) MultipartFile file) { 29 | return ResponseEntity.status(HttpStatus.CREATED) 30 | .body(modelMapper.map(jobService.createJob(request, file), JobDto.class)); 31 | } 32 | 33 | @PostMapping("/getJobsThatFitYourNeeds/{needs}") 34 | ResponseEntity> getJobsThatFitYourNeeds(@PathVariable String needs) { 35 | return ResponseEntity.ok(jobService.getJobsThatFitYourNeeds(needs).stream() 36 | .map(job -> modelMapper.map(job, JobDto.class)).toList()); 37 | } 38 | 39 | @GetMapping("/getAll") 40 | ResponseEntity> getAll() { 41 | return ResponseEntity.ok(jobService.getAll().stream() 42 | .map(job -> modelMapper.map(job, JobDto.class)).toList()); 43 | } 44 | 45 | @GetMapping("/getJobById/{id}") 46 | ResponseEntity getJobById(@PathVariable String id) { 47 | return ResponseEntity.ok(modelMapper.map(jobService.getJobById(id), JobDto.class)); 48 | } 49 | 50 | @GetMapping("/getJobsByCategoryId/{id}") 51 | ResponseEntity> getJobsByCategoryId(@PathVariable String id) { 52 | return ResponseEntity.ok(jobService.getJobsByCategoryId(id).stream() 53 | .map(job -> modelMapper.map(job, JobDto.class)).toList()); 54 | } 55 | 56 | @PutMapping("/update") 57 | @PreAuthorize("hasRole('ADMIN')") 58 | ResponseEntity updateJob(@Valid @RequestPart JobUpdateRequest request, 59 | @RequestPart(required = false) MultipartFile file) { 60 | return ResponseEntity.ok(modelMapper.map(jobService.updateJob(request, file), JobDto.class)); 61 | } 62 | 63 | @DeleteMapping("/deleteJobById/{id}") 64 | @PreAuthorize("hasRole('ADMIN')") 65 | ResponseEntity deleteJobById(@PathVariable String id) { 66 | jobService.deleteJobById(id); 67 | return ResponseEntity.ok().build(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/controller/OfferController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.controller; 2 | 3 | import com.safalifter.jobservice.dto.OfferDto; 4 | import com.safalifter.jobservice.request.offer.MakeAnOfferRequest; 5 | import com.safalifter.jobservice.request.offer.OfferUpdateRequest; 6 | import com.safalifter.jobservice.service.OfferService; 7 | import lombok.RequiredArgsConstructor; 8 | import org.modelmapper.ModelMapper; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.Valid; 15 | import java.util.List; 16 | 17 | @RestController 18 | @RequestMapping("/v1/job-service/offer") 19 | @RequiredArgsConstructor 20 | public class OfferController { 21 | private final OfferService offerService; 22 | private final ModelMapper modelMapper; 23 | 24 | @PostMapping("/makeAnOffer") 25 | public ResponseEntity makeAnOffer(@Valid @RequestBody MakeAnOfferRequest request) { 26 | return ResponseEntity.status(HttpStatus.CREATED) 27 | .body(modelMapper.map(offerService.makeAnOffer(request), OfferDto.class)); 28 | } 29 | 30 | @GetMapping("/getOfferById/{id}") 31 | public ResponseEntity getOfferById(@PathVariable String id) { 32 | return ResponseEntity.ok(modelMapper.map(offerService.getOfferById(id), OfferDto.class)); 33 | } 34 | 35 | @GetMapping("/getOffersByUserId/{id}") 36 | public ResponseEntity> getOffersByUserId(@PathVariable String id) { 37 | return ResponseEntity.ok(offerService.getOffersByUserId(id).stream() 38 | .map(offer -> modelMapper.map(offer, OfferDto.class)).toList()); 39 | } 40 | 41 | @GetMapping("/getOffersByAdvertId/{id}") 42 | public ResponseEntity> getOffersByAdvertId(@PathVariable String id) { 43 | return ResponseEntity.ok(offerService.getOffersByAdvertId(id).stream() 44 | .map(offer -> modelMapper.map(offer, OfferDto.class)).toList()); 45 | } 46 | 47 | @PutMapping("/update") 48 | @PreAuthorize("hasRole('ADMIN') or @offerService.authorizeCheck(#request.id, principal)") 49 | public ResponseEntity updateOfferById(@Valid @RequestBody OfferUpdateRequest request) { 50 | return ResponseEntity.ok(modelMapper.map(offerService.updateOfferById(request), OfferDto.class)); 51 | } 52 | 53 | @DeleteMapping("/deleteOfferById/{id}") 54 | @PreAuthorize("hasRole('ADMIN') or @offerService.authorizeCheck(#id, principal)") 55 | public ResponseEntity deleteOfferById(@PathVariable String id) { 56 | offerService.deleteOfferById(id); 57 | return ResponseEntity.ok().build(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/dto/AdvertDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.safalifter.jobservice.enums.AdvertStatus; 5 | import com.safalifter.jobservice.enums.Advertiser; 6 | import lombok.Data; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | @JsonInclude(JsonInclude.Include.NON_NULL) 12 | public class AdvertDto { 13 | private String id; 14 | private String name; 15 | private String description; 16 | private int deliveryTime; 17 | private int price; 18 | private AdvertStatus status; 19 | private Advertiser advertiser; 20 | private String userId; 21 | private String jobId; 22 | private List imagesId; 23 | } 24 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/dto/CategoryDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class CategoryDto { 11 | private String id; 12 | private String name; 13 | private String description; 14 | private List jobs; 15 | private List imagesId; 16 | } 17 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/dto/JobDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class JobDto { 11 | private String id; 12 | private String name; 13 | private String description; 14 | private String categoryId; 15 | private List adverts; 16 | private List keys; 17 | private List imagesId; 18 | } 19 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/dto/OfferDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.safalifter.jobservice.enums.OfferStatus; 5 | import lombok.Data; 6 | 7 | @Data 8 | @JsonInclude(JsonInclude.Include.NON_NULL) 9 | public class OfferDto { 10 | private String id; 11 | private String userId; 12 | private String advertId; 13 | private int offeredPrice; 14 | private OfferStatus status; 15 | } 16 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | @Data 7 | @JsonInclude(JsonInclude.Include.NON_NULL) 8 | public class UserDto { 9 | private String id; 10 | private String username; 11 | private String email; 12 | } 13 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/enums/AdvertStatus.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.enums; 2 | 3 | public enum AdvertStatus { 4 | OPEN, CLOSED, CANCELLED, ASSIGNED, REVIEWED 5 | } 6 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/enums/Advertiser.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.enums; 2 | 3 | public enum Advertiser { 4 | EMPLOYEE, CUSTOMER 5 | } 6 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/enums/OfferStatus.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.enums; 2 | 3 | public enum OfferStatus { 4 | OPEN, CLOSED, ACCEPTED, REJECTED 5 | } 6 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/exc/GeneralExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.exc; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.lang.NonNull; 7 | import org.springframework.security.access.AccessDeniedException; 8 | import org.springframework.validation.FieldError; 9 | import org.springframework.web.bind.MethodArgumentNotValidException; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.RestControllerAdvice; 12 | import org.springframework.web.context.request.WebRequest; 13 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | @RestControllerAdvice 19 | public class GeneralExceptionHandler extends ResponseEntityExceptionHandler { 20 | 21 | @Override 22 | protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, 23 | @NonNull HttpHeaders headers, 24 | @NonNull HttpStatus status, 25 | @NonNull WebRequest request) { 26 | Map errors = new HashMap<>(); 27 | ex.getBindingResult().getAllErrors() 28 | .forEach(x -> errors.put(((FieldError) x).getField(), x.getDefaultMessage())); 29 | return ResponseEntity.badRequest().body(errors); 30 | } 31 | 32 | @ExceptionHandler(GenericErrorResponse.class) 33 | public ResponseEntity genericError(GenericErrorResponse exception) { 34 | Map errors = new HashMap<>(); 35 | errors.put("error", exception.getMessage()); 36 | return new ResponseEntity<>(errors, exception.getHttpStatus()); 37 | } 38 | 39 | @ExceptionHandler(Exception.class) 40 | public final ResponseEntity handleAllException(Exception ex) { 41 | Map errors = new HashMap<>(); 42 | errors.put("error", ex.getMessage()); 43 | return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); 44 | } 45 | 46 | @ExceptionHandler(NotFoundException.class) 47 | public ResponseEntity notFoundException(NotFoundException exception) { 48 | Map errors = new HashMap<>(); 49 | errors.put("error", exception.getMessage()); 50 | return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND); 51 | } 52 | 53 | @ExceptionHandler(UnauthorizedException.class) 54 | public ResponseEntity unauthorizedException(UnauthorizedException exception) { 55 | Map errors = new HashMap<>(); 56 | errors.put("error", exception.getMessage()); 57 | return new ResponseEntity<>(errors, HttpStatus.UNAUTHORIZED); 58 | } 59 | 60 | @ExceptionHandler(AccessDeniedException.class) 61 | public ResponseEntity accessDeniedException(AccessDeniedException exception) { 62 | Map errors = new HashMap<>(); 63 | errors.put("error", exception.getMessage()); 64 | return new ResponseEntity<>(errors, HttpStatus.FORBIDDEN); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/exc/GenericErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.exc; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | @Builder 8 | @Getter 9 | public class GenericErrorResponse extends RuntimeException { 10 | private final String message; 11 | private final HttpStatus httpStatus; 12 | 13 | public GenericErrorResponse(String message, HttpStatus httpStatus) { 14 | super(message); 15 | this.message = message; 16 | this.httpStatus = httpStatus; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/exc/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.exc; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.NOT_FOUND) 7 | public class NotFoundException extends RuntimeException { 8 | public NotFoundException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/exc/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.exc; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 7 | public class UnauthorizedException extends RuntimeException { 8 | public UnauthorizedException(String message) { 9 | super(message); 10 | } 11 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/jwt/JwtAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.jwt; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.lang.NonNull; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.util.StringUtils; 12 | import org.springframework.web.filter.OncePerRequestFilter; 13 | 14 | import javax.servlet.FilterChain; 15 | import javax.servlet.ServletException; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | import java.util.Collections; 20 | 21 | @Component 22 | @RequiredArgsConstructor 23 | public class JwtAuthenticationFilter extends OncePerRequestFilter { 24 | private final JwtUtil jwtUtil; 25 | 26 | @Override 27 | protected void doFilterInternal(@NonNull HttpServletRequest request, 28 | @NonNull HttpServletResponse response, 29 | @NonNull FilterChain filterChain) throws ServletException, IOException { 30 | try { 31 | String token = request.getHeader("Authorization"); 32 | 33 | if (StringUtils.hasText(token) && token.startsWith("Bearer ")) { 34 | Claims claims = jwtUtil.getClaims(token.substring(7)); 35 | 36 | SimpleGrantedAuthority authority = new SimpleGrantedAuthority(claims.getIssuer()); 37 | UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( 38 | claims.getSubject(), null, Collections.singleton(authority)); 39 | authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); 40 | 41 | SecurityContextHolder.getContext().setAuthentication(authentication); 42 | } 43 | 44 | } catch (Exception e) { 45 | System.out.println(e.getMessage()); 46 | } 47 | filterChain.doFilter(request, response); 48 | } 49 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/jwt/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.jwt; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.io.Decoders; 6 | import io.jsonwebtoken.security.Keys; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.security.Key; 10 | 11 | @Component 12 | public class JwtUtil { 13 | public static final String SECRET = "5367566B59703373367639792F423F4528482B4D6251655468576D5A71347437"; 14 | 15 | private Key getSignKey() { 16 | byte[] keyBytes = Decoders.BASE64.decode(SECRET); 17 | return Keys.hmacShaKeyFor(keyBytes); 18 | } 19 | 20 | public Claims getClaims(String token) { 21 | return Jwts.parserBuilder() 22 | .setSigningKey(getSignKey()) 23 | .build() 24 | .parseClaimsJws(token) 25 | .getBody(); 26 | } 27 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/model/Advert.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.model; 2 | 3 | import com.safalifter.jobservice.enums.AdvertStatus; 4 | import com.safalifter.jobservice.enums.Advertiser; 5 | import lombok.*; 6 | 7 | import javax.persistence.*; 8 | import java.util.List; 9 | 10 | @Entity(name = "adverts") 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | @Getter 15 | @Setter 16 | public class Advert extends BaseEntity { 17 | private String userId; 18 | private String name; 19 | private String description; 20 | private int deliveryTime; 21 | private int price; 22 | private String imageId; 23 | 24 | @Enumerated(EnumType.STRING) 25 | private AdvertStatus status; 26 | 27 | @Enumerated(EnumType.STRING) 28 | private Advertiser advertiser; 29 | 30 | @ManyToOne(fetch = FetchType.LAZY, optional = false) 31 | @JoinColumn(name = "job_id") 32 | private Job job; 33 | 34 | @OneToMany(mappedBy = "advert", cascade = CascadeType.ALL) 35 | private List offers; 36 | } 37 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/model/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.model; 2 | 3 | 4 | import lombok.Getter; 5 | import org.hibernate.annotations.CreationTimestamp; 6 | import org.hibernate.annotations.GenericGenerator; 7 | import org.hibernate.annotations.UpdateTimestamp; 8 | 9 | import javax.persistence.GeneratedValue; 10 | import javax.persistence.Id; 11 | import javax.persistence.MappedSuperclass; 12 | import java.io.Serializable; 13 | import java.time.LocalDateTime; 14 | 15 | @MappedSuperclass 16 | @Getter 17 | public abstract class BaseEntity implements Serializable { 18 | @Id 19 | @GeneratedValue(generator = "UUID") 20 | @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") 21 | private String id; 22 | 23 | @CreationTimestamp 24 | private LocalDateTime creationTimestamp; 25 | 26 | @UpdateTimestamp 27 | private LocalDateTime updateTimestamp; 28 | } 29 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/model/Category.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.model; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.CascadeType; 6 | import javax.persistence.Entity; 7 | import javax.persistence.OneToMany; 8 | import java.util.List; 9 | 10 | @Entity(name = "categories") 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | @Getter 15 | @Setter 16 | public class Category extends BaseEntity { 17 | private String name; 18 | private String description; 19 | private String imageId; 20 | 21 | @OneToMany(mappedBy = "category", cascade = CascadeType.ALL) 22 | private List jobs; 23 | } -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/model/Job.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.model; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | @Entity(name = "jobs") 10 | @Builder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Getter 14 | @Setter 15 | public class Job extends BaseEntity { 16 | private String name; 17 | private String description; 18 | private String imageId; 19 | 20 | @ManyToOne(fetch = FetchType.LAZY, optional = false) 21 | @JoinColumn(name = "category_id") 22 | private Category category; 23 | 24 | @OneToMany(mappedBy = "job", cascade = CascadeType.ALL) 25 | private List adverts; 26 | 27 | @ElementCollection 28 | private List keys = Collections.emptyList(); 29 | } 30 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/model/Offer.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.model; 2 | 3 | import com.safalifter.jobservice.enums.OfferStatus; 4 | import lombok.*; 5 | 6 | import javax.persistence.*; 7 | 8 | @Entity(name = "offers") 9 | @Builder 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Getter 13 | @Setter 14 | public class Offer extends BaseEntity { 15 | private String userId; 16 | private int offeredPrice; 17 | 18 | @Enumerated(EnumType.STRING) 19 | private OfferStatus status; 20 | 21 | @ManyToOne(fetch = FetchType.LAZY, optional = false) 22 | @JoinColumn(name = "advert_id") 23 | private Advert advert; 24 | } 25 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/repository/AdvertRepository.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.repository; 2 | 3 | import com.safalifter.jobservice.enums.Advertiser; 4 | import com.safalifter.jobservice.model.Advert; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.List; 8 | 9 | public interface AdvertRepository extends JpaRepository { 10 | List getAdvertsByUserIdAndAdvertiser(String id, Advertiser advertiser); 11 | } 12 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/repository/CategoryRepository.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.repository; 2 | 3 | import com.safalifter.jobservice.model.Category; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface CategoryRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/repository/JobRepository.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.repository; 2 | 3 | import com.safalifter.jobservice.model.Job; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface JobRepository extends JpaRepository { 9 | List getJobsByCategoryId(String id); 10 | 11 | List getJobsByKeysContainsIgnoreCase(String key); 12 | } 13 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/repository/OfferRepository.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.repository; 2 | 3 | import com.safalifter.jobservice.model.Offer; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface OfferRepository extends JpaRepository { 9 | List getOffersByUserId(String id); 10 | 11 | List getOffersByAdvertId(String id); 12 | } 13 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/advert/AdvertCreateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.advert; 2 | 3 | import com.safalifter.jobservice.enums.Advertiser; 4 | import lombok.Data; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | import javax.validation.constraints.NotNull; 8 | 9 | @Data 10 | public class AdvertCreateRequest { 11 | @NotBlank(message = "Advert name is required") 12 | private String name; 13 | private String description; 14 | @NotNull(message = "Delivery time is required") 15 | private int deliveryTime; 16 | @NotNull(message = "Price is required") 17 | private int price; 18 | @NotBlank(message = "Advertiser is required") 19 | private Advertiser advertiser; 20 | @NotBlank(message = "User id is required") 21 | private String userId; 22 | @NotBlank(message = "Job id is required") 23 | private String jobId; 24 | } 25 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/advert/AdvertUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.advert; 2 | 3 | import com.safalifter.jobservice.enums.AdvertStatus; 4 | import lombok.Data; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | @Data 9 | public class AdvertUpdateRequest { 10 | @NotBlank(message = "Advert id is required") 11 | private String id; 12 | private String name; 13 | private String description; 14 | private int deliveryTime; 15 | private int price; 16 | private AdvertStatus status; 17 | } 18 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/category/CategoryCreateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.category; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Data 8 | public class CategoryCreateRequest { 9 | @NotBlank(message = "Category name is required") 10 | private String name; 11 | private String description; 12 | } 13 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/category/CategoryUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.category; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Data 8 | public class CategoryUpdateRequest { 9 | @NotBlank(message = "Category id is required") 10 | private String id; 11 | private String name; 12 | private String description; 13 | } 14 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/job/JobCreateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.job; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Data 8 | public class JobCreateRequest { 9 | @NotBlank(message = "Job name is required") 10 | private String name; 11 | private String description; 12 | @NotBlank(message = "Category id is required") 13 | private String categoryId; 14 | private String[] keys; 15 | } 16 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/job/JobUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.job; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Data 8 | public class JobUpdateRequest { 9 | @NotBlank(message = "Job id is required") 10 | private String id; 11 | private String name; 12 | private String description; 13 | private String categoryId; 14 | private String[] keys; 15 | } 16 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/notification/SendNotificationRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.notification; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class SendNotificationRequest { 9 | private String userId; 10 | private String offerId; 11 | private String message; 12 | } 13 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/offer/MakeAnOfferRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.offer; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | import javax.validation.constraints.NotNull; 7 | 8 | @Data 9 | public class MakeAnOfferRequest { 10 | @NotBlank(message = "User id is required") 11 | private String userId; 12 | @NotBlank(message = "Advert id is required") 13 | private String advertId; 14 | @NotNull(message = "Offered price is required") 15 | private int offeredPrice; 16 | } 17 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/request/offer/OfferUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.request.offer; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Data 8 | public class OfferUpdateRequest { 9 | @NotBlank(message = "Offer id is required") 10 | private String id; 11 | private int offeredPrice; 12 | } 13 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/service/AdvertService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.service; 2 | 3 | import com.safalifter.jobservice.client.FileStorageClient; 4 | import com.safalifter.jobservice.client.UserServiceClient; 5 | import com.safalifter.jobservice.dto.UserDto; 6 | import com.safalifter.jobservice.enums.AdvertStatus; 7 | import com.safalifter.jobservice.enums.Advertiser; 8 | import com.safalifter.jobservice.exc.NotFoundException; 9 | import com.safalifter.jobservice.model.Advert; 10 | import com.safalifter.jobservice.model.Job; 11 | import com.safalifter.jobservice.repository.AdvertRepository; 12 | import com.safalifter.jobservice.request.advert.AdvertCreateRequest; 13 | import com.safalifter.jobservice.request.advert.AdvertUpdateRequest; 14 | import lombok.RequiredArgsConstructor; 15 | import org.modelmapper.ModelMapper; 16 | import org.springframework.stereotype.Service; 17 | import org.springframework.web.multipart.MultipartFile; 18 | 19 | import java.util.List; 20 | import java.util.Optional; 21 | 22 | @Service 23 | @RequiredArgsConstructor 24 | public class AdvertService { 25 | private final AdvertRepository advertRepository; 26 | private final JobService jobService; 27 | private final UserServiceClient userServiceclient; 28 | private final FileStorageClient fileStorageClient; 29 | private final ModelMapper modelMapper; 30 | 31 | public Advert createAdvert(AdvertCreateRequest request, MultipartFile file) { 32 | String userId = getUserById(request.getUserId()).getId(); 33 | Job job = jobService.getJobById(request.getJobId()); 34 | 35 | String imageId = null; 36 | 37 | if (file != null) 38 | imageId = fileStorageClient.uploadImageToFIleSystem(file).getBody(); 39 | 40 | Advert toSave = Advert.builder() 41 | .userId(userId) 42 | .job(job) 43 | .name(request.getName()) 44 | .advertiser(request.getAdvertiser()) 45 | .deliveryTime(request.getDeliveryTime()) 46 | .description(request.getDescription()) 47 | .price(request.getPrice()) 48 | .status(AdvertStatus.OPEN) 49 | .imageId(imageId) 50 | .build(); 51 | return advertRepository.save(toSave); 52 | } 53 | 54 | public List getAll() { 55 | return advertRepository.findAll(); 56 | } 57 | 58 | public Advert getAdvertById(String id) { 59 | return findAdvertById(id); 60 | } 61 | 62 | public List getAdvertsByUserId(String id, Advertiser type) { 63 | String userId = getUserById(id).getId(); 64 | return advertRepository.getAdvertsByUserIdAndAdvertiser(userId, type); 65 | } 66 | 67 | public UserDto getUserById(String id) { 68 | return Optional.ofNullable(userServiceclient.getUserById(id).getBody()) 69 | .orElseThrow(() -> new NotFoundException("User not found")); 70 | } 71 | 72 | public Advert updateAdvertById(AdvertUpdateRequest request, MultipartFile file) { 73 | Advert toUpdate = findAdvertById(request.getId()); 74 | modelMapper.map(request, toUpdate); 75 | 76 | if (file != null) { 77 | String imageId = fileStorageClient.uploadImageToFIleSystem(file).getBody(); 78 | if (imageId != null) { 79 | fileStorageClient.deleteImageFromFileSystem(toUpdate.getImageId()); 80 | toUpdate.setImageId(imageId); 81 | } 82 | } 83 | 84 | return advertRepository.save(toUpdate); 85 | } 86 | 87 | public void deleteAdvertById(String id) { 88 | advertRepository.deleteById(id); 89 | } 90 | 91 | public boolean authorizeCheck(String id, String principal) { 92 | return getUserById(getAdvertById(id).getUserId()).getUsername().equals(principal); 93 | } 94 | 95 | protected Advert findAdvertById(String id) { 96 | return advertRepository.findById(id) 97 | .orElseThrow(() -> new NotFoundException("Advert not found")); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.service; 2 | 3 | import com.safalifter.jobservice.client.FileStorageClient; 4 | import com.safalifter.jobservice.exc.NotFoundException; 5 | import com.safalifter.jobservice.model.Category; 6 | import com.safalifter.jobservice.repository.CategoryRepository; 7 | import com.safalifter.jobservice.request.category.CategoryCreateRequest; 8 | import com.safalifter.jobservice.request.category.CategoryUpdateRequest; 9 | import lombok.RequiredArgsConstructor; 10 | import org.modelmapper.ModelMapper; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.web.multipart.MultipartFile; 13 | 14 | import java.util.List; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class CategoryService { 19 | private final CategoryRepository categoryRepository; 20 | private final FileStorageClient fileStorageClient; 21 | private final ModelMapper modelMapper; 22 | 23 | public Category createCategory(CategoryCreateRequest request, MultipartFile file) { 24 | String imageId = null; 25 | 26 | if (file != null) 27 | imageId = fileStorageClient.uploadImageToFIleSystem(file).getBody(); 28 | 29 | return categoryRepository.save( 30 | Category.builder() 31 | .name(request.getName()) 32 | .description(request.getDescription()) 33 | .imageId(imageId) 34 | .build()); 35 | } 36 | 37 | public List getAll() { 38 | return categoryRepository.findAll(); 39 | } 40 | 41 | public Category getCategoryById(String id) { 42 | return findCategoryById(id); 43 | } 44 | 45 | public Category updateCategoryById(CategoryUpdateRequest request, MultipartFile file) { 46 | Category toUpdate = findCategoryById(request.getId()); 47 | modelMapper.map(request, toUpdate); 48 | 49 | if (file != null) { 50 | String imageId = fileStorageClient.uploadImageToFIleSystem(file).getBody(); 51 | if (imageId != null) { 52 | fileStorageClient.deleteImageFromFileSystem(toUpdate.getImageId()); 53 | toUpdate.setImageId(imageId); 54 | } 55 | } 56 | 57 | return categoryRepository.save(toUpdate); 58 | } 59 | 60 | public void deleteCategoryById(String id) { 61 | categoryRepository.deleteById(id); 62 | } 63 | 64 | protected Category findCategoryById(String id) { 65 | return categoryRepository.findById(id) 66 | .orElseThrow(() -> new NotFoundException("Category not found")); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/service/JobService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.service; 2 | 3 | import com.safalifter.jobservice.client.FileStorageClient; 4 | import com.safalifter.jobservice.exc.NotFoundException; 5 | import com.safalifter.jobservice.model.Category; 6 | import com.safalifter.jobservice.model.Job; 7 | import com.safalifter.jobservice.repository.JobRepository; 8 | import com.safalifter.jobservice.request.job.JobCreateRequest; 9 | import com.safalifter.jobservice.request.job.JobUpdateRequest; 10 | import lombok.RequiredArgsConstructor; 11 | import org.modelmapper.ModelMapper; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.web.multipart.MultipartFile; 14 | 15 | import java.util.*; 16 | import java.util.stream.Collectors; 17 | 18 | @Service 19 | @RequiredArgsConstructor 20 | public class JobService { 21 | private final JobRepository jobRepository; 22 | private final CategoryService categoryService; 23 | private final FileStorageClient fileStorageClient; 24 | private final ModelMapper modelMapper; 25 | 26 | public Job createJob(JobCreateRequest request, MultipartFile file) { 27 | Category category = categoryService.getCategoryById(request.getCategoryId()); 28 | 29 | String imageId = null; 30 | 31 | if (file != null) 32 | imageId = fileStorageClient.uploadImageToFIleSystem(file).getBody(); 33 | 34 | return jobRepository.save(Job.builder() 35 | .name(request.getName()) 36 | .description(request.getDescription()) 37 | .category(category) 38 | .keys(Optional.of(List.of(request.getKeys())) 39 | .orElse(new ArrayList<>())) 40 | .imageId(imageId) 41 | .build()); 42 | } 43 | 44 | public List getAll() { 45 | return jobRepository.findAll(); 46 | } 47 | 48 | public Job getJobById(String id) { 49 | return findJobById(id); 50 | } 51 | 52 | public Job updateJob(JobUpdateRequest request, MultipartFile file) { 53 | Job toUpdate = findJobById(request.getCategoryId()); 54 | modelMapper.map(request, toUpdate); 55 | 56 | if (file != null) { 57 | String imageId = fileStorageClient.uploadImageToFIleSystem(file).getBody(); 58 | if (imageId != null) { 59 | fileStorageClient.deleteImageFromFileSystem(toUpdate.getImageId()); 60 | toUpdate.setImageId(imageId); 61 | } 62 | } 63 | 64 | return jobRepository.save(toUpdate); 65 | } 66 | 67 | public void deleteJobById(String id) { 68 | jobRepository.deleteById(id); 69 | } 70 | 71 | public List getJobsByCategoryId(String id) { 72 | return jobRepository.getJobsByCategoryId(id); 73 | } 74 | 75 | public List getJobsThatFitYourNeeds(String needs) { 76 | String[] keys = needs.replaceAll("\"", "").split(" "); 77 | HashMap map = new HashMap<>(); 78 | Arrays.stream(keys).forEach(key -> jobRepository.getJobsByKeysContainsIgnoreCase(key) 79 | .forEach(job -> { 80 | if (map.containsKey(job.getId())) { 81 | int count = map.get(job.getId()); 82 | map.put(job.getId(), count + 1); 83 | } else map.put(job.getId(), 1); 84 | })); 85 | return map.entrySet().stream() 86 | .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) 87 | .map(entry -> findJobById(entry.getKey())) 88 | .collect(Collectors.toList()); 89 | } 90 | 91 | protected Job findJobById(String id) { 92 | return jobRepository.findById(id) 93 | .orElseThrow(() -> new NotFoundException("Job not found")); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /job-service/src/main/java/com/safalifter/jobservice/service/OfferService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice.service; 2 | 3 | import com.safalifter.jobservice.client.UserServiceClient; 4 | import com.safalifter.jobservice.dto.UserDto; 5 | import com.safalifter.jobservice.enums.OfferStatus; 6 | import com.safalifter.jobservice.exc.NotFoundException; 7 | import com.safalifter.jobservice.model.Advert; 8 | import com.safalifter.jobservice.model.Offer; 9 | import com.safalifter.jobservice.repository.OfferRepository; 10 | import com.safalifter.jobservice.request.notification.SendNotificationRequest; 11 | import com.safalifter.jobservice.request.offer.MakeAnOfferRequest; 12 | import com.safalifter.jobservice.request.offer.OfferUpdateRequest; 13 | import lombok.RequiredArgsConstructor; 14 | import org.apache.kafka.clients.admin.NewTopic; 15 | import org.modelmapper.ModelMapper; 16 | import org.springframework.kafka.core.KafkaTemplate; 17 | import org.springframework.stereotype.Service; 18 | 19 | import java.util.List; 20 | import java.util.Optional; 21 | 22 | @Service 23 | @RequiredArgsConstructor 24 | public class OfferService { 25 | private final OfferRepository offerRepository; 26 | private final AdvertService advertService; 27 | private final UserServiceClient userServiceclient; 28 | private final KafkaTemplate kafkaTemplate; 29 | private final NewTopic topic; 30 | private final ModelMapper modelMapper; 31 | 32 | public Offer makeAnOffer(MakeAnOfferRequest request) { 33 | String userId = getUserById(request.getUserId()).getId(); 34 | Advert advert = advertService.getAdvertById(request.getAdvertId()); 35 | Offer toSave = Offer.builder() 36 | .userId(userId) 37 | .advert(advert) 38 | .offeredPrice(request.getOfferedPrice()) 39 | .status(OfferStatus.OPEN).build(); 40 | offerRepository.save(toSave); 41 | 42 | SendNotificationRequest notification = SendNotificationRequest.builder() 43 | .message("You have received an offer for your advertising.") 44 | .userId(advert.getUserId()) 45 | .offerId(toSave.getId()).build(); 46 | 47 | kafkaTemplate.send(topic.name(), notification); 48 | return toSave; 49 | } 50 | 51 | public Offer getOfferById(String id) { 52 | return findOfferById(id); 53 | } 54 | 55 | public List getOffersByAdvertId(String id) { 56 | Advert advert = advertService.getAdvertById(id); 57 | return offerRepository.getOffersByAdvertId(advert.getId()); 58 | } 59 | 60 | public List getOffersByUserId(String id) { 61 | String userId = getUserById(id).getId(); 62 | return offerRepository.getOffersByUserId(userId); 63 | } 64 | 65 | public UserDto getUserById(String id) { 66 | return Optional.ofNullable(userServiceclient.getUserById(id).getBody()) 67 | .orElseThrow(() -> new NotFoundException("User not found")); 68 | } 69 | 70 | public Offer updateOfferById(OfferUpdateRequest request) { 71 | Offer toUpdate = findOfferById(request.getId()); 72 | modelMapper.map(request, toUpdate); 73 | return offerRepository.save(toUpdate); 74 | } 75 | 76 | public void deleteOfferById(String id) { 77 | offerRepository.deleteById(id); 78 | } 79 | 80 | public boolean authorizeCheck(String id, String principal) { 81 | return getUserById(getOfferById(id).getUserId()).getUsername().equals(principal); 82 | } 83 | 84 | protected Offer findOfferById(String id) { 85 | return offerRepository.findById(id) 86 | .orElseThrow(() -> new NotFoundException("Offer not found")); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /job-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=0 2 | 3 | application-title=Job Service 4 | 5 | spring.application.name=job-service 6 | 7 | spring.kafka.producer.bootstrap-servers=localhost:9092 8 | spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer 9 | spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer 10 | spring.kafka.topic.name=notificationTopic 11 | 12 | spring.config.import=configserver:http://localhost:8888/ -------------------------------------------------------------------------------- /job-service/src/test/java/com/safalifter/jobservice/JobServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.jobservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class JobServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /notification-service/.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 | -------------------------------------------------------------------------------- /notification-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/notification-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /notification-service/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /notification-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | notification-service 13 | 0.0.1-SNAPSHOT 14 | notification-service 15 | notification-service 16 | 17 | 17 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.postgresql 30 | postgresql 31 | runtime 32 | 33 | 34 | org.springframework.kafka 35 | spring-kafka 36 | 37 | 38 | org.springframework.kafka 39 | spring-kafka-test 40 | test 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starter-netflix-eureka-client 45 | 4.0.2 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-starter-config 50 | 51 | 52 | org.projectlombok 53 | lombok 54 | true 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-test 59 | test 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.springframework.cloud 67 | spring-cloud-dependencies 68 | 2021.0.8 69 | pom 70 | import 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-maven-plugin 79 | 80 | 81 | 82 | org.projectlombok 83 | lombok 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/NotificationServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class NotificationServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(NotificationServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/config/KafkaConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice.config; 2 | 3 | import org.apache.kafka.clients.admin.NewTopic; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.kafka.config.TopicBuilder; 8 | 9 | @Configuration 10 | public class KafkaConfig { 11 | @Value("${spring.kafka.topic.name}") 12 | private String topicName; 13 | 14 | @Bean 15 | public NewTopic topic() { 16 | return TopicBuilder.name(topicName) 17 | .partitions(10) 18 | .replicas(1) 19 | .build(); 20 | } 21 | } -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/controller/NotificationController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice.controller; 2 | 3 | import com.safalifter.notificationservice.model.Notification; 4 | import com.safalifter.notificationservice.service.NotificationService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.List; 13 | 14 | @RestController 15 | @RequestMapping("/v1/notification") 16 | @RequiredArgsConstructor 17 | public class NotificationController { 18 | private final NotificationService notificationService; 19 | 20 | @GetMapping("/getAllByUserId/{userId}") 21 | public ResponseEntity> getAllByUserId(@PathVariable String userId) { 22 | return ResponseEntity.ok(notificationService.getAllByUserId(userId)); 23 | } 24 | } -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/listeners/NotificationListener.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice.listeners; 2 | 3 | import com.safalifter.notificationservice.request.SendNotificationRequest; 4 | import com.safalifter.notificationservice.service.NotificationService; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.kafka.annotation.KafkaListener; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @RequiredArgsConstructor 12 | @Slf4j 13 | public class NotificationListener { 14 | private final NotificationService notificationService; 15 | 16 | @KafkaListener(topics = {"${spring.kafka.topic.name}"}, groupId = "${spring.kafka.consumer.group-id}") 17 | public void consume(final SendNotificationRequest request) { 18 | log.info("Consumed message: {}", request.toString()); 19 | notificationService.save(request); 20 | } 21 | } -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/model/Notification.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice.model; 2 | 3 | import lombok.*; 4 | import org.hibernate.annotations.CreationTimestamp; 5 | import org.hibernate.annotations.GenericGenerator; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.Id; 10 | import java.time.LocalDateTime; 11 | 12 | @Entity(name = "notifications") 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Getter 17 | @Setter 18 | public class Notification { 19 | @Id 20 | @GeneratedValue(generator = "UUID") 21 | @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") 22 | private String id; 23 | 24 | private String userId; 25 | private String offerId; 26 | private String message; 27 | 28 | @CreationTimestamp 29 | private LocalDateTime creationTimestamp; 30 | } 31 | -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/repository/NotificationRepository.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice.repository; 2 | 3 | import com.safalifter.notificationservice.model.Notification; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface NotificationRepository extends JpaRepository { 9 | List findAllByUserIdOrderByCreationTimestampDesc(String id); 10 | } 11 | -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/request/SendNotificationRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice.request; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | @Builder 8 | public class SendNotificationRequest { 9 | private String userId; 10 | private String offerId; 11 | private String message; 12 | } 13 | -------------------------------------------------------------------------------- /notification-service/src/main/java/com/safalifter/notificationservice/service/NotificationService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice.service; 2 | 3 | import com.safalifter.notificationservice.model.Notification; 4 | import com.safalifter.notificationservice.repository.NotificationRepository; 5 | import com.safalifter.notificationservice.request.SendNotificationRequest; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | import java.util.UUID; 11 | 12 | @Service 13 | @RequiredArgsConstructor 14 | public class NotificationService { 15 | private final NotificationRepository notificationRepository; 16 | 17 | public void save(SendNotificationRequest request) { 18 | var notification = Notification.builder() 19 | .id(UUID.randomUUID().toString()) 20 | .userId(request.getUserId()) 21 | .offerId(request.getOfferId()) 22 | .message(request.getMessage()) 23 | .build(); 24 | notificationRepository.save(notification); 25 | } 26 | 27 | public List getAllByUserId(String id) { 28 | return notificationRepository.findAllByUserIdOrderByCreationTimestampDesc(id); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /notification-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=0 2 | 3 | spring.application.name=notification-service 4 | 5 | spring.kafka.consumer.bootstrap-servers= localhost:9092 6 | spring.kafka.consumer.group-id= notification 7 | spring.kafka.consumer.auto-offset-reset= earliest 8 | spring.kafka.consumer.key-deserializer= org.apache.kafka.common.serialization.StringDeserializer 9 | spring.kafka.consumer.value-deserializer= org.springframework.kafka.support.serializer.JsonDeserializer 10 | spring.kafka.consumer.properties.spring.json.trusted.packages=* 11 | spring.kafka.topic.name==notificationTopic 12 | 13 | spring.config.import=configserver:http://localhost:8888/ -------------------------------------------------------------------------------- /notification-service/src/test/java/com/safalifter/notificationservice/NotificationServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.notificationservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class NotificationServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /screenshots/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/auth.png -------------------------------------------------------------------------------- /screenshots/category-advert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/category-advert.png -------------------------------------------------------------------------------- /screenshots/eureka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/eureka.png -------------------------------------------------------------------------------- /screenshots/file-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/file-download.png -------------------------------------------------------------------------------- /screenshots/file-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/file-upload.png -------------------------------------------------------------------------------- /screenshots/kafka-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/kafka-ui.png -------------------------------------------------------------------------------- /screenshots/offer-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/offer-job.png -------------------------------------------------------------------------------- /screenshots/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/screenshots/user.png -------------------------------------------------------------------------------- /user-service/.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 | -------------------------------------------------------------------------------- /user-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incredidev9285/springboot-microservices/11e87758665923d5ecaefe1e067e42707c319c5d/user-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /user-service/.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 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /user-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.14 9 | 10 | 11 | com.safalifter 12 | user-service 13 | 0.0.1-SNAPSHOT 14 | user-service 15 | Demo project for Spring Boot 16 | 17 | 17 18 | 2021.0.8 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-validation 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-netflix-eureka-client 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-openfeign 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-config 44 | 45 | 46 | org.postgresql 47 | postgresql 48 | runtime 49 | 50 | 51 | org.projectlombok 52 | lombok 53 | true 54 | 55 | 56 | 57 | org.modelmapper 58 | modelmapper 59 | 3.1.1 60 | 61 | 62 | 63 | org.springdoc 64 | springdoc-openapi-ui 65 | 1.6.15 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-security 70 | 71 | 72 | io.jsonwebtoken 73 | jjwt-api 74 | 0.11.5 75 | 76 | 77 | io.jsonwebtoken 78 | jjwt-impl 79 | 0.11.5 80 | 81 | 82 | io.jsonwebtoken 83 | jjwt-jackson 84 | 0.11.5 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-starter-test 89 | test 90 | 91 | 92 | 93 | 94 | 95 | org.springframework.cloud 96 | spring-cloud-dependencies 97 | ${spring-cloud.version} 98 | pom 99 | import 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | org.springframework.boot 108 | spring-boot-maven-plugin 109 | 110 | 111 | 112 | org.projectlombok 113 | lombok 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/UserServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice; 2 | 3 | import com.safalifter.userservice.enums.Role; 4 | import com.safalifter.userservice.model.User; 5 | import com.safalifter.userservice.repository.UserRepository; 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 10 | import org.springframework.cloud.openfeign.EnableFeignClients; 11 | 12 | @SpringBootApplication 13 | @EnableEurekaClient 14 | @EnableFeignClients 15 | public class UserServiceApplication implements CommandLineRunner { 16 | private final UserRepository userRepository; 17 | 18 | public UserServiceApplication(UserRepository userRepository) { 19 | this.userRepository = userRepository; 20 | } 21 | 22 | public static void main(String[] args) { 23 | SpringApplication.run(UserServiceApplication.class, args); 24 | } 25 | 26 | @Override 27 | public void run(String... args) { 28 | final String pass = "$2a$10$2529eBq3R6Y41t03Mku2I.2Nh3W0p25lt.s.85mG0kiAvxI4bsAHa"; 29 | var admin = User.builder() 30 | .username("admin") 31 | .email("admin@gmail.com") 32 | .password(pass) 33 | .role(Role.ADMIN).build(); 34 | if (userRepository.findByUsername("admin").isEmpty()) userRepository.save(admin); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/client/CustomErrorDecoder.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.client; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.safalifter.userservice.exc.GenericErrorResponse; 5 | import feign.Response; 6 | import feign.codec.ErrorDecoder; 7 | import org.apache.commons.io.IOUtils; 8 | import org.springframework.http.HttpStatus; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.Map; 14 | 15 | public class CustomErrorDecoder implements ErrorDecoder { 16 | private final ObjectMapper mapper = new ObjectMapper(); 17 | 18 | @Override 19 | public Exception decode(String methodKey, Response response) { 20 | try (InputStream body = response.body().asInputStream()) { 21 | Map errors = 22 | mapper.readValue(IOUtils.toString(body, StandardCharsets.UTF_8), Map.class); 23 | return GenericErrorResponse 24 | .builder() 25 | .httpStatus(HttpStatus.valueOf(response.status())) 26 | .message(errors.get("error")) 27 | .build(); 28 | 29 | } catch (IOException exception) { 30 | throw GenericErrorResponse.builder() 31 | .httpStatus(HttpStatus.valueOf(response.status())) 32 | .message(exception.getMessage()) 33 | .build(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/client/FileStorageClient.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.client; 2 | 3 | import org.springframework.cloud.openfeign.FeignClient; 4 | import org.springframework.http.MediaType; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.DeleteMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestPart; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | @FeignClient(name = "file-storage", path = "/v1/file-storage") 13 | public interface FileStorageClient { 14 | @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) 15 | ResponseEntity uploadImageToFIleSystem(@RequestPart("image") MultipartFile file); 16 | 17 | @DeleteMapping("/delete/{id}") 18 | ResponseEntity deleteImageFromFileSystem(@PathVariable String id); 19 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/config/BeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.config; 2 | 3 | 4 | import org.modelmapper.Conditions; 5 | import org.modelmapper.ModelMapper; 6 | import org.modelmapper.convention.MatchingStrategies; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 10 | 11 | @Configuration 12 | public class BeanConfig { 13 | @Bean 14 | public ModelMapper getModelMapper() { 15 | ModelMapper modelMapper = new ModelMapper(); 16 | modelMapper.getConfiguration() 17 | .setMatchingStrategy(MatchingStrategies.LOOSE) 18 | .setPropertyCondition(Conditions.isNotNull()); 19 | return modelMapper; 20 | } 21 | 22 | @Bean 23 | public BCryptPasswordEncoder bCryptPasswordEncoder() { 24 | return new BCryptPasswordEncoder(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/config/FeignConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.config; 2 | 3 | import com.safalifter.userservice.client.CustomErrorDecoder; 4 | import feign.codec.ErrorDecoder; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class FeignConfig { 10 | @Bean 11 | public ErrorDecoder errorDecoder() { 12 | return new CustomErrorDecoder(); 13 | } 14 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/config/OpenApiConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.config; 2 | 3 | import io.swagger.v3.oas.models.Components; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import io.swagger.v3.oas.models.info.License; 7 | import io.swagger.v3.oas.models.security.SecurityRequirement; 8 | import io.swagger.v3.oas.models.security.SecurityScheme; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | @Configuration 14 | public class OpenApiConfig { 15 | @Bean 16 | public OpenAPI openAPI(@Value("${application-title}") String title, 17 | @Value("${application-description}") String description, 18 | @Value("${application-version}") String version, 19 | @Value("${application-license}") String license) { 20 | return new OpenAPI().addSecurityItem(new SecurityRequirement() 21 | .addList("Bearer Authentication")) 22 | .components(new Components().addSecuritySchemes 23 | ("Bearer Authentication", createAPIKeyScheme())) 24 | .info(new Info() 25 | .title(title) 26 | .description(description) 27 | .version(version) 28 | .license(new License().name(license))); 29 | } 30 | 31 | private SecurityScheme createAPIKeyScheme() { 32 | return new SecurityScheme().type(SecurityScheme.Type.HTTP) 33 | .bearerFormat("JWT") 34 | .scheme("bearer"); 35 | } 36 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.config; 2 | 3 | import com.safalifter.userservice.jwt.JwtAuthenticationFilter; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.lang.NonNull; 8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; 12 | import org.springframework.security.web.SecurityFilterChain; 13 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 14 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 15 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 16 | 17 | @Configuration 18 | @EnableWebSecurity 19 | @EnableGlobalMethodSecurity(prePostEnabled = true) 20 | @RequiredArgsConstructor 21 | public class SecurityConfig { 22 | private final JwtAuthenticationFilter jwtAuthenticationFilter; 23 | 24 | @Bean 25 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 26 | return http.csrf().disable() 27 | .authorizeRequests() 28 | .anyRequest().permitAll() 29 | .and() 30 | .formLogin().disable() 31 | .httpBasic().disable() 32 | .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) 33 | .build(); 34 | } 35 | 36 | @Bean 37 | public WebSecurityCustomizer webSecurityCustomizer() { 38 | return (web) -> web.ignoring().antMatchers( 39 | "/swagger-resources/**", 40 | "/swagger-ui.html/**", 41 | "/swagger-resources/**", 42 | "/swagger-ui/**", 43 | "/v3/api-docs/**"); 44 | } 45 | 46 | @Bean 47 | public WebMvcConfigurer corsConfigurer() { 48 | return new WebMvcConfigurer() { 49 | @Override 50 | public void addCorsMappings(@NonNull CorsRegistry registry) { 51 | registry.addMapping("/**") 52 | .allowedMethods("*"); 53 | } 54 | }; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.controller; 2 | 3 | import com.safalifter.userservice.dto.AuthUserDto; 4 | import com.safalifter.userservice.dto.UserDto; 5 | import com.safalifter.userservice.request.RegisterRequest; 6 | import com.safalifter.userservice.request.UserUpdateRequest; 7 | import com.safalifter.userservice.service.UserService; 8 | import lombok.RequiredArgsConstructor; 9 | import org.modelmapper.ModelMapper; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | import org.springframework.web.multipart.MultipartFile; 14 | 15 | import javax.validation.Valid; 16 | import java.util.List; 17 | 18 | @RestController 19 | @RequestMapping("/v1/user") 20 | @RequiredArgsConstructor 21 | public class UserController { 22 | private final UserService userService; 23 | private final ModelMapper modelMapper; 24 | 25 | @PostMapping("/save") 26 | public ResponseEntity save(@Valid @RequestBody RegisterRequest request) { 27 | return ResponseEntity.ok(modelMapper.map(userService.saveUser(request), UserDto.class)); 28 | } 29 | 30 | @GetMapping("/getAll") 31 | @PreAuthorize("hasRole('ADMIN')") 32 | public ResponseEntity> getAll() { 33 | return ResponseEntity.ok(userService.getAll().stream() 34 | .map(user -> modelMapper.map(user, UserDto.class)).toList()); 35 | } 36 | 37 | @GetMapping("/getUserById/{id}") 38 | public ResponseEntity getUserById(@PathVariable String id) { 39 | return ResponseEntity.ok(modelMapper.map(userService.getUserById(id), UserDto.class)); 40 | } 41 | 42 | @GetMapping("/getUserByEmail/{email}") 43 | public ResponseEntity getUserByEmail(@PathVariable String email) { 44 | return ResponseEntity.ok(modelMapper.map(userService.getUserByEmail(email), UserDto.class)); 45 | } 46 | 47 | @GetMapping("/getUserByUsername/{username}") 48 | public ResponseEntity getUserByUsername(@PathVariable String username) { 49 | return ResponseEntity.ok(modelMapper.map(userService.getUserByUsername(username), AuthUserDto.class)); 50 | } 51 | 52 | @PutMapping("/update") 53 | @PreAuthorize("hasRole('ADMIN') or @userService.getUserById(#request.id).username == principal") 54 | public ResponseEntity updateUserById(@Valid @RequestPart UserUpdateRequest request, 55 | @RequestPart(required = false) MultipartFile file) { 56 | return ResponseEntity.ok(modelMapper.map(userService.updateUserById(request, file), UserDto.class)); 57 | } 58 | 59 | @DeleteMapping("/deleteUserById/{id}") 60 | @PreAuthorize("hasRole('ADMIN') or @userService.getUserById(#id).username == principal") 61 | public ResponseEntity deleteUserById(@PathVariable String id) { 62 | userService.deleteUserById(id); 63 | return ResponseEntity.ok().build(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/dto/AuthUserDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.dto; 2 | 3 | import com.safalifter.userservice.enums.Role; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class AuthUserDto { 8 | private String id; 9 | private String username; 10 | private String password; 11 | private Role role; 12 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.safalifter.userservice.model.UserDetails; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | @Data 10 | @JsonInclude(JsonInclude.Include.NON_NULL) 11 | public class UserDto { 12 | private String id; 13 | private String username; 14 | private String email; 15 | private UserDetails userDetails; 16 | } 17 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/enums/Active.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.enums; 2 | 3 | public enum Active { 4 | ACTIVE, INACTIVE 5 | } 6 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/enums/Role.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.enums; 2 | 3 | public enum Role { 4 | ADMIN, USER 5 | } 6 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/exc/GeneralExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.exc; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.lang.NonNull; 7 | import org.springframework.security.access.AccessDeniedException; 8 | import org.springframework.validation.FieldError; 9 | import org.springframework.web.bind.MethodArgumentNotValidException; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.RestControllerAdvice; 12 | import org.springframework.web.context.request.WebRequest; 13 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | @RestControllerAdvice 19 | public class GeneralExceptionHandler extends ResponseEntityExceptionHandler { 20 | 21 | @Override 22 | protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, 23 | @NonNull HttpHeaders headers, 24 | @NonNull HttpStatus status, 25 | @NonNull WebRequest request) { 26 | Map errors = new HashMap<>(); 27 | ex.getBindingResult().getAllErrors() 28 | .forEach(x -> errors.put(((FieldError) x).getField(), x.getDefaultMessage())); 29 | return ResponseEntity.badRequest().body(errors); 30 | } 31 | 32 | @ExceptionHandler(GenericErrorResponse.class) 33 | public ResponseEntity genericError(GenericErrorResponse exception) { 34 | Map errors = new HashMap<>(); 35 | errors.put("error", exception.getMessage()); 36 | return new ResponseEntity<>(errors, exception.getHttpStatus()); 37 | } 38 | 39 | @ExceptionHandler(Exception.class) 40 | public final ResponseEntity handleAllException(Exception ex) { 41 | Map errors = new HashMap<>(); 42 | errors.put("error", ex.getMessage()); 43 | return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); 44 | } 45 | 46 | @ExceptionHandler(NotFoundException.class) 47 | public ResponseEntity notFoundException(NotFoundException exception) { 48 | Map errors = new HashMap<>(); 49 | errors.put("error", exception.getMessage()); 50 | return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND); 51 | } 52 | 53 | @ExceptionHandler(UnauthorizedException.class) 54 | public ResponseEntity unauthorizedException(UnauthorizedException exception) { 55 | Map errors = new HashMap<>(); 56 | errors.put("error", exception.getMessage()); 57 | return new ResponseEntity<>(errors, HttpStatus.UNAUTHORIZED); 58 | } 59 | 60 | @ExceptionHandler(AccessDeniedException.class) 61 | public ResponseEntity accessDeniedException(AccessDeniedException exception) { 62 | Map errors = new HashMap<>(); 63 | errors.put("error", exception.getMessage()); 64 | return new ResponseEntity<>(errors, HttpStatus.FORBIDDEN); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/exc/GenericErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.exc; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import org.springframework.http.HttpStatus; 6 | 7 | @Builder 8 | @Getter 9 | public class GenericErrorResponse extends RuntimeException { 10 | private final String message; 11 | private final HttpStatus httpStatus; 12 | 13 | public GenericErrorResponse(String message, HttpStatus httpStatus) { 14 | super(message); 15 | this.message = message; 16 | this.httpStatus = httpStatus; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/exc/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.exc; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.NOT_FOUND) 7 | public class NotFoundException extends RuntimeException { 8 | public NotFoundException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/exc/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.exc; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 7 | public class UnauthorizedException extends RuntimeException { 8 | public UnauthorizedException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/jwt/JwtAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.jwt; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.lang.NonNull; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.util.StringUtils; 12 | import org.springframework.web.filter.OncePerRequestFilter; 13 | 14 | import javax.servlet.FilterChain; 15 | import javax.servlet.ServletException; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | import java.util.Collections; 20 | 21 | @Component 22 | @RequiredArgsConstructor 23 | public class JwtAuthenticationFilter extends OncePerRequestFilter { 24 | private final JwtUtil jwtUtil; 25 | 26 | @Override 27 | protected void doFilterInternal(@NonNull HttpServletRequest request, 28 | @NonNull HttpServletResponse response, 29 | @NonNull FilterChain filterChain) throws ServletException, IOException { 30 | try { 31 | String token = request.getHeader("Authorization"); 32 | 33 | if (StringUtils.hasText(token) && token.startsWith("Bearer ")) { 34 | Claims claims = jwtUtil.getClaims(token.substring(7)); 35 | 36 | SimpleGrantedAuthority authority = new SimpleGrantedAuthority(claims.getIssuer()); 37 | UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( 38 | claims.getSubject(), null, Collections.singleton(authority)); 39 | authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); 40 | 41 | SecurityContextHolder.getContext().setAuthentication(authentication); 42 | } 43 | 44 | } catch (Exception e) { 45 | System.out.println(e.getMessage()); 46 | } 47 | filterChain.doFilter(request, response); 48 | } 49 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/jwt/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.jwt; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.io.Decoders; 6 | import io.jsonwebtoken.security.Keys; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.security.Key; 10 | 11 | @Component 12 | public class JwtUtil { 13 | public static final String SECRET = "5367566B59703373367639792F423F4528482B4D6251655468576D5A71347437"; 14 | 15 | private Key getSignKey() { 16 | byte[] keyBytes = Decoders.BASE64.decode(SECRET); 17 | return Keys.hmacShaKeyFor(keyBytes); 18 | } 19 | 20 | public Claims getClaims(String token) { 21 | return Jwts.parserBuilder() 22 | .setSigningKey(getSignKey()) 23 | .build() 24 | .parseClaimsJws(token) 25 | .getBody(); 26 | } 27 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/model/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.model; 2 | 3 | 4 | import lombok.Getter; 5 | import org.hibernate.annotations.CreationTimestamp; 6 | import org.hibernate.annotations.GenericGenerator; 7 | import org.hibernate.annotations.UpdateTimestamp; 8 | 9 | import javax.persistence.GeneratedValue; 10 | import javax.persistence.Id; 11 | import javax.persistence.MappedSuperclass; 12 | import java.io.Serializable; 13 | import java.time.LocalDateTime; 14 | 15 | @MappedSuperclass 16 | @Getter 17 | public abstract class BaseEntity implements Serializable { 18 | @Id 19 | @GeneratedValue(generator = "UUID") 20 | @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") 21 | private String id; 22 | 23 | @CreationTimestamp 24 | private LocalDateTime creationTimestamp; 25 | 26 | @UpdateTimestamp 27 | private LocalDateTime updateTimestamp; 28 | } 29 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/model/User.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.model; 2 | 3 | import com.safalifter.userservice.enums.Active; 4 | import com.safalifter.userservice.enums.Role; 5 | import lombok.*; 6 | 7 | import javax.persistence.*; 8 | 9 | @Entity(name = "users") 10 | @Builder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Getter 14 | @Setter 15 | public class User extends BaseEntity { 16 | @Column(unique = true, nullable = false) 17 | private String username; 18 | 19 | @Column(nullable = false) 20 | private String password; 21 | 22 | @Column(unique = true, nullable = false, updatable = false) 23 | private String email; 24 | 25 | @Enumerated(EnumType.STRING) 26 | private Role role; 27 | 28 | @Enumerated(EnumType.STRING) 29 | private Active active; 30 | 31 | @Embedded 32 | private UserDetails userDetails; 33 | } 34 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/model/UserDetails.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.*; 5 | 6 | import javax.persistence.Embeddable; 7 | 8 | @Embeddable 9 | @Builder 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Getter 13 | @Setter 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | public class UserDetails { 16 | private String firstName; 17 | private String lastName; 18 | private String phoneNumber; 19 | private String country; 20 | private String city; 21 | private String address; 22 | private String postalCode; 23 | private String aboutMe; 24 | private String profilePicture; 25 | } 26 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.repository; 2 | 3 | import com.safalifter.userservice.enums.Active; 4 | import com.safalifter.userservice.model.User; 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 | 12 | Optional findByEmail(String email); 13 | 14 | Optional findByUsername(String username); 15 | 16 | List findAllByActive(Active active); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/request/RegisterRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.request; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.*; 6 | 7 | @Data 8 | public class RegisterRequest { 9 | @NotBlank(message = "Username is required") 10 | @Size(min = 6, message = "Username must be at least 6 characters") 11 | private String username; 12 | @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$", 13 | message = "Password must be at least 8 characters and contain at least one letter and one number") 14 | @NotNull(message = "Password is required") 15 | private String password; 16 | @Email(message = "Email should be valid") 17 | private String email; 18 | } 19 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/request/UserUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.request; 2 | 3 | import com.safalifter.userservice.model.UserDetails; 4 | import lombok.Data; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | @Data 9 | public class UserUpdateRequest { 10 | @NotBlank(message = "Id is required") 11 | private String id; 12 | private String username; 13 | private String password; 14 | private UserDetails userDetails; 15 | } 16 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/safalifter/userservice/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice.service; 2 | 3 | import com.safalifter.userservice.client.FileStorageClient; 4 | import com.safalifter.userservice.enums.Active; 5 | import com.safalifter.userservice.enums.Role; 6 | import com.safalifter.userservice.exc.NotFoundException; 7 | import com.safalifter.userservice.model.User; 8 | import com.safalifter.userservice.model.UserDetails; 9 | import com.safalifter.userservice.repository.UserRepository; 10 | import com.safalifter.userservice.request.RegisterRequest; 11 | import com.safalifter.userservice.request.UserUpdateRequest; 12 | import lombok.RequiredArgsConstructor; 13 | import org.modelmapper.ModelMapper; 14 | import org.springframework.security.crypto.password.PasswordEncoder; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.web.multipart.MultipartFile; 17 | 18 | import java.util.List; 19 | 20 | @Service 21 | @RequiredArgsConstructor 22 | public class UserService { 23 | private final UserRepository userRepository; 24 | private final PasswordEncoder passwordEncoder; 25 | private final FileStorageClient fileStorageClient; 26 | private final ModelMapper modelMapper; 27 | 28 | public User saveUser(RegisterRequest request) { 29 | User toSave = User.builder() 30 | .username(request.getUsername()) 31 | .password(passwordEncoder.encode(request.getPassword())) 32 | .email(request.getEmail()) 33 | .role(Role.USER) 34 | .active(Active.ACTIVE).build(); 35 | return userRepository.save(toSave); 36 | } 37 | 38 | public List getAll() { 39 | return userRepository.findAllByActive(Active.ACTIVE); 40 | } 41 | 42 | public User getUserById(String id) { 43 | return findUserById(id); 44 | } 45 | 46 | public User getUserByEmail(String email) { 47 | return findUserByEmail(email); 48 | } 49 | 50 | public User getUserByUsername(String username) { 51 | return findUserByUsername(username); 52 | } 53 | 54 | public User updateUserById(UserUpdateRequest request, MultipartFile file) { 55 | User toUpdate = findUserById(request.getId()); 56 | 57 | request.setUserDetails(updateUserDetails(toUpdate.getUserDetails(), request.getUserDetails(), file)); 58 | modelMapper.map(request, toUpdate); 59 | 60 | return userRepository.save(toUpdate); 61 | } 62 | 63 | public void deleteUserById(String id) { 64 | User toDelete = findUserById(id); 65 | toDelete.setActive(Active.INACTIVE); 66 | userRepository.save(toDelete); 67 | } 68 | 69 | protected User findUserById(String id) { 70 | return userRepository.findById(id) 71 | .orElseThrow(() -> new NotFoundException("User not found")); 72 | } 73 | 74 | protected User findUserByEmail(String email) { 75 | return userRepository.findByEmail(email) 76 | .orElseThrow(() -> new NotFoundException("User not found")); 77 | } 78 | 79 | protected User findUserByUsername(String username) { 80 | return userRepository.findByUsername(username) 81 | .orElseThrow(() -> new NotFoundException("User not found")); 82 | } 83 | 84 | private UserDetails updateUserDetails(UserDetails toUpdate, UserDetails request, MultipartFile file) { 85 | toUpdate = toUpdate == null ? new UserDetails() : toUpdate; 86 | 87 | if (file != null) { 88 | String profilePicture = fileStorageClient.uploadImageToFIleSystem(file).getBody(); 89 | if (profilePicture != null) { 90 | fileStorageClient.deleteImageFromFileSystem(toUpdate.getProfilePicture()); 91 | toUpdate.setProfilePicture(profilePicture); 92 | } 93 | } 94 | 95 | modelMapper.map(request, toUpdate); 96 | 97 | return toUpdate; 98 | } 99 | } -------------------------------------------------------------------------------- /user-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=0 2 | 3 | spring.application.name=user-service 4 | 5 | application-title=User Service 6 | 7 | spring.config.import=configserver:http://localhost:8888/ -------------------------------------------------------------------------------- /user-service/src/test/java/com/safalifter/userservice/UserServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.safalifter.userservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class UserServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------