Add Spring Boot backend module for DPC community data API with account management and factions leaderboard#110
Add Spring Boot backend module for DPC community data API with account management and factions leaderboard#110
Conversation
…Flyway migration, tests, Dockerfile, and Docker Compose integration Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/bd6589fe-5971-41d2-842b-e5a071d82195 Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…ey comparison, and fail-fast on missing API key Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/bd6589fe-5971-41d2-842b-e5a071d82195 Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new dpc-api/ Spring Boot module to serve a community data REST API (starting with Factions) alongside the existing Next.js website, plus local-dev infrastructure via Docker Compose.
Changes:
- Introduces a Spring Boot 3.2.x + JPA + Flyway + PostgreSQL-backed API module with
/api/v1/factionsendpoints and API-key auth on write methods. - Adds Flyway migration + JPA entity/DTO/service/controller layers for faction ingestion and public reads.
- Adds Docker Compose services for
dpc-db(Postgres) anddpc-api, plus MockMvc + H2 integration tests.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| dpc-api/src/test/resources/application-test.yml | Test profile config for H2 + disables Flyway. |
| dpc-api/src/test/java/com/dansplugins/api/controller/FactionControllerTest.java | MockMvc integration tests for auth/validation/paging/read endpoints. |
| dpc-api/src/test/java/com/dansplugins/api/DpcApiApplicationTests.java | Basic Spring context load test. |
| dpc-api/src/main/resources/db/migration/V1__create_factions_table.sql | Flyway migration to create factions table + index. |
| dpc-api/src/main/resources/application.yml | Runtime configuration for Postgres/Flyway and API key property. |
| dpc-api/src/main/java/com/dansplugins/api/service/FactionService.java | Service layer for create/list/get faction(s). |
| dpc-api/src/main/java/com/dansplugins/api/repository/FactionRepository.java | Spring Data JPA repository for Faction. |
| dpc-api/src/main/java/com/dansplugins/api/filter/ApiKeyAuthFilter.java | API-key auth filter applied to write HTTP methods. |
| dpc-api/src/main/java/com/dansplugins/api/entity/Faction.java | JPA entity mapping and timestamp lifecycle hooks. |
| dpc-api/src/main/java/com/dansplugins/api/dto/FactionResponse.java | API response DTO mapping from entity. |
| dpc-api/src/main/java/com/dansplugins/api/dto/FactionRequest.java | Request DTO with Bean Validation constraints. |
| dpc-api/src/main/java/com/dansplugins/api/controller/FactionController.java | REST controller for create/list/get endpoints. |
| dpc-api/src/main/java/com/dansplugins/api/config/ApiKeyConfig.java | Fail-fast validation that API key is configured. |
| dpc-api/src/main/java/com/dansplugins/api/DpcApiApplication.java | Spring Boot application entrypoint. |
| dpc-api/pom.xml | Maven build definition and dependencies. |
| dpc-api/mvnw.cmd | Maven wrapper script (Windows). |
| dpc-api/mvnw | Maven wrapper script (Unix). |
| dpc-api/README.md | Module documentation (local dev, config, endpoints). |
| dpc-api/Dockerfile | Container image definition for the API. |
| dpc-api/.mvn/wrapper/maven-wrapper.properties | Maven wrapper distribution configuration. |
| compose.yml | Adds dpc-db and dpc-api services for local dev. |
| .gitignore | Ignores Maven build outputs and un-ignores dpc-api/pom.xml. |
| FROM eclipse-temurin:17-jre-alpine | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| COPY target/dpc-api-*.jar app.jar |
There was a problem hiding this comment.
The image build expects a pre-built JAR at target/dpc-api-*.jar, but docker compose up --build will run docker build without running Maven first (and dpc-api/target/ is gitignored). Use a multi-stage Dockerfile that builds the JAR (e.g., run ./mvnw -DskipTests package in a builder stage) or adjust Compose to build the JAR before building the image.
| FROM eclipse-temurin:17-jre-alpine | |
| WORKDIR /app | |
| COPY target/dpc-api-*.jar app.jar | |
| FROM eclipse-temurin:17-jdk-alpine AS builder | |
| WORKDIR /build | |
| COPY mvnw pom.xml ./ | |
| COPY .mvn .mvn | |
| COPY src src | |
| RUN ./mvnw -DskipTests package | |
| FROM eclipse-temurin:17-jre-alpine | |
| WORKDIR /app | |
| COPY --from=builder /build/target/dpc-api-*.jar app.jar |
| DB_NAME: dpc | ||
| DB_USERNAME: dpc | ||
| DB_PASSWORD: dpc | ||
| DPC_API_KEY: ${DPC_API_KEY:-changeme} |
There was a problem hiding this comment.
DPC_API_KEY defaults to changeme in Compose, which makes it easy to accidentally deploy/run with a known API key. Consider removing the default and failing fast (let the API refuse to start when the key isn’t provided), or at minimum generate a random dev-only key and document it.
| DPC_API_KEY: ${DPC_API_KEY:-changeme} | |
| DPC_API_KEY: ${DPC_API_KEY:?DPC_API_KEY must be set} |
| CREATE TABLE factions ( | ||
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||
| name VARCHAR(255) NOT NULL, |
There was a problem hiding this comment.
gen_random_uuid() requires the pgcrypto extension in Postgres; on a fresh postgres:16-alpine instance this migration can fail unless the extension is enabled. Either add CREATE EXTENSION IF NOT EXISTS pgcrypto; in the migration or remove the DB-side default if UUIDs are generated by Hibernate.
|
|
||
| @Column(name = "member_count", nullable = false) | ||
| private int memberCount; | ||
|
|
There was a problem hiding this comment.
The migration defines description as TEXT, but the entity maps it as a default String (typically VARCHAR(255)), and spring.jpa.hibernate.ddl-auto is set to validate. This can cause schema validation failures at startup. Align the mappings (e.g., mark the field as @Lob / @Column(columnDefinition = "TEXT") or change the column type in the migration).
| @Column(columnDefinition = "TEXT") |
| public void validateApiKey() { | ||
| if (apiKey == null || apiKey.isBlank()) { | ||
| throw new IllegalStateException( | ||
| "DPC_API_KEY environment variable must be set. " |
There was a problem hiding this comment.
The startup validation message says the DPC_API_KEY environment variable must be set, but the actual requirement is that dpc.api.key is configured (which could come from other property sources). Consider wording this as “dpc.api.key must be configured (e.g., via DPC_API_KEY env var)” to avoid misleading operators.
| "DPC_API_KEY environment variable must be set. " | |
| "`dpc.api.key` must be configured (e.g., via DPC_API_KEY environment variable). " |
| ddl-auto: validate | ||
| open-in-view: false | ||
| flyway: | ||
| enabled: true |
There was a problem hiding this comment.
Pageable parameters are fully client-controlled here; without a configured max page size, callers can request extremely large size values and force expensive DB queries. Consider setting spring.data.web.pageable.max-page-size (and possibly a default page size) in application.yml to put an upper bound on size.
| enabled: true | |
| enabled: true | |
| data: | |
| web: | |
| pageable: | |
| default-page-size: 20 | |
| max-page-size: 200 |
| <dependency> | ||
| <groupId>org.flywaydb</groupId> | ||
| <artifactId>flyway-core</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.postgresql</groupId> | ||
| <artifactId>postgresql</artifactId> | ||
| <scope>runtime</scope> | ||
| </dependency> |
There was a problem hiding this comment.
With Spring Boot 3.2.x the managed Flyway version is in the 10.x line, where several databases (including PostgreSQL) require an additional flyway-database-postgresql dependency. With only flyway-core present, Flyway can fail at runtime with an “Unsupported Database: PostgreSQL …” error. Add the PostgreSQL Flyway database module (or pin Flyway to a version where Postgres support is bundled).
|
@copilot address comments |
…emove gen_random_uuid, TEXT column type, pageable limits, flyway-database-postgresql Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/b23ddfca-6a02-4589-9023-ab80ea2b9f0b Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
All 7 review comments addressed in
All 9 tests pass. |
|
@copilot How do operators get an API key are we gonna have account creation in this PR? |
No account creation in this PR. The current auth model is a single shared secret: the person deploying the API sets If per-operator keys or self-service registration is needed down the road, that would be a separate feature (e.g., an |
|
@copilot the ability to auto register is a requirement; ensure this PR aligns with Dans-Plugins/Medieval-Factions#1959 |
- POST /api/v1/register: self-service API key registration (no auth required) - API keys stored as SHA-256 hashes in new api_keys table (V2 migration) - Auth filter validates against DB-stored keys instead of single env var - POST /api/v1/factions now accepts JSON array (batch sync) with upsert - Added serverIp and discordLink optional fields to faction model - Removed single shared DPC_API_KEY requirement from compose.yml - All 14 tests pass Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/2aca508b-3b95-439a-a987-ad12f6e61b0b Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…6 private Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/2aca508b-3b95-439a-a987-ad12f6e61b0b Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…tion, handle with 409 Conflict Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/7fad899b-4623-4ecb-b7ad-676088c1d53e Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
@copilot Revert this change and update description |
Updated the PR description to comprehensively cover all work in this PR. I'm unable to revert the PR title through my available tools — please manually change it back to "Add Spring Boot backend module for DPC community data API with account management and factions leaderboard" via the GitHub UI (click the title to edit it). |
|
@copilot Make the API port for the backend configurable (including what port the webapp targets) |
- application.yml: server.port now reads from SERVER_PORT env var (default 8080) - compose.yml: published port uses API_PORT env var (default 45345) - Frontend already uses NEXT_PUBLIC_API_URL for targeting the API - README.md: documented SERVER_PORT, API_PORT, and custom port examples - CONFIG.md: added NEXT_PUBLIC_API_URL documentation and API port section Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/134309d2-5fbf-4569-8f2c-f6c8ee85a699 Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/134309d2-5fbf-4569-8f2c-f6c8ee85a699 Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
API port is now configurable at all layers in
All documented in README.md and CONFIG.md. 75 tests pass. |
|
@copilot The |
Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/b0d37686-af56-43bd-89e2-192fb978ef1d Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
NEXT_PUBLIC_API_URL=http://myhost:9090 API_PORT=9090 JWT_SECRET="..." docker compose up --buildUpdated |
|
@copilot Review API practices using https://en.wikipedia.org/wiki/Richardson_Maturity_Model as a reference |
Added in
Reviewed against the Richardson Maturity Model (Levels 0–3). The API currently sits at Level 2 with solid fundamentals. No blocking issues found. Level 0 — Single endpoint ✅ PassedThe API does not use a single endpoint for everything. Each resource has its own URI. Level 1 — Resources ✅ PassedDistinct resource URIs are in place:
URI hierarchy is clean and models the resource ownership well ( Level 2 — HTTP Verbs ✅ PassedCorrect verb usage throughout:
All error responses use appropriate status codes (400, 401, 404, 409, 500) with RFC 9457 ProblemDetail bodies. GET endpoints are safe and idempotent. DELETE returns 204. Content negotiation uses JSON throughout. Level 3 — Hypermedia (HATEOAS)
|
| .requestMatchers("/api/v1/accounts/me/**").authenticated() | ||
| // Write endpoints require API key (handled by ApiKeyAuthFilter) | ||
| .requestMatchers(HttpMethod.POST, "/api/v1/factions").permitAll() | ||
| .anyRequest().permitAll() |
There was a problem hiding this comment.
anyRequest().permitAll() makes the default security posture “allow everything”, which can accidentally expose future controllers/endpoints if they’re added without updating the security rules. Consider switching the default to denyAll() (or at least authenticated()) and explicitly permitting only the intended public endpoints (factions GET, register/login, Swagger) while keeping faction writes gated by the API-key filter.
| .anyRequest().permitAll() | |
| .anyRequest().denyAll() |
| ```bash | ||
| curl -X POST http://localhost:8080/api/v1/accounts/register \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ "username": "myserver", "password": "secure-pass-123" }' | ||
| ``` |
There was a problem hiding this comment.
The README mixes ports: it explains Docker Compose publishes the API on ${API_PORT:-45345}, but the curl examples below use http://localhost:8080 (which won’t work when following the Compose instructions). Suggest clarifying per-run mode (Compose vs non-Docker) or using 45345 in the Compose-focused examples.
| useEffect(() => { | ||
| const saved = localStorage.getItem('dpc-token') | ||
| if (saved) { | ||
| setToken(saved) | ||
| } | ||
| }, []) |
There was a problem hiding this comment.
JWT is persisted in localStorage. This makes the token accessible to any XSS on the site and is generally considered a high-risk storage mechanism for bearer tokens. Consider using an HttpOnly cookie-based session/token (or at least keeping the token in memory + short expiry/refresh flow) to reduce token exfiltration risk.
| const res = await fetch(`${API_BASE}/api/v1/accounts/me/api-keys`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${token}`, | ||
| }, | ||
| body: JSON.stringify({serverName}), |
There was a problem hiding this comment.
The Authorization header uses Bearer ${token} where token is typed string | null. If this handler ever runs after token is cleared (e.g., session expiration races with a click), it will send Bearer null. Consider guarding early when token is null and/or passing a non-null jwt param into these handlers to keep the header correct.
|
@copilot address comments |
Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/89ccda1f-002b-4398-93d6-a89bcb43253c Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
All 4 review comments addressed in
75 tests pass, 0 CodeQL alerts.
Reviewed against the Richardson Maturity Model (Levels 0–3). The API sits at Level 2 with solid fundamentals and no blocking gaps. Level 0 (Swamp of POX) ✅ — Not a single-endpoint RPC. Each resource has distinct URIs. Level 1 (Resources) ✅ — Clean resource hierarchy: Level 2 (HTTP Verbs) ✅ — Correct verb/status code usage: POST→201 for creates, GET→200 for reads, DELETE→204 for deletes. Errors use 400/401/404/409/500 with RFC 9457 ProblemDetail bodies. GET is safe/idempotent. Level 3 (HATEOAS) |
Spring Boot Alignment TaskYou are a coding agent tasked with auditing and aligning this Spring Boot repository Work through each category below systematically. For every issue found, fix it 1. Dependency InjectionAudit for:
Fix by:
2. API ConsistencyAudit for:
Fix by:
3. Exception HandlingAudit for:
Fix by:
4. TestingAudit for:
Fix by:
5. Constraints
Definition of DoneAll of the following must be true before considering the task complete:
|
…, dead code removal, edge-case tests Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/794a4533-6764-4751-957c-46441e60bd89 Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Dans-Plugins/dansplugins-dot-com/sessions/794a4533-6764-4751-957c-46441e60bd89 Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
... All 6 categories audited and fixed in 1. Dependency Injection ✅ No issues — all injection via 2. API Consistency — Fixed:
3. Exception Handling — Fixed:
4. Testing — 3 new edge-case tests:
5. Configuration & Environment ✅ No issues — all values externalized via env vars with sensible defaults. 6. Code Quality — Fixed:
|
NEXT_PUBLIC_API_URLconfigurable via compose.yml (done in d2db387)anyRequest().permitAll()toanyRequest().denyAll()InvalidCredentialsException,ResourceNotFoundException), MapStruct mappers,@Validatedon controllers, dead code removal, Javadoc on services, edge-case tests (78 total)✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.