Frontend - React-JobApplyTracker
A production-ready Spring Boot REST API for tracking job applications, built with Java 21, Spring Security JWT authentication, MariaDB, and comprehensive test coverage.
- Java 21
- Spring Boot 3.2 (Web, Data JPA, Security, Validation)
- Spring Security with stateless JWT authentication
- JWT + Refresh Tokens (access: 15 min, refresh: 7 days with rotation)
- Resilience4j Rate Limiting on auth endpoints
- MariaDB (production) / Testcontainers (tests)
- Flyway for DB migrations
- JUnit 5 + Mockito (unit tests)
- Testcontainers + MockMvc (integration tests)
- RestAssured (E2E tests)
- Maven
.
├── src/
│ ├── main/java/com/jobtracker/
│ │ ├── config/ # Security, JWT, CORS, filters
│ │ ├── controller/ # REST controllers
│ │ ├── dto/ # Request/Response DTOs
│ │ ├── entity/ # JPA entities
│ │ ├── exception/ # Global exception handling
│ │ ├── mapper/ # Entity-DTO mappers
│ │ ├── repository/ # Spring Data JPA repositories
│ │ ├── service/ # Business logic
│ │ └── util/ # Utilities
│ ├── main/resources/
│ │ ├── application.yml
│ │ └── db/migration/ # Flyway migrations
│ └── test/java/com/jobtracker/
│ ├── unit/ # Mockito unit tests
│ ├── integration/ # SpringBootTest + Testcontainers + MockMvc
│ └── e2e/ # RestAssured end-to-end tests
├── pom.xml
├── Dockerfile
├── docker-compose.yml
└── .github/workflows/ci.yml
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register a new user |
| POST | /api/auth/login |
Login and receive tokens |
| POST | /api/auth/refresh |
Refresh access token |
| POST | /api/auth/logout |
Logout and revoke refresh token |
| POST | /api/auth/forgot-password |
Request password reset |
| POST | /api/auth/reset-password |
Reset password with token |
| GET | /api/auth/me |
Get current user info |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/applications |
Create application |
| GET | /api/applications |
List all (paginated + filterable) |
| GET | /api/applications/{id} |
Get by ID |
| PUT | /api/applications/{id} |
Full update |
| PATCH | /api/applications/{id}/status |
Update status |
| PATCH | /api/applications/{id}/reminder |
Toggle reminder |
| DELETE | /api/applications/{id} |
Delete |
| GET | /api/applications/upcoming |
Upcoming next steps |
| GET | /api/applications/overdue |
Overdue next steps |
RHFiz a RH - Aguardando AtualizaçãoFiz a Hiring Manager - Aguardando AtualizaçãoTeste TécnicoFiz teste Técnico - aguardando atualizaçãoRH (Negociação)
docker-compose up -dThe API will be available at http://localhost:8080.
export DB_URL=jdbc:mariadb://localhost:3306/jobtracker?createDatabaseIfNotExist=true
export DB_USERNAME=jobtracker
export DB_PASSWORD=jobtracker
export JWT_SECRET=your-secret-key-at-least-256-bits-long
mvn spring-boot:run# All tests
mvn verify
# Unit tests only
mvn test -Dtest="com.jobtracker.unit.*"
# Integration tests only
mvn test -Dtest="com.jobtracker.integration.*"
# E2E tests only
mvn test -Dtest="com.jobtracker.e2e.*"Note: Integration and E2E tests require Docker to be running (Testcontainers pulls a MariaDB image automatically).
This project includes a startup seeder that can generate fake job applications using the Java library net.datafaker:datafaker.
The seeder is disabled by default and only runs when explicitly enabled.
Required parameters:
APP_SEED_ENABLED=trueAPP_SEED_USER_EMAIL=<existing user email>
Optional:
APP_SEED_COUNT=1000(default is1000)
Example with Maven:
export APP_SEED_ENABLED=true
export APP_SEED_USER_EMAIL=user@example.com
export APP_SEED_COUNT=1000
mvn spring-boot:runExample with java -jar:
APP_SEED_ENABLED=true APP_SEED_USER_EMAIL=user@example.com APP_SEED_COUNT=1500 java -jar target/job-tracker-1.0.0.jarIf APP_SEED_ENABLED=true and APP_SEED_USER_EMAIL is not provided (or the user does not exist), the application startup fails with a clear error.
| Variable | Default | Description |
|---|---|---|
DB_URL |
jdbc:mariadb://localhost:3306/jobtracker |
JDBC URL |
DB_USERNAME |
jobtracker |
DB username |
DB_PASSWORD |
jobtracker |
DB password |
JWT_SECRET |
(dev default) | JWT signing secret (min 256 bits) |
JWT_ACCESS_TOKEN_EXPIRATION_MS |
900000 |
Access token TTL (15 min) |
JWT_REFRESH_TOKEN_EXPIRATION_MS |
604800000 |
Refresh token TTL (7 days) |
CORS_ALLOWED_ORIGINS |
http://localhost:3000,http://localhost:5173 |
Allowed CORS origins |
RATE_LIMIT_AUTH_LOGIN_LIMIT_FOR_PERIOD |
10 |
Max login requests allowed per refresh period |
RATE_LIMIT_AUTH_LOGIN_REFRESH_PERIOD |
1m |
Window used by the login rate limiter |
OTEL_EXPORTER_OTLP_ENDPOINT |
http://localhost:4317 |
OTLP gRPC endpoint (Jaeger/OpenTelemetry collector) |
PROMETHEUS_URL |
http://localhost:9090 |
Prometheus base URL for observability integrations |
SERVER_PORT |
8080 |
Server port |
Spring Boot Actuator runs on a dedicated management port 8081, completely separate from the main API port (8080). This port is never exposed to the host machine in Docker Compose — it is only reachable within the internal infra_network Docker network.
Prometheus scrapes metrics directly from the container over the internal network:
scrape_configs:
- job_name: job-tracker
static_configs:
- targets: ['app:8081']
metrics_path: /actuator/prometheus
scheme: http
scrape_interval: 15sNo authentication token is required — network-level isolation (Docker bridge network) is the security boundary. The Actuator is unreachable from outside the Docker network.
Auth endpoints are protected with Resilience4j rate limiters. When a limit is exceeded, the API returns 429 Too Many Requests with the standard error payload used by the application.
GitHub Actions workflow (.github/workflows/ci.yml) triggers on push/PR to main:
- Checkout
- Setup Java 21
- Build project
- Run unit tests
- Run integration tests (Testcontainers)
- Run E2E tests (Testcontainers + RestAssured)
- Full
mvn verify
Swagger UI is available at http://localhost:8080/swagger-ui.html when the app is running.