Initial commit

This commit is contained in:
kageru 2019-09-19 22:04:23 +02:00
commit e80c97e450
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
8 changed files with 211 additions and 0 deletions

3
.gitignore vendored Normal file
View File

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

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

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

2
settings.gradle Normal file
View File

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

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?)

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"
}
}

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)

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)