The backend service for the CV Management and Deterministic Internship Candidate Filtering System, developed for the Department of Computer Science, University of Ruhuna.
It provides a secure REST API for student onboarding, profile and portfolio management, official academic records, ATS-oriented CV generation, internship-request administration, deterministic candidate filtering, manual shortlist management, exports, and auditable system operations.
Important
This is the backend repository. The frontend is maintained separately in the organisation's frontend repository.
- Overview
- Features
- Technology stack
- Architecture
- Getting started
- Configuration
- API overview
- Database and migrations
- File processing and background jobs
- Testing
- Docker
- Project structure
- Scripts and automation
- Operations and troubleshooting
- Contributing
- Security
- License
The system supports two authenticated user types:
- Student — verifies an eligible university identity, manages a profile and supporting records, declares skills, maintains projects, views official results and GPA, previews a CV, and saves/downloads an active PDF CV.
- Administrator — manages companies and internship requests, imports official academic ledgers, inspects registered students, runs deterministic candidate filters, builds and finalizes shortlists, and creates CSV/ZIP exports.
The application intentionally uses deterministic, explainable criteria. It does not perform AI scoring, candidate ranking, match-percentage calculation, or automatic final selection.
- Eligible-student verification by index number and university email.
- OTP verification, expiry, resend cooldowns, and attempt limits.
- Separate student and administrator login flows.
- OTP-based password recovery.
- Stateless JWT bearer authentication and role-based access control.
- BCrypt password hashing and security-event auditing.
- Dashboard metrics.
- Personal details, contact links, and professional links.
- Profile-photo and certificate-evidence uploads with content policies.
- Certificates, awards, activities, and work experience.
- Taxonomy-backed skills and competency levels.
- Project portfolio with linked skills.
- Read-only official academic records and calculated GPA.
- CV source-freshness reporting, preview, save, and PDF download.
- Administrator dashboard metrics.
- CSV academic-ledger upload, validation, staging inspection, and commit.
- Read-only student inspection, including latest saved CVs.
- Company and internship-request CRUD.
- Taxonomy-backed required skills.
- Persisted deterministic filtering using current official GPA and declared skills.
- Manual shortlist creation, candidate management, and conflict-aware finalization.
- Asynchronous shortlist-summary CSV and latest-CV ZIP exports.
- PostgreSQL persistence and Flyway schema migrations.
- Optimistic concurrency using strong
ETagandIf-Matchheaders. - Correlation IDs and structured API errors.
- Audit-event persistence and operational metrics.
- Health checks for the application, database, and migration state.
- File storage for academic imports, profile assets, CVs, and exports.
| Area | Technology |
|---|---|
| Language/runtime | Java 21 |
| Framework | Spring Boot 4.1.0 |
| API | Spring Web MVC, Jakarta Validation, Jackson |
| Security | Spring Security, JWT, BCrypt, RBAC |
| Persistence | Spring Data JPA / Hibernate |
| Database | PostgreSQL 16 |
| Migrations | Flyway |
| CSV | Apache Commons CSV |
| Spring Mail, SMTP, development log mode | |
| PDF generation | XeLaTeX with a backend-owned ATS template |
| Testing | JUnit, Spring Boot Test, H2, Testcontainers |
| Contract | OpenAPI 3.1.1 |
| Build | Maven Wrapper |
| Containers | Docker and Docker Compose |
| CI | GitHub Actions |
The service is a modular monolith organized by business capability. It retains the deployment and transactional simplicity of one Spring Boot application while enforcing explicit module ownership.
flowchart LR
Client[Web client / API consumer] --> API[Spring MVC controllers]
API --> Security[JWT authentication and RBAC]
Security --> Modules[Feature application services]
Modules --> Domain[Domain policies and ports]
Modules --> Persistence[Spring Data JPA]
Persistence --> DB[(PostgreSQL)]
Modules --> Storage[(File storage)]
Modules --> Mail[SMTP / log email]
Modules --> Latex[XeLaTeX renderer]
Flyway[Flyway migrations] --> DB
Feature modules normally follow this shape:
modules/<feature>/
├── api/ # Controllers and request/response DTOs
├── application/ # Use-case orchestration
├── domain/ # Policies, models, exceptions, and ports
├── mapper/ # Boundary/model conversion
└── persistence/ # Entities, repositories, queries, and projections
Cross-cutting code lives in config, infrastructure, and shared. See docs/architecture for boundaries and dependency rules, and docs/adr for Architecture Decision Records.
- Java Development Kit 21
- Docker Engine and Docker Compose v2
- Git
- XeLaTeX on
PATHfor local PDF generation
Maven does not need to be installed separately; the Maven Wrapper is included. The production Docker image includes TeX Live/XeLaTeX. Without XeLaTeX, the API starts but CV PDF generation fails.
git clone https://github.com/UOR-Internship-Management-System/backend.git
cd backendSafe local defaults are provided. Copy the example file if you want explicit local configuration:
cp .env.example .envPowerShell:
Copy-Item .env.example .envDocker Compose reads .env automatically. Spring Boot does not automatically import arbitrary .env files when launched directly, so place overridden values in the shell or IDE run configuration. Never commit .env or real secrets.
docker compose -f docker/docker-compose.dev.yml up -d
docker compose -f docker/docker-compose.dev.yml psThis starts PostgreSQL 16 on localhost:5432, creates cv_management, and uses the persistent cv_management_postgres_data volume.
Linux/macOS/Git Bash:
./mvnw spring-boot:run -Dspring-boot.run.profiles=localWindows PowerShell:
.\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=local"local is the default profile. On first startup, Flyway applies pending migrations before the API accepts traffic.
curl http://localhost:8080/api/v1/health
curl http://localhost:8080/actuator/healthExample application health response:
{
"status": "UP",
"service": "cv-management-backend",
"timestamp": "2026-01-01T00:00:00Z",
"database": "UP",
"appliedMigrations": 56
}The migration count changes as the schema evolves.
docker compose -f docker/docker-compose.dev.yml downTo permanently discard the local database volume:
docker compose -f docker/docker-compose.dev.yml down -vBase configuration is in application.yml, with local, dev, test, and prod profile files alongside it.
| Profile | Purpose | Notes |
|---|---|---|
local |
Developer workstation | CV_DB_* defaults, detailed authorized health, out-of-order Flyway enabled |
dev |
Shared development | CV_DB_*, application debug logging |
test |
Automated tests | H2 where suitable and PostgreSQL Testcontainers for integration coverage |
prod |
Production | Explicit DATABASE_*, JWT, SMTP, origin, and public URL required |
| Variable | Default | Description |
|---|---|---|
SERVER_PORT |
8080 |
HTTP port |
SPRING_PROFILES_ACTIVE |
local |
Active profile |
CV_DB_HOST |
localhost |
Local/development PostgreSQL host |
CV_DB_PORT |
5432 |
Local/development PostgreSQL port |
CV_DB_NAME |
cv_management |
Local/development database |
CV_DB_USERNAME |
cv_user |
Local/development database user |
CV_DB_PASSWORD |
cv_local_password |
Local/development password |
DATABASE_URL |
— | Production JDBC URL |
DATABASE_USERNAME |
— | Production database user |
DATABASE_PASSWORD |
— | Production database password |
FRONTEND_ORIGIN |
http://localhost:5173 |
Allowed CORS origin(s), comma-separated |
PUBLIC_BASE_URL |
http://localhost:8080 |
Base URL used in public file links |
| Variable | Default | Description |
|---|---|---|
JWT_SECRET |
Local placeholder | Token-signing secret; replace outside local development |
JWT_ISSUER |
cv-management |
Token issuer |
JWT_ACCESS_TOKEN_TTL |
PT30M |
Access-token lifetime |
JWT_REFRESH_TOKEN_TTL |
P7D |
Refresh-token lifetime |
APP_AUTH_OTP_LENGTH |
6 |
OTP length |
APP_AUTH_OTP_TTL |
PT5M |
OTP lifetime |
APP_AUTH_OTP_MAX_ATTEMPTS |
3 |
Maximum verification attempts |
APP_AUTH_OTP_RESEND_COOLDOWN |
PT60S |
Resend delay |
APP_AUTH_OTP_MAX_RESENDS |
3 |
Maximum resends |
Durations use ISO-8601 notation: PT30M is 30 minutes and P7D is seven days.
| Variable | Default | Description |
|---|---|---|
APP_EMAIL_MODE |
log |
log for development or smtp for delivery |
APP_EMAIL_FROM |
no-reply@cvmanagement.local |
Sender address |
SMTP_HOST |
— | SMTP host |
SMTP_PORT |
587 |
SMTP port |
SMTP_USERNAME |
— | SMTP username |
SMTP_PASSWORD |
— | SMTP password |
Development log mode writes OTPs to the application log. Never use it in production. The prod profile forces authenticated STARTTLS SMTP.
| Variable | Default | Description |
|---|---|---|
ACADEMIC_LEDGER_STORAGE_ROOT |
./data/academic-ledger |
Ledger upload root |
ACADEMIC_LEDGER_MAX_FILE_SIZE_BYTES |
5242880 |
5 MiB domain limit |
ACADEMIC_LEDGER_WORKER_ENABLED |
true |
Enable ledger processing |
ACADEMIC_LEDGER_WORKER_POLL_DELAY_MS |
2000 |
Ledger poll delay |
CV_STORAGE_ROOT |
./data/cv |
Generated CV root |
CV_LATEX_COMMAND |
xelatex |
XeLaTeX command/path |
CV_LATEX_TIMEOUT |
PT10S |
Compilation timeout |
CV_PDF_MAX_BYTES |
5242880 |
Maximum PDF size |
CV_PDF_MAX_CONCURRENT |
2 |
Concurrent generation limit |
CV_PREVIEW_TTL |
PT15M |
Preview lifetime |
EXPORT_STORAGE_ROOT |
./data/exports |
Export root |
EXPORT_WORKER_ENABLED |
true |
Enable export processing |
EXPORT_WORKER_POLL_DELAY_MS |
2000 |
Export poll delay |
EXPORT_RETENTION |
P7D |
Export retention |
See .env.example and the YAML files for cleanup, retry, batching, and GPA-rounding controls.
All business endpoints are versioned below:
http://localhost:8080/api/v1
The canonical contract is docs/api/CV_Management_API_OpenAPI_v1.6.0.yaml. A synchronized runtime copy is kept in src/main/resources/openapi.
Note
The OpenAPI document represents the complete approved Version 1 contract and may contain reserved operations before their controller is activated. Audit events are persisted, for example, while the administrator audit-query controller remains a reserved boundary. Use controller tests and feature release evidence to judge deployment readiness for an individual operation.
| Route | Purpose |
|---|---|
GET /api/v1/health |
Application, database, and migration health |
GET /actuator/health |
Spring Boot health probe |
POST /api/v1/student-verifications |
Start student verification |
POST /api/v1/student-verifications/{id}/otp/verify |
Verify onboarding OTP |
POST /api/v1/student-verifications/{id}/otp/resend |
Resend onboarding OTP |
POST /api/v1/student-verifications/{id}/password |
Create student password |
POST /api/v1/auth/student/login |
Student login |
POST /api/v1/auth/admin/login |
Administrator login |
/api/v1/password-resets/** |
Password recovery |
GET /api/v1/files/{fileAssetId}/content |
Token-validated file content |
| Base route | Purpose |
|---|---|
/api/v1/auth/me |
Current account |
/api/v1/auth/logout |
Logout/token invalidation |
/api/v1/me/dashboard/metrics |
Dashboard summary |
/api/v1/me/profile |
Profile and supporting records |
/api/v1/me/profile/photo |
Profile photo |
/api/v1/me/profile/certificates/{id}/evidence |
Certificate evidence |
/api/v1/skill-taxonomy |
Taxonomy, clusters, categories, skills |
/api/v1/me/declared-skills |
Declared skills |
/api/v1/me/projects |
Project portfolio |
/api/v1/me/academic-records |
Official results and GPA |
/api/v1/me/cv |
CV freshness, preview, save, download |
| Base route | Purpose |
|---|---|
/api/v1/admin/dashboard/metrics |
Dashboard summary |
/api/v1/admin/academic-ledger/uploads |
Ledger workflow |
/api/v1/admin/academic-records |
Academic-record queries |
/api/v1/admin/students |
Student inspection |
/api/v1/admin/companies |
Company management |
/api/v1/admin/internship-requests |
Internship requests and required skills |
/api/v1/admin/candidate-filtering/runs |
Deterministic filtering |
/api/v1/admin/shortlists |
Shortlists and finalization |
/api/v1/admin/exports |
CSV/ZIP export jobs |
Schemas, pagination, validation, conditional headers, operation IDs, and endpoint-specific errors are documented in OpenAPI. Integration collections and environments are under postman.
The API is stateless. Send the access token with protected requests:
Authorization: Bearer <access-token>/api/v1/me/**requiresSTUDENT./api/v1/admin/**requiresADMIN.- Onboarding, login, password reset, health, and tokenized file routes are public.
- CSRF is disabled because no server-side browser session is used.
- CORS allows configured origins and
GET,POST,PUT,PATCH,DELETE, andOPTIONS.
Backend authorization is authoritative; frontend route guards are only a user-experience aid.
- Clients may send
X-Correlation-Id; otherwise the backend creates one. - Responses expose
X-Correlation-Idfor log correlation. - Validation and application failures return structured errors rather than stack traces.
- Authentication failures return
401; authorization failures return403. - Mutable versioned resources return an
ETagsuch as"3". Send it asIf-Matchon the next mutation. Missing preconditions can return428; stale versions can return412or the endpoint's documented conflict response.
PostgreSQL is the target database and Flyway is the only schema authority.
- Migrations:
src/main/resources/db/migration - PostgreSQL bootstrap:
docker/postgres/init - Runbook:
docs/runbook/database-migrations.md
Hibernate uses ddl-auto: validate: it validates mappings but does not create or modify runtime tables. Never edit an already-applied shared migration; add a new versioned migration.
Flyway runs automatically at application startup. The helper below starts PostgreSQL and triggers the local migration path:
./scripts/migrate-local.shThe schemas cover authentication, student identity/profile data, skills, academic staging and official records, CV metadata, companies, internship requests, filtering, shortlists, export jobs, files, and audit events.
Academic data enters through a controlled administrator CSV workflow:
- upload a UTF-8 CSV with the required ordered headers;
- validate and stage its rows asynchronously;
- inspect staged rows and validation results;
- atomically commit a valid upload to official records.
The domain limit is 5 MiB. Duplicate active/committed content is rejected, and invalid uploads do not modify official results.
The backend assembles approved data, applies the ATS template in src/main/resources/templates/cv, invokes XeLaTeX with timeout/concurrency limits, validates the output, and stores the PDF. Previews expire, and freshness metadata reports whether a saved CV is behind its source profile, skills, projects, or academic data.
Shortlist CSVs and bulk latest-CV ZIPs run as background jobs. A client creates a job, polls its state, and downloads the artifact when ready. Worker behavior and retention are configurable.
Run the suite:
./mvnw -B testWindows:
.\mvnw.cmd -B testPackage the application:
./mvnw -B packageBuild without rerunning tests:
./mvnw -B package -DskipTestsCoverage includes application startup, controllers and HTTP contracts, authentication/authorization, OTP policies, validation, services, repositories, PostgreSQL integration, empty-database Flyway migration, architecture rules, scope guardrails, files, academic ledgers, CV generation, filtering, shortlists, exports, and auditing.
Docker must be running for Testcontainers-backed tests. Postman suites and release evidence are under postman, and the testing strategy is under docs/testing.
Check the canonical OpenAPI copies:
./scripts/validate-openapi.shStart the persistent development database:
docker compose -f docker/docker-compose.dev.yml up -dStart the disposable test database on port 5433:
docker compose -f docker/docker-compose.test.yml up -dThe test database uses tmpfs; its contents disappear when stopped.
Build the application image:
docker build -f docker/Dockerfile -t cv-management-backend .The multi-stage image builds with Java 21, installs XeLaTeX and fonts, runs as non-root appuser, exposes port 8080, and does not bake in secrets.
Run it against the development database:
docker run --rm -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=local \
-e CV_DB_HOST=host.docker.internal \
-e CV_DB_PORT=5432 \
-e CV_DB_NAME=cv_management \
-e CV_DB_USERNAME=cv_user \
-e CV_DB_PASSWORD=cv_local_password \
-e JWT_SECRET=replace-with-a-long-local-development-secret \
-e APP_EMAIL_MODE=log \
-v cv_management_files:/app/data \
cv-management-backendOn Linux, use a database hostname reachable from the container or add an appropriate host mapping.
backend/
├── .github/workflows/ # CI and validation
├── .mvn/ # Maven Wrapper support
├── data/ # Local runtime file roots
├── docker/ # Dockerfile, Compose, PostgreSQL bootstrap
├── docs/
│ ├── adr/ # Architecture decisions
│ ├── api/ # OpenAPI contracts
│ ├── architecture/ # Boundaries and dependency rules
│ ├── operations/ # Operational guidance
│ ├── runbook/ # Deployment, migration, rollback, monitoring
│ └── testing/ # Strategy and reports
├── postman/ # Collections and release evidence
├── scripts/ # Development and verification helpers
├── src/
│ ├── main/
│ │ ├── java/.../{config,infrastructure,modules,shared}/
│ │ └── resources/{db,openapi,templates}/
│ └── test/ # Unit, contract, integration, architecture tests
├── .env.example
├── pom.xml
├── mvnw / mvnw.cmd
└── README.md
| Script | Purpose |
|---|---|
scripts/dev-start.sh |
Start PostgreSQL and the local application |
scripts/dev-stop.sh |
Stop the development Compose stack |
scripts/run-tests.sh |
Run Maven tests |
scripts/migrate-local.sh |
Start PostgreSQL and run local Flyway startup |
scripts/validate-openapi.sh |
Verify OpenAPI version and synchronized copies |
scripts/verify-sprint1-closure.* |
Historical foundation-verification scripts |
scripts/db/*.sql |
Release preflight and database verification |
Postman feature folders also contain Unix and PowerShell Newman runners.
GitHub Actions runs for pull requests and pushes to main, develop, feature/**, and codex/**:
| Workflow | Checks |
|---|---|
| Backend CI | Tests and package build |
| Code Quality | Main/test compilation and workflow validation |
| Dependency Check | Maven dependency resolution and dependency tree |
Recommended pre-push checks:
./mvnw -B test
./mvnw -B package -DskipTests
docker compose -f docker/docker-compose.dev.yml config
docker compose -f docker/docker-compose.test.yml config
./scripts/validate-openapi.shUse SPRING_PROFILES_ACTIVE=prod and supply:
DATABASE_URL,DATABASE_USERNAME, andDATABASE_PASSWORD;- a strong
JWT_SECRETand expectedJWT_ISSUER; FRONTEND_ORIGINandPUBLIC_BASE_URL;- SMTP sender, host, port, username, and password;
- persistent storage for CVs, academic ledgers, exports, and profile files.
Terminate TLS at a trusted proxy/load balancer, restrict database access, rotate secrets, back up PostgreSQL and files together, and test migration and rollback procedures. See docs/runbook for deployment, monitoring, incident response, rollback, and CV-generation guidance.
GET /actuator/healthsupports platform probes.GET /api/v1/healthchecks the database and reports applied migrations.- Application timestamps and persistence use UTC.
- Correlation IDs are included in response headers and logging context.
- Production health output hides internal details.
- Audit operations are documented in
docs/operations/BMD-012-audit-operations-runbook.md.
docker compose -f docker/docker-compose.dev.yml ps
docker compose -f docker/docker-compose.dev.yml logs postgresConfirm port 5432 is free and CV_DB_* values match Compose.
Do not switch Hibernate to schema creation. Inspect the migration history and startup error. For disposable local data only, docker compose -f docker/docker-compose.dev.yml down -v rebuilds the database on the next start. Never do this to shared or production data.
xelatex --versionInstall XeLaTeX, run the Docker image, or point CV_LATEX_COMMAND to the executable.
Set APP_EMAIL_MODE=log and inspect application logs. SMTP requires valid credentials and connectivity. Log mode is development-only.
Start Docker Desktop/Engine and ensure the current user can access the Docker daemon.
SERVER_PORT=8081 ./mvnw spring-boot:run -Dspring-boot.run.profiles=localPowerShell:
$env:SERVER_PORT = "8081"
.\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=local"Read CONTRIBUTING.md before opening a pull request. Work branches are created from develop using feature/, fix/, chore/, or hotfix/ conventions.
Use conventional-style commits:
<type>(<scope>): <short summary>
Examples:
feat(cv): add source freshness endpoint
fix(auth): enforce OTP resend cooldown
test(shortlists): cover stale finalization version
docs(api): clarify export polling contract
Add tests for behavior changes, keep OpenAPI copies synchronized, and preserve the reduced-scope guardrails.
docs/api/CV_Management_API_OpenAPI_v1.6.0.yamlsrc/main/resources/openapi/CV_Management_API_OpenAPI_v1.6.0.yaml
Older versioned contract files are retained only as historical artifacts.
Do not disclose suspected vulnerabilities in a public issue. Follow SECURITY.md.
Never commit database/SMTP credentials, JWT secrets, .env files, OTPs, tokens, password hashes, student data, generated CVs, ledger uploads, or exports.
Copyright © 2026 University of Ruhuna, Department of Computer Science.
This repository is proprietary and all rights are reserved. Unauthorized copying, distribution, modification, or use is prohibited without prior written permission. See LICENSE.