feat(security): add internal secret handling and Redis integration for bot events
This commit is contained in:
@@ -6,7 +6,13 @@ import scala.compiletime.uninitialized
|
||||
import java.time.Instant
|
||||
|
||||
@Entity
|
||||
@Table(name = "game_records")
|
||||
@Table(
|
||||
name = "game_records",
|
||||
indexes = Array(
|
||||
new Index(name = "idx_game_records_white_id", columnList = "whiteId"),
|
||||
new Index(name = "idx_game_records_black_id", columnList = "blackId"),
|
||||
),
|
||||
)
|
||||
class GameRecord extends PanacheEntityBase:
|
||||
// scalafix:off DisableSyntax.var
|
||||
@Id
|
||||
@@ -79,4 +85,11 @@ class GameRecord extends PanacheEntityBase:
|
||||
|
||||
@Column
|
||||
var pendingDrawOffer: String = uninitialized
|
||||
|
||||
// Game result
|
||||
@Column
|
||||
var result: String = uninitialized
|
||||
|
||||
@Column
|
||||
var terminationReason: String = uninitialized
|
||||
// scalafix:on
|
||||
|
||||
@@ -21,4 +21,6 @@ case class GameWritebackEventDto(
|
||||
clockMoveDeadline: Option[Long],
|
||||
clockActiveColor: Option[String],
|
||||
pendingDrawOffer: Option[String],
|
||||
result: Option[String] = None,
|
||||
terminationReason: Option[String] = None,
|
||||
)
|
||||
|
||||
+2
-4
@@ -24,8 +24,6 @@ class GameWritebackStreamListener:
|
||||
val topic = redisson.getTopic("game-writeback")
|
||||
topic.addListener(
|
||||
classOf[String],
|
||||
new MessageListener[String]:
|
||||
def onMessage(channel: CharSequence, json: String): Unit =
|
||||
Try(objectMapper.readValue(json, classOf[GameWritebackEventDto])).toOption
|
||||
.foreach(writebackService.writeBack),
|
||||
(channel: CharSequence, json: String) => Try(objectMapper.readValue(json, classOf[GameWritebackEventDto])).toOption
|
||||
.foreach(writebackService.writeBack)
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.persistence.EntityManager
|
||||
import scala.compiletime.uninitialized
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
@ApplicationScoped
|
||||
class GameRecordRepository:
|
||||
@@ -21,3 +22,25 @@ class GameRecordRepository:
|
||||
|
||||
def merge(record: GameRecord): Unit =
|
||||
em.merge(record)
|
||||
|
||||
def findByPlayerId(playerId: String, offset: Int, limit: Int): List[GameRecord] =
|
||||
em.createQuery(
|
||||
"SELECT g FROM GameRecord g WHERE g.whiteId = :id OR g.blackId = :id ORDER BY g.updatedAt DESC",
|
||||
classOf[GameRecord],
|
||||
).setParameter("id", playerId)
|
||||
.setFirstResult(offset)
|
||||
.setMaxResults(limit)
|
||||
.getResultList
|
||||
.asScala
|
||||
.toList
|
||||
|
||||
def findByPlayerIdRunning(playerId: String, offset: Int, limit: Int): List[GameRecord] =
|
||||
em.createQuery(
|
||||
"SELECT g FROM GameRecord g WHERE g.whiteId = :id OR g.blackId = :id AND g.result = null ORDER BY g.updatedAt DESC",
|
||||
classOf[GameRecord],
|
||||
).setParameter("id", playerId)
|
||||
.setFirstResult(offset)
|
||||
.setMaxResults(limit)
|
||||
.getResultList
|
||||
.asScala
|
||||
.toList
|
||||
|
||||
@@ -5,6 +5,7 @@ import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.ws.rs.*
|
||||
import jakarta.ws.rs.core.{MediaType, Response}
|
||||
import jakarta.ws.rs.DefaultValue
|
||||
import scala.compiletime.uninitialized
|
||||
|
||||
@Path("/game")
|
||||
@@ -22,3 +23,23 @@ class StoreGameResource:
|
||||
repository
|
||||
.findByGameId(gameId)
|
||||
.fold(Response.status(404).build())(r => Response.ok(r).build())
|
||||
|
||||
@GET
|
||||
@Path("/running/{playerId}")
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
def getRunning(
|
||||
@PathParam("playerId") playerId: String,
|
||||
@QueryParam("offset") @DefaultValue("0") offset: Int,
|
||||
@QueryParam("limit") @DefaultValue("20") limit: Int,
|
||||
): Response =
|
||||
Response.ok(repository.findByPlayerIdRunning(playerId, offset, limit)).build()
|
||||
|
||||
@GET
|
||||
@Path("/history/{playerId}")
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
def getHistory(
|
||||
@PathParam("playerId") playerId: String,
|
||||
@QueryParam("offset") @DefaultValue("0") offset: Int,
|
||||
@QueryParam("limit") @DefaultValue("20") limit: Int,
|
||||
): Response =
|
||||
Response.ok(repository.findByPlayerId(playerId, offset, limit)).build()
|
||||
|
||||
@@ -41,6 +41,8 @@ class GameWritebackService:
|
||||
record.clockMoveDeadline = event.clockMoveDeadline.map(java.lang.Long.valueOf).orNull
|
||||
record.clockActiveColor = event.clockActiveColor.orNull
|
||||
record.pendingDrawOffer = event.pendingDrawOffer.orNull
|
||||
record.result = event.result.orNull
|
||||
record.terminationReason = event.terminationReason.orNull
|
||||
record.createdAt = Instant.now()
|
||||
record.updatedAt = Instant.now()
|
||||
repository.persist(record)
|
||||
@@ -64,6 +66,8 @@ class GameWritebackService:
|
||||
r.clockMoveDeadline = event.clockMoveDeadline.map(java.lang.Long.valueOf).orNull
|
||||
r.clockActiveColor = event.clockActiveColor.orNull
|
||||
r.pendingDrawOffer = event.pendingDrawOffer.orNull
|
||||
r.result = event.result.orNull
|
||||
r.terminationReason = event.terminationReason.orNull
|
||||
r.updatedAt = Instant.now()
|
||||
repository.merge(r)
|
||||
case _ => ()
|
||||
|
||||
Reference in New Issue
Block a user