package moe.kageru.kodeshare import io.ktor.application.ApplicationCall import io.ktor.application.call import io.ktor.http.ContentType import io.ktor.http.HttpHeaders import io.ktor.http.HttpStatusCode import io.ktor.http.content.PartData import io.ktor.http.content.forEachPart import io.ktor.http.content.streamProvider import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.Location import io.ktor.locations.get import io.ktor.request.receiveMultipart import io.ktor.response.respond import io.ktor.response.respondRedirect import io.ktor.response.respondText import io.ktor.routing.Routing import io.ktor.routing.get import io.ktor.routing.post import moe.kageru.kodeshare.pages.Css import moe.kageru.kodeshare.pages.Homepage import moe.kageru.kodeshare.persistence.PasteDao @KtorExperimentalLocationsAPI @ExperimentalStdlibApi object Routes { fun Routing.createRoutes() { get("/") { call.respond(Homepage.content) } get("/style.css") { call.respondText(Css.default, ContentType.Text.CSS) } post("/") { call.handlePost() } get { req -> call.handleGet(req) } } @ExperimentalStdlibApi private suspend fun ApplicationCall.handlePost() { receiveMultipart().forEachPart { part -> when (part) { is PartData.FileItem -> { val content = part.streamProvider().use { it.readBytes().decodeToString() } respondToUpload(content)?.let { id -> Log.info("Saving new file paste with ID $id") } ?: Log.info("Invalid upload: $content") } is PartData.FormItem -> { val content = part.value respondToUpload(content)?.let { id -> Log.info("Saving new text paste with ID $id") } ?: Log.info("Invalid upload: $content") } is PartData.BinaryItem -> { Log.warn("Received binary item from upload form. This shouldn’t happen.") } } } } private suspend fun ApplicationCall.respondToUpload(content: String): Long? { content.ifBlank { respondText("Empty pastes are not allowed", ContentType.Text.Any, HttpStatusCode.BadRequest) return null } val id = PasteDao.insert(Paste(content, null)).id // This will show the URL in a terminal when uploading via curl, // while also redirecting browser uploads to the newly created paste. // May seem odd to return code 302, but it seems to be the only way. response.headers.append(HttpHeaders.Location, "$id") respond(HttpStatusCode.Found, "$id") return id } private suspend fun ApplicationCall.handleGet(req: PasteRequest) { val id = req.id Log.info("Retrieving paste $id") PasteDao.select(id)?.data?.let { paste -> respondText(paste.html ?: paste.content, ContentType.Text.Plain) } ?: respond(HttpStatusCode.NotFound, "nothing found for id $id") } } @KtorExperimentalLocationsAPI @Location("/{id}") data class PasteRequest(val id: Long)