refactoring
This commit is contained in:
22
src/main/kotlin/eventDemo/configuration/Configure.kt
Normal file
22
src/main/kotlin/eventDemo/configuration/Configure.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.GameEventReactionListener
|
||||
import io.ktor.server.application.Application
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
fun Application.configure() {
|
||||
configureKoin()
|
||||
|
||||
configureSecurity()
|
||||
|
||||
configureSerialization()
|
||||
|
||||
configureSockets()
|
||||
configureWebSocketsGameRoute(get(), get())
|
||||
|
||||
configureHttp()
|
||||
configureHttpRouting()
|
||||
|
||||
GameEventReactionListener(get(), get())
|
||||
.init()
|
||||
}
|
||||
38
src/main/kotlin/eventDemo/configuration/HTTP.kt
Normal file
38
src/main/kotlin/eventDemo/configuration/HTTP.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.plugins.cors.routing.CORS
|
||||
|
||||
fun Application.configureHttp() {
|
||||
install(CORS) {
|
||||
allowMethod(HttpMethod.Options)
|
||||
allowMethod(HttpMethod.Put)
|
||||
allowMethod(HttpMethod.Post)
|
||||
allowMethod(HttpMethod.Delete)
|
||||
allowMethod(HttpMethod.Patch)
|
||||
allowHeader(HttpHeaders.Authorization)
|
||||
allowHeader("MyCustomHeader")
|
||||
anyHost() // @TODO: Don't do this in production if possible. Try to limit it.
|
||||
}
|
||||
}
|
||||
|
||||
class BadRequestException(
|
||||
val httpError: HttpErrorBadRequest,
|
||||
) : Exception()
|
||||
|
||||
class HttpErrorBadRequest(
|
||||
statusCode: HttpStatusCode,
|
||||
val title: String = statusCode.description,
|
||||
val invalidParams: List<InvalidParam>,
|
||||
) {
|
||||
val statusCode: Int = statusCode.value
|
||||
|
||||
data class InvalidParam(
|
||||
val name: String,
|
||||
val reason: String,
|
||||
)
|
||||
}
|
||||
28
src/main/kotlin/eventDemo/configuration/Koin.kt
Normal file
28
src/main/kotlin/eventDemo/configuration/Koin.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.event.GameEventBus
|
||||
import eventDemo.app.event.GameEventStream
|
||||
import eventDemo.libs.event.EventBusInMemory
|
||||
import eventDemo.libs.event.EventStreamInMemory
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import org.koin.dsl.module
|
||||
import org.koin.ktor.plugin.Koin
|
||||
import org.koin.logger.slf4jLogger
|
||||
|
||||
fun Application.configureKoin() {
|
||||
install(Koin) {
|
||||
slf4jLogger()
|
||||
modules(appKoinModule)
|
||||
}
|
||||
}
|
||||
|
||||
val appKoinModule =
|
||||
module {
|
||||
single {
|
||||
GameEventStream(get(), EventStreamInMemory())
|
||||
}
|
||||
single {
|
||||
GameEventBus(EventBusInMemory())
|
||||
}
|
||||
}
|
||||
30
src/main/kotlin/eventDemo/configuration/Routing.kt
Normal file
30
src/main/kotlin/eventDemo/configuration/Routing.kt
Normal file
@@ -0,0 +1,30 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.actions.readGameState
|
||||
import eventDemo.app.actions.readLastPlayedCard
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.plugins.autohead.AutoHeadResponse
|
||||
import io.ktor.server.plugins.statuspages.StatusPages
|
||||
import io.ktor.server.resources.Resources
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.routing.routing
|
||||
|
||||
fun Application.configureHttpRouting() {
|
||||
install(AutoHeadResponse)
|
||||
install(Resources)
|
||||
install(StatusPages) {
|
||||
exception<BadRequestException> { call, cause ->
|
||||
call.respondText(text = "400: $cause", status = HttpStatusCode.BadRequest)
|
||||
}
|
||||
exception<Throwable> { call, cause ->
|
||||
call.respondText(text = "500: $cause", status = HttpStatusCode.InternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
routing {
|
||||
readLastPlayedCard()
|
||||
readGameState()
|
||||
}
|
||||
}
|
||||
58
src/main/kotlin/eventDemo/configuration/Security.kt
Normal file
58
src/main/kotlin/eventDemo/configuration/Security.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.authentication
|
||||
import io.ktor.server.auth.jwt.JWTPrincipal
|
||||
import io.ktor.server.auth.jwt.jwt
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.post
|
||||
import io.ktor.server.routing.routing
|
||||
import java.util.Date
|
||||
|
||||
fun Application.configureSecurity() {
|
||||
// TODO: read the jwt property from the config file
|
||||
val jwtRealm = "Play card game"
|
||||
val jwtIssuer = "PlayCardGame"
|
||||
val jwtSecret = "secret"
|
||||
authentication {
|
||||
jwt {
|
||||
realm = jwtRealm
|
||||
verifier(
|
||||
JWT
|
||||
.require(Algorithm.HMAC256(jwtSecret))
|
||||
.withIssuer(jwtIssuer)
|
||||
.build(),
|
||||
)
|
||||
validate { credential ->
|
||||
if (credential.payload.getClaim("username").asString() != "") {
|
||||
JWTPrincipal(credential.payload)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
challenge { defaultScheme, realm ->
|
||||
call.respond(HttpStatusCode.Unauthorized, "Token is not valid or has expired")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
routing {
|
||||
post("login/{username}") {
|
||||
val username = call.parameters["username"]
|
||||
|
||||
val token =
|
||||
JWT
|
||||
.create()
|
||||
.withIssuer(jwtIssuer)
|
||||
.withClaim("username", username)
|
||||
.withExpiresAt(Date(System.currentTimeMillis() + 60000))
|
||||
.sign(Algorithm.HMAC256(jwtSecret))
|
||||
|
||||
call.respond(hashMapOf("token" to token))
|
||||
}
|
||||
}
|
||||
}
|
||||
83
src/main/kotlin/eventDemo/configuration/Serialization.kt
Normal file
83
src/main/kotlin/eventDemo/configuration/Serialization.kt
Normal file
@@ -0,0 +1,83 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.GameId
|
||||
import eventDemo.app.entity.Player.PlayerId
|
||||
import eventDemo.libs.command.CommandId
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import java.util.UUID
|
||||
|
||||
fun Application.configureSerialization() {
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
serializersModule =
|
||||
SerializersModule {
|
||||
contextual(UUID::class) { UUIDSerializer }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object CommandIdSerializer : KSerializer<CommandId> {
|
||||
override fun deserialize(decoder: Decoder): CommandId = CommandId(decoder.decodeString())
|
||||
|
||||
override fun serialize(
|
||||
encoder: Encoder,
|
||||
value: CommandId,
|
||||
) {
|
||||
encoder.encodeString(value.toString())
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("CommandId", PrimitiveKind.STRING)
|
||||
}
|
||||
|
||||
object PlayerIdSerializer : KSerializer<PlayerId> {
|
||||
override fun deserialize(decoder: Decoder): PlayerId = PlayerId(UUID.fromString(decoder.decodeString()))
|
||||
|
||||
override fun serialize(
|
||||
encoder: Encoder,
|
||||
value: PlayerId,
|
||||
) {
|
||||
encoder.encodeString(value.id.toString())
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("GameId", PrimitiveKind.STRING)
|
||||
}
|
||||
|
||||
object GameIdSerializer : KSerializer<GameId> {
|
||||
override fun deserialize(decoder: Decoder): GameId = GameId(UUID.fromString(decoder.decodeString()))
|
||||
|
||||
override fun serialize(
|
||||
encoder: Encoder,
|
||||
value: GameId,
|
||||
) {
|
||||
encoder.encodeString(value.id.toString())
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("GameId", PrimitiveKind.STRING)
|
||||
}
|
||||
|
||||
object UUIDSerializer : KSerializer<UUID> {
|
||||
override fun deserialize(decoder: Decoder): UUID = UUID.fromString(decoder.decodeString())
|
||||
|
||||
override fun serialize(
|
||||
encoder: Encoder,
|
||||
value: UUID,
|
||||
) {
|
||||
encoder.encodeString(value.toString())
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
|
||||
}
|
||||
50
src/main/kotlin/eventDemo/configuration/Sockets.kt
Normal file
50
src/main/kotlin/eventDemo/configuration/Sockets.kt
Normal file
@@ -0,0 +1,50 @@
|
||||
package eventDemo.configuration
|
||||
|
||||
import eventDemo.app.GameEventPlayerNotificationListener
|
||||
import eventDemo.app.actions.GameCommandHandler
|
||||
import eventDemo.app.entity.Player
|
||||
import eventDemo.app.event.GameEventBus
|
||||
import eventDemo.app.event.GameEventStream
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.auth.jwt.JWTPrincipal
|
||||
import io.ktor.server.auth.principal
|
||||
import io.ktor.server.routing.routing
|
||||
import io.ktor.server.websocket.WebSockets
|
||||
import io.ktor.server.websocket.pingPeriod
|
||||
import io.ktor.server.websocket.timeout
|
||||
import io.ktor.server.websocket.webSocket
|
||||
import java.time.Duration
|
||||
|
||||
fun Application.configureSockets() {
|
||||
install(WebSockets) {
|
||||
pingPeriod = Duration.ofSeconds(15)
|
||||
timeout = Duration.ofSeconds(15)
|
||||
maxFrameSize = Long.MAX_VALUE
|
||||
masking = false
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.configureWebSocketsGameRoute(
|
||||
eventStream: GameEventStream,
|
||||
eventBus: GameEventBus,
|
||||
) {
|
||||
routing {
|
||||
authenticate {
|
||||
webSocket("/game") {
|
||||
GameCommandHandler(eventStream, incoming, outgoing).init(call.getPlayer())
|
||||
GameEventPlayerNotificationListener(eventBus, outgoing).init()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ApplicationCall.getPlayer() =
|
||||
principal<JWTPrincipal>()!!.run {
|
||||
Player(
|
||||
id = payload.getClaim("playerid").asString(),
|
||||
name = payload.getClaim("username").asString(),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user