Initial commit
This commit is contained in:
commit
e80c97e450
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.gradle/
|
||||
build/
|
||||
kodeshare.log
|
32
build.gradle.kts
Normal file
32
build.gradle.kts
Normal file
@ -0,0 +1,32 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.3.50"
|
||||
application
|
||||
}
|
||||
|
||||
group = "moe.kageru"
|
||||
version = "0.1.0"
|
||||
|
||||
val mainClass = "moe.kageru.kodeshare.KodeshareKt"
|
||||
application {
|
||||
mainClassName = mainClass
|
||||
}
|
||||
val ktorVersion = "1.2.4"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
||||
implementation("io.ktor:ktor-locations:$ktorVersion")
|
||||
implementation("org.jetbrains.exposed:exposed:0.17.3")
|
||||
implementation("org.mariadb.jdbc:mariadb-java-client:2.4.4")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@ -0,0 +1 @@
|
||||
kotlin.code.style=official
|
2
settings.gradle
Normal file
2
settings.gradle
Normal file
@ -0,0 +1,2 @@
|
||||
rootProject.name = 'kodeshare'
|
||||
|
22
src/main/kotlin/moe/kageru/kodeshare/Kodeshare.kt
Normal file
22
src/main/kotlin/moe/kageru/kodeshare/Kodeshare.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package moe.kageru.kodeshare
|
||||
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.DefaultHeaders
|
||||
import io.ktor.locations.Locations
|
||||
import io.ktor.routing.routing
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.netty.Netty
|
||||
import moe.kageru.kodeshare.Routes.createRoutes
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
fun main() {
|
||||
embeddedServer(Netty, 9092) {
|
||||
install(DefaultHeaders)
|
||||
install(Locations)
|
||||
routing {
|
||||
createRoutes()
|
||||
}
|
||||
}.start(wait = true)
|
||||
}
|
||||
|
||||
data class Paste(val content: String, val html: String?)
|
32
src/main/kotlin/moe/kageru/kodeshare/Log.kt
Normal file
32
src/main/kotlin/moe/kageru/kodeshare/Log.kt
Normal file
@ -0,0 +1,32 @@
|
||||
package moe.kageru.kodeshare
|
||||
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.logging.FileHandler
|
||||
import java.util.logging.Formatter
|
||||
import java.util.logging.LogRecord
|
||||
import java.util.logging.Logger
|
||||
|
||||
object Log {
|
||||
private val log: Logger by lazy {
|
||||
val log = Logger.getGlobal()
|
||||
val fh = FileHandler("kodeshare.log", true)
|
||||
val formatter = LogFormatter()
|
||||
fh.formatter = formatter
|
||||
log.addHandler(fh)
|
||||
return@lazy log
|
||||
}
|
||||
|
||||
fun info(message: String) {
|
||||
log.info(message)
|
||||
}
|
||||
}
|
||||
|
||||
private class LogFormatter : Formatter() {
|
||||
private val timeFormatter: DateTimeFormatter =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault())
|
||||
|
||||
override fun format(record: LogRecord): String {
|
||||
return "[${record.level}] ${timeFormatter.format(record.instant)}: ${record.message}\n"
|
||||
}
|
||||
}
|
65
src/main/kotlin/moe/kageru/kodeshare/Routes.kt
Normal file
65
src/main/kotlin/moe/kageru/kodeshare/Routes.kt
Normal file
@ -0,0 +1,65 @@
|
||||
package moe.kageru.kodeshare
|
||||
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.ContentType
|
||||
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.Location
|
||||
import io.ktor.locations.get
|
||||
import io.ktor.request.receiveMultipart
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.routing.get
|
||||
import io.ktor.routing.post
|
||||
import moe.kageru.kodeshare.persistence.PasteDao
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
object Routes {
|
||||
fun Routing.createRoutes() {
|
||||
get("/") {
|
||||
call.respondText("Hello, world!", ContentType.Text.Html)
|
||||
}
|
||||
post("/") {
|
||||
save(call)
|
||||
}
|
||||
get<PasteRequest> { req ->
|
||||
respondGet(call, req)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
suspend fun save(call: ApplicationCall) {
|
||||
call.receiveMultipart().forEachPart { part ->
|
||||
when(part) {
|
||||
is PartData.FileItem -> {
|
||||
Log.info("new file")
|
||||
val content = part.streamProvider().use { it.readAllBytes().decodeToString() }
|
||||
val id = PasteDao.insert(Paste(content = content, html = null)).id
|
||||
call.respond(HttpStatusCode.Created, "$id")
|
||||
Log.info("Saving new paste with ID $id")
|
||||
}
|
||||
is PartData.FormItem -> {
|
||||
Log.info("form item")
|
||||
}
|
||||
is PartData.BinaryItem -> {
|
||||
Log.info("binary item")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun respondGet(call: ApplicationCall, req: PasteRequest) {
|
||||
val id = req.id
|
||||
Log.info("Retrieving paste $id")
|
||||
PasteDao.select(id)?.data?.let { paste ->
|
||||
call.respondText(paste.html ?: paste.content, ContentType.Text.Html)
|
||||
} ?: call.respond(HttpStatusCode.NotFound, "nothing found for id $id")
|
||||
}
|
||||
|
||||
@Location("/{id}")
|
||||
data class PasteRequest(val id: Long)
|
54
src/main/kotlin/moe/kageru/kodeshare/persistence/PasteDao.kt
Normal file
54
src/main/kotlin/moe/kageru/kodeshare/persistence/PasteDao.kt
Normal file
@ -0,0 +1,54 @@
|
||||
package moe.kageru.kodeshare.persistence
|
||||
|
||||
import moe.kageru.kodeshare.Paste
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.mariadb.jdbc.MariaDbDataSource
|
||||
|
||||
object PasteDao {
|
||||
fun select(id: Long): Transient<Paste>? = transaction {
|
||||
PasteTable.select { PasteTable.id.eq(id) }.firstOrNull()
|
||||
}?.toPaste()
|
||||
|
||||
fun insert(paste: Paste): Transient<Paste> = transaction {
|
||||
val id = PasteTable.insert {
|
||||
it[content] = paste.content
|
||||
it[html] = paste.html
|
||||
}[PasteTable.id]
|
||||
Transient(id, paste)
|
||||
}
|
||||
|
||||
init {
|
||||
val source = MariaDbDataSource().apply {
|
||||
userName = "kodepaste"
|
||||
setPassword("12345")
|
||||
databaseName = "kode"
|
||||
}
|
||||
Database.connect(source)
|
||||
transaction {
|
||||
SchemaUtils.drop(PasteTable)
|
||||
SchemaUtils.create(PasteTable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResultRow.toPaste() = Transient(
|
||||
get(PasteTable.id),
|
||||
Paste(
|
||||
content = get(PasteTable.content),
|
||||
html = get(PasteTable.html)
|
||||
)
|
||||
)
|
||||
|
||||
private object PasteTable : Table() {
|
||||
val id = long("id").primaryKey().autoIncrement()
|
||||
val content = text("content")
|
||||
val html = text("html").nullable()
|
||||
}
|
||||
|
||||
/*
|
||||
* Better have that one generic class
|
||||
* after deciding to make everything else
|
||||
* not generic because it would be overkill
|
||||
*/
|
||||
data class Transient<DATA>(val id: Long, val data: DATA)
|
Loading…
Reference in New Issue
Block a user