From 02607728ab7c9bd157aaee4fbed41d4904080295 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 13 Apr 2026 15:35:17 +0000
Subject: [PATCH 1/4] feat: integrate distributed tracing with OpenTelemetry
and Jaeger
Agent-Logs-Url: https://github.com/vitorhugo-java/SpringBoot-JobApplyTracker/sessions/609122bb-e5ee-4c48-ad22-bec8f3d6a72f
Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
---
.env.example | 14 +++++
.gitignore | 3 +
backend/Dockerfile | 12 ++++
backend/pom.xml | 24 ++++++++
.../service/ApplicationService.java | 24 ++++++--
.../com/jobtracker/service/AuthService.java | 53 +++++++++++------
backend/src/main/resources/application.yml | 15 +++++
backend/src/main/resources/logback-spring.xml | 20 +++++++
.../JobTrackerApplicationTests.java | 3 +-
docker-compose.yml | 57 +++++++++++++++++++
10 files changed, 203 insertions(+), 22 deletions(-)
create mode 100644 .env.example
create mode 100644 backend/Dockerfile
create mode 100644 backend/src/main/resources/logback-spring.xml
create mode 100644 docker-compose.yml
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..3014572
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,14 @@
+# Copy this file to .env and fill in real values.
+# The .env file is excluded from version control.
+
+# Database
+DB_NAME=jobtracker
+DB_USERNAME=jobtracker
+DB_PASSWORD=change_me
+DB_ROOT_PASSWORD=change_me_root
+
+# JWT
+JWT_SECRET=replace_with_a_256_bit_random_string
+
+# Tracing (1.0 = 100 %; lower for production)
+TRACING_SAMPLING_PROBABILITY=1.0
diff --git a/.gitignore b/.gitignore
index e6e4424..021fc5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,6 @@ target/
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
+
+# Environment variables – never commit secrets
+.env
diff --git a/backend/Dockerfile b/backend/Dockerfile
new file mode 100644
index 0000000..f52209c
--- /dev/null
+++ b/backend/Dockerfile
@@ -0,0 +1,12 @@
+FROM maven:3.9-eclipse-temurin-21 AS builder
+WORKDIR /app
+COPY pom.xml .
+RUN mvn -B -q dependency:go-offline
+COPY src ./src
+RUN mvn -B -q package -DskipTests
+
+FROM eclipse-temurin:21-jre
+WORKDIR /app
+COPY --from=builder /app/target/*.jar app.jar
+EXPOSE 8080
+ENTRYPOINT ["java", "-jar", "app.jar"]
diff --git a/backend/pom.xml b/backend/pom.xml
index 73940b7..091513e 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -84,6 +84,30 @@
2.5.0
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
+
+
+ io.opentelemetry
+ opentelemetry-exporter-otlp
+
+
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
org.springframework.boot
diff --git a/backend/src/main/java/com/jobtracker/service/ApplicationService.java b/backend/src/main/java/com/jobtracker/service/ApplicationService.java
index 2bc4094..6cf5ab2 100644
--- a/backend/src/main/java/com/jobtracker/service/ApplicationService.java
+++ b/backend/src/main/java/com/jobtracker/service/ApplicationService.java
@@ -8,6 +8,8 @@
import com.jobtracker.mapper.ApplicationMapper;
import com.jobtracker.repository.ApplicationRepository;
import com.jobtracker.util.SecurityUtils;
+import io.micrometer.tracing.Span;
+import io.micrometer.tracing.Tracer;
import jakarta.persistence.criteria.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@@ -34,21 +36,33 @@ public class ApplicationService {
private final ApplicationRepository applicationRepository;
private final ApplicationMapper applicationMapper;
private final SecurityUtils securityUtils;
+ private final Tracer tracer;
public ApplicationService(ApplicationRepository applicationRepository,
ApplicationMapper applicationMapper,
- SecurityUtils securityUtils) {
+ SecurityUtils securityUtils,
+ Tracer tracer) {
this.applicationRepository = applicationRepository;
this.applicationMapper = applicationMapper;
this.securityUtils = securityUtils;
+ this.tracer = tracer;
}
@Transactional
public ApplicationResponse create(ApplicationRequest request) {
- JobApplication app = new JobApplication();
- mapRequestToEntity(request, app);
- app.setUser(securityUtils.getCurrentUser());
- return applicationMapper.toResponse(applicationRepository.save(app));
+ Span span = tracer.nextSpan().name("create-application").start();
+ try (Tracer.SpanInScope ignored = tracer.withSpan(span)) {
+ span.tag("vacancy", request.vacancyName() != null ? request.vacancyName() : "unknown");
+ JobApplication app = new JobApplication();
+ mapRequestToEntity(request, app);
+ app.setUser(securityUtils.getCurrentUser());
+ return applicationMapper.toResponse(applicationRepository.save(app));
+ } catch (Exception e) {
+ span.error(e);
+ throw e;
+ } finally {
+ span.end();
+ }
}
@Transactional(readOnly = true)
diff --git a/backend/src/main/java/com/jobtracker/service/AuthService.java b/backend/src/main/java/com/jobtracker/service/AuthService.java
index 103c707..71cf2c8 100644
--- a/backend/src/main/java/com/jobtracker/service/AuthService.java
+++ b/backend/src/main/java/com/jobtracker/service/AuthService.java
@@ -10,6 +10,8 @@
import com.jobtracker.exception.ResourceNotFoundException;
import com.jobtracker.mapper.AuthMapper;
import com.jobtracker.repository.UserRepository;
+import io.micrometer.tracing.Span;
+import io.micrometer.tracing.Tracer;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -27,19 +29,22 @@ public class AuthService {
private final RefreshTokenService refreshTokenService;
private final PasswordResetService passwordResetService;
private final AuthMapper authMapper;
+ private final Tracer tracer;
public AuthService(UserRepository userRepository,
PasswordEncoder passwordEncoder,
JwtService jwtService,
RefreshTokenService refreshTokenService,
PasswordResetService passwordResetService,
- AuthMapper authMapper) {
+ AuthMapper authMapper,
+ Tracer tracer) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.jwtService = jwtService;
this.refreshTokenService = refreshTokenService;
this.passwordResetService = passwordResetService;
this.authMapper = authMapper;
+ this.tracer = tracer;
}
@Transactional
@@ -62,26 +67,42 @@ public AuthResponse register(RegisterRequest request) {
@Transactional
public AuthResponse login(LoginRequest request) {
- User user = userRepository.findByEmail(request.email())
- .orElseThrow(() -> new BadCredentialsException("Invalid credentials"));
-
- if (!passwordEncoder.matches(request.password(), user.getPasswordHash())) {
- throw new BadCredentialsException("Invalid credentials");
+ Span span = tracer.nextSpan().name("login").start();
+ try (Tracer.SpanInScope ignored = tracer.withSpan(span)) {
+ User user = userRepository.findByEmail(request.email())
+ .orElseThrow(() -> new BadCredentialsException("Invalid credentials"));
+
+ if (!passwordEncoder.matches(request.password(), user.getPasswordHash())) {
+ throw new BadCredentialsException("Invalid credentials");
+ }
+
+ return buildAuthResponse(user);
+ } catch (Exception e) {
+ span.error(e);
+ throw e;
+ } finally {
+ span.end();
}
-
- return buildAuthResponse(user);
}
@Transactional
public RefreshResponse refresh(RefreshTokenRequest request) {
- RefreshToken newRefreshToken = refreshTokenService.verifyAndRotate(request.refreshToken());
- User user = newRefreshToken.getUser();
-
- UserDetails userDetails = new org.springframework.security.core.userdetails.User(
- user.getEmail(), user.getPasswordHash(), Collections.emptyList());
- String accessToken = jwtService.generateToken(userDetails);
-
- return new RefreshResponse(accessToken, newRefreshToken.getToken());
+ Span span = tracer.nextSpan().name("token-refresh").start();
+ try (Tracer.SpanInScope ignored = tracer.withSpan(span)) {
+ RefreshToken newRefreshToken = refreshTokenService.verifyAndRotate(request.refreshToken());
+ User user = newRefreshToken.getUser();
+
+ UserDetails userDetails = new org.springframework.security.core.userdetails.User(
+ user.getEmail(), user.getPasswordHash(), Collections.emptyList());
+ String accessToken = jwtService.generateToken(userDetails);
+
+ return new RefreshResponse(accessToken, newRefreshToken.getToken());
+ } catch (Exception e) {
+ span.error(e);
+ throw e;
+ } finally {
+ span.end();
+ }
}
@Transactional
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index 3b46e74..e05d7d8 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -35,3 +35,18 @@ springdoc:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
+
+management:
+ endpoints:
+ web:
+ exposure:
+ include: health,info,prometheus,metrics,tracing
+ tracing:
+ sampling:
+ probability: ${TRACING_SAMPLING_PROBABILITY:1.0}
+ otlp:
+ tracing:
+ endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4317}
+ metrics:
+ tags:
+ application: ${spring.application.name}
diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..bd5a90c
--- /dev/null
+++ b/backend/src/main/resources/logback-spring.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-}] [%X{spanId:-}] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java b/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java
index 95cb03b..2f16b81 100644
--- a/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java
+++ b/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java
@@ -15,7 +15,8 @@
"jwt.secret=TestSecretKeyThatIsAtLeast256BitsLongForTestingPurposesOnly",
"jwt.access-token-expiration-ms=900000",
"jwt.refresh-token-expiration-ms=604800000",
- "cors.allowed-origins=http://localhost:3000"
+ "cors.allowed-origins=http://localhost:3000",
+ "management.tracing.sampling.probability=0.0"
})
class JobTrackerApplicationTests {
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..ddea3fd
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,57 @@
+version: "3.9"
+
+services:
+
+ db:
+ image: mariadb:11
+ container_name: jobtracker-db
+ restart: unless-stopped
+ environment:
+ MARIADB_DATABASE: ${DB_NAME:-jobtracker}
+ MARIADB_USER: ${DB_USERNAME:-jobtracker}
+ MARIADB_PASSWORD: ${DB_PASSWORD:-jobtracker}
+ MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
+ ports:
+ - "3306:3306"
+ volumes:
+ - db_data:/var/lib/mysql
+ healthcheck:
+ test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ jaeger:
+ image: jaegertracing/all-in-one:latest
+ container_name: jobtracker-jaeger
+ restart: unless-stopped
+ ports:
+ - "16686:16686" # Jaeger UI
+ - "4317:4317" # OTLP gRPC receiver
+ - "4318:4318" # OTLP HTTP receiver
+ environment:
+ - COLLECTOR_OTLP_ENABLED=true
+
+ app:
+ build:
+ context: ./backend
+ dockerfile: Dockerfile
+ container_name: jobtracker-app
+ restart: unless-stopped
+ depends_on:
+ db:
+ condition: service_healthy
+ jaeger:
+ condition: service_started
+ ports:
+ - "8080:8080"
+ environment:
+ DB_URL: jdbc:mariadb://db:3306/${DB_NAME:-jobtracker}?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
+ DB_USERNAME: ${DB_USERNAME:-jobtracker}
+ DB_PASSWORD: ${DB_PASSWORD:-jobtracker}
+ OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
+ TRACING_SAMPLING_PROBABILITY: ${TRACING_SAMPLING_PROBABILITY:-1.0}
+ SERVER_PORT: 8080
+
+volumes:
+ db_data:
From a03d5d6cfffc01349b462af7c768ede8c44a63d2 Mon Sep 17 00:00:00 2001
From: Vitor Hugo
Date: Mon, 13 Apr 2026 17:56:42 +0000
Subject: [PATCH 2/4] feat: refine error handling in AuthService, adjust
logging levels, and update Jaeger image version
---
.../java/com/jobtracker/service/AuthService.java | 8 ++++++--
backend/src/main/resources/application.yml | 2 +-
backend/src/main/resources/logback-spring.xml | 12 +++++++++---
docker-compose.yml | 2 +-
4 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/backend/src/main/java/com/jobtracker/service/AuthService.java b/backend/src/main/java/com/jobtracker/service/AuthService.java
index 71cf2c8..8b0ff34 100644
--- a/backend/src/main/java/com/jobtracker/service/AuthService.java
+++ b/backend/src/main/java/com/jobtracker/service/AuthService.java
@@ -76,7 +76,9 @@ public AuthResponse login(LoginRequest request) {
throw new BadCredentialsException("Invalid credentials");
}
- return buildAuthResponse(user);
+ return buildAuthResponse(user);
+ } catch (BadCredentialsException e) {
+ throw e;
} catch (Exception e) {
span.error(e);
throw e;
@@ -96,7 +98,9 @@ public RefreshResponse refresh(RefreshTokenRequest request) {
user.getEmail(), user.getPasswordHash(), Collections.emptyList());
String accessToken = jwtService.generateToken(userDetails);
- return new RefreshResponse(accessToken, newRefreshToken.getToken());
+ return buildAuthResponse(user);
+ } catch (BadCredentialsException e) {
+ throw e;
} catch (Exception e) {
span.error(e);
throw e;
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index e05d7d8..9901d74 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -40,7 +40,7 @@ management:
endpoints:
web:
exposure:
- include: health,info,prometheus,metrics,tracing
+ include: health,info,prometheus,metrics
tracing:
sampling:
probability: ${TRACING_SAMPLING_PROBABILITY:1.0}
diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml
index bd5a90c..b88b9c2 100644
--- a/backend/src/main/resources/logback-spring.xml
+++ b/backend/src/main/resources/logback-spring.xml
@@ -14,7 +14,13 @@
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/docker-compose.yml b/docker-compose.yml
index ddea3fd..31b16be 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -22,7 +22,7 @@ services:
retries: 5
jaeger:
- image: jaegertracing/all-in-one:latest
+ image: jaegertracing/all-in-one:1.57.0
container_name: jobtracker-jaeger
restart: unless-stopped
ports:
From 9e1af50941197533414d7d2d885059c5b6940d03 Mon Sep 17 00:00:00 2001
From: Vitor Hugo
Date: Mon, 13 Apr 2026 17:59:01 +0000
Subject: [PATCH 3/4] conflict
---
Dockerfile.orig | 22 +-
.../JobTrackerApplicationTests.java.orig | 26 +++
docker-compose.yml.orig | 69 +++++-
pom.xml.orig | 197 ++++++++++++++++++
.../jobtracker/service/AuthService.java.orig | 183 ++++++++++++++++
src/main/resources/application.yml.orig | 82 ++++++++
src/main/resources/logback-spring.xml.orig | 31 ++-
7 files changed, 600 insertions(+), 10 deletions(-)
create mode 100644 backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig
create mode 100644 pom.xml.orig
create mode 100644 src/main/java/com/jobtracker/service/AuthService.java.orig
create mode 100644 src/main/resources/application.yml.orig
diff --git a/Dockerfile.orig b/Dockerfile.orig
index 06d3ddf..acb89d8 100644
--- a/Dockerfile.orig
+++ b/Dockerfile.orig
@@ -1,3 +1,17 @@
+<<<<<<< HEAD:backend/Dockerfile
+FROM maven:3.9-eclipse-temurin-21 AS builder
+WORKDIR /app
+COPY pom.xml .
+RUN mvn -B -q dependency:go-offline
+COPY src ./src
+RUN mvn -B -q package -DskipTests
+
+FROM eclipse-temurin:21-jre
+WORKDIR /app
+COPY --from=builder /app/target/*.jar app.jar
+EXPOSE 8080
+ENTRYPOINT ["java", "-jar", "app.jar"]
+=======
# ============================================================
# Stage 1: Build
# ============================================================
@@ -36,12 +50,13 @@ RUN chown -R appuser:appgroup /app
USER appuser
-# Expose application port
+# Expose application port and management port
EXPOSE 8080
+EXPOSE 8081
-# Health check – relies on Spring Actuator
+# Health check – relies on Spring Actuator management port
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
- CMD wget -qO- http://localhost:8080/actuator/health || exit 1
+ CMD wget -qO- http://localhost:8081/actuator/health || exit 1
# JVM tuning flags for containers (respects cgroup memory limits)
ENV JAVA_OPTS="-XX:+UseContainerSupport \
@@ -50,3 +65,4 @@ ENV JAVA_OPTS="-XX:+UseContainerSupport \
-Djava.security.egd=file:/dev/./urandom"
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar app.jar"]
+>>>>>>> origin/main:Dockerfile
diff --git a/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig b/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig
new file mode 100644
index 0000000..2f16b81
--- /dev/null
+++ b/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig
@@ -0,0 +1,26 @@
+package com.jobtracker;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestPropertySource;
+
+@SpringBootTest
+@TestPropertySource(properties = {
+ "spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1",
+ "spring.datasource.driver-class-name=org.h2.Driver",
+ "spring.datasource.username=sa",
+ "spring.datasource.password=",
+ "spring.jpa.hibernate.ddl-auto=create-drop",
+ "spring.flyway.enabled=false",
+ "jwt.secret=TestSecretKeyThatIsAtLeast256BitsLongForTestingPurposesOnly",
+ "jwt.access-token-expiration-ms=900000",
+ "jwt.refresh-token-expiration-ms=604800000",
+ "cors.allowed-origins=http://localhost:3000",
+ "management.tracing.sampling.probability=0.0"
+})
+class JobTrackerApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+}
diff --git a/docker-compose.yml.orig b/docker-compose.yml.orig
index 5600e15..6d4c574 100644
--- a/docker-compose.yml.orig
+++ b/docker-compose.yml.orig
@@ -2,6 +2,21 @@ version: "3.9"
services:
+<<<<<<< HEAD
+ db:
+ image: mariadb:11
+ container_name: jobtracker-db
+ restart: unless-stopped
+ environment:
+ MARIADB_DATABASE: ${DB_NAME:-jobtracker}
+ MARIADB_USER: ${DB_USERNAME:-jobtracker}
+ MARIADB_PASSWORD: ${DB_PASSWORD:-jobtracker}
+ MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
+ ports:
+ - "3306:3306"
+ volumes:
+ - db_data:/var/lib/mysql
+=======
# ── Spring Boot application ───────────────────────────────
app:
build:
@@ -21,7 +36,7 @@ services:
mariadb:
condition: service_healthy
healthcheck:
- test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
+ test: ["CMD", "wget", "-qO-", "http://localhost:8081/actuator/health"]
interval: 30s
timeout: 10s
retries: 5
@@ -41,11 +56,49 @@ services:
MARIADB_DATABASE: jobtracker
volumes:
- mariadb_data:/var/lib/mysql
+>>>>>>> origin/main
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
+<<<<<<< HEAD
+
+ jaeger:
+ image: jaegertracing/all-in-one:1.57.0
+ container_name: jobtracker-jaeger
+ restart: unless-stopped
+ ports:
+ - "16686:16686" # Jaeger UI
+ - "4317:4317" # OTLP gRPC receiver
+ - "4318:4318" # OTLP HTTP receiver
+ environment:
+ - COLLECTOR_OTLP_ENABLED=true
+
+ app:
+ build:
+ context: ./backend
+ dockerfile: Dockerfile
+ container_name: jobtracker-app
+ restart: unless-stopped
+ depends_on:
+ db:
+ condition: service_healthy
+ jaeger:
+ condition: service_started
+ ports:
+ - "8080:8080"
+ environment:
+ DB_URL: jdbc:mariadb://db:3306/${DB_NAME:-jobtracker}?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
+ DB_USERNAME: ${DB_USERNAME:-jobtracker}
+ DB_PASSWORD: ${DB_PASSWORD:-jobtracker}
+ OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
+ TRACING_SAMPLING_PROBABILITY: ${TRACING_SAMPLING_PROBABILITY:-1.0}
+ SERVER_PORT: 8080
+
+volumes:
+ db_data:
+=======
start_period: 30s
networks:
- job-tracker-net
@@ -53,7 +106,7 @@ services:
# ── Prometheus ────────────────────────────────────────────
prometheus:
- image: prom/prometheus:latest
+ image: prom/prometheus:v2.54.1
container_name: job-tracker-prometheus
ports:
- "9090:9090"
@@ -65,13 +118,19 @@ services:
- "--storage.tsdb.path=/prometheus"
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
- "--web.console.templates=/usr/share/prometheus/consoles"
+ healthcheck:
+ test: ["CMD", "wget", "-qO-", "http://localhost:9090/-/healthy"]
+ interval: 15s
+ timeout: 5s
+ retries: 3
+ start_period: 15s
networks:
- job-tracker-net
restart: unless-stopped
# ── Grafana ───────────────────────────────────────────────
grafana:
- image: grafana/grafana:latest
+ image: grafana/grafana:10.4.8
container_name: job-tracker-grafana
ports:
- "3000:3000"
@@ -84,7 +143,8 @@ services:
- ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards:ro
- ./grafana/dashboards:/etc/grafana/dashboards:ro
depends_on:
- - prometheus
+ prometheus:
+ condition: service_healthy
networks:
- job-tracker-net
restart: unless-stopped
@@ -97,3 +157,4 @@ volumes:
networks:
job-tracker-net:
driver: bridge
+>>>>>>> origin/main
diff --git a/pom.xml.orig b/pom.xml.orig
new file mode 100644
index 0000000..96ffbcd
--- /dev/null
+++ b/pom.xml.orig
@@ -0,0 +1,197 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+ com.jobtracker
+ job-tracker
+ 1.0.0
+ job-tracker
+ Spring Boot Job Application Tracker API
+
+
+ 21
+ 0.12.5
+ 1.19.8
+ 5.4.0
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ runtime
+
+
+
+
+ org.flywaydb
+ flyway-core
+
+
+ org.flywaydb
+ flyway-mysql
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jjwt.version}
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jjwt.version}
+ runtime
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.5.0
+
+
+<<<<<<< HEAD:backend/pom.xml
+
+=======
+
+>>>>>>> origin/main:pom.xml
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+<<<<<<< HEAD:backend/pom.xml
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
+
+
+ io.opentelemetry
+ opentelemetry-exporter-otlp
+
+
+
+=======
+
+>>>>>>> origin/main:pom.xml
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
+<<<<<<< HEAD:backend/pom.xml
+=======
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ 7.4
+
+
+>>>>>>> origin/main:pom.xml
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+
+ org.testcontainers
+ junit-jupiter
+ ${testcontainers.version}
+ test
+
+
+ org.testcontainers
+ mariadb
+ ${testcontainers.version}
+ test
+
+
+
+
+ io.rest-assured
+ rest-assured
+ ${rest-assured.version}
+ test
+
+
+ io.rest-assured
+ json-path
+ ${rest-assured.version}
+ test
+
+
+ io.rest-assured
+ spring-mock-mvc
+ ${rest-assured.version}
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*Test.java
+ **/*Tests.java
+ **/*IT.java
+
+
+
+
+
+
diff --git a/src/main/java/com/jobtracker/service/AuthService.java.orig b/src/main/java/com/jobtracker/service/AuthService.java.orig
new file mode 100644
index 0000000..a83cd0c
--- /dev/null
+++ b/src/main/java/com/jobtracker/service/AuthService.java.orig
@@ -0,0 +1,183 @@
+package com.jobtracker.service;
+
+import com.jobtracker.config.JwtService;
+import com.jobtracker.dto.auth.*;
+import com.jobtracker.entity.PasswordResetToken;
+import com.jobtracker.entity.RefreshToken;
+import com.jobtracker.entity.User;
+import com.jobtracker.exception.BadRequestException;
+import com.jobtracker.exception.ConflictException;
+import com.jobtracker.exception.ResourceNotFoundException;
+import com.jobtracker.mapper.AuthMapper;
+import com.jobtracker.repository.UserRepository;
+<<<<<<< HEAD:backend/src/main/java/com/jobtracker/service/AuthService.java
+import io.micrometer.tracing.Span;
+import io.micrometer.tracing.Tracer;
+=======
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+>>>>>>> origin/main:src/main/java/com/jobtracker/service/AuthService.java
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collections;
+
+@Service
+public class AuthService {
+
+ private static final Logger log = LoggerFactory.getLogger(AuthService.class);
+
+ private final UserRepository userRepository;
+ private final PasswordEncoder passwordEncoder;
+ private final JwtService jwtService;
+ private final RefreshTokenService refreshTokenService;
+ private final PasswordResetService passwordResetService;
+ private final AuthMapper authMapper;
+ private final Tracer tracer;
+
+ public AuthService(UserRepository userRepository,
+ PasswordEncoder passwordEncoder,
+ JwtService jwtService,
+ RefreshTokenService refreshTokenService,
+ PasswordResetService passwordResetService,
+ AuthMapper authMapper,
+ Tracer tracer) {
+ this.userRepository = userRepository;
+ this.passwordEncoder = passwordEncoder;
+ this.jwtService = jwtService;
+ this.refreshTokenService = refreshTokenService;
+ this.passwordResetService = passwordResetService;
+ this.authMapper = authMapper;
+ this.tracer = tracer;
+ }
+
+ @Transactional
+ public AuthResponse register(RegisterRequest request) {
+ if (!request.password().equals(request.confirmPassword())) {
+ throw new BadRequestException("Passwords do not match");
+ }
+ if (userRepository.existsByEmail(request.email())) {
+ throw new ConflictException("Email already registered");
+ }
+
+ User user = new User();
+ user.setName(request.name());
+ user.setEmail(request.email());
+ user.setPasswordHash(passwordEncoder.encode(request.password()));
+ user = userRepository.save(user);
+ log.info("event=REGISTRATION_SUCCESS email={} userId={}", user.getEmail(), user.getId());
+ return buildAuthResponse(user);
+ }
+
+ @Transactional
+ public AuthResponse login(LoginRequest request) {
+<<<<<<< HEAD:backend/src/main/java/com/jobtracker/service/AuthService.java
+ Span span = tracer.nextSpan().name("login").start();
+ try (Tracer.SpanInScope ignored = tracer.withSpan(span)) {
+ User user = userRepository.findByEmail(request.email())
+ .orElseThrow(() -> new BadCredentialsException("Invalid credentials"));
+
+ if (!passwordEncoder.matches(request.password(), user.getPasswordHash())) {
+ throw new BadCredentialsException("Invalid credentials");
+ }
+
+ return buildAuthResponse(user);
+ } catch (BadCredentialsException e) {
+ throw e;
+ } catch (Exception e) {
+ span.error(e);
+ throw e;
+ } finally {
+ span.end();
+ }
+=======
+ User user = userRepository.findByEmail(request.email())
+ .orElseThrow(() -> {
+ log.warn("event=LOGIN_FAILURE reason=USER_NOT_FOUND email={}", request.email());
+ return new BadCredentialsException("Invalid credentials");
+ });
+
+ if (!passwordEncoder.matches(request.password(), user.getPasswordHash())) {
+ log.warn("event=LOGIN_FAILURE reason=WRONG_PASSWORD userId={}", user.getId());
+ throw new BadCredentialsException("Invalid credentials");
+ }
+
+ log.info("event=LOGIN_SUCCESS userId={}", user.getId());
+ return buildAuthResponse(user);
+>>>>>>> origin/main:src/main/java/com/jobtracker/service/AuthService.java
+ }
+
+ @Transactional
+ public RefreshResponse refresh(RefreshTokenRequest request) {
+ Span span = tracer.nextSpan().name("token-refresh").start();
+ try (Tracer.SpanInScope ignored = tracer.withSpan(span)) {
+ RefreshToken newRefreshToken = refreshTokenService.verifyAndRotate(request.refreshToken());
+ User user = newRefreshToken.getUser();
+
+ UserDetails userDetails = new org.springframework.security.core.userdetails.User(
+ user.getEmail(), user.getPasswordHash(), Collections.emptyList());
+ String accessToken = jwtService.generateToken(userDetails);
+
+ return buildAuthResponse(user);
+ } catch (BadCredentialsException e) {
+ throw e;
+ } catch (Exception e) {
+ span.error(e);
+ throw e;
+ } finally {
+ span.end();
+ }
+ }
+
+ @Transactional
+ public MessageResponse forgotPassword(ForgotPasswordRequest request) {
+ userRepository.findByEmail(request.email()).ifPresent(user -> {
+ PasswordResetToken token = passwordResetService.createResetToken(user);
+ // In a real application, you would send an email with the reset token
+ // For now, we log it (do not expose token in response for security)
+ });
+ // Always return success to prevent email enumeration
+ return new MessageResponse("If an account with that email exists, a password reset link has been sent");
+ }
+
+ @Transactional
+ public MessageResponse resetPassword(ResetPasswordRequest request) {
+ if (!request.newPassword().equals(request.confirmPassword())) {
+ throw new BadRequestException("Passwords do not match");
+ }
+
+ PasswordResetToken resetToken = passwordResetService.verifyToken(request.token());
+ User user = resetToken.getUser();
+ user.setPasswordHash(passwordEncoder.encode(request.newPassword()));
+ userRepository.save(user);
+
+ passwordResetService.markTokenAsUsed(resetToken);
+ // Revoke all existing refresh tokens for security
+ refreshTokenService.revokeAllByUserId(user.getId());
+
+ return new MessageResponse("Password has been reset successfully");
+ }
+
+ @Transactional
+ public MessageResponse logout(LogoutRequest request) {
+ refreshTokenService.revokeToken(request.refreshToken());
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ String userEmail = (auth != null && auth.isAuthenticated()) ? auth.getName() : "unknown";
+ log.info("event=LOGOUT_SUCCESS userEmail={}", userEmail);
+ return new MessageResponse("Logged out successfully");
+ }
+
+ private AuthResponse buildAuthResponse(User user) {
+ UserDetails userDetails = new org.springframework.security.core.userdetails.User(
+ user.getEmail(), user.getPasswordHash(), Collections.emptyList());
+ String accessToken = jwtService.generateToken(userDetails);
+ RefreshToken refreshToken = refreshTokenService.createRefreshToken(user);
+
+ return new AuthResponse(accessToken, refreshToken.getToken(), authMapper.toUserResponse(user));
+ }
+}
diff --git a/src/main/resources/application.yml.orig b/src/main/resources/application.yml.orig
new file mode 100644
index 0000000..fc49375
--- /dev/null
+++ b/src/main/resources/application.yml.orig
@@ -0,0 +1,82 @@
+spring:
+ application:
+ name: job-tracker
+ datasource:
+ url: ${DB_URL:jdbc:mariadb://localhost:3306/jobtracker?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true}
+ username: ${DB_USERNAME:jobtracker}
+ password: ${DB_PASSWORD:jobtracker}
+ driver-class-name: org.mariadb.jdbc.Driver
+ jpa:
+ hibernate:
+ ddl-auto: validate
+ show-sql: false
+ properties:
+ hibernate:
+ dialect: org.hibernate.dialect.MariaDBDialect
+ format_sql: true
+ flyway:
+ enabled: true
+ locations: classpath:db/migration
+ baseline-on-migrate: true
+
+server:
+ port: ${SERVER_PORT:8080}
+
+jwt:
+ secret: ${JWT_SECRET:ThisIsAVeryLongSecretKeyForJWTTokensAndItMustBeAtLeast256BitsLong}
+ access-token-expiration-ms: ${JWT_ACCESS_TOKEN_EXPIRATION_MS:900000}
+ refresh-token-expiration-ms: ${JWT_REFRESH_TOKEN_EXPIRATION_MS:604800000}
+
+cors:
+ allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:3000,http://localhost:5173}
+
+management:
+ server:
+ port: ${MANAGEMENT_PORT:8081}
+ endpoints:
+ web:
+ exposure:
+ include: health,info,metrics,prometheus
+ endpoint:
+ health:
+ show-details: when_authorized
+ metrics:
+ tags:
+ application: ${spring.application.name}
+ distribution:
+ percentiles-histogram:
+ http.server.requests: true
+
+springdoc:
+ api-docs:
+ path: /v3/api-docs
+ swagger-ui:
+ path: /swagger-ui.html
+<<<<<<< HEAD:backend/src/main/resources/application.yml
+
+management:
+ endpoints:
+ web:
+ exposure:
+ include: health,info,prometheus,metrics
+ tracing:
+ sampling:
+ probability: ${TRACING_SAMPLING_PROBABILITY:1.0}
+ otlp:
+ tracing:
+ endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4317}
+ metrics:
+ tags:
+ application: ${spring.application.name}
+=======
+ group-configs:
+ - group: auth
+ display-name: Auth API
+ paths-to-match: /api/auth/**
+ - group: applications
+ display-name: Application API
+ paths-to-match: /api/applications/**
+ - group: dashboard
+ display-name: Dashboard API
+ paths-to-match: /api/dashboard/**
+>>>>>>> origin/main:src/main/resources/application.yml
diff --git a/src/main/resources/logback-spring.xml.orig b/src/main/resources/logback-spring.xml.orig
index 3e3368c..a4cc835 100644
--- a/src/main/resources/logback-spring.xml.orig
+++ b/src/main/resources/logback-spring.xml.orig
@@ -1,12 +1,36 @@
+<<<<<<< HEAD:backend/src/main/resources/logback-spring.xml
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-}] [%X{spanId:-}] %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+=======
-
+
-
- {"timestamp":"%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ}","level":"%level","app":"${appName}","logger":"%logger{36}","thread":"%thread","message":"%msg"}%n
+
+ {"app":"${appName}"}
@@ -37,5 +61,6 @@
+>>>>>>> origin/main:src/main/resources/logback-spring.xml
From c74fb51095f340c1eacbc9d3241cf47f53fcfc08 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 13 Apr 2026 18:21:48 +0000
Subject: [PATCH 4/4] fix: resolve merge conflicts and apply reviewer feedback
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Resolve all 6 conflicted files (pom.xml, application.yml, logback-spring.xml,
AuthService.java, Dockerfile, docker-compose.yml)
- Remove 4 leftover merge-artifact files (docker-compose_BACKUP/BASE/LOCAL/REMOTE)
- Remove all .orig backup files
- pom.xml: keep tracing deps + logstash + testcontainers (both sides merged)
- application.yml: single management block with tracing/OTLP config + springdoc groups
- logback-spring.xml: origin/main structure with traceId/spanId added to CONSOLE pattern
- AuthService: do not call span.error() for BadCredentialsException; fix refresh()
to correctly return RefreshResponse instead of AuthResponse
- ApplicationService: remove high-cardinality vacancy tag from span
- Dockerfile: use maven:3.9-eclipse-temurin-21 image; add non-root user; root-level paths
- docker-compose.yml: full merge – Jaeger 1.57.0 + Prometheus/Grafana; require DB
passwords via :? syntax; env-var driven JWT_SECRET
- ApplicationServiceTest/AuthServiceTest: add @Mock(RETURNS_DEEP_STUBS) Tracer
- application-test.yml: disable tracing (probability=0.0) in test profile
Agent-Logs-Url: https://github.com/vitorhugo-java/SpringBoot-JobApplyTracker/sessions/1f21e07a-4b0e-477b-b5db-c700a3631326
Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
---
Dockerfile | 29 +--
Dockerfile.orig | 68 ------
.../JobTrackerApplicationTests.java.orig | 26 ---
docker-compose.yml | 80 ++-----
docker-compose.yml.orig | 160 --------------
docker-compose_BACKUP_20088.yml | 99 ---------
docker-compose_BASE_20088.yml | 0
docker-compose_LOCAL_20088.yml | 99 ---------
docker-compose_REMOTE_20088.yml | 41 ----
pom.xml | 12 --
pom.xml.orig | 197 ------------------
.../config/RequestLoggingFilter.java.orig | 48 -----
.../GlobalExceptionHandler.java.orig | 81 -------
.../service/ApplicationService.java | 1 -
.../com/jobtracker/service/AuthService.java | 37 +---
.../jobtracker/service/AuthService.java.orig | 183 ----------------
src/main/resources/application.yml | 24 +--
src/main/resources/application.yml.orig | 82 --------
src/main/resources/logback-spring.xml | 33 +--
src/main/resources/logback-spring.xml.orig | 66 ------
.../unit/ApplicationServiceTest.java | 3 +
.../com/jobtracker/unit/AuthServiceTest.java | 3 +
src/test/resources/application-test.yml | 5 +
23 files changed, 59 insertions(+), 1318 deletions(-)
delete mode 100644 Dockerfile.orig
delete mode 100644 backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig
delete mode 100644 docker-compose.yml.orig
delete mode 100644 docker-compose_BACKUP_20088.yml
delete mode 100644 docker-compose_BASE_20088.yml
delete mode 100644 docker-compose_LOCAL_20088.yml
delete mode 100644 docker-compose_REMOTE_20088.yml
delete mode 100644 pom.xml.orig
delete mode 100644 src/main/java/com/jobtracker/config/RequestLoggingFilter.java.orig
delete mode 100644 src/main/java/com/jobtracker/exception/GlobalExceptionHandler.java.orig
delete mode 100644 src/main/java/com/jobtracker/service/AuthService.java.orig
delete mode 100644 src/main/resources/application.yml.orig
delete mode 100644 src/main/resources/logback-spring.xml.orig
diff --git a/Dockerfile b/Dockerfile
index acb89d8..994a491 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,35 +1,18 @@
-<<<<<<< HEAD:backend/Dockerfile
-FROM maven:3.9-eclipse-temurin-21 AS builder
-WORKDIR /app
-COPY pom.xml .
-RUN mvn -B -q dependency:go-offline
-COPY src ./src
-RUN mvn -B -q package -DskipTests
-
-FROM eclipse-temurin:21-jre
-WORKDIR /app
-COPY --from=builder /app/target/*.jar app.jar
-EXPOSE 8080
-ENTRYPOINT ["java", "-jar", "app.jar"]
-=======
# ============================================================
# Stage 1: Build
# ============================================================
-FROM eclipse-temurin:21-jdk-alpine AS build
-
-# Install Maven
-RUN apk add --no-cache maven
+FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /workspace
# Copy POM first to cache dependency layer
-COPY backend/pom.xml .
+COPY pom.xml .
# Download dependencies (cached unless pom.xml changes)
RUN mvn dependency:go-offline -B --no-transfer-progress
# Copy source code and build the fat JAR, skipping tests
-COPY backend/src src
+COPY src src
RUN mvn package -DskipTests -B --no-transfer-progress
# ============================================================
@@ -43,10 +26,7 @@ RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# Copy the built JAR from the build stage
-COPY --from=build /workspace/target/*.jar app.jar
-
-# Ensure the app directory is owned by the non-root user
-RUN chown -R appuser:appgroup /app
+COPY --from=build --chown=appuser:appgroup /workspace/target/*.jar app.jar
USER appuser
@@ -65,4 +45,3 @@ ENV JAVA_OPTS="-XX:+UseContainerSupport \
-Djava.security.egd=file:/dev/./urandom"
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar app.jar"]
->>>>>>> origin/main:Dockerfile
diff --git a/Dockerfile.orig b/Dockerfile.orig
deleted file mode 100644
index acb89d8..0000000
--- a/Dockerfile.orig
+++ /dev/null
@@ -1,68 +0,0 @@
-<<<<<<< HEAD:backend/Dockerfile
-FROM maven:3.9-eclipse-temurin-21 AS builder
-WORKDIR /app
-COPY pom.xml .
-RUN mvn -B -q dependency:go-offline
-COPY src ./src
-RUN mvn -B -q package -DskipTests
-
-FROM eclipse-temurin:21-jre
-WORKDIR /app
-COPY --from=builder /app/target/*.jar app.jar
-EXPOSE 8080
-ENTRYPOINT ["java", "-jar", "app.jar"]
-=======
-# ============================================================
-# Stage 1: Build
-# ============================================================
-FROM eclipse-temurin:21-jdk-alpine AS build
-
-# Install Maven
-RUN apk add --no-cache maven
-
-WORKDIR /workspace
-
-# Copy POM first to cache dependency layer
-COPY backend/pom.xml .
-
-# Download dependencies (cached unless pom.xml changes)
-RUN mvn dependency:go-offline -B --no-transfer-progress
-
-# Copy source code and build the fat JAR, skipping tests
-COPY backend/src src
-RUN mvn package -DskipTests -B --no-transfer-progress
-
-# ============================================================
-# Stage 2: Runtime
-# ============================================================
-FROM eclipse-temurin:21-jre-alpine AS runtime
-
-# Create a non-root user for security
-RUN addgroup -S appgroup && adduser -S appuser -G appgroup
-
-WORKDIR /app
-
-# Copy the built JAR from the build stage
-COPY --from=build /workspace/target/*.jar app.jar
-
-# Ensure the app directory is owned by the non-root user
-RUN chown -R appuser:appgroup /app
-
-USER appuser
-
-# Expose application port and management port
-EXPOSE 8080
-EXPOSE 8081
-
-# Health check – relies on Spring Actuator management port
-HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
- CMD wget -qO- http://localhost:8081/actuator/health || exit 1
-
-# JVM tuning flags for containers (respects cgroup memory limits)
-ENV JAVA_OPTS="-XX:+UseContainerSupport \
- -XX:MaxRAMPercentage=75.0 \
- -XX:+ExitOnOutOfMemoryError \
- -Djava.security.egd=file:/dev/./urandom"
-
-ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar app.jar"]
->>>>>>> origin/main:Dockerfile
diff --git a/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig b/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig
deleted file mode 100644
index 2f16b81..0000000
--- a/backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java.orig
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.jobtracker;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.TestPropertySource;
-
-@SpringBootTest
-@TestPropertySource(properties = {
- "spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1",
- "spring.datasource.driver-class-name=org.h2.Driver",
- "spring.datasource.username=sa",
- "spring.datasource.password=",
- "spring.jpa.hibernate.ddl-auto=create-drop",
- "spring.flyway.enabled=false",
- "jwt.secret=TestSecretKeyThatIsAtLeast256BitsLongForTestingPurposesOnly",
- "jwt.access-token-expiration-ms=900000",
- "jwt.refresh-token-expiration-ms=604800000",
- "cors.allowed-origins=http://localhost:3000",
- "management.tracing.sampling.probability=0.0"
-})
-class JobTrackerApplicationTests {
-
- @Test
- void contextLoads() {
- }
-}
diff --git a/docker-compose.yml b/docker-compose.yml
index 6d4c574..abb7187 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,21 +2,6 @@ version: "3.9"
services:
-<<<<<<< HEAD
- db:
- image: mariadb:11
- container_name: jobtracker-db
- restart: unless-stopped
- environment:
- MARIADB_DATABASE: ${DB_NAME:-jobtracker}
- MARIADB_USER: ${DB_USERNAME:-jobtracker}
- MARIADB_PASSWORD: ${DB_PASSWORD:-jobtracker}
- MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
- ports:
- - "3306:3306"
- volumes:
- - db_data:/var/lib/mysql
-=======
# ── Spring Boot application ───────────────────────────────
app:
build:
@@ -26,15 +11,18 @@ services:
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:3306/jobtracker?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
- SPRING_DATASOURCE_USERNAME: root
- SPRING_DATASOURCE_PASSWORD: root
- # WARNING: Replace this secret before any production use. This value is for development only.
- JWT_SECRET: ChangeThisToAVeryLongSecretKeyForJWTTokensInDevelopment
- CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://localhost:5173"
+ DB_URL: jdbc:mariadb://mariadb:3306/${DB_NAME:-jobtracker}?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
+ DB_USERNAME: ${DB_USERNAME:-jobtracker}
+ DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
+ JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
+ CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-"http://localhost:3000,http://localhost:5173"}
+ OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
+ TRACING_SAMPLING_PROBABILITY: ${TRACING_SAMPLING_PROBABILITY:-1.0}
depends_on:
mariadb:
condition: service_healthy
+ jaeger:
+ condition: service_started
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8081/actuator/health"]
interval: 30s
@@ -52,54 +40,32 @@ services:
ports:
- "3306:3306"
environment:
- MARIADB_ROOT_PASSWORD: root
- MARIADB_DATABASE: jobtracker
+ MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?DB_ROOT_PASSWORD is required}
+ MARIADB_DATABASE: ${DB_NAME:-jobtracker}
+ MARIADB_USER: ${DB_USERNAME:-jobtracker}
+ MARIADB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
volumes:
- mariadb_data:/var/lib/mysql
->>>>>>> origin/main
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
-<<<<<<< HEAD
+ start_period: 30s
+ networks:
+ - job-tracker-net
+ restart: unless-stopped
+ # ── Jaeger (distributed tracing) ─────────────────────────
jaeger:
- image: jaegertracing/all-in-one:1.57.0
- container_name: jobtracker-jaeger
- restart: unless-stopped
+ image: jaegertracing/all-in-one:1.57.0
+ container_name: job-tracker-jaeger
ports:
- "16686:16686" # Jaeger UI
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
environment:
- COLLECTOR_OTLP_ENABLED=true
-
- app:
- build:
- context: ./backend
- dockerfile: Dockerfile
- container_name: jobtracker-app
- restart: unless-stopped
- depends_on:
- db:
- condition: service_healthy
- jaeger:
- condition: service_started
- ports:
- - "8080:8080"
- environment:
- DB_URL: jdbc:mariadb://db:3306/${DB_NAME:-jobtracker}?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
- DB_USERNAME: ${DB_USERNAME:-jobtracker}
- DB_PASSWORD: ${DB_PASSWORD:-jobtracker}
- OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
- TRACING_SAMPLING_PROBABILITY: ${TRACING_SAMPLING_PROBABILITY:-1.0}
- SERVER_PORT: 8080
-
-volumes:
- db_data:
-=======
- start_period: 30s
networks:
- job-tracker-net
restart: unless-stopped
@@ -135,8 +101,8 @@ volumes:
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER: admin
- GF_SECURITY_ADMIN_PASSWORD: admin
+ GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
+ GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin}
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro
@@ -157,4 +123,4 @@ volumes:
networks:
job-tracker-net:
driver: bridge
->>>>>>> origin/main
+
diff --git a/docker-compose.yml.orig b/docker-compose.yml.orig
deleted file mode 100644
index 6d4c574..0000000
--- a/docker-compose.yml.orig
+++ /dev/null
@@ -1,160 +0,0 @@
-version: "3.9"
-
-services:
-
-<<<<<<< HEAD
- db:
- image: mariadb:11
- container_name: jobtracker-db
- restart: unless-stopped
- environment:
- MARIADB_DATABASE: ${DB_NAME:-jobtracker}
- MARIADB_USER: ${DB_USERNAME:-jobtracker}
- MARIADB_PASSWORD: ${DB_PASSWORD:-jobtracker}
- MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
- ports:
- - "3306:3306"
- volumes:
- - db_data:/var/lib/mysql
-=======
- # ── Spring Boot application ───────────────────────────────
- app:
- build:
- context: .
- dockerfile: Dockerfile
- container_name: job-tracker-app
- ports:
- - "8080:8080"
- environment:
- SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:3306/jobtracker?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
- SPRING_DATASOURCE_USERNAME: root
- SPRING_DATASOURCE_PASSWORD: root
- # WARNING: Replace this secret before any production use. This value is for development only.
- JWT_SECRET: ChangeThisToAVeryLongSecretKeyForJWTTokensInDevelopment
- CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://localhost:5173"
- depends_on:
- mariadb:
- condition: service_healthy
- healthcheck:
- test: ["CMD", "wget", "-qO-", "http://localhost:8081/actuator/health"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 60s
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── MariaDB ───────────────────────────────────────────────
- mariadb:
- image: mariadb:11
- container_name: job-tracker-mariadb
- ports:
- - "3306:3306"
- environment:
- MARIADB_ROOT_PASSWORD: root
- MARIADB_DATABASE: jobtracker
- volumes:
- - mariadb_data:/var/lib/mysql
->>>>>>> origin/main
- healthcheck:
- test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
- interval: 10s
- timeout: 5s
- retries: 5
-<<<<<<< HEAD
-
- jaeger:
- image: jaegertracing/all-in-one:1.57.0
- container_name: jobtracker-jaeger
- restart: unless-stopped
- ports:
- - "16686:16686" # Jaeger UI
- - "4317:4317" # OTLP gRPC receiver
- - "4318:4318" # OTLP HTTP receiver
- environment:
- - COLLECTOR_OTLP_ENABLED=true
-
- app:
- build:
- context: ./backend
- dockerfile: Dockerfile
- container_name: jobtracker-app
- restart: unless-stopped
- depends_on:
- db:
- condition: service_healthy
- jaeger:
- condition: service_started
- ports:
- - "8080:8080"
- environment:
- DB_URL: jdbc:mariadb://db:3306/${DB_NAME:-jobtracker}?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
- DB_USERNAME: ${DB_USERNAME:-jobtracker}
- DB_PASSWORD: ${DB_PASSWORD:-jobtracker}
- OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
- TRACING_SAMPLING_PROBABILITY: ${TRACING_SAMPLING_PROBABILITY:-1.0}
- SERVER_PORT: 8080
-
-volumes:
- db_data:
-=======
- start_period: 30s
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── Prometheus ────────────────────────────────────────────
- prometheus:
- image: prom/prometheus:v2.54.1
- container_name: job-tracker-prometheus
- ports:
- - "9090:9090"
- volumes:
- - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- - prometheus_data:/prometheus
- command:
- - "--config.file=/etc/prometheus/prometheus.yml"
- - "--storage.tsdb.path=/prometheus"
- - "--web.console.libraries=/usr/share/prometheus/console_libraries"
- - "--web.console.templates=/usr/share/prometheus/consoles"
- healthcheck:
- test: ["CMD", "wget", "-qO-", "http://localhost:9090/-/healthy"]
- interval: 15s
- timeout: 5s
- retries: 3
- start_period: 15s
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── Grafana ───────────────────────────────────────────────
- grafana:
- image: grafana/grafana:10.4.8
- container_name: job-tracker-grafana
- ports:
- - "3000:3000"
- environment:
- GF_SECURITY_ADMIN_USER: admin
- GF_SECURITY_ADMIN_PASSWORD: admin
- volumes:
- - grafana_data:/var/lib/grafana
- - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro
- - ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards:ro
- - ./grafana/dashboards:/etc/grafana/dashboards:ro
- depends_on:
- prometheus:
- condition: service_healthy
- networks:
- - job-tracker-net
- restart: unless-stopped
-
-volumes:
- mariadb_data:
- prometheus_data:
- grafana_data:
-
-networks:
- job-tracker-net:
- driver: bridge
->>>>>>> origin/main
diff --git a/docker-compose_BACKUP_20088.yml b/docker-compose_BACKUP_20088.yml
deleted file mode 100644
index 5600e15..0000000
--- a/docker-compose_BACKUP_20088.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-version: "3.9"
-
-services:
-
- # ── Spring Boot application ───────────────────────────────
- app:
- build:
- context: .
- dockerfile: Dockerfile
- container_name: job-tracker-app
- ports:
- - "8080:8080"
- environment:
- SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:3306/jobtracker?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
- SPRING_DATASOURCE_USERNAME: root
- SPRING_DATASOURCE_PASSWORD: root
- # WARNING: Replace this secret before any production use. This value is for development only.
- JWT_SECRET: ChangeThisToAVeryLongSecretKeyForJWTTokensInDevelopment
- CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://localhost:5173"
- depends_on:
- mariadb:
- condition: service_healthy
- healthcheck:
- test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 60s
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── MariaDB ───────────────────────────────────────────────
- mariadb:
- image: mariadb:11
- container_name: job-tracker-mariadb
- ports:
- - "3306:3306"
- environment:
- MARIADB_ROOT_PASSWORD: root
- MARIADB_DATABASE: jobtracker
- volumes:
- - mariadb_data:/var/lib/mysql
- healthcheck:
- test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
- interval: 10s
- timeout: 5s
- retries: 5
- start_period: 30s
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── Prometheus ────────────────────────────────────────────
- prometheus:
- image: prom/prometheus:latest
- container_name: job-tracker-prometheus
- ports:
- - "9090:9090"
- volumes:
- - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- - prometheus_data:/prometheus
- command:
- - "--config.file=/etc/prometheus/prometheus.yml"
- - "--storage.tsdb.path=/prometheus"
- - "--web.console.libraries=/usr/share/prometheus/console_libraries"
- - "--web.console.templates=/usr/share/prometheus/consoles"
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── Grafana ───────────────────────────────────────────────
- grafana:
- image: grafana/grafana:latest
- container_name: job-tracker-grafana
- ports:
- - "3000:3000"
- environment:
- GF_SECURITY_ADMIN_USER: admin
- GF_SECURITY_ADMIN_PASSWORD: admin
- volumes:
- - grafana_data:/var/lib/grafana
- - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro
- - ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards:ro
- - ./grafana/dashboards:/etc/grafana/dashboards:ro
- depends_on:
- - prometheus
- networks:
- - job-tracker-net
- restart: unless-stopped
-
-volumes:
- mariadb_data:
- prometheus_data:
- grafana_data:
-
-networks:
- job-tracker-net:
- driver: bridge
diff --git a/docker-compose_BASE_20088.yml b/docker-compose_BASE_20088.yml
deleted file mode 100644
index e69de29..0000000
diff --git a/docker-compose_LOCAL_20088.yml b/docker-compose_LOCAL_20088.yml
deleted file mode 100644
index 5600e15..0000000
--- a/docker-compose_LOCAL_20088.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-version: "3.9"
-
-services:
-
- # ── Spring Boot application ───────────────────────────────
- app:
- build:
- context: .
- dockerfile: Dockerfile
- container_name: job-tracker-app
- ports:
- - "8080:8080"
- environment:
- SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:3306/jobtracker?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
- SPRING_DATASOURCE_USERNAME: root
- SPRING_DATASOURCE_PASSWORD: root
- # WARNING: Replace this secret before any production use. This value is for development only.
- JWT_SECRET: ChangeThisToAVeryLongSecretKeyForJWTTokensInDevelopment
- CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://localhost:5173"
- depends_on:
- mariadb:
- condition: service_healthy
- healthcheck:
- test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
- interval: 30s
- timeout: 10s
- retries: 5
- start_period: 60s
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── MariaDB ───────────────────────────────────────────────
- mariadb:
- image: mariadb:11
- container_name: job-tracker-mariadb
- ports:
- - "3306:3306"
- environment:
- MARIADB_ROOT_PASSWORD: root
- MARIADB_DATABASE: jobtracker
- volumes:
- - mariadb_data:/var/lib/mysql
- healthcheck:
- test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
- interval: 10s
- timeout: 5s
- retries: 5
- start_period: 30s
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── Prometheus ────────────────────────────────────────────
- prometheus:
- image: prom/prometheus:latest
- container_name: job-tracker-prometheus
- ports:
- - "9090:9090"
- volumes:
- - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- - prometheus_data:/prometheus
- command:
- - "--config.file=/etc/prometheus/prometheus.yml"
- - "--storage.tsdb.path=/prometheus"
- - "--web.console.libraries=/usr/share/prometheus/console_libraries"
- - "--web.console.templates=/usr/share/prometheus/consoles"
- networks:
- - job-tracker-net
- restart: unless-stopped
-
- # ── Grafana ───────────────────────────────────────────────
- grafana:
- image: grafana/grafana:latest
- container_name: job-tracker-grafana
- ports:
- - "3000:3000"
- environment:
- GF_SECURITY_ADMIN_USER: admin
- GF_SECURITY_ADMIN_PASSWORD: admin
- volumes:
- - grafana_data:/var/lib/grafana
- - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro
- - ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards:ro
- - ./grafana/dashboards:/etc/grafana/dashboards:ro
- depends_on:
- - prometheus
- networks:
- - job-tracker-net
- restart: unless-stopped
-
-volumes:
- mariadb_data:
- prometheus_data:
- grafana_data:
-
-networks:
- job-tracker-net:
- driver: bridge
diff --git a/docker-compose_REMOTE_20088.yml b/docker-compose_REMOTE_20088.yml
deleted file mode 100644
index bdf6d43..0000000
--- a/docker-compose_REMOTE_20088.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-version: '3.8'
-
-services:
- db:
- image: mariadb:11.2
- container_name: jobtracker-db
- environment:
- MYSQL_ROOT_PASSWORD: root
- MYSQL_DATABASE: jobtracker
- MYSQL_USER: jobtracker
- MYSQL_PASSWORD: jobtracker
- ports:
- - "3306:3306"
- volumes:
- - mariadb_data:/var/lib/mysql
- healthcheck:
- test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
- interval: 10s
- timeout: 5s
- retries: 5
-
- app:
- build: .
- container_name: jobtracker-app
- ports:
- - "8080:8080"
- environment:
- DB_URL: jdbc:mariadb://db:3306/jobtracker?createDatabaseIfNotExist=true&characterEncoding=UTF-8&useUnicode=true
- DB_USERNAME: jobtracker
- DB_PASSWORD: jobtracker
- JWT_SECRET: ${JWT_SECRET:-ChangeThisToASecureRandomSecretKeyInProductionAtLeast256BitsLong}
- JWT_ACCESS_TOKEN_EXPIRATION_MS: 900000
- JWT_REFRESH_TOKEN_EXPIRATION_MS: 604800000
- CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:3000}
- depends_on:
- db:
- condition: service_healthy
- restart: unless-stopped
-
-volumes:
- mariadb_data:
diff --git a/pom.xml b/pom.xml
index 96ffbcd..cd5b9bb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,17 +86,12 @@
2.5.0
-<<<<<<< HEAD:backend/pom.xml
-
-=======
->>>>>>> origin/main:pom.xml
org.springframework.boot
spring-boot-starter-actuator
-<<<<<<< HEAD:backend/pom.xml
io.micrometer
@@ -109,25 +104,18 @@
opentelemetry-exporter-otlp
-
-=======
->>>>>>> origin/main:pom.xml
io.micrometer
micrometer-registry-prometheus
-<<<<<<< HEAD:backend/pom.xml
-=======
net.logstash.logback
logstash-logback-encoder
7.4
-
->>>>>>> origin/main:pom.xml
org.springframework.boot
diff --git a/pom.xml.orig b/pom.xml.orig
deleted file mode 100644
index 96ffbcd..0000000
--- a/pom.xml.orig
+++ /dev/null
@@ -1,197 +0,0 @@
-
-
- 4.0.0
-
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.2.5
-
-
-
- com.jobtracker
- job-tracker
- 1.0.0
- job-tracker
- Spring Boot Job Application Tracker API
-
-
- 21
- 0.12.5
- 1.19.8
- 5.4.0
-
-
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
-
- org.springframework.boot
- spring-boot-starter-security
-
-
-
-
- org.mariadb.jdbc
- mariadb-java-client
- runtime
-
-
-
-
- org.flywaydb
- flyway-core
-
-
- org.flywaydb
- flyway-mysql
-
-
-
-
- io.jsonwebtoken
- jjwt-api
- ${jjwt.version}
-
-
- io.jsonwebtoken
- jjwt-impl
- ${jjwt.version}
- runtime
-
-
- io.jsonwebtoken
- jjwt-jackson
- ${jjwt.version}
- runtime
-
-
-
-
- org.springdoc
- springdoc-openapi-starter-webmvc-ui
- 2.5.0
-
-
-<<<<<<< HEAD:backend/pom.xml
-
-=======
-
->>>>>>> origin/main:pom.xml
-
- org.springframework.boot
- spring-boot-starter-actuator
-
-
-<<<<<<< HEAD:backend/pom.xml
-
-
- io.micrometer
- micrometer-tracing-bridge-otel
-
-
-
-
- io.opentelemetry
- opentelemetry-exporter-otlp
-
-
-
-=======
-
->>>>>>> origin/main:pom.xml
-
- io.micrometer
- micrometer-registry-prometheus
-
-
-<<<<<<< HEAD:backend/pom.xml
-=======
-
-
- net.logstash.logback
- logstash-logback-encoder
- 7.4
-
-
->>>>>>> origin/main:pom.xml
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
- org.springframework.security
- spring-security-test
- test
-
-
-
-
- org.testcontainers
- junit-jupiter
- ${testcontainers.version}
- test
-
-
- org.testcontainers
- mariadb
- ${testcontainers.version}
- test
-
-
-
-
- io.rest-assured
- rest-assured
- ${rest-assured.version}
- test
-
-
- io.rest-assured
- json-path
- ${rest-assured.version}
- test
-
-
- io.rest-assured
- spring-mock-mvc
- ${rest-assured.version}
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- **/*Test.java
- **/*Tests.java
- **/*IT.java
-
-
-
-
-
-
diff --git a/src/main/java/com/jobtracker/config/RequestLoggingFilter.java.orig b/src/main/java/com/jobtracker/config/RequestLoggingFilter.java.orig
deleted file mode 100644
index d270a3a..0000000
--- a/src/main/java/com/jobtracker/config/RequestLoggingFilter.java.orig
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.jobtracker.config;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.lang.NonNull;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.stereotype.Component;
-import org.springframework.web.filter.OncePerRequestFilter;
-
-import java.io.IOException;
-
-@Component
-public class RequestLoggingFilter extends OncePerRequestFilter {
-
- private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);
-
- @Override
- protected void doFilterInternal(@NonNull HttpServletRequest request,
- @NonNull HttpServletResponse response,
- @NonNull FilterChain filterChain) throws ServletException, IOException {
- long start = System.currentTimeMillis();
- try {
- filterChain.doFilter(request, response);
- } finally {
- long duration = System.currentTimeMillis() - start;
- String userId = resolveUserId();
- log.info("method={} path={} status={} duration={}ms userId={}",
- request.getMethod(),
- request.getRequestURI(),
- response.getStatus(),
- duration,
- userId);
- }
- }
-
- private String resolveUserId() {
- Authentication auth = SecurityContextHolder.getContext().getAuthentication();
- if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getPrincipal())) {
- return auth.getName();
- }
- return "anonymous";
- }
-}
diff --git a/src/main/java/com/jobtracker/exception/GlobalExceptionHandler.java.orig b/src/main/java/com/jobtracker/exception/GlobalExceptionHandler.java.orig
deleted file mode 100644
index 0d4b520..0000000
--- a/src/main/java/com/jobtracker/exception/GlobalExceptionHandler.java.orig
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.jobtracker.exception;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.validation.FieldError;
-import org.springframework.web.bind.MethodArgumentNotValidException;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.RestControllerAdvice;
-
-import java.time.LocalDateTime;
-import java.util.HashMap;
-import java.util.Map;
-
-@RestControllerAdvice
-public class GlobalExceptionHandler {
-
- private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
-
- @ExceptionHandler(ResourceNotFoundException.class)
- public ResponseEntity