kodeshare/src/main/kotlin/moe/kageru/kodeshare/Routes.kt
2019-09-22 19:54:12 +02:00

92 lines
3.2 KiB
Kotlin

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<PasteRequest> { 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)