Browse Source

Initial commit

master
kageru 1 year ago
commit
e80c97e450
Signed by: kageru <kageru@encode.moe> GPG Key ID: 8282A2BEA4ADA3D2
8 changed files with 211 additions and 0 deletions
  1. +3
    -0
      .gitignore
  2. +32
    -0
      build.gradle.kts
  3. +1
    -0
      gradle.properties
  4. +2
    -0
      settings.gradle
  5. +22
    -0
      src/main/kotlin/moe/kageru/kodeshare/Kodeshare.kt
  6. +32
    -0
      src/main/kotlin/moe/kageru/kodeshare/Log.kt
  7. +65
    -0
      src/main/kotlin/moe/kageru/kodeshare/Routes.kt
  8. +54
    -0
      src/main/kotlin/moe/kageru/kodeshare/persistence/PasteDao.kt

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
.gradle/
build/
kodeshare.log

+ 32
- 0
build.gradle.kts View 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
- 0
gradle.properties View File

@@ -0,0 +1 @@
kotlin.code.style=official

+ 2
- 0
settings.gradle View File

@@ -0,0 +1,2 @@
rootProject.name = 'kodeshare'


+ 22
- 0
src/main/kotlin/moe/kageru/kodeshare/Kodeshare.kt View 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
- 0
src/main/kotlin/moe/kageru/kodeshare/Log.kt View 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
- 0
src/main/kotlin/moe/kageru/kodeshare/Routes.kt View 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
- 0
src/main/kotlin/moe/kageru/kodeshare/persistence/PasteDao.kt View 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…
Cancel
Save