Public Access
feat(version): answer the build commit and time
GET /api/version is public. CI passes GIT_SHA and BUILD_TIME as Docker build args; a local build answers dev.
This commit is contained in:
1 parent
01552ecd89
commit
88aae0101f
9 files changed
+78
-1
No files matched your search
@@ -52,6 +52,7 @@ jobs:
|
|||||||
BRANCH="$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]')"
|
BRANCH="$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]')"
|
||||||
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
|
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
|
||||||
echo "sha=${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT"
|
echo "sha=${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -69,6 +70,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
build-args: |
|
||||||
|
GIT_SHA=${{ github.sha }}
|
||||||
|
BUILD_TIME=${{ steps.prep.outputs.build_time }}
|
||||||
tags: |
|
tags: |
|
||||||
git.andreas-dahm.eu/lobo_corp/tbp-core-service:${{ steps.prep.outputs.branch }}
|
git.andreas-dahm.eu/lobo_corp/tbp-core-service:${{ steps.prep.outputs.branch }}
|
||||||
git.andreas-dahm.eu/lobo_corp/tbp-core-service:${{ steps.prep.outputs.branch }}-${{ steps.prep.outputs.sha }}
|
git.andreas-dahm.eu/lobo_corp/tbp-core-service:${{ steps.prep.outputs.branch }}-${{ steps.prep.outputs.sha }}
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ FROM eclipse-temurin:25-jre-alpine
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Passed by the CI workflow; shown by GET /api/version
|
||||||
|
ARG GIT_SHA=dev
|
||||||
|
ARG BUILD_TIME=
|
||||||
|
ENV APP_BUILD_COMMIT=$GIT_SHA APP_BUILD_TIME=$BUILD_TIME
|
||||||
|
|
||||||
# Expose the port your Spring Boot app runs on
|
# Expose the port your Spring Boot app runs on
|
||||||
EXPOSE 8090
|
EXPOSE 8090
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ accepted only by `GET /api/public/profiles/library` in the `X-Profile-Ticket` he
|
|||||||
| `GET` | `/api/library/sync-status`, `/summary`, `/activity`, `/wishlist`, `/steam-profile`, `/games-count` | JWT |
|
| `GET` | `/api/library/sync-status`, `/summary`, `/activity`, `/wishlist`, `/steam-profile`, `/games-count` | JWT |
|
||||||
| `GET` | `/api/public/profiles/library` | profile ticket |
|
| `GET` | `/api/public/profiles/library` | profile ticket |
|
||||||
| `GET` | `/api/stats/public` | public |
|
| `GET` | `/api/stats/public` | public |
|
||||||
|
| `GET` | `/api/version` (`{commit, builtAt}` of the running image, from the CI build; `dev` locally) | public |
|
||||||
|
|
||||||
## Tech stack
|
## Tech stack
|
||||||
|
|
||||||
@@ -118,6 +119,7 @@ flowchart LR
|
|||||||
LibApi["api"] --> LibService["service"] --> LibRepo["repo"]
|
LibApi["api"] --> LibService["service"] --> LibRepo["repo"]
|
||||||
end
|
end
|
||||||
Stats["stats"]
|
Stats["stats"]
|
||||||
|
Version["version"]
|
||||||
Steam["steam_adapter"]
|
Steam["steam_adapter"]
|
||||||
Igdb["igdb_adapter"]
|
Igdb["igdb_adapter"]
|
||||||
Speedrun["speedrun_adapter"]
|
Speedrun["speedrun_adapter"]
|
||||||
@@ -140,6 +142,7 @@ flowchart LR
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `library` | Sync, backlog, manual games, playtime history, wishlist, achievements, public profile data |
|
| `library` | Sync, backlog, manual games, playtime history, wishlist, achievements, public profile data |
|
||||||
| `stats` | Public numbers for the landing page |
|
| `stats` | Public numbers for the landing page |
|
||||||
|
| `version` | `GET /api/version`: commit and build time of the running image |
|
||||||
| `steam_adapter` | Steam Web API client, availability check of Steam CDN pictures |
|
| `steam_adapter` | Steam Web API client, availability check of Steam CDN pictures |
|
||||||
| `igdb_adapter` | IGDB client (Twitch OAuth): time to beat, game tags for moods, release years, game search |
|
| `igdb_adapter` | IGDB client (Twitch OAuth): time to beat, game tags for moods, release years, game search |
|
||||||
| `speedrun_adapter` | speedrun.com client: game search by name, verified runs |
|
| `speedrun_adapter` | speedrun.com client: game search by name, verified runs |
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ public class SecurityConfig {
|
|||||||
"/actuator/health", // Docker health check; details are off
|
"/actuator/health", // Docker health check; details are off
|
||||||
"/error",
|
"/error",
|
||||||
"/api/stats/public",
|
"/api/stats/public",
|
||||||
"/api/public/**" // Public profiles; PublicProfileController checks the ticket
|
"/api/public/**", // Public profiles; PublicProfileController checks the ticket
|
||||||
|
"/api/version" // Build of the running image, shown in the settings
|
||||||
};
|
};
|
||||||
|
|
||||||
private final OncePerRequestFilter jwtAuthFilter;
|
private final OncePerRequestFilter jwtAuthFilter;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package core_service.modules.version.api;
|
||||||
|
|
||||||
|
import core_service.modules.version.api.dto.VersionDto;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class VersionController {
|
||||||
|
|
||||||
|
private final VersionDto version;
|
||||||
|
|
||||||
|
public VersionController(@Value("${app.build.commit}") String commit, @Value("${app.build.time}") String builtAt) {
|
||||||
|
this.version = new VersionDto(commit.isBlank() ? "dev" : commit, builtAt.isBlank() ? null : builtAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/api/version")
|
||||||
|
public ResponseEntity<VersionDto> getVersion() {
|
||||||
|
return ResponseEntity.ok(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package core_service.modules.version.api.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param commit full git hash the image was built from, "dev" outside the CI build
|
||||||
|
* @param builtAt ISO-8601 UTC build time, null outside the CI build
|
||||||
|
*/
|
||||||
|
public record VersionDto(String commit, String builtAt) {}
|
||||||
@@ -38,6 +38,10 @@ security:
|
|||||||
public-key: ${JWT_PUBLIC_KEY:}
|
public-key: ${JWT_PUBLIC_KEY:}
|
||||||
jwks-url: ${JWT_JWKS_URL:}
|
jwks-url: ${JWT_JWKS_URL:}
|
||||||
app:
|
app:
|
||||||
|
build:
|
||||||
|
# Filled from the CI run by the Dockerfile.
|
||||||
|
commit: ${APP_BUILD_COMMIT:dev}
|
||||||
|
time: ${APP_BUILD_TIME:}
|
||||||
cors:
|
cors:
|
||||||
allowed-origins: "https://tbp.andreas-dahm.eu"
|
allowed-origins: "https://tbp.andreas-dahm.eu"
|
||||||
steam:
|
steam:
|
||||||
|
|||||||
@@ -90,4 +90,11 @@ class PublicEndpointsTest {
|
|||||||
mockMvc.perform(get("/api/public/profiles/library")).andExpect(status().isUnauthorized())
|
mockMvc.perform(get("/api/public/profiles/library")).andExpect(status().isUnauthorized())
|
||||||
.andExpect(jsonPath("$.code").value("INVALID_TICKET"));
|
.andExpect(jsonPath("$.code").value("INVALID_TICKET"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void versionIsPublic() throws Exception {
|
||||||
|
mockMvc.perform(get("/api/version"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.commit").isString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package core_service.modules.version.api;
|
||||||
|
|
||||||
|
import core_service.modules.version.api.dto.VersionDto;
|
||||||
|
import core_service.shared.test.AbstractUnitTest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class VersionControllerTest extends AbstractUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void answersTheBuildOfTheImage() {
|
||||||
|
VersionDto version = new VersionController("4ec47f1aa0b1", "2026-09-27T08:05:00Z").getVersion().getBody();
|
||||||
|
|
||||||
|
assertEquals(new VersionDto("4ec47f1aa0b1", "2026-09-27T08:05:00Z"), version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyBuildArgsCountAsLocalBuild() {
|
||||||
|
VersionDto version = new VersionController("", "").getVersion().getBody();
|
||||||
|
|
||||||
|
assertEquals(new VersionDto("dev", null), version);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user