From 6fd711d67a557e26e541ff7b067241206298413a Mon Sep 17 00:00:00 2001 From: MrPowerGamerBR Date: Mon, 14 Oct 2024 16:50:54 -0300 Subject: [PATCH] Update to Ktor 3.0.0 --- backend/build.gradle.kts | 2 +- .../backend/GalleryOfDreamsBackend.kt | 5 -- .../routes/api/PostArtistWithFanArtRoute.kt | 36 +++++++++++--- .../routes/api/PostCheckFanArtRoute.kt | 33 ++++++++++--- .../backend/routes/api/PostFanArtRoute.kt | 36 +++++++++++--- .../perfectdreams/sequins/ktor/BaseRoute.kt | 49 +++++++++++++++++++ buildSrc/src/main/kotlin/Versions.kt | 2 +- 7 files changed, 134 insertions(+), 29 deletions(-) create mode 100644 backend/src/main/kotlin/net/perfectdreams/sequins/ktor/BaseRoute.kt diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index f1e8886..1b8addf 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { implementation("io.ktor:ktor-server-compression:${Versions.KTOR}") implementation("io.ktor:ktor-server-caching-headers:${Versions.KTOR}") - implementation("ch.qos.logback:logback-classic:1.3.0-alpha11") + implementation("ch.qos.logback:logback-classic:1.5.10") implementation("commons-codec:commons-codec:1.15") // https://mvnrepository.com/artifact/org.jsoup/jsoup diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt index 15e6694..3481d2a 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt @@ -14,7 +14,6 @@ import io.ktor.server.engine.* import io.ktor.server.http.content.* import io.ktor.server.plugins.cachingheaders.* import io.ktor.server.plugins.compression.* -import io.ktor.server.plugins.cors.* import io.ktor.server.routing.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -139,10 +138,6 @@ class GalleryOfDreamsBackend(val languageManager: LanguageManager) { } val server = embeddedServer(CIO, port = System.getenv("GALLERYOFDREAMS_WEBSERVER_PORT")?.toIntOrNull() ?: 8080) { - install(CORS) { - anyHost() - } - // Enables gzip and deflate compression install(Compression) diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostArtistWithFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostArtistWithFanArtRoute.kt index c309553..075d5dc 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostArtistWithFanArtRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostArtistWithFanArtRoute.kt @@ -2,13 +2,16 @@ package net.perfectdreams.galleryofdreams.backend.routes.api import club.minnced.discord.webhook.send.AllowedMentions import club.minnced.discord.webhook.send.WebhookMessageBuilder +import io.ktor.http.* import io.ktor.http.content.* import io.ktor.server.application.* import io.ktor.server.request.* +import io.ktor.utils.io.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.io.readByteArray import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import net.perfectdreams.dreamstorageservice.data.api.CreateImageLinkRequest @@ -36,18 +39,35 @@ class PostArtistWithFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenti val (fanArtArtist, response) = withContext(Dispatchers.IO) { // Receive the uploaded file val multipart = call.receiveMultipart() - val parts = multipart.readAllParts() - val filePart = parts.first { it.name == "file" } as PartData.FileItem - val attributesPart = parts.first { it.name == "attributes" } as PartData.FormItem - val attributes = Json.decodeFromString(attributesPart.value) + var attributesString: String? = null + var contentType: ContentType? = null + var fileToBeStored: ByteArray? = null - val fileToBeStored = filePart.streamProvider.invoke().readAllBytes() - val contentType = filePart.contentType ?: error("Missing Content-Type!") + multipart.forEachPart { part -> + when (part) { + is PartData.FormItem -> { + if (part.name == "file") + attributesString = part.value + } + + is PartData.FileItem -> { + if (part.name == "file") { + contentType = part.contentType + fileToBeStored = part.provider().readRemaining().readByteArray() + } + } + + else -> {} + } + part.dispose() + } + + val attributes = Json.decodeFromString(attributesString!!) val uploadResult = m.dreamStorageServiceClient.uploadImage( - fileToBeStored, - contentType, + fileToBeStored!!, + contentType!!, UploadImageRequest(false) ) diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostCheckFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostCheckFanArtRoute.kt index 0bf8289..ce596e6 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostCheckFanArtRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostCheckFanArtRoute.kt @@ -1,10 +1,13 @@ package net.perfectdreams.galleryofdreams.backend.routes.api +import io.ktor.http.* import io.ktor.server.application.* import io.ktor.http.content.* import io.ktor.server.request.* +import io.ktor.utils.io.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import kotlinx.io.readByteArray import net.perfectdreams.dreamstorageservice.data.api.CheckImageRequest import net.perfectdreams.dreamstorageservice.data.api.ImageDoesNotExistResponse import net.perfectdreams.dreamstorageservice.data.api.ImageExistsResponse @@ -23,15 +26,33 @@ class PostCheckFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenticatio val response = withContext(Dispatchers.IO) { // Receive the uploaded file val multipart = call.receiveMultipart() - val parts = multipart.readAllParts() - val filePart = parts.first { it.name == "file" } as PartData.FileItem - val fileToBeStored = filePart.streamProvider.invoke().readAllBytes() - val contentType = filePart.contentType ?: error("Missing Content-Type!") + var attributesString: String? = null + var contentType: ContentType? = null + var fileToBeStored: ByteArray? = null + + multipart.forEachPart { part -> + when (part) { + is PartData.FormItem -> { + if (part.name == "file") + attributesString = part.value + } + + is PartData.FileItem -> { + if (part.name == "file") { + contentType = part.contentType + fileToBeStored = part.provider().readRemaining().readByteArray() + } + } + + else -> {} + } + part.dispose() + } val checkResult = m.dreamStorageServiceClient.checkImage( - fileToBeStored, - contentType, + fileToBeStored!!, + contentType!!, CheckImageRequest(false) ) diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostFanArtRoute.kt index 02819e8..ade3959 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostFanArtRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostFanArtRoute.kt @@ -2,14 +2,17 @@ package net.perfectdreams.galleryofdreams.backend.routes.api import club.minnced.discord.webhook.send.AllowedMentions import club.minnced.discord.webhook.send.WebhookMessageBuilder +import io.ktor.http.* import io.ktor.http.content.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.util.* +import io.ktor.utils.io.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.io.readByteArray import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import net.perfectdreams.dreamstorageservice.data.api.CreateImageLinkRequest @@ -41,18 +44,35 @@ class PostFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenticationRout // Receive the uploaded file val multipart = call.receiveMultipart() - val parts = multipart.readAllParts() - val filePart = parts.first { it.name == "file" } as PartData.FileItem - val attributesPart = parts.first { it.name == "attributes" } as PartData.FormItem - val attributes = Json.decodeFromString(attributesPart.value) + var attributesString: String? = null + var contentType: ContentType? = null + var fileToBeStored: ByteArray? = null - val fileToBeStored = filePart.streamProvider.invoke().readAllBytes() - val contentType = filePart.contentType ?: error("Missing Content-Type!") + multipart.forEachPart { part -> + when (part) { + is PartData.FormItem -> { + if (part.name == "file") + attributesString = part.value + } + + is PartData.FileItem -> { + if (part.name == "file") { + contentType = part.contentType + fileToBeStored = part.provider().readRemaining().readByteArray() + } + } + + else -> {} + } + part.dispose() + } + + val attributes = Json.decodeFromString(attributesString!!) val uploadResult = m.dreamStorageServiceClient.uploadImage( - fileToBeStored, - contentType, + fileToBeStored ?: error("Missing fileToBeStored!"), + contentType ?: error("Missing Content-Type!"), UploadImageRequest(false) ) diff --git a/backend/src/main/kotlin/net/perfectdreams/sequins/ktor/BaseRoute.kt b/backend/src/main/kotlin/net/perfectdreams/sequins/ktor/BaseRoute.kt new file mode 100644 index 0000000..21f7be7 --- /dev/null +++ b/backend/src/main/kotlin/net/perfectdreams/sequins/ktor/BaseRoute.kt @@ -0,0 +1,49 @@ +package net.perfectdreams.sequins.ktor + +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.routing.* +import io.ktor.util.pipeline.* + +/** + * A [BaseRoute] is used to register Ktor routes on the [routing] section, allowing you to split up routes in different classes. + * + * One of the advantages of using [BaseRoute] is that all routes are in different files, keeping the code base tidy and nice. + * + * Another advantage is that the HTTP Method is inferred from the class name, so, if the class is named `PostUserDataRoute`, the route will be + * registered as a POST instead of an GET. + * + * All HTTP Methods, except GET, needs to be explictly set in the class name. + * + * @param path the path's route + */ +abstract class BaseRoute(val path: String) { + abstract suspend fun onRequest(call: ApplicationCall) + + fun register(routing: Routing) = registerWithPath(routing, path) { onRequest(call) } + fun registerWithPath(routing: Routing, path: String) = registerWithPath(routing, path) { onRequest(call) } + + fun registerWithPath(routing: Routing, path: String, callback: RoutingHandler) { + val method = getMethod() + when (method) { + HttpMethod.Get -> routing.get(path, callback) + HttpMethod.Post -> routing.post(path, callback) + HttpMethod.Patch -> routing.patch(path, callback) + HttpMethod.Put -> routing.put(path, callback) + HttpMethod.Delete -> routing.delete(path, callback) + else -> routing.get(path, callback) + } + } + + open fun getMethod(): HttpMethod { + val className = this::class.simpleName?.lowercase() ?: "Unknown" + return when { + className.startsWith("get") -> HttpMethod.Get + className.startsWith("post") -> HttpMethod.Post + className.startsWith("patch") -> HttpMethod.Patch + className.startsWith("put") -> HttpMethod.Put + className.startsWith("delete") -> HttpMethod.Delete + else -> HttpMethod.Get + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index b5b1bfc..6128fb8 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -4,6 +4,6 @@ object Versions { const val JIB = "3.2.1" const val KOTLINX_SERIALIZATION = "1.6.0" const val PROMETHEUS = "0.12.0" - const val KTOR = "2.3.3" + const val KTOR = "3.0.0" const val I18N_HELPER = "0.0.5-SNAPSHOT" } \ No newline at end of file