diff --git a/README.md b/README.md index 4bfe102..602200c 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,33 @@ src/ └── java/ # Test classes ``` +## Docker Support + +## Kubernetes/Minikube Deployment + +### Prerequisites +- Minikube +- kubectl +- Docker + +### Deploy to Minikube +```bash +# Create k8s directory and copy configuration files +mkdir k8s +chmod +x k8s/deploy.sh + +# Deploy to Minikube +./k8s/deploy.sh + +# Get application URL +minikube service spring-app --url +``` + +### Cleanup +```bash +kubectl delete -f k8s/ +``` + ## Contributing 1. Fork the repository diff --git a/build.gradle b/build.gradle index 4f72d9b..5d81320 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'org.springframework.boot' version '3.1.4' id 'io.spring.dependency-management' version '1.1.3' id 'java' + id 'org.flywaydb.flyway' version '9.22.3' } group = 'com.boilerplate' @@ -38,15 +39,30 @@ dependencies { implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'jakarta.validation:jakarta.validation-api:3.0.0' - implementation 'org.hibernate.validator:hibernate-validator:6.2.0.Final' - implementation 'org.glassfish:javax.el:3.0.0' - implementation 'org.springdoc:springdoc-openapi-ui:1.6.14' + implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' + implementation 'org.glassfish:jakarta.el:4.0.2' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' - implementation 'io.swagger.core.v3:swagger-annotations:2.1.2' - implementation 'org.flywaydb:flyway-core:9.16.0' + implementation 'org.flywaydb:flyway-database-postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' compileOnly 'org.projectlombok:lombok:1.18.28' annotationProcessor 'org.projectlombok:lombok:1.18.28' +} + +flyway { + url = 'jdbc:postgresql://localhost:5432/boilerplate' + user = 'postgres' + password = 'postgres' +} + +task createMigration { + doLast { + def migrationName = project.hasProperty('migrationName') ? project.getProperty('migrationName') : 'migration' + def timestamp = new Date().format('yyyyMMddHHmmss') + def fileName = "V${timestamp}__${migrationName}.sql" + def migrationDir = new File("${projectDir}/src/main/resources/db/migration") + migrationDir.mkdirs() + new File(migrationDir, fileName).createNewFile() + } } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 0fd8a9d..5df5abe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,7 @@ version: '3.8' services: app: + container_name: spring-boilerplate platform: linux/arm64 build: context: . @@ -21,6 +22,7 @@ services: SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 postgres: + container_name: pg_master image: postgres:16.2 ports: - "5432:5432" @@ -32,12 +34,15 @@ services: - postgres_data:/var/lib/postgresql/data redis: + container_name: redis_master image: redis:7.0.11 + command: redis-server --requirepass redis123 ports: - "6379:6379" zookeeper: - image: bitnami/zookeeper:3.8.0 + container_name: zookeeper_master + image: bitnami/zookeeper:3.8.1 platform: linux/arm64 environment: ZOOKEEPER_CLIENT_PORT: 2181 @@ -45,12 +50,19 @@ services: ALLOW_ANONYMOUS_LOGIN: "yes" ports: - "22181:2181" + healthcheck: + test: ["CMD", "nc", "-z", "localhost", "2181"] + interval: 10s + timeout: 5s + retries: 5 kafka: + container_name: kafka_master image: bitnami/kafka:latest platform: linux/arm64 depends_on: - - zookeeper + zookeeper: + condition: service_healthy ports: - "29092:29092" environment: diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a79..e18bc25 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/k8s/app-deployment.yml b/k8s/app-deployment.yml new file mode 100644 index 0000000..0909a2a --- /dev/null +++ b/k8s/app-deployment.yml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spring-app +spec: + replicas: 1 + selector: + matchLabels: + app: spring-app + template: + metadata: + labels: + app: spring-app + spec: + containers: + - name: spring-app + image: spring-boot-boilerplate:latest + ports: + - containerPort: 8080 + env: + - name: SPRING_DATASOURCE_URL + value: jdbc:postgresql://postgres:5432/boilerplate + - name: SPRING_REDIS_HOST + value: redis + - name: SPRING_KAFKA_BOOTSTRAP_SERVERS + value: kafka:9092 + imagePullPolicy: Never + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: spring-app +spec: + selector: + app: spring-app + ports: + - port: 8080 + type: LoadBalancer \ No newline at end of file diff --git a/k8s/deploy.sh b/k8s/deploy.sh new file mode 100755 index 0000000..25a0a40 --- /dev/null +++ b/k8s/deploy.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Start Minikube if not running +minikube status || minikube start + +# Build the application Docker image +eval $(minikube docker-env) +docker build -t spring-boot-boilerplate:latest . + +# Create k8s resources +kubectl apply -f k8s/storage.yml +kubectl apply -f k8s/postgres-deployment.yml +kubectl apply -f k8s/redis-deployment.yml +kubectl apply -f k8s/kafka-deployment.yml +kubectl apply -f k8s/app-deployment.yml + +# Wait for deployments +echo "Waiting for deployments to be ready..." +kubectl wait --for=condition=available --timeout=300s deployment/postgres +kubectl wait --for=condition=available --timeout=300s deployment/redis +kubectl wait --for=condition=available --timeout=300s deployment/zookeeper +kubectl wait --for=condition=available --timeout=300s deployment/kafka +kubectl wait --for=condition=available --timeout=300s deployment/spring-app + +# Get the application URL +echo "Application URL:" +minikube service spring-app --url \ No newline at end of file diff --git a/k8s/kafka-deployment.yml b/k8s/kafka-deployment.yml new file mode 100644 index 0000000..13af0d6 --- /dev/null +++ b/k8s/kafka-deployment.yml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zookeeper +spec: + replicas: 1 + selector: + matchLabels: + app: zookeeper + template: + metadata: + labels: + app: zookeeper + spec: + containers: + - name: zookeeper + image: bitnami/zookeeper:3.8.0 + ports: + - containerPort: 2181 + env: + - name: ALLOW_ANONYMOUS_LOGIN + value: "yes" +--- +apiVersion: v1 +kind: Service +metadata: + name: zookeeper +spec: + selector: + app: zookeeper + ports: + - port: 2181 + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka +spec: + replicas: 1 + selector: + matchLabels: + app: kafka + template: + metadata: + labels: + app: kafka + spec: + containers: + - name: kafka + image: bitnami/kafka:latest + ports: + - containerPort: 9092 + env: + - name: KAFKA_BROKER_ID + value: "1" + - name: KAFKA_ZOOKEEPER_CONNECT + value: "zookeeper:2181" + - name: KAFKA_LISTENERS + value: "PLAINTEXT://:9092" + - name: KAFKA_ADVERTISED_LISTENERS + value: "PLAINTEXT://kafka:9092" + - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR + value: "1" +--- +apiVersion: v1 +kind: Service +metadata: + name: kafka +spec: + selector: + app: kafka + ports: + - port: 9092 + type: ClusterIP \ No newline at end of file diff --git a/k8s/postgres-deployment.yml b/k8s/postgres-deployment.yml new file mode 100644 index 0000000..da2ce1b --- /dev/null +++ b/k8s/postgres-deployment.yml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:16.2 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_DB + value: boilerplate + - name: POSTGRES_USER + value: postgres + - name: POSTGRES_PASSWORD + value: postgres + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + volumes: + - name: postgres-storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres +spec: + selector: + app: postgres + ports: + - port: 5432 + type: ClusterIP \ No newline at end of file diff --git a/k8s/redis-deployment.yml b/k8s/redis-deployment.yml new file mode 100644 index 0000000..c372d12 --- /dev/null +++ b/k8s/redis-deployment.yml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis:7.0.11 + ports: + - containerPort: 6379 + args: ["--requirepass", "redis123"] +--- +apiVersion: v1 +kind: Service +metadata: + name: redis +spec: + selector: + app: redis + ports: + - port: 6379 + type: ClusterIP \ No newline at end of file diff --git a/k8s/storage.yml b/k8s/storage.yml new file mode 100644 index 0000000..02c345b --- /dev/null +++ b/k8s/storage.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4f0e212 --- /dev/null +++ b/pom.xml @@ -0,0 +1,5 @@ + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.1.0 + \ No newline at end of file diff --git a/src/main/java/com/boilerplate/SpringBootBoilerplateApplication.java b/src/main/java/com/boilerplate/SpringBootBoilerplateApplication.java index 5aa7612..d14db21 100644 --- a/src/main/java/com/boilerplate/SpringBootBoilerplateApplication.java +++ b/src/main/java/com/boilerplate/SpringBootBoilerplateApplication.java @@ -1,15 +1,27 @@ package com.boilerplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.boot.context.event.ApplicationStartingEvent; +import org.springframework.context.event.EventListener; @SpringBootApplication @EntityScan("com.boilerplate.entity") @EnableJpaRepositories("com.boilerplate.repository") public class SpringBootBoilerplateApplication { + private static final Logger logger = LoggerFactory.getLogger(SpringBootBoilerplateApplication.class); + public static void main(String[] args) { + logger.info("🚀 Starting Spring Boot Boilerplate Application..."); SpringApplication.run(SpringBootBoilerplateApplication.class, args); } + + @EventListener(ApplicationStartingEvent.class) + public void onStart() { + logger.info("⚙️ Initializing application components..."); + } } \ No newline at end of file diff --git a/src/main/java/com/boilerplate/config/ApplicationStartupListener.java b/src/main/java/com/boilerplate/config/ApplicationStartupListener.java new file mode 100644 index 0000000..4e731df --- /dev/null +++ b/src/main/java/com/boilerplate/config/ApplicationStartupListener.java @@ -0,0 +1,70 @@ +package com.boilerplate.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; +import javax.sql.DataSource; +import org.springframework.core.env.Environment; +import redis.clients.jedis.Jedis; + +@Component +public class ApplicationStartupListener implements ApplicationListener { + private static final Logger logger = LoggerFactory.getLogger(ApplicationStartupListener.class); + + private final DataSource dataSource; + private final KafkaTemplate kafkaTemplate; + private final Environment environment; + + public ApplicationStartupListener(DataSource dataSource, + KafkaTemplate kafkaTemplate, + Environment environment) { + this.dataSource = dataSource; + this.kafkaTemplate = kafkaTemplate; + this.environment = environment; + } + + @Override + public void onApplicationEvent(ApplicationStartedEvent event) { + checkDatabaseConnection(); + checkRedisConnection(); + checkKafkaConnection(); + logger.info("All components initialized successfully"); + } + + private void checkDatabaseConnection() { + try { + dataSource.getConnection(); + logger.info("✅ Database connection established successfully"); + } catch (Exception e) { + logger.error("❌ Failed to connect to database: {}", e.getMessage()); + } + } + + private void checkRedisConnection() { + try { + Jedis jedis = new Jedis( + environment.getProperty("spring.redis.host", "localhost"), + environment.getProperty("spring.redis.port", Integer.class, 6379) + ); + jedis.auth(environment.getProperty("spring.redis.password")); + + String response = jedis.ping(); + jedis.close(); + logger.info("✅ Redis connection established successfully: {}", response); + } catch (Exception e) { + logger.error("❌ Failed to connect to Redis: {}", e.getMessage()); + } + } + + private void checkKafkaConnection() { + try { + kafkaTemplate.getDefaultTopic(); + logger.info("✅ Kafka connection initialized successfully"); + } catch (Exception e) { + logger.error("❌ Failed to initialize Kafka: {}", e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/boilerplate/config/RedisConfig.java b/src/main/java/com/boilerplate/config/RedisConfig.java index 0b9cf80..9a5c362 100644 --- a/src/main/java/com/boilerplate/config/RedisConfig.java +++ b/src/main/java/com/boilerplate/config/RedisConfig.java @@ -15,12 +15,15 @@ public class RedisConfig { @Value("${spring.redis.port}") private int redisPort; + @Value("${spring.redis.password}") + private String redisPassword; + @Bean public JedisPool jedisPool() { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(128); poolConfig.setMaxIdle(128); poolConfig.setMinIdle(16); - return new JedisPool(poolConfig, redisHost, redisPort); + return new JedisPool(poolConfig, redisHost, redisPort, 2000, redisPassword); } } \ No newline at end of file diff --git a/src/main/java/com/boilerplate/config/SecurityConfig.java b/src/main/java/com/boilerplate/config/SecurityConfig.java index c2e10a1..e4594e6 100644 --- a/src/main/java/com/boilerplate/config/SecurityConfig.java +++ b/src/main/java/com/boilerplate/config/SecurityConfig.java @@ -26,13 +26,15 @@ public class SecurityConfig { }; @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth - .requestMatchers(NO_AUTH_WHITELIST).permitAll() + .requestMatchers("/v3/api-docs/**", + "/swagger-ui/**", + "/swagger-ui.html").permitAll() + .requestMatchers(NO_AUTH_WHITELIST).permitAll() .requestMatchers("/api/v1/auth/**").permitAll() - .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**").permitAll() .anyRequest().authenticated() ) .sessionManagement(session -> session diff --git a/src/main/java/com/boilerplate/config/SwaggerConfig.java b/src/main/java/com/boilerplate/config/SwaggerConfig.java deleted file mode 100644 index d55a243..0000000 --- a/src/main/java/com/boilerplate/config/SwaggerConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.boilerplate.config; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; -import io.swagger.v3.oas.annotations.info.Info; -import io.swagger.v3.oas.annotations.security.SecurityScheme; -import org.springframework.context.annotation.Configuration; - -@Configuration -@OpenAPIDefinition( - info = @Info( - title = "Spring Boot Boilerplate API", - version = "1.0", - description = "Spring Boot Boilerplate API Documentation" - ) -) -@SecurityScheme( - name = "bearerAuth", - type = SecuritySchemeType.HTTP, - bearerFormat = "JWT", - scheme = "bearer" -) -public class SwaggerConfig { -} \ No newline at end of file diff --git a/src/main/java/com/boilerplate/controller/AuthController.java b/src/main/java/com/boilerplate/controller/AuthController.java index 376a644..920c1c8 100644 --- a/src/main/java/com/boilerplate/controller/AuthController.java +++ b/src/main/java/com/boilerplate/controller/AuthController.java @@ -4,8 +4,6 @@ import com.boilerplate.dto.LoginRequestDto; import com.boilerplate.dto.UserRegistrationDto; import com.boilerplate.service.AuthService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -14,13 +12,11 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/auth") -@Tag(name = "Authentication", description = "Authentication management APIs") public class AuthController { private final AuthService authService; @PostMapping("/register") - @Operation(summary = "Register a new user") public ResponseEntity register( @Valid @RequestBody UserRegistrationDto request ) { @@ -28,7 +24,6 @@ public ResponseEntity register( } @PostMapping("/login") - @Operation(summary = "Login with email and password") public ResponseEntity login( @Valid @RequestBody LoginRequestDto request ) { @@ -36,7 +31,6 @@ public ResponseEntity login( } @PostMapping("/verify-otp") - @Operation(summary = "Verify OTP for account activation") public ResponseEntity verifyOtp( @RequestParam String email, @RequestParam String otp @@ -45,7 +39,6 @@ public ResponseEntity verifyOtp( } @PostMapping("/refresh-token") - @Operation(summary = "Refresh authentication token") public ResponseEntity refreshToken( @RequestHeader("Authorization") String token ) { @@ -53,7 +46,6 @@ public ResponseEntity refreshToken( } @PostMapping("/logout") - @Operation(summary = "Logout and invalidate token") public ResponseEntity logout( @RequestHeader("Authorization") String token ) { diff --git a/src/main/java/com/boilerplate/controller/HealthCheckController.java b/src/main/java/com/boilerplate/controller/HealthCheckController.java index f584959..eac456e 100644 --- a/src/main/java/com/boilerplate/controller/HealthCheckController.java +++ b/src/main/java/com/boilerplate/controller/HealthCheckController.java @@ -3,13 +3,79 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.core.env.Environment; +import javax.sql.DataSource; +import redis.clients.jedis.Jedis; +import lombok.RequiredArgsConstructor; +import java.util.HashMap; +import java.util.Map; @RestController +@RequiredArgsConstructor public class HealthCheckController { + private final DataSource dataSource; + private final KafkaTemplate kafkaTemplate; + private final Environment environment; + @GetMapping("/health") - public ResponseEntity health() { - return ResponseEntity.ok("Healthy"); + public ResponseEntity> health() { + Map status = new HashMap<>(); + Map components = new HashMap<>(); + + // Check Database + try { + dataSource.getConnection(); + components.put("database", Map.of( + "status", "UP", + "message", "Database connection is healthy" + )); + } catch (Exception e) { + components.put("database", Map.of( + "status", "DOWN", + "message", e.getMessage() + )); + } + + // Check Redis + try { + Jedis jedis = new Jedis( + environment.getProperty("spring.redis.host", "localhost"), + environment.getProperty("spring.redis.port", Integer.class, 6379) + ); + jedis.auth(environment.getProperty("spring.redis.password")); + String response = jedis.ping(); + jedis.close(); + components.put("redis", Map.of( + "status", "UP", + "message", "Redis connection is healthy", + "response", response + )); + } catch (Exception e) { + components.put("redis", Map.of( + "status", "DOWN", + "message", e.getMessage() + )); + } + + // Check Kafka + try { + kafkaTemplate.getDefaultTopic(); + components.put("kafka", Map.of( + "status", "UP", + "message", "Kafka connection is healthy" + )); + } catch (Exception e) { + components.put("kafka", Map.of( + "status", "DOWN", + "message", e.getMessage() + )); + } + + status.put("status", components.values().stream().allMatch(c -> ((Map) c).get("status").equals("UP")) ? "UP" : "DOWN"); + status.put("components", components); + return ResponseEntity.ok(status); } @GetMapping("/ping") diff --git a/src/main/java/com/boilerplate/controller/PasswordController.java b/src/main/java/com/boilerplate/controller/PasswordController.java index 75a9177..808b0b9 100644 --- a/src/main/java/com/boilerplate/controller/PasswordController.java +++ b/src/main/java/com/boilerplate/controller/PasswordController.java @@ -3,8 +3,6 @@ import com.boilerplate.dto.PasswordResetRequestDto; import com.boilerplate.dto.PasswordUpdateDto; import com.boilerplate.service.PasswordService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -13,20 +11,17 @@ @RestController @RequestMapping("/api/v1/password") @RequiredArgsConstructor -@Tag(name = "Password", description = "Password management APIs") public class PasswordController { private final PasswordService passwordService; @PostMapping("/forgot") - @Operation(summary = "Request password reset") public ResponseEntity requestPasswordReset(@Valid @RequestBody PasswordResetRequestDto request) { passwordService.initiatePasswordReset(request.getEmail()); return ResponseEntity.ok().build(); } @PostMapping("/reset") - @Operation(summary = "Reset password using token") public ResponseEntity resetPassword( @RequestParam String token, @Valid @RequestBody PasswordUpdateDto request diff --git a/src/main/java/com/boilerplate/controller/UserProfileController.java b/src/main/java/com/boilerplate/controller/UserProfileController.java index 5d72d6e..2803280 100644 --- a/src/main/java/com/boilerplate/controller/UserProfileController.java +++ b/src/main/java/com/boilerplate/controller/UserProfileController.java @@ -2,9 +2,6 @@ import com.boilerplate.dto.UserProfileUpdateDto; import com.boilerplate.service.UserProfileService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -13,26 +10,21 @@ @RestController @RequestMapping("/api/v1/profile") @RequiredArgsConstructor -@Tag(name = "User Profile", description = "User profile management APIs") -@SecurityRequirement(name = "bearerAuth") public class UserProfileController { private final UserProfileService userProfileService; @GetMapping - @Operation(summary = "Get current user's profile") public ResponseEntity getCurrentUserProfile() { return ResponseEntity.ok(userProfileService.getCurrentUserProfile()); } @PutMapping - @Operation(summary = "Update current user's profile") public ResponseEntity updateProfile(@Valid @RequestBody UserProfileUpdateDto profileDto) { return ResponseEntity.ok(userProfileService.updateProfile(profileDto)); } @DeleteMapping - @Operation(summary = "Delete current user's profile") public ResponseEntity deleteProfile() { userProfileService.deleteProfile(); return ResponseEntity.ok().build(); diff --git a/src/main/java/com/boilerplate/service/TokenCacheService.java b/src/main/java/com/boilerplate/service/TokenCacheService.java index 97c87ce..56ea620 100644 --- a/src/main/java/com/boilerplate/service/TokenCacheService.java +++ b/src/main/java/com/boilerplate/service/TokenCacheService.java @@ -5,8 +5,6 @@ import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; -import java.util.concurrent.TimeUnit; - @Service @RequiredArgsConstructor public class TokenCacheService { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6cd6fe5..45f7418 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,6 +16,7 @@ spring: hibernate: format_sql: true dialect: org.hibernate.dialect.PostgreSQLDialect + open-in-view: false security: debug: true @@ -23,6 +24,7 @@ spring: redis: host: localhost port: 6379 + password: redis123 kafka: bootstrap-servers: localhost:29092 @@ -34,6 +36,9 @@ spring: auto-offset-reset: earliest key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + # properties: + # security.protocol: PLAINTEXT + # auto.create.topics.enable: true mail: host: smtp.gmail.com @@ -61,12 +66,6 @@ jwt: server: port: 8080 -springdoc: - api-docs: - path: /v1/api-docs - swagger-ui: - path: /swagger-ui.html - management: endpoints: web: diff --git a/src/main/resources/db/migration/V20250202111951__spring-app-migration.sql b/src/main/resources/db/migration/V20250202111951__spring-app-migration.sql new file mode 100644 index 0000000..e69de29