kodeshare/src/main/kotlin/moe/kageru/kodeshare/Routes.kt

92 lines
3.2 KiB
Kotlin
Raw Normal View History

2019-09-19 22:04:23 +02:00
package moe.kageru.kodeshare
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.http.ContentType
2019-09-22 18:26:28 +02:00
import io.ktor.http.HttpHeaders
2019-09-19 22:04:23 +02:00
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.PartData
import io.ktor.http.content.forEachPart
import io.ktor.http.content.streamProvider
2019-09-22 18:26:28 +02:00
import io.ktor.locations.KtorExperimentalLocationsAPI
2019-09-19 22:04:23 +02:00
import io.ktor.locations.Location
import io.ktor.locations.get
import io.ktor.request.receiveMultipart
import io.ktor.response.respond
2019-09-22 18:26:28 +02:00
import io.ktor.response.respondRedirect
2019-09-19 22:04:23 +02:00
import io.ktor.response.respondText
import io.ktor.routing.Routing
import io.ktor.routing.get
import io.ktor.routing.post
2019-09-22 18:26:28 +02:00
import moe.kageru.kodeshare.pages.Css
import moe.kageru.kodeshare.pages.Homepage
2019-09-19 22:04:23 +02:00
import moe.kageru.kodeshare.persistence.PasteDao
2019-09-22 18:26:28 +02:00
@KtorExperimentalLocationsAPI
2019-09-19 22:04:23 +02:00
@ExperimentalStdlibApi
object Routes {
fun Routing.createRoutes() {
get("/") {
2019-09-22 18:26:28 +02:00
call.respond(Homepage.content)
}
get("/style.css") {
call.respondText(Css.default, ContentType.Text.CSS)
2019-09-19 22:04:23 +02:00
}
post("/") {
2019-09-22 18:26:28 +02:00
call.handlePost()
2019-09-19 22:04:23 +02:00
}
get<PasteRequest> { req ->
2019-09-22 18:26:28 +02:00
call.handleGet(req)
2019-09-19 22:04:23 +02:00
}
}
2019-09-22 18:26:28 +02:00
@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.")
}
2019-09-19 22:04:23 +02:00
}
}
}
2019-09-22 18:26:28 +02:00
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")
}
2019-09-19 22:04:23 +02:00
}
2019-09-22 18:26:28 +02:00
@KtorExperimentalLocationsAPI
2019-09-19 22:04:23 +02:00
@Location("/{id}")
2019-09-22 18:26:28 +02:00
data class PasteRequest(val id: Long)