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 )