feat(coordinator): add Redis integration and improve configuration for game state management
Build & Test (NowChessSystems) TeamCity build was removed from queue

This commit is contained in:
2026-04-26 18:25:03 +02:00
parent f327441089
commit 106b4d3b7e
56 changed files with 1072 additions and 1139 deletions
@@ -1,7 +1,16 @@
package de.nowchess.account.config
import de.nowchess.account.client.{CoreCreateGameRequest, CoreGameResponse, CorePlayerInfo, CoreTimeControl}
import de.nowchess.account.domain.{UserAccount, BotAccount, OfficialBotAccount, Challenge, ChallengeColor, ChallengeStatus, DeclineReason, TimeControl}
import de.nowchess.account.domain.{
BotAccount,
Challenge,
ChallengeColor,
ChallengeStatus,
DeclineReason,
OfficialBotAccount,
TimeControl,
UserAccount,
}
import de.nowchess.account.dto.*
import io.quarkus.runtime.annotations.RegisterForReflection
@@ -18,7 +18,7 @@ class AlreadyLoggedInFilter extends ContainerRequestFilter:
// scalafix:on
override def filter(context: ContainerRequestContext): Unit =
val path = context.getUriInfo.getPath
val path = context.getUriInfo.getPath
val method = context.getMethod
if isProtectedEndpoint(path, method) && isAuthenticated then
@@ -26,14 +26,15 @@ class AlreadyLoggedInFilter extends ContainerRequestFilter:
Response
.status(Response.Status.BAD_REQUEST)
.entity("""{"error":"Already logged in"}""")
.build()
.build(),
)
private def isAuthenticated: Boolean =
// scalafix:off DisableSyntax.null
try jwt.getName != null
catch case _ => false
// scalafix:on DisableSyntax.null
catch
case _ => false
// scalafix:on DisableSyntax.null
private def isProtectedEndpoint(path: String, method: String): Boolean =
(path.contains("/api/account") || path.contains("/account")) &&
@@ -1,6 +1,6 @@
package de.nowchess.account.repository
import de.nowchess.account.domain.{UserAccount, BotAccount, OfficialBotAccount}
import de.nowchess.account.domain.{BotAccount, OfficialBotAccount, UserAccount}
import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import jakarta.persistence.EntityManager
@@ -1,6 +1,6 @@
package de.nowchess.account.resource
import de.nowchess.account.domain.{UserAccount, BotAccount, OfficialBotAccount}
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
@@ -100,7 +100,7 @@ class AccountResource:
@RolesAllowed(Array("**"))
def listBotAccounts(): Response =
val ownerId = UUID.fromString(jwt.getSubject)
val bots = accountService.getBotAccounts(ownerId)
val bots = accountService.getBotAccounts(ownerId)
Response.ok(bots.map(toBotDto)).build()
@PUT
@@ -1,9 +1,9 @@
package de.nowchess.account.service
import de.nowchess.account.domain.{UserAccount, BotAccount, OfficialBotAccount}
import de.nowchess.account.domain.{BotAccount, OfficialBotAccount, UserAccount}
import de.nowchess.account.dto.{LoginRequest, RegisterRequest}
import de.nowchess.account.error.AccountError
import de.nowchess.account.repository.{UserAccountRepository, BotAccountRepository, OfficialBotAccountRepository}
import de.nowchess.account.repository.{BotAccountRepository, OfficialBotAccountRepository, UserAccountRepository}
import io.quarkus.elytron.security.common.BcryptUtil
import io.smallrye.jwt.build.Jwt
import jakarta.enterprise.context.ApplicationScoped
@@ -31,7 +31,8 @@ class AccountService:
@Transactional
def register(req: RegisterRequest): Either[AccountError, UserAccount] =
if userAccountRepository.findByUsername(req.username).isDefined then Left(AccountError.UsernameTaken(req.username))
else if userAccountRepository.findByEmail(req.email).isDefined then Left(AccountError.EmailAlreadyRegistered(req.email))
else if userAccountRepository.findByEmail(req.email).isDefined then
Left(AccountError.EmailAlreadyRegistered(req.email))
else
val account = new UserAccount()
account.username = req.username
@@ -53,7 +54,7 @@ class AccountService:
.issuer("nowchess")
.subject(account.id.toString)
.claim("username", account.username)
.sign()
.sign(),
)
def findByUsername(username: String): Option[UserAccount] =
@@ -83,7 +84,7 @@ class AccountService:
def getBotAccountWithOwnerCheck(botId: UUID, ownerId: UUID): Option[Option[BotAccount]] =
botAccountRepository.findById(botId) match
case None => Some(None)
case None => Some(None)
case Some(bot) => Some(Option(bot).filter(_.owner.id == ownerId))
@Transactional
@@ -17,7 +17,7 @@ import de.nowchess.account.dto.{
TimeControlDto,
}
import de.nowchess.account.error.ChallengeError
import de.nowchess.account.repository.{UserAccountRepository, ChallengeRepository}
import de.nowchess.account.repository.{ChallengeRepository, UserAccountRepository}
import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import jakarta.transaction.Transactional
@@ -46,7 +46,7 @@ class ChallengeService:
@Transactional
def create(challengerId: UUID, destUsername: String, req: ChallengeRequest): Either[ChallengeError, Challenge] =
for
destUser <- userAccountRepository.findByUsername(destUsername).toRight(ChallengeError.UserNotFound(destUsername))
destUser <- userAccountRepository.findByUsername(destUsername).toRight(ChallengeError.UserNotFound(destUsername))
challenger <- userAccountRepository.findById(challengerId).toRight(ChallengeError.ChallengerNotFound)
_ <- Either.cond(challenger.id != destUser.id, (), ChallengeError.CannotChallengeSelf)
_ <- Either.cond(