Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a20bee3b93 | |||
| 1ae455eb99 | |||
| bc500e3e94 | |||
| 2dbb1d0b67 | |||
| 252851de1c | |||
| a253fa78df | |||
| ae3ef766e8 | |||
| 487711628f | |||
| 008d72d826 | |||
| 576e3fea9b | |||
| 1f66b7bde7 | |||
| 381161f003 | |||
| d0150fdfa4 | |||
| 29072efbfb | |||
| dcc5ce1195 | |||
| 354db11972 | |||
| 9296db88b7 | |||
| ec22a9585e | |||
| 411eed2453 | |||
| af6b0ed8b7 | |||
| bcd8257db2 | |||
| d61fe97b4c |
@@ -57,6 +57,13 @@ jobs:
|
||||
- rule
|
||||
- store
|
||||
- ws
|
||||
arch:
|
||||
- name: default
|
||||
march_flag: ""
|
||||
tag_suffix: ""
|
||||
- name: x86_v2
|
||||
march_flag: "-march=x86-64-v2"
|
||||
tag_suffix: "-x86_v2"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -66,12 +73,12 @@ jobs:
|
||||
run: |
|
||||
if [ -f "modules/${{ matrix.module }}/versions.env" ]; then
|
||||
source modules/${{ matrix.module }}/versions.env
|
||||
VERSION="${MAJOR}.${MINOR}.${PATCH}"
|
||||
echo "[${{ matrix.module }}] Version: ${VERSION}"
|
||||
VERSION="${MAJOR}.${MINOR}.${PATCH}${{ matrix.arch.tag_suffix }}"
|
||||
echo "[${{ matrix.module }}/${{ matrix.arch.name }}] Version: ${VERSION}"
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "[${{ matrix.module }}] No versions.env found — using 'latest'"
|
||||
echo "version=latest" >> "$GITHUB_OUTPUT"
|
||||
echo "[${{ matrix.module }}/${{ matrix.arch.name }}] No versions.env found — using 'latest${{ matrix.arch.tag_suffix }}'"
|
||||
echo "version=latest${{ matrix.arch.tag_suffix }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Check if image exists in GHCR
|
||||
@@ -83,12 +90,12 @@ jobs:
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
EXISTING_TAGS=$(gh api "orgs/now-chess/packages/container/${PACKAGE}/versions" \
|
||||
--jq '.[].metadata.container.tags[]' 2>/dev/null || echo "")
|
||||
echo "[${{ matrix.module }}] Existing tags: $(echo "${EXISTING_TAGS}" | tr '\n' ' ' | xargs)"
|
||||
echo "[${{ matrix.module }}/${{ matrix.arch.name }}] Existing tags: $(echo "${EXISTING_TAGS}" | tr '\n' ' ' | xargs)"
|
||||
if echo "${EXISTING_TAGS}" | grep -qx "${VERSION}"; then
|
||||
echo "[${{ matrix.module }}] Image ${VERSION} already exists — skipping build"
|
||||
echo "[${{ matrix.module }}/${{ matrix.arch.name }}] Image ${VERSION} already exists — skipping build"
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "[${{ matrix.module }}] Image ${VERSION} not found — will build"
|
||||
echo "[${{ matrix.module }}/${{ matrix.arch.name }}] Image ${VERSION} not found — will build"
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
@@ -112,7 +119,13 @@ jobs:
|
||||
|
||||
- name: Build native binary
|
||||
if: steps.image-check.outputs.exists == 'false'
|
||||
run: ./gradlew :modules:${{ matrix.module }}:build -x test -Dquarkus.native.enabled=true -Dquarkus.package.jar.enabled=false -Dquarkus.profile=deployed --no-daemon
|
||||
run: |
|
||||
MARCH="${{ matrix.arch.march_flag }}"
|
||||
MARCH_ARG=""
|
||||
if [ -n "$MARCH" ]; then
|
||||
MARCH_ARG="-Dquarkus.native.additional-build-args=$MARCH"
|
||||
fi
|
||||
./gradlew :modules:${{ matrix.module }}:build -x test -Dquarkus.native.enabled=true -Dquarkus.package.jar.enabled=false -Dquarkus.profile=deployed $MARCH_ARG --no-daemon
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: steps.image-check.outputs.exists == 'false'
|
||||
@@ -134,7 +147,7 @@ jobs:
|
||||
images: ghcr.io/now-chess/now-chess-systems/${{ matrix.module }}
|
||||
tags: |
|
||||
type=raw,value=${{ steps.version.outputs.version }}
|
||||
type=raw,value=latest
|
||||
type=raw,value=latest${{ matrix.arch.tag_suffix }}
|
||||
|
||||
- name: Build and push
|
||||
if: steps.image-check.outputs.exists == 'false'
|
||||
@@ -145,5 +158,5 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha,scope=${{ matrix.module }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.module }}
|
||||
cache-from: type=gha,scope=${{ matrix.module }}-${{ matrix.arch.name }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.module }}-${{ matrix.arch.name }}
|
||||
|
||||
Generated
-19
@@ -5,25 +5,6 @@
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="ms-21" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/modules" />
|
||||
<option value="$PROJECT_DIR$/modules/account" />
|
||||
<option value="$PROJECT_DIR$/modules/api" />
|
||||
<option value="$PROJECT_DIR$/modules/bot-platform" />
|
||||
<option value="$PROJECT_DIR$/modules/coordinator" />
|
||||
<option value="$PROJECT_DIR$/modules/core" />
|
||||
<option value="$PROJECT_DIR$/modules/io" />
|
||||
<option value="$PROJECT_DIR$/modules/json" />
|
||||
<option value="$PROJECT_DIR$/modules/official-bots" />
|
||||
<option value="$PROJECT_DIR$/modules/rule" />
|
||||
<option value="$PROJECT_DIR$/modules/security" />
|
||||
<option value="$PROJECT_DIR$/modules/store" />
|
||||
<option value="$PROJECT_DIR$/modules/ws" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
Generated
-1
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
|
||||
Generated
-6
@@ -1,11 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CommitMessageInspectionProfile">
|
||||
<profile version="1.0">
|
||||
<inspection_tool class="CommitFormat" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CommitNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
<component name="IssueNavigationConfiguration">
|
||||
<option name="links">
|
||||
<list>
|
||||
|
||||
@@ -10,11 +10,6 @@ post {
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{token}}
|
||||
@@ -22,9 +17,20 @@ headers {
|
||||
|
||||
body:json {
|
||||
{
|
||||
"color": "random",
|
||||
"timeControl": {
|
||||
"limitSeconds": 600,
|
||||
"incrementSeconds": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vars:pre-request {
|
||||
username: bdc
|
||||
token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJub3djaGVzcyIsInN1YiI6ImY3MGQxNDIwLTdhZmEtNGVjMy05Mzg1LWViYWU0N2U3NGY4OCIsInVzZXJuYW1lIjoicGxheWVyMSIsImlhdCI6MTc3OTE5MjkwOCwiZXhwIjoxNzc5MTkzMjA4LCJqdGkiOiJkMDhlZmRhMi01ZjliLTQyNjgtOGM3MC1iNDA2OTViNDhiMTUifQ.q68R2bUdRQ5QwEIfcP0d2g_Wac94qd4K6BzP-PC94x-tDpT3leUY8ZpqY6YzoNv-ywp5sm47-WC539DyUqdzDgPVLLSmOMjRxG-HpaNdXwsXlp8_C7KGlgkK_XSMa3Gq6S4AfUbaXbRhPg5NZz2MRosu0BY2ed0ISvmEfX5XEdBRlKgD14BIKAsEFv3tHtarFS1RGpcSoHV2oeIk_m-VHUC78N-ciNHmBH6mZna_fVHgMocOIrEsTZuUms0Zacmebvh2tAcf36xux1Bm2awJff19zReD-A2o9jucrKcM3Im5BJ6JtuWAsHEae9KLmuol6S2pldvNOmUn3egFUYz0yQ
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
@@ -283,3 +283,68 @@
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
## (2026-05-19)
|
||||
|
||||
### Features
|
||||
|
||||
* **account:** implement token pair handling for login and refresh endpoints ([9296db8](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9296db88b7131bbda9b9b0da65c327ef9063ee31))
|
||||
* add authentication permissions for metrics endpoints in application.yml ([04edd4d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/04edd4d6fd8a63196c36f6d67992832febc9bebb))
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add H2 database configuration for testing environment ([39c9e49](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39c9e492cef2515368c074da9406f95e9c0c9e64))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **logging:** add DEBUG/INFO/WARN logging across services (NCS-72) ([#41](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/41)) ([804a4bf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/804a4bf179e3dfb19e2be4390e7e543caf5237c6))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** add InternalClientHeadersFactory for custom client headers management ([e279c39](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e279c39246470156bf11e745ee72204018d4229d))
|
||||
* NCS-84 More Verbose Logging ([#51](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/51)) ([4ad92ab](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4ad92ab23698267f8faa59c4e18388d4a0042cca))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* **tests:** update token path to accessToken in ChallengeResourceTest ([354db11](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/354db11972342c47a1034303c11bccfb92e60109))
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
## (2026-05-21)
|
||||
|
||||
### Features
|
||||
|
||||
* **account:** implement token pair handling for login and refresh endpoints ([9296db8](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9296db88b7131bbda9b9b0da65c327ef9063ee31))
|
||||
* add authentication permissions for metrics endpoints in application.yml ([04edd4d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/04edd4d6fd8a63196c36f6d67992832febc9bebb))
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add H2 database configuration for testing environment ([39c9e49](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39c9e492cef2515368c074da9406f95e9c0c9e64))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **logging:** add DEBUG/INFO/WARN logging across services (NCS-72) ([#41](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/41)) ([804a4bf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/804a4bf179e3dfb19e2be4390e7e543caf5237c6))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **account:** configure JDBC connection pool size to prevent exhaustion under load ([29072ef](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/29072efbfb1cfa1c3b1a85b4c1a587c971d245f9))
|
||||
* **auth:** add InternalClientHeadersFactory for custom client headers management ([e279c39](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e279c39246470156bf11e745ee72204018d4229d))
|
||||
* NCS-84 More Verbose Logging ([#51](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/51)) ([4ad92ab](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4ad92ab23698267f8faa59c4e18388d4a0042cca))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* **tests:** update token path to accessToken in ChallengeResourceTest ([354db11](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/354db11972342c47a1034303c11bccfb92e60109))
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
|
||||
@@ -30,8 +30,15 @@ nowchess:
|
||||
prefix: nowchess
|
||||
internal:
|
||||
secret: 123abc
|
||||
rate-limit:
|
||||
enabled: true
|
||||
requests-per-window: 60
|
||||
window-seconds: 60
|
||||
|
||||
"%test":
|
||||
nowchess:
|
||||
rate-limit:
|
||||
enabled: false
|
||||
quarkus:
|
||||
datasource:
|
||||
db-kind: h2
|
||||
@@ -76,6 +83,9 @@ nowchess:
|
||||
password: ${DB_PASSWORD}
|
||||
jdbc:
|
||||
url: ${DB_URL}
|
||||
min-size: 10
|
||||
max-size: 50
|
||||
acquisition-timeout: 30
|
||||
hibernate-orm:
|
||||
schema-management:
|
||||
strategy: update
|
||||
@@ -86,6 +96,8 @@ nowchess:
|
||||
prefix: ${REDIS_PREFIX:nowchess}
|
||||
internal:
|
||||
secret: ${INTERNAL_SECRET}
|
||||
rate-limit:
|
||||
gatling-secret: ${GATLING_SECRET}
|
||||
mp:
|
||||
jwt:
|
||||
verify:
|
||||
|
||||
+2
-1
@@ -25,7 +25,8 @@ import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
classOf[DeclineReason],
|
||||
classOf[TimeControl],
|
||||
classOf[LoginRequest],
|
||||
classOf[TokenResponse],
|
||||
classOf[RefreshRequest],
|
||||
classOf[TokenPairResponse],
|
||||
classOf[PlayerInfo],
|
||||
classOf[PublicAccountDto],
|
||||
classOf[BotAccountDto],
|
||||
|
||||
@@ -6,7 +6,6 @@ import scala.compiletime.uninitialized
|
||||
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
import scala.Conversion
|
||||
|
||||
@Entity
|
||||
@Table(name = "challenges")
|
||||
@@ -34,13 +33,14 @@ class Challenge extends PanacheEntityBase:
|
||||
@Column(nullable = true, columnDefinition = "varchar(255)")
|
||||
var declineReason: DeclineReason = uninitialized
|
||||
|
||||
var timeControlType: String = uninitialized
|
||||
@Column(nullable = true)
|
||||
var limitSeconds: java.lang.Integer = uninitialized
|
||||
|
||||
@Column(nullable = true)
|
||||
var timeControlLimit: java.lang.Integer = uninitialized
|
||||
var incrementSeconds: java.lang.Integer = uninitialized
|
||||
|
||||
@Column(nullable = true)
|
||||
var timeControlIncrement: java.lang.Integer = uninitialized
|
||||
var daysPerMove: java.lang.Integer = uninitialized
|
||||
|
||||
var createdAt: Instant = uninitialized
|
||||
|
||||
@@ -52,5 +52,6 @@ class Challenge extends PanacheEntityBase:
|
||||
|
||||
def gameIdOpt: Option[String] = Option(gameId)
|
||||
def declineReasonOpt: Option[DeclineReason] = Option(declineReason)
|
||||
def timeControlLimitOpt: Option[Int] = Option(timeControlLimit).map(_.intValue())
|
||||
def timeControlIncrementOpt: Option[Int] = Option(timeControlIncrement).map(_.intValue())
|
||||
def limitSecondsOpt: Option[Int] = Option(limitSeconds).map(_.intValue())
|
||||
def incrementSecondsOpt: Option[Int] = Option(incrementSeconds).map(_.intValue())
|
||||
def daysPerMoveOpt: Option[Int] = Option(daysPerMove).map(_.intValue())
|
||||
|
||||
@@ -4,13 +4,15 @@ case class RegisterRequest(username: String, email: String, password: String)
|
||||
|
||||
case class LoginRequest(username: String, password: String)
|
||||
|
||||
case class TokenResponse(token: String)
|
||||
case class RefreshRequest(refreshToken: String)
|
||||
|
||||
case class TokenPairResponse(accessToken: String, refreshToken: String)
|
||||
|
||||
case class PlayerInfo(id: String, name: String, rating: Int)
|
||||
|
||||
case class PublicAccountDto(id: String, username: String, rating: Int, createdAt: String)
|
||||
|
||||
case class TimeControlDto(`type`: String, limit: Option[Int], increment: Option[Int])
|
||||
case class TimeControlDto(limitSeconds: Option[Int], incrementSeconds: Option[Int], daysPerMove: Option[Int])
|
||||
|
||||
case class ChallengeRequest(color: String, timeControl: TimeControlDto)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ enum AccountError:
|
||||
case UsernameTaken(username: String)
|
||||
case EmailAlreadyRegistered(email: String)
|
||||
case InvalidCredentials
|
||||
case InvalidRefreshToken
|
||||
case UserNotFound
|
||||
case BotNotFound
|
||||
case BotLimitExceeded
|
||||
@@ -15,6 +16,7 @@ enum AccountError:
|
||||
case UsernameTaken(u) => s"Username '$u' is already taken"
|
||||
case EmailAlreadyRegistered(e) => s"Email '$e' is already registered"
|
||||
case InvalidCredentials => "Invalid credentials"
|
||||
case InvalidRefreshToken => "Invalid or expired refresh token"
|
||||
case UserNotFound => "User not found"
|
||||
case BotNotFound => "Bot account not found"
|
||||
case BotLimitExceeded => "Maximum of 5 bot accounts per user exceeded"
|
||||
|
||||
@@ -4,6 +4,7 @@ import de.nowchess.account.domain.{BotAccount, OfficialBotAccount, UserAccount}
|
||||
import de.nowchess.account.dto.*
|
||||
import de.nowchess.account.error.AccountError
|
||||
import de.nowchess.account.service.AccountService
|
||||
import de.nowchess.security.RateLimited
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
@@ -16,6 +17,7 @@ import java.util.UUID
|
||||
|
||||
@Path("/api/account")
|
||||
@ApplicationScoped
|
||||
@RateLimited
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
class AccountResource:
|
||||
@@ -40,8 +42,19 @@ class AccountResource:
|
||||
@Path("/login")
|
||||
def login(req: LoginRequest): Response =
|
||||
accountService.login(req) match
|
||||
case Right(token) =>
|
||||
Response.ok(TokenResponse(token)).build()
|
||||
case Right((accessToken, refreshToken)) =>
|
||||
Response.ok(TokenPairResponse(accessToken, refreshToken)).build()
|
||||
case Left(AccountError.UserBanned) =>
|
||||
Response.status(Response.Status.FORBIDDEN).entity(ErrorDto(AccountError.UserBanned.message)).build()
|
||||
case Left(error) =>
|
||||
Response.status(Response.Status.UNAUTHORIZED).entity(ErrorDto(error.message)).build()
|
||||
|
||||
@POST
|
||||
@Path("/refresh")
|
||||
def refresh(req: RefreshRequest): Response =
|
||||
accountService.refresh(req.refreshToken) match
|
||||
case Right((accessToken, refreshToken)) =>
|
||||
Response.ok(TokenPairResponse(accessToken, refreshToken)).build()
|
||||
case Left(AccountError.UserBanned) =>
|
||||
Response.status(Response.Status.FORBIDDEN).entity(ErrorDto(AccountError.UserBanned.message)).build()
|
||||
case Left(error) =>
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.nowchess.account.resource
|
||||
import de.nowchess.account.dto.*
|
||||
import de.nowchess.account.error.ChallengeError
|
||||
import de.nowchess.account.service.ChallengeService
|
||||
import de.nowchess.security.RateLimited
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
@@ -16,6 +17,7 @@ import java.util.UUID
|
||||
@Path("/api/challenge")
|
||||
@ApplicationScoped
|
||||
@RolesAllowed(Array("**"))
|
||||
@RateLimited
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
class ChallengeResource:
|
||||
|
||||
+2
@@ -3,6 +3,7 @@ package de.nowchess.account.resource
|
||||
import de.nowchess.account.client.{CoreCreateGameRequest, CoreGameClient, CorePlayerInfo}
|
||||
import de.nowchess.account.dto.{ErrorDto, OfficialChallengeResponse}
|
||||
import de.nowchess.account.service.{AccountService, EventPublisher}
|
||||
import de.nowchess.security.RateLimited
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
@@ -19,6 +20,7 @@ import java.util.concurrent.ThreadLocalRandom
|
||||
@Path("/api/challenge/official")
|
||||
@ApplicationScoped
|
||||
@RolesAllowed(Array("**"))
|
||||
@RateLimited
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
class OfficialChallengeResource:
|
||||
|
||||
@@ -6,6 +6,7 @@ import de.nowchess.account.error.AccountError
|
||||
import de.nowchess.account.repository.{BotAccountRepository, OfficialBotAccountRepository, UserAccountRepository}
|
||||
import io.micrometer.core.instrument.MeterRegistry
|
||||
import io.quarkus.elytron.security.common.BcryptUtil
|
||||
import io.smallrye.jwt.auth.principal.JWTParser
|
||||
import io.smallrye.jwt.build.Jwt
|
||||
import jakarta.annotation.PostConstruct
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
@@ -34,6 +35,9 @@ class AccountService:
|
||||
|
||||
@Inject
|
||||
var meterRegistry: MeterRegistry = uninitialized
|
||||
|
||||
@Inject
|
||||
var jwtParser: JWTParser = uninitialized
|
||||
// scalafix:on
|
||||
|
||||
@PostConstruct
|
||||
@@ -66,7 +70,7 @@ class AccountService:
|
||||
log.infof("User %s registered successfully", req.username)
|
||||
Right(account)
|
||||
|
||||
def login(req: LoginRequest): Either[AccountError, String] =
|
||||
def login(req: LoginRequest): Either[AccountError, (String, String)] =
|
||||
val result = authenticateUser(req)
|
||||
result match
|
||||
case Right(_) => meterRegistry.counter("nowchess.auth.logins", "result", "success").increment()
|
||||
@@ -75,7 +79,7 @@ class AccountService:
|
||||
meterRegistry.counter("nowchess.auth.login.failures", "reason", loginFailureReason(error)).increment()
|
||||
result
|
||||
|
||||
private def authenticateUser(req: LoginRequest): Either[AccountError, String] =
|
||||
private def authenticateUser(req: LoginRequest): Either[AccountError, (String, String)] =
|
||||
userAccountRepository.findByUsername(req.username) match
|
||||
case None =>
|
||||
log.warnf("Login failed for unknown user %s", req.username)
|
||||
@@ -89,16 +93,39 @@ class AccountService:
|
||||
Left(AccountError.UserBanned)
|
||||
else
|
||||
log.infof("User %s logged in successfully", req.username)
|
||||
Right(
|
||||
Jwt
|
||||
.issuer("nowchess")
|
||||
.subject(account.id.toString)
|
||||
.claim("username", account.username)
|
||||
.sign(),
|
||||
)
|
||||
Right((generateAccessToken(account), generateRefreshToken(account.id)))
|
||||
|
||||
def refresh(refreshToken: String): Either[AccountError, (String, String)] =
|
||||
try
|
||||
val parsed = jwtParser.parse(refreshToken)
|
||||
if parsed.getClaim[String]("type") != "refresh" then Left(AccountError.InvalidRefreshToken)
|
||||
else
|
||||
val userId = UUID.fromString(parsed.getSubject)
|
||||
userAccountRepository.findById(userId) match
|
||||
case None => Left(AccountError.UserNotFound)
|
||||
case Some(u) if u.banned => Left(AccountError.UserBanned)
|
||||
case Some(u) => Right((generateAccessToken(u), generateRefreshToken(u.id)))
|
||||
catch case _: Throwable => Left(AccountError.InvalidRefreshToken)
|
||||
|
||||
private def generateAccessToken(account: UserAccount): String =
|
||||
Jwt
|
||||
.issuer("nowchess")
|
||||
.subject(account.id.toString)
|
||||
.claim("username", account.username)
|
||||
.expiresIn(3600)
|
||||
.sign()
|
||||
|
||||
private def generateRefreshToken(userId: UUID): String =
|
||||
Jwt
|
||||
.issuer("nowchess")
|
||||
.subject(userId.toString)
|
||||
.claim("type", "refresh")
|
||||
.expiresIn(30L * 24 * 3600)
|
||||
.sign()
|
||||
|
||||
private def loginFailureReason(error: AccountError): String = error match
|
||||
case AccountError.InvalidCredentials => "invalid_credentials"
|
||||
case AccountError.InvalidRefreshToken => "invalid_refresh_token"
|
||||
case AccountError.UserBanned => "user_banned"
|
||||
case AccountError.UsernameTaken(_) => "username_taken"
|
||||
case AccountError.EmailAlreadyRegistered(_) => "email_registered"
|
||||
|
||||
@@ -88,9 +88,9 @@ class ChallengeService:
|
||||
challenge.destUser = destUser
|
||||
challenge.color = color
|
||||
challenge.status = ChallengeStatus.Created
|
||||
challenge.timeControlType = req.timeControl.`type`
|
||||
challenge.timeControlLimit = req.timeControl.limit.map(java.lang.Integer.valueOf).orNull
|
||||
challenge.timeControlIncrement = req.timeControl.increment.map(java.lang.Integer.valueOf).orNull
|
||||
challenge.limitSeconds = req.timeControl.limitSeconds.map(java.lang.Integer.valueOf).orNull
|
||||
challenge.incrementSeconds = req.timeControl.incrementSeconds.map(java.lang.Integer.valueOf).orNull
|
||||
challenge.daysPerMove = req.timeControl.daysPerMove.map(java.lang.Integer.valueOf).orNull
|
||||
challenge.createdAt = Instant.now()
|
||||
challenge.expiresAt = Instant.now().plus(24, ChronoUnit.HOURS)
|
||||
challengeRepository.persist(challenge)
|
||||
@@ -200,10 +200,9 @@ class ChallengeService:
|
||||
if ThreadLocalRandom.current().nextBoolean() then (challenger, destUser) else (destUser, challenger)
|
||||
|
||||
private def buildTimeControl(challenge: Challenge): Option[CoreTimeControl] =
|
||||
challenge.timeControlType match
|
||||
case "unlimited" => None
|
||||
case "correspondence" => Some(CoreTimeControl(None, None, challenge.timeControlLimitOpt))
|
||||
case _ => Some(CoreTimeControl(challenge.timeControlLimitOpt, challenge.timeControlIncrementOpt, None))
|
||||
if challenge.limitSecondsOpt.isEmpty && challenge.incrementSecondsOpt.isEmpty && challenge.daysPerMoveOpt.isEmpty
|
||||
then None
|
||||
else Some(CoreTimeControl(challenge.limitSecondsOpt, challenge.incrementSecondsOpt, challenge.daysPerMoveOpt))
|
||||
|
||||
private def parseColor(raw: String): Either[ChallengeError, ChallengeColor] =
|
||||
raw.toLowerCase match
|
||||
@@ -227,7 +226,7 @@ class ChallengeService:
|
||||
destUser = PlayerInfo(c.destUser.id.toString, c.destUser.username, c.destUser.rating),
|
||||
variant = "standard",
|
||||
color = c.color.toString.toLowerCase,
|
||||
timeControl = TimeControlDto(c.timeControlType, c.timeControlLimitOpt, c.timeControlIncrementOpt),
|
||||
timeControl = TimeControlDto(c.limitSecondsOpt, c.incrementSecondsOpt, c.daysPerMoveOpt),
|
||||
status = c.status.toString.toLowerCase,
|
||||
declineReason = c.declineReasonOpt.map(_.toString.toLowerCase),
|
||||
gameId = c.gameIdOpt,
|
||||
|
||||
@@ -34,3 +34,17 @@ nowchess:
|
||||
secret: test-secret
|
||||
auth:
|
||||
enabled: false
|
||||
rate-limit:
|
||||
enabled: false
|
||||
|
||||
"%rate-limit-test":
|
||||
nowchess:
|
||||
internal:
|
||||
secret: test-secret
|
||||
auth:
|
||||
enabled: false
|
||||
rate-limit:
|
||||
enabled: true
|
||||
requests-per-window: 3
|
||||
window-seconds: 60
|
||||
gatling-secret: gatling-test-secret
|
||||
|
||||
+52
-3
@@ -32,7 +32,24 @@ class AccountResourceTest:
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.path[String]("token")
|
||||
.path[String]("accessToken")
|
||||
|
||||
private def registerAndLoginPair(username: String): (String, String) =
|
||||
givenRequest()
|
||||
.body(registerBody(username))
|
||||
.when()
|
||||
.post("/api/account")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
val resp = givenRequest()
|
||||
.body(loginBody(username))
|
||||
.when()
|
||||
.post("/api/account/login")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.response()
|
||||
(resp.path[String]("accessToken"), resp.path[String]("refreshToken"))
|
||||
|
||||
@Test
|
||||
def registerReturns200(): Unit =
|
||||
@@ -57,7 +74,7 @@ class AccountResourceTest:
|
||||
.body("error", containsString("bob"))
|
||||
|
||||
@Test
|
||||
def loginReturns200WithToken(): Unit =
|
||||
def loginReturns200WithTokenPair(): Unit =
|
||||
givenRequest().body(registerBody("charlie")).when().post("/api/account")
|
||||
givenRequest()
|
||||
.body(loginBody("charlie"))
|
||||
@@ -65,7 +82,8 @@ class AccountResourceTest:
|
||||
.post("/api/account/login")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.body("token", notNullValue())
|
||||
.body("accessToken", notNullValue())
|
||||
.body("refreshToken", notNullValue())
|
||||
|
||||
@Test
|
||||
def loginUnauthorizedOnWrongPassword(): Unit =
|
||||
@@ -105,3 +123,34 @@ class AccountResourceTest:
|
||||
.get("/api/account/doesnotexist")
|
||||
.`then`()
|
||||
.statusCode(404)
|
||||
|
||||
@Test
|
||||
def refreshReturnsNewTokenPair(): Unit =
|
||||
val (_, refreshToken) = registerAndLoginPair("refresh_user")
|
||||
givenRequest()
|
||||
.body(s"""{"refreshToken":"$refreshToken"}""")
|
||||
.when()
|
||||
.post("/api/account/refresh")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.body("accessToken", notNullValue())
|
||||
.body("refreshToken", notNullValue())
|
||||
|
||||
@Test
|
||||
def refreshWithInvalidTokenReturns401(): Unit =
|
||||
givenRequest()
|
||||
.body("""{"refreshToken":"invalid.token.value"}""")
|
||||
.when()
|
||||
.post("/api/account/refresh")
|
||||
.`then`()
|
||||
.statusCode(401)
|
||||
|
||||
@Test
|
||||
def refreshWithAccessTokenReturns401(): Unit =
|
||||
val accessToken = registerAndLogin("refresh_bad_type")
|
||||
givenRequest()
|
||||
.body(s"""{"refreshToken":"$accessToken"}""")
|
||||
.when()
|
||||
.post("/api/account/refresh")
|
||||
.`then`()
|
||||
.statusCode(401)
|
||||
|
||||
+2
-2
@@ -41,10 +41,10 @@ class ChallengeResourceTest:
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.path[String]("token")
|
||||
.path[String]("accessToken")
|
||||
|
||||
private val clockBody =
|
||||
"""{"color":"random","timeControl":{"type":"clock","limit":300,"increment":5}}"""
|
||||
"""{"color":"random","timeControl":{"limitSeconds":300,"incrementSeconds":5}}"""
|
||||
|
||||
private def authed(token: String) =
|
||||
givenRequest().header("Authorization", s"Bearer $token")
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package de.nowchess.account.resource
|
||||
|
||||
import de.nowchess.security.RateLimitFilter
|
||||
import jakarta.ws.rs.container.ContainerRequestContext
|
||||
import jakarta.ws.rs.core.Response
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.Mockito.*
|
||||
|
||||
import java.util.Optional
|
||||
|
||||
class RateLimitFilterTest:
|
||||
|
||||
private def newFilter(limit: Long = 2L): RateLimitFilter =
|
||||
val f = new RateLimitFilter()
|
||||
f.enabled = true
|
||||
f.requestsPerWindow = limit
|
||||
f.windowSeconds = 60
|
||||
f.gatlingSecret = Optional.of("gatling-secret")
|
||||
f
|
||||
|
||||
private def ctx(ip: String = "10.0.0.1", gatlingSecret: String = "") =
|
||||
val c = mock(classOf[ContainerRequestContext])
|
||||
when(c.getHeaderString("X-Forwarded-For")).thenReturn(ip)
|
||||
if gatlingSecret.nonEmpty then when(c.getHeaderString("X-Gatling-Secret")).thenReturn(gatlingSecret)
|
||||
c
|
||||
|
||||
@Test
|
||||
def allowsRequestsUpToLimit(): Unit =
|
||||
val filter = newFilter()
|
||||
for _ <- 1 to 2 do
|
||||
val c = ctx()
|
||||
filter.filter(c)
|
||||
verify(c, never()).abortWith(any())
|
||||
|
||||
@Test
|
||||
def blocks429WhenLimitExceeded(): Unit =
|
||||
val filter = newFilter(limit = 2L)
|
||||
filter.filter(ctx("1.2.3.4"))
|
||||
filter.filter(ctx("1.2.3.4"))
|
||||
val c = ctx("1.2.3.4")
|
||||
filter.filter(c)
|
||||
val captor = ArgumentCaptor.forClass(classOf[Response])
|
||||
verify(c).abortWith(captor.capture())
|
||||
assertEquals(429, captor.getValue.getStatus)
|
||||
|
||||
@Test
|
||||
def gatlingSecretBypasses(): Unit =
|
||||
val filter = newFilter(limit = 1L)
|
||||
for _ <- 1 to 5 do
|
||||
val c = ctx("1.2.3.5", "gatling-secret")
|
||||
filter.filter(c)
|
||||
verify(c, never()).abortWith(any())
|
||||
|
||||
@Test
|
||||
def emptyGatlingSecretDisablesGatlingBypass(): Unit =
|
||||
val filter = newFilter(limit = 1L)
|
||||
filter.gatlingSecret = Optional.empty()
|
||||
filter.filter(ctx("2.2.2.2", "gatling-secret"))
|
||||
val c = ctx("2.2.2.2", "gatling-secret")
|
||||
filter.filter(c)
|
||||
verify(c).abortWith(any())
|
||||
|
||||
@Test
|
||||
def doesNothingWhenDisabled(): Unit =
|
||||
val filter = newFilter()
|
||||
filter.enabled = false
|
||||
for _ <- 1 to 5 do
|
||||
val c = ctx()
|
||||
filter.filter(c)
|
||||
verify(c, never()).abortWith(any())
|
||||
|
||||
@Test
|
||||
def tracksDifferentIpsSeparately(): Unit =
|
||||
val filter = newFilter(limit = 1L)
|
||||
for ip <- List("10.1.1.1", "10.1.1.2", "10.1.1.3") do
|
||||
val c = ctx(ip)
|
||||
filter.filter(c)
|
||||
verify(c, never()).abortWith(any())
|
||||
|
||||
@Test
|
||||
def usesXForwardedForFirstSegment(): Unit =
|
||||
val filter = newFilter(limit = 1L)
|
||||
filter.filter(ctx("203.0.113.1, 10.0.0.1"))
|
||||
val c = ctx("203.0.113.1, 10.0.0.1")
|
||||
filter.filter(c)
|
||||
verify(c).abortWith(any())
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=16
|
||||
MINOR=18
|
||||
PATCH=0
|
||||
|
||||
@@ -109,3 +109,39 @@
|
||||
### Bug Fixes
|
||||
|
||||
* IO microservice ([#38](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/38)) ([a386f57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a386f57c21d34ead6cc6f92836c52b714597e289))
|
||||
## (2026-05-22)
|
||||
|
||||
### Features
|
||||
|
||||
* **dto:** update GameWritebackEventDto for JSON deserialization and remove unused mixin ([576e3fe](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/576e3fea9bf1082549ea53efd3288474c42be93d))
|
||||
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([f088c4e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f088c4e9ffcc498d3d1b6f01e8f50042d5830d55))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([8744bee](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8744bee2dd20966dae90a09c21a43d5b06f59e00))
|
||||
* **rule:** Rules as a microservice ([#39](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/39)) ([093134d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/093134d36c6844ba02a36a28d5d044f09291cd1d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dependencies:** correct Jackson databind dependency group ID ([008d72d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/008d72d826707c04205bac7de25170fae5fed861))
|
||||
* IO microservice ([#38](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/38)) ([a386f57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a386f57c21d34ead6cc6f92836c52b714597e289))
|
||||
## (2026-05-31)
|
||||
|
||||
### Features
|
||||
|
||||
* **dto:** update GameWritebackEventDto for JSON deserialization and remove unused mixin ([576e3fe](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/576e3fea9bf1082549ea53efd3288474c42be93d))
|
||||
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([f088c4e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f088c4e9ffcc498d3d1b6f01e8f50042d5830d55))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([8744bee](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8744bee2dd20966dae90a09c21a43d5b06f59e00))
|
||||
* **rule:** Rules as a microservice ([#39](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/39)) ([093134d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/093134d36c6844ba02a36a28d5d044f09291cd1d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dependencies:** correct Jackson databind dependency group ID ([008d72d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/008d72d826707c04205bac7de25170fae5fed861))
|
||||
* IO microservice ([#38](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/38)) ([a386f57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a386f57c21d34ead6cc6f92836c52b714597e289))
|
||||
|
||||
@@ -49,6 +49,7 @@ dependencies {
|
||||
strictly(versions["SCALA_LIBRARY"]!!)
|
||||
}
|
||||
}
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:${versions["JACKSON"]!!}")
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.nowchess.api.dto
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
|
||||
case class GameWritebackEventDto(
|
||||
gameId: String,
|
||||
fen: String,
|
||||
@@ -14,11 +16,11 @@ case class GameWritebackEventDto(
|
||||
limitSeconds: Option[Int],
|
||||
incrementSeconds: Option[Int],
|
||||
daysPerMove: Option[Int],
|
||||
whiteRemainingMs: Option[Long],
|
||||
blackRemainingMs: Option[Long],
|
||||
incrementMs: Option[Long],
|
||||
clockLastTickAt: Option[Long],
|
||||
clockMoveDeadline: Option[Long],
|
||||
@JsonDeserialize(contentAs = classOf[java.lang.Long]) whiteRemainingMs: Option[Long],
|
||||
@JsonDeserialize(contentAs = classOf[java.lang.Long]) blackRemainingMs: Option[Long],
|
||||
@JsonDeserialize(contentAs = classOf[java.lang.Long]) incrementMs: Option[Long],
|
||||
@JsonDeserialize(contentAs = classOf[java.lang.Long]) clockLastTickAt: Option[Long],
|
||||
@JsonDeserialize(contentAs = classOf[java.lang.Long]) clockMoveDeadline: Option[Long],
|
||||
clockActiveColor: Option[String],
|
||||
pendingDrawOffer: Option[String],
|
||||
result: Option[String] = None,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=12
|
||||
MINOR=14
|
||||
PATCH=0
|
||||
|
||||
@@ -1692,3 +1692,70 @@
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
## (2026-05-31)
|
||||
|
||||
### Features
|
||||
|
||||
* add authentication permissions for metrics endpoints in application.yml ([04edd4d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/04edd4d6fd8a63196c36f6d67992832febc9bebb))
|
||||
* add CORS configuration and reorder JWT settings in application.yml ([a49f9be](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a49f9be146f04c14561c305d980846a92f8c12b2))
|
||||
* add GameRules stub with PositionStatus enum ([76d4168](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/76d4168038de23e5d6083d4e8f0504fbf31d15a3))
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add MovedInCheck/Checkmate/Stalemate MoveResult variants (stub dispatch) ([8b7ec57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8b7ec57e5ea6ee1615a1883848a426dc07d26364))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add GameWritebackEventDto to reflection targets ([87f29a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87f29a720422f538ef70699533500e060337b8ea))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* implement clock expiry scanning and handling for game records ([#53](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/53)) ([8f9eb12](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8f9eb12f663efabe4dc72b94394438652ad0ef02))
|
||||
* implement GameRules with isInCheck, legalMoves, gameStatus ([94a02ff](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/94a02ff6849436d9496c70a0f16c21666dae8e4e))
|
||||
* implement legal castling ([#1](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/1)) ([00d326c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/00d326c1ba67711fbe180f04e1100c3f01dd0254))
|
||||
* implement periodic scaling checks and enhance instance management in AutoScaler ([3f12f69](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f12f695f132b92f634d98df2c037292498b6e86))
|
||||
* **logging:** add DEBUG/INFO/WARN logging across services (NCS-72) ([#41](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/41)) ([804a4bf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/804a4bf179e3dfb19e2be4390e7e543caf5237c6))
|
||||
* NCS-10 Implement Pawn Promotion ([#12](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/12)) ([13bfc16](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/13bfc16cfe25db78ec607db523ca6d993c13430c))
|
||||
* NCS-11 50-move rule ([#9](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/9)) ([412ed98](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/412ed986a95703a3b282276540153480ceed229d))
|
||||
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* NCS-16 Core Separation via Patterns ([#10](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/10)) ([1361dfc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1361dfc89553b146864fb8ff3526cf12cf3f293a))
|
||||
* NCS-17 Implement basic ScalaFX UI ([#14](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/14)) ([3ff8031](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ff80318b4f16c59733a46498581a5c27f048287))
|
||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([f088c4e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f088c4e9ffcc498d3d1b6f01e8f50042d5830d55))
|
||||
* NCS-40 Rework Draw System ([#34](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/34)) ([33e785d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/33e785d22af87724839b62ae91dfe74a05b398c3))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([8744bee](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8744bee2dd20966dae90a09c21a43d5b06f59e00))
|
||||
* NCS-53 changed IO to MicroService for easier scaling ([#37](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/37)) ([b5a2966](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b5a2966adafa9650f0f7d601bdeb8fdd13710327))
|
||||
* NCS-6 Implementing FEN & PGN ([#7](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/7)) ([f28e69d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f28e69dc181416aa2f221fdc4b45c2cda5efbf07))
|
||||
* NCS-78 Add Traceability to the Applications ([#48](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/48)) ([c96a09b](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c96a09bb5cee59fc23205bb63baa8b217a7e1b00))
|
||||
* NCS-9 En passant implementation ([#8](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/8)) ([919beb3](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/919beb3b4bfa8caf2f90976a415fe9b19b7e9747))
|
||||
* **redis:** implement game writeback stream processing with error handling and retries ([ae3ef76](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ae3ef766e8b7596a09e466cd4fb386119f17ca5c))
|
||||
* **rule:** Rules as a microservice ([#39](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/39)) ([093134d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/093134d36c6844ba02a36a28d5d044f09291cd1d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
* wire check/checkmate/stalemate into processMove and gameLoop ([5264a22](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5264a225418b885c5e6ea6411b96f85e38837f6c))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add missing kings to gameLoop capture test board ([aedd787](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/aedd787b77203c2af934751dba7b784eaf165032))
|
||||
* **auth:** change InternalAuthFilter to use @Singleton and add HTTP tests for secret validation ([c08d530](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c08d5303eb9e70d36c8eebf6a061ccb71e118fe5))
|
||||
* **auth:** update InternalAuthFilter to use @ApplicationScoped and add index-dependency configuration ([6e0fd95](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6e0fd9523e001756ce7109e639ebb54be4fcdabf))
|
||||
* **core:** add logs to trace subscribeGame call in createGame ([f5614c3](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f5614c358255598ba1230e42a56b22934d79183c))
|
||||
* correct test board positions and captureOutput/withInput interaction ([f0481e2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f0481e2561b779df00925b46ee281dc36a795150))
|
||||
* **heartbeat:** inject ObjectMapper into InstanceHeartbeatService ([#42](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/42)) ([0c98151](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/0c981517da1f94cd10ae396e47bde2b35d0b3ba0))
|
||||
* IO microservice ([#38](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/38)) ([a386f57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a386f57c21d34ead6cc6f92836c52b714597e289))
|
||||
* Lints ([dc224ab](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dc224abe26acf5361c56956006e1cc51b75b0b7e))
|
||||
* NCS-84 More Verbose Logging ([#51](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/51)) ([4ad92ab](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4ad92ab23698267f8faa59c4e18388d4a0042cca))
|
||||
* NCS-85 Database Writeback fails without Logs ([#52](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/52)) ([7323908](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/73239088d985f01aa6b1067ed9097a845e471d4f))
|
||||
* **redis:** add max pool wait time and switch to ReactiveRedisDataSource for heartbeat updates ([33e5017](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/33e5017f51a998327b180f778f73964cc10c05d3))
|
||||
* **redis:** enhance GameRedisSubscriberManager to use ReactiveRedisDataSource and improve subscription handling ([0eb752d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/0eb752d4935377f75aab710b7f4eda4b29098e6a))
|
||||
* **redis:** prevent concurrent Redis heartbeat refreshes using AtomicBoolean ([847b132](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/847b13202cb909d18ca3304c27ebe17ce2312b8e))
|
||||
* **redis:** simplify refreshRedisHeartbeat logic and ensure proper error handling ([1813ea1](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1813ea1d2d5d093f7925f87371b5e29820bf1136))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* resolve 6 coordinator bugs (cache eviction, rebalance race, pod matching, lookup inefficiency) ([5619c82](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5619c8223ad7091706909eda8c907a29d215fd30))
|
||||
* update documentation to reflect new functions in CoordinatorGrpcServer and InstanceRegistry ([f7ce4df](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f7ce4df595cbdc2ef84122781f4851ff140c0f44))
|
||||
* update main class path in build configuration and adjust VCS directory mapping ([7b1f8b1](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7b1f8b117623d327232a1a92a8a44d18582e0189))
|
||||
* update move validation to check for king safety ([#13](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/13)) ([e5e20c5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e5e20c566e368b12ca1dc59680c34e9112bf6762))
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
|
||||
+3
-1
@@ -20,6 +20,7 @@ import jakarta.enterprise.inject.Instance
|
||||
import jakarta.inject.Inject
|
||||
import org.jboss.logging.Logger
|
||||
import scala.compiletime.uninitialized
|
||||
import scala.jdk.CollectionConverters.*
|
||||
import scala.util.Try
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.function.Consumer
|
||||
@@ -77,7 +78,8 @@ class GameRedisSubscriberManager:
|
||||
s"${redisConfig.prefix}:game:$gameId:s2c"
|
||||
|
||||
def subscribeGame(gameId: String): Unit =
|
||||
val writebackFn: String => Unit = json => redis.pubsub(classOf[String]).publish("game-writeback", json)
|
||||
val writebackFn: String => Unit = json =>
|
||||
redis.stream(classOf[String]).xadd(s"${redisConfig.prefix}:game-writeback", Map("data" -> json).asJava)
|
||||
val obs = new GameRedisPublisher(
|
||||
gameId,
|
||||
registry,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=45
|
||||
MINOR=46
|
||||
PATCH=0
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.nowchess.security;
|
||||
|
||||
import jakarta.ws.rs.NameBinding;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@NameBinding
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
public @interface RateLimited {}
|
||||
@@ -0,0 +1,70 @@
|
||||
package de.nowchess.security
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.ws.rs.container.{ContainerRequestContext, ContainerRequestFilter}
|
||||
import jakarta.ws.rs.core.Response
|
||||
import jakarta.ws.rs.ext.Provider
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty
|
||||
import org.jboss.logging.Logger
|
||||
import scala.compiletime.uninitialized
|
||||
|
||||
import java.time.{Duration, Instant}
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
@Provider
|
||||
@RateLimited
|
||||
@ApplicationScoped
|
||||
class RateLimitFilter extends ContainerRequestFilter:
|
||||
|
||||
@ConfigProperty(name = "nowchess.rate-limit.enabled", defaultValue = "true")
|
||||
// scalafix:off DisableSyntax.var
|
||||
var enabled: Boolean = uninitialized
|
||||
|
||||
@ConfigProperty(name = "nowchess.rate-limit.requests-per-window", defaultValue = "60")
|
||||
var requestsPerWindow: Long = uninitialized
|
||||
|
||||
@ConfigProperty(name = "nowchess.rate-limit.window-seconds", defaultValue = "60")
|
||||
var windowSeconds: Long = uninitialized
|
||||
|
||||
@ConfigProperty(name = "nowchess.rate-limit.gatling-secret")
|
||||
var gatlingSecret: Optional[String] = uninitialized
|
||||
// scalafix:on DisableSyntax.var
|
||||
|
||||
private val log = Logger.getLogger(classOf[RateLimitFilter])
|
||||
private val counters = new ConcurrentHashMap[String, RateWindow]()
|
||||
|
||||
override def filter(ctx: ContainerRequestContext): Unit =
|
||||
val ip = clientIp(ctx)
|
||||
if enabled && !isGatlingRequest(ctx) && isOverLimit(ip) then
|
||||
val path = Option(ctx.getUriInfo).map(_.getPath).getOrElse("-")
|
||||
log.warnf("Rate limit exceeded for IP %s on %s %s", ip, ctx.getMethod, path)
|
||||
ctx.abortWith(Response.status(429).build())
|
||||
|
||||
private def isGatlingRequest(ctx: ContainerRequestContext): Boolean =
|
||||
gatlingSecret.isPresent &&
|
||||
Option(ctx.getHeaderString("X-Gatling-Secret")).contains(gatlingSecret.get())
|
||||
|
||||
private def isOverLimit(ip: String): Boolean =
|
||||
val now = Instant.now()
|
||||
val window = counters.compute(
|
||||
ip,
|
||||
(_, current) =>
|
||||
// scalafix:off DisableSyntax.null
|
||||
if current == null || isExpired(current.start, now) then RateWindow(now, new AtomicLong(0))
|
||||
// scalafix:on DisableSyntax.null
|
||||
else current,
|
||||
)
|
||||
window.count.incrementAndGet() > requestsPerWindow
|
||||
|
||||
private def clientIp(ctx: ContainerRequestContext): String =
|
||||
Option(ctx.getHeaderString("X-Forwarded-For"))
|
||||
.map(_.split(",").head.trim)
|
||||
.orElse(Option(ctx.getHeaderString("X-Real-IP")))
|
||||
.getOrElse("unknown")
|
||||
|
||||
private def isExpired(start: Instant, now: Instant): Boolean =
|
||||
Duration.between(start, now).getSeconds >= windowSeconds
|
||||
|
||||
private final case class RateWindow(start: Instant, count: AtomicLong)
|
||||
@@ -307,3 +307,149 @@
|
||||
* **redis:** add log message for starting Writeback listener ([b610678](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b610678005de645115f48348e66aa9e6f5deb3d5))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
## (2026-05-19)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* **config:** update application.yml to nest HTTP port configuration ([3efebd5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3efebd5ed0493159c51f7246d18d59bac58cf875))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* implement clock expiry scanning and handling for game records ([#53](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/53)) ([8f9eb12](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8f9eb12f663efabe4dc72b94394438652ad0ef02))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* **redis:** add @Startup annotation to GameWritebackStreamListener ([d61fe97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d61fe97b4c8e2db5e34b4a14d995297cc09f9435))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure full hierarchy registration for reflection in NativeReflectionConfig ([ebba729](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ebba729af3265df1619dfbe46fd1945b2a7e30b7))
|
||||
* NCS-85 Database Writeback fails without Logs ([#52](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/52)) ([7323908](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/73239088d985f01aa6b1067ed9097a845e471d4f))
|
||||
* **redis:** add log message for starting Writeback listener ([b610678](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b610678005de645115f48348e66aa9e6f5deb3d5))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
## (2026-05-19)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* **config:** update application.yml to nest HTTP port configuration ([3efebd5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3efebd5ed0493159c51f7246d18d59bac58cf875))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* implement clock expiry scanning and handling for game records ([#53](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/53)) ([8f9eb12](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8f9eb12f663efabe4dc72b94394438652ad0ef02))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* **redis:** add @Startup annotation to GameWritebackStreamListener ([d61fe97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d61fe97b4c8e2db5e34b4a14d995297cc09f9435))
|
||||
* **redis:** use ManagedExecutor for asynchronous writeback processing ([af6b0ed](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/af6b0ed8b73724fcc8f20dfccbe6fe8f84fd792d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure full hierarchy registration for reflection in NativeReflectionConfig ([ebba729](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ebba729af3265df1619dfbe46fd1945b2a7e30b7))
|
||||
* NCS-85 Database Writeback fails without Logs ([#52](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/52)) ([7323908](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/73239088d985f01aa6b1067ed9097a845e471d4f))
|
||||
* **redis:** add log message for starting Writeback listener ([b610678](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b610678005de645115f48348e66aa9e6f5deb3d5))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
## (2026-05-22)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add GameWritebackEventDtoMixin for JSON deserialization ([381161f](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/381161f00345612a1789e08243746083dff884c5))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* **config:** update application.yml to nest HTTP port configuration ([3efebd5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3efebd5ed0493159c51f7246d18d59bac58cf875))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* implement clock expiry scanning and handling for game records ([#53](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/53)) ([8f9eb12](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8f9eb12f663efabe4dc72b94394438652ad0ef02))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* **redis:** add @Startup annotation to GameWritebackStreamListener ([d61fe97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d61fe97b4c8e2db5e34b4a14d995297cc09f9435))
|
||||
* **redis:** use ManagedExecutor for asynchronous writeback processing ([af6b0ed](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/af6b0ed8b73724fcc8f20dfccbe6fe8f84fd792d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure full hierarchy registration for reflection in NativeReflectionConfig ([ebba729](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ebba729af3265df1619dfbe46fd1945b2a7e30b7))
|
||||
* NCS-85 Database Writeback fails without Logs ([#52](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/52)) ([7323908](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/73239088d985f01aa6b1067ed9097a845e471d4f))
|
||||
* **redis:** add log message for starting Writeback listener ([b610678](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b610678005de645115f48348e66aa9e6f5deb3d5))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
## (2026-05-22)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add GameWritebackEventDtoMixin for JSON deserialization ([381161f](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/381161f00345612a1789e08243746083dff884c5))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* **config:** update application.yml to nest HTTP port configuration ([3efebd5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3efebd5ed0493159c51f7246d18d59bac58cf875))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **dto:** update GameWritebackEventDto for JSON deserialization and remove unused mixin ([576e3fe](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/576e3fea9bf1082549ea53efd3288474c42be93d))
|
||||
* implement clock expiry scanning and handling for game records ([#53](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/53)) ([8f9eb12](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8f9eb12f663efabe4dc72b94394438652ad0ef02))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* **redis:** add @Startup annotation to GameWritebackStreamListener ([d61fe97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d61fe97b4c8e2db5e34b4a14d995297cc09f9435))
|
||||
* **redis:** use ManagedExecutor for asynchronous writeback processing ([af6b0ed](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/af6b0ed8b73724fcc8f20dfccbe6fe8f84fd792d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure full hierarchy registration for reflection in NativeReflectionConfig ([ebba729](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ebba729af3265df1619dfbe46fd1945b2a7e30b7))
|
||||
* NCS-85 Database Writeback fails without Logs ([#52](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/52)) ([7323908](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/73239088d985f01aa6b1067ed9097a845e471d4f))
|
||||
* **redis:** add log message for starting Writeback listener ([b610678](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b610678005de645115f48348e66aa9e6f5deb3d5))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
## (2026-05-31)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add GameWritebackEventDtoMixin for JSON deserialization ([381161f](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/381161f00345612a1789e08243746083dff884c5))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* **config:** update application.yml to nest HTTP port configuration ([3efebd5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3efebd5ed0493159c51f7246d18d59bac58cf875))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **dto:** update GameWritebackEventDto for JSON deserialization and remove unused mixin ([576e3fe](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/576e3fea9bf1082549ea53efd3288474c42be93d))
|
||||
* implement clock expiry scanning and handling for game records ([#53](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/53)) ([8f9eb12](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8f9eb12f663efabe4dc72b94394438652ad0ef02))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* **redis:** add @Startup annotation to GameWritebackStreamListener ([d61fe97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d61fe97b4c8e2db5e34b4a14d995297cc09f9435))
|
||||
* **redis:** implement game writeback stream processing with error handling and retries ([ae3ef76](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ae3ef766e8b7596a09e466cd4fb386119f17ca5c))
|
||||
* **redis:** use ManagedExecutor for asynchronous writeback processing ([af6b0ed](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/af6b0ed8b73724fcc8f20dfccbe6fe8f84fd792d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure full hierarchy registration for reflection in NativeReflectionConfig ([ebba729](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ebba729af3265df1619dfbe46fd1945b2a7e30b7))
|
||||
* NCS-85 Database Writeback fails without Logs ([#52](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/52)) ([7323908](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/73239088d985f01aa6b1067ed9097a845e471d4f))
|
||||
* **redis:** add log message for starting Writeback listener ([b610678](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b610678005de645115f48348e66aa9e6f5deb3d5))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* **store:** replace null check with Option for stream messages ([252851d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/252851de1cd715f797847e0660ee501c3a77237e))
|
||||
|
||||
+78
-14
@@ -2,16 +2,22 @@ package de.nowchess.store.redis
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import de.nowchess.api.dto.GameWritebackEventDto
|
||||
import de.nowchess.store.config.RedisConfig
|
||||
import de.nowchess.store.service.GameWritebackService
|
||||
import io.quarkus.redis.datasource.RedisDataSource
|
||||
import io.quarkus.redis.datasource.stream.{StreamMessage, XGroupCreateArgs, XReadGroupArgs}
|
||||
import io.quarkus.runtime.Startup
|
||||
import jakarta.annotation.PostConstruct
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
import org.eclipse.microprofile.context.ManagedExecutor
|
||||
import org.jboss.logging.Logger
|
||||
import scala.compiletime.uninitialized
|
||||
import scala.jdk.CollectionConverters.*
|
||||
import scala.util.{Failure, Success, Try}
|
||||
import java.util.function.Consumer
|
||||
import java.util.UUID
|
||||
|
||||
@Startup
|
||||
@ApplicationScoped
|
||||
class GameWritebackStreamListener:
|
||||
@Inject
|
||||
@@ -19,21 +25,79 @@ class GameWritebackStreamListener:
|
||||
var redis: RedisDataSource = uninitialized
|
||||
@Inject var objectMapper: ObjectMapper = uninitialized
|
||||
@Inject var writebackService: GameWritebackService = uninitialized
|
||||
@Inject var executor: ManagedExecutor = uninitialized
|
||||
@Inject var redisConfig: RedisConfig = uninitialized
|
||||
// scalafix:on
|
||||
|
||||
private val log = Logger.getLogger(classOf[GameWritebackStreamListener])
|
||||
private val log = Logger.getLogger(classOf[GameWritebackStreamListener])
|
||||
private val groupName = "store-writeback"
|
||||
|
||||
private def streamKey = s"${redisConfig.prefix}:game-writeback"
|
||||
private def dlqKey = s"${redisConfig.prefix}:game-writeback-dlq"
|
||||
private val maxRetries = 3
|
||||
private val consumerId = UUID.randomUUID().toString
|
||||
|
||||
@PostConstruct
|
||||
def startListening(): Unit =
|
||||
val handler: Consumer[String] = json =>
|
||||
Try(objectMapper.readValue(json, classOf[GameWritebackEventDto])) match
|
||||
case Failure(ex) =>
|
||||
log.errorf(ex, "Failed to parse game-writeback event: %s", json)
|
||||
case Success(event) =>
|
||||
Try(writebackService.writeBack(event)) match
|
||||
case Failure(ex) =>
|
||||
log.errorf(ex, "Failed to write back game event for gameId=%s", event.gameId)
|
||||
case Success(_) => ()
|
||||
redis.pubsub(classOf[String]).subscribe("game-writeback", handler)
|
||||
log.infof("Started listening to Writebacks")
|
||||
()
|
||||
createGroupIfAbsent()
|
||||
executor.submit(
|
||||
new Runnable:
|
||||
def run(): Unit = pollLoop(),
|
||||
)
|
||||
log.infof("Started listening to game-writeback stream (consumer=%s)", consumerId)
|
||||
|
||||
private def createGroupIfAbsent(): Unit =
|
||||
Try(redis.stream(classOf[String]).xgroupCreate(streamKey, groupName, "0", new XGroupCreateArgs().mkstream())) match
|
||||
case Failure(ex) if Option(ex.getMessage).exists(_.contains("BUSYGROUP")) => ()
|
||||
case Failure(ex) => log.warnf(ex, "Failed to create consumer group")
|
||||
case Success(_) => ()
|
||||
|
||||
private def pollLoop(): Unit =
|
||||
while true do
|
||||
Try {
|
||||
val messages = redis
|
||||
.stream(classOf[String])
|
||||
.xreadgroup(
|
||||
groupName,
|
||||
consumerId,
|
||||
streamKey,
|
||||
">",
|
||||
new XReadGroupArgs().count(10).block(java.time.Duration.ofSeconds(2)),
|
||||
)
|
||||
Option(messages).foreach(_.forEach(msg => handleMessage(msg)))
|
||||
} match
|
||||
case Failure(ex) => log.warnf(ex, "Error in writeback poll loop")
|
||||
case Success(_) => ()
|
||||
|
||||
private def handleMessage(msg: StreamMessage[String, String, String]): Unit =
|
||||
val payload = msg.payload()
|
||||
val json = payload.get("data")
|
||||
val attempt = Option(payload.get("attempt")).flatMap(_.toIntOption).getOrElse(0)
|
||||
|
||||
Try(objectMapper.readValue(json, classOf[GameWritebackEventDto])) match
|
||||
case Failure(ex) =>
|
||||
log.errorf(ex, "Unparseable writeback event, sending to DLQ: %s", json)
|
||||
xadd(dlqKey, json, attempt)
|
||||
ack(msg.id())
|
||||
case Success(event) =>
|
||||
Try(writebackService.writeBack(event)) match
|
||||
case Success(_) =>
|
||||
ack(msg.id())
|
||||
case Failure(ex) if attempt + 1 < maxRetries =>
|
||||
log.warnf(ex, "Writeback failed for gameId=%s attempt=%d, retrying", event.gameId, attempt)
|
||||
xadd(streamKey, json, attempt + 1)
|
||||
ack(msg.id())
|
||||
case Failure(ex) =>
|
||||
log.errorf(ex, "Writeback failed for gameId=%s after %d attempts, sending to DLQ", event.gameId, maxRetries)
|
||||
xadd(dlqKey, json, attempt)
|
||||
ack(msg.id())
|
||||
|
||||
private def ack(id: String): Unit =
|
||||
Try(redis.stream(classOf[String]).xack(streamKey, groupName, id)) match
|
||||
case Failure(ex) => log.warnf(ex, "Failed to ack message %s", id)
|
||||
case Success(_) => ()
|
||||
|
||||
private def xadd(key: String, json: String, attempt: Int): Unit =
|
||||
Try(redis.stream(classOf[String]).xadd(key, Map("data" -> json, "attempt" -> attempt.toString).asJava)) match
|
||||
case Failure(ex) => log.errorf(ex, "Failed to publish to stream %s", key)
|
||||
case Success(_) => ()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=18
|
||||
MINOR=23
|
||||
PATCH=0
|
||||
|
||||
Reference in New Issue
Block a user