Compare commits
4 Commits
074c7b3d56
...
getting-th
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b77f2fa371 | ||
|
|
7a100c96c9 | ||
|
|
ccd5225c3b | ||
|
|
8207d09703 |
@@ -1,5 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.1.20"
|
kotlin("jvm") version "2.1.20"
|
||||||
|
id("io.ktor.plugin") version "2.3.4"
|
||||||
|
kotlin("plugin.serialization") version "1.9.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "org.calvin.erfmann"
|
group = "org.calvin.erfmann"
|
||||||
@@ -11,6 +13,22 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
|
|
||||||
|
// Ktor server dependencies
|
||||||
|
implementation("io.ktor:ktor-server-netty:2.3.4")
|
||||||
|
implementation("io.ktor:ktor-server-core:2.3.4")
|
||||||
|
implementation("io.ktor:ktor-server-content-negotiation:2.3.4")
|
||||||
|
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.4")
|
||||||
|
|
||||||
|
//Logging
|
||||||
|
implementation("ch.qos.logback:logback-classic:1.5.13")
|
||||||
|
|
||||||
|
//Ktor Websockets and CORS
|
||||||
|
implementation("io.ktor:ktor-server-websockets:2.3.4")
|
||||||
|
implementation("io.ktor:ktor-server-cors:2.3.4")
|
||||||
|
implementation("io.ktor:ktor-client-core:2.3.4")
|
||||||
|
implementation("io.ktor:ktor-client-cio:2.3.4")
|
||||||
|
implementation("io.ktor:ktor-client-content-negotiation:2.3.4")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
@@ -19,3 +37,23 @@ tasks.test {
|
|||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(23)
|
jvmToolchain(23)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register<Jar>("fatJar") {
|
||||||
|
group = "build"
|
||||||
|
description = "Assemble a fat jar with all dependencies."
|
||||||
|
|
||||||
|
archiveClassifier.set("all")
|
||||||
|
|
||||||
|
manifest {
|
||||||
|
attributes["Main-Class"] = "MainKt" // ggf. anpassen auf dein Main Package
|
||||||
|
}
|
||||||
|
|
||||||
|
from(sourceSets.main.get().output)
|
||||||
|
|
||||||
|
dependsOn(configurations.runtimeClasspath)
|
||||||
|
from({
|
||||||
|
configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
|
||||||
|
})
|
||||||
|
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package org.calvin.erfmann
|
package org.calvin.erfmann
|
||||||
|
|
||||||
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
|
|
||||||
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
|
|
||||||
fun main() {
|
|
||||||
val name = "Kotlin"
|
|
||||||
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
|
|
||||||
// to see how IntelliJ IDEA suggests fixing it.
|
|
||||||
println("Hello, " + name + "!")
|
|
||||||
|
|
||||||
for (i in 1..5) {
|
|
||||||
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
|
||||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
|
import org.calvin.erfmann.api.apiServ
|
||||||
println("i = $i")
|
|
||||||
}
|
val apiServ = apiServ()
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
apiServ.startServer()
|
||||||
|
|
||||||
}
|
}
|
||||||
55
Server/FastKeyValueServer/src/main/kotlin/api/apiServ.kt
Normal file
55
Server/FastKeyValueServer/src/main/kotlin/api/apiServ.kt
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package org.calvin.erfmann.api
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.HttpClientConfig
|
||||||
|
import io.ktor.client.engine.cio.CIO
|
||||||
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
|
import io.ktor.server.application.Application
|
||||||
|
import io.ktor.server.application.install
|
||||||
|
import io.ktor.server.engine.embeddedServer
|
||||||
|
import io.ktor.server.netty.Netty
|
||||||
|
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.server.websocket.WebSockets
|
||||||
|
import io.ktor.server.websocket.pingPeriod
|
||||||
|
import io.ktor.server.websocket.timeout
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.calvin.erfmann.api.plugins.configureRouting
|
||||||
|
import org.calvin.erfmann.stuff.authService
|
||||||
|
import org.calvin.erfmann.theGoodStuff.PoolManager
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
class apiServ {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var authService = authService("password")
|
||||||
|
var poolManager = PoolManager()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun startServer(){
|
||||||
|
embeddedServer(Netty, port = 9000, host = "0.0.0.0") {
|
||||||
|
module()
|
||||||
|
}.start(wait = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Application.module() {
|
||||||
|
install(ContentNegotiation) { // SERVER Plugin mit Alias
|
||||||
|
json()
|
||||||
|
}
|
||||||
|
install(WebSockets.Plugin) {
|
||||||
|
pingPeriod = Duration.ofSeconds(15)
|
||||||
|
timeout = Duration.ofSeconds(30)
|
||||||
|
maxFrameSize = Long.MAX_VALUE
|
||||||
|
masking = false
|
||||||
|
}
|
||||||
|
configureRouting(
|
||||||
|
authService,
|
||||||
|
poolManager
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.calvin.erfmann.api.plugins
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.engine.cio.CIO
|
||||||
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
|
import io.ktor.server.application.Application
|
||||||
|
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.server.routing.routing
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.calvin.erfmann.api.routes.getValue
|
||||||
|
import org.calvin.erfmann.api.routes.helloRoutes
|
||||||
|
import org.calvin.erfmann.api.routes.login
|
||||||
|
import org.calvin.erfmann.api.routes.patchValue
|
||||||
|
import org.calvin.erfmann.api.routes.setValue
|
||||||
|
import org.calvin.erfmann.stuff.authService
|
||||||
|
import org.calvin.erfmann.theGoodStuff.PoolManager
|
||||||
|
|
||||||
|
fun Application.configureRouting(authService: authService, poolManager: PoolManager) {
|
||||||
|
routing {
|
||||||
|
helloRoutes()
|
||||||
|
login(authService)
|
||||||
|
getValue(authService, poolManager)
|
||||||
|
setValue(authService, poolManager)
|
||||||
|
patchValue(authService, poolManager)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package org.calvin.erfmann.api.routes
|
||||||
|
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.request.receive
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.post
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.calvin.erfmann.stuff.authService
|
||||||
|
import org.calvin.erfmann.theGoodStuff.PoolManager
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GetValueRequest(val token: String, val pool: String, val key: String,)
|
||||||
|
|
||||||
|
|
||||||
|
fun Route.getValue(authService: authService, poolManager: PoolManager) {
|
||||||
|
|
||||||
|
get("/value") {
|
||||||
|
val request = call.receive<GetValueRequest>()
|
||||||
|
|
||||||
|
val isValid = authService.isTokenValid(request.token)
|
||||||
|
if (isValid) {
|
||||||
|
val pool = poolManager.getPool(request.pool)
|
||||||
|
if (pool != null) {
|
||||||
|
val value = pool.getValueValue(request.key)
|
||||||
|
if (value != null) {
|
||||||
|
call.respond(mapOf("value" to value))
|
||||||
|
} else {
|
||||||
|
call.respond(mapOf("error" to "Key not found"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call.respond(mapOf("error" to "Pool not found"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call.respond(mapOf("error" to "Invalid token"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.calvin.erfmann.api.routes
|
||||||
|
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun Route.helloRoutes() {
|
||||||
|
route("/hello") {
|
||||||
|
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
|
call.respond(mapOf("message" to "hallo du knecht"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.calvin.erfmann.api.routes
|
||||||
|
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.request.receive
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.post
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.calvin.erfmann.stuff.authService
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LoginRequest(val password: String)
|
||||||
|
|
||||||
|
|
||||||
|
fun Route.login(authService: authService) {
|
||||||
|
|
||||||
|
post("/login") {
|
||||||
|
val request = call.receive<LoginRequest>()
|
||||||
|
|
||||||
|
val token = authService.getToken(request.password)
|
||||||
|
|
||||||
|
if (token != null) {
|
||||||
|
call.respond(mapOf("token" to token))
|
||||||
|
} else {
|
||||||
|
call.respond(mapOf("error" to "Invalid password"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package org.calvin.erfmann.api.routes
|
||||||
|
|
||||||
|
import io.ktor.server.routing.patch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.request.receive
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.post
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.calvin.erfmann.stuff.authService
|
||||||
|
import org.calvin.erfmann.theGoodStuff.PoolManager
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PatchValueRequest(val token: String, val pool: String, val key: String, val value: String)
|
||||||
|
|
||||||
|
|
||||||
|
fun Route.patchValue(authService: authService, poolManager: PoolManager) {
|
||||||
|
|
||||||
|
patch("/value") {
|
||||||
|
val request = call.receive<SetValueRequest>()
|
||||||
|
|
||||||
|
val isValid = authService.isTokenValid(request.token)
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
val pool = poolManager.getPool(request.pool)
|
||||||
|
if (pool != null) {
|
||||||
|
pool.setValueValue(request.key, request.value)
|
||||||
|
call.respond(mapOf("status" to "success"))
|
||||||
|
} else {
|
||||||
|
call.respond(mapOf("error" to "Pool not found"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call.respond(mapOf("error" to "Invalid token"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.calvin.erfmann.api.routes
|
||||||
|
|
||||||
|
import io.ktor.server.application.call
|
||||||
|
import io.ktor.server.request.receive
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.post
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.calvin.erfmann.stuff.authService
|
||||||
|
import org.calvin.erfmann.theGoodStuff.PoolManager
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SetValueRequest(val token: String, val pool: String, val key: String, val value: String)
|
||||||
|
|
||||||
|
|
||||||
|
fun Route.setValue(authService: authService, poolManager: PoolManager) {
|
||||||
|
|
||||||
|
post("/value") {
|
||||||
|
val request = call.receive<SetValueRequest>()
|
||||||
|
|
||||||
|
val isValid = authService.isTokenValid(request.token)
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
val pool = poolManager.getPool(request.pool)
|
||||||
|
if (pool != null) {
|
||||||
|
pool.setValueValue(request.key, request.value)
|
||||||
|
call.respond(mapOf("status" to "success"))
|
||||||
|
} else {
|
||||||
|
poolManager.createPool(request.pool).setValueValue(request.key, request.value)
|
||||||
|
call.respond(mapOf("status" to "success"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call.respond(mapOf("error" to "Invalid token"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.calvin.erfmann.api.utils
|
||||||
|
|
||||||
|
import java.security.SecureRandom
|
||||||
|
|
||||||
|
class tokenGenerator {
|
||||||
|
private val CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
private val secureRandom = SecureRandom()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generiert einen sicheren Token.
|
||||||
|
* @param length Standardmäßig 32 Zeichen, kann aber angepasst werden.
|
||||||
|
*/
|
||||||
|
fun generate(length: Int = 32): String {
|
||||||
|
return (1..length)
|
||||||
|
.map { CHARACTERS[secureRandom.nextInt(CHARACTERS.length)] }
|
||||||
|
.joinToString("")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.calvin.erfmann.stuff
|
||||||
|
|
||||||
|
import org.calvin.erfmann.api.utils.tokenGenerator
|
||||||
|
|
||||||
|
class authService(password: String) {
|
||||||
|
val password: String = password
|
||||||
|
|
||||||
|
var activeTokens = mutableListOf<String>()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun getToken(inputPassword: String): String?{
|
||||||
|
if (inputPassword != password){
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val token = tokenGenerator().generate()
|
||||||
|
activeTokens.add(token)
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isTokenValid(token: String): Boolean{
|
||||||
|
return activeTokens.contains(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.calvin.erfmann.theGoodStuff
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Pool(initName: String) {
|
||||||
|
|
||||||
|
var name: String = initName
|
||||||
|
var values: MutableMap<String, Value> = mutableMapOf()
|
||||||
|
|
||||||
|
fun getValueValue(key: String): String? {
|
||||||
|
val value = values[key]
|
||||||
|
return value?.getValueValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValueValue(key: String, newValue: String) {
|
||||||
|
|
||||||
|
|
||||||
|
val value = values[key]
|
||||||
|
if (value != null) {
|
||||||
|
value.setValueValue(newValue)
|
||||||
|
} else {
|
||||||
|
values[key] = Value(key, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateValueValue(key: String, newValue: String) {
|
||||||
|
val value = values[key]
|
||||||
|
if (value != null) {
|
||||||
|
value.setValueValue(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVersion(key: String, version: Long): String? {
|
||||||
|
val value = values[key]
|
||||||
|
return value?.getVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteValue(key: String) {
|
||||||
|
values.remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isKeyPresent(key: String): Boolean {
|
||||||
|
return values.containsKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.calvin.erfmann.theGoodStuff
|
||||||
|
|
||||||
|
class PoolManager{
|
||||||
|
var pools: MutableMap<String, Pool> = mutableMapOf()
|
||||||
|
|
||||||
|
fun getPool(poolName: String): Pool? {
|
||||||
|
return pools[poolName]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPool(poolName: String): Pool {
|
||||||
|
val pool = Pool(poolName)
|
||||||
|
pools[poolName] = pool
|
||||||
|
return pool
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deletePool(poolName: String) {
|
||||||
|
pools.remove(poolName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPoolPresent(poolName: String): Boolean {
|
||||||
|
return pools.containsKey(poolName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.calvin.erfmann.theGoodStuff
|
||||||
|
|
||||||
|
class Value(initKey: String, initValue: String) {
|
||||||
|
|
||||||
|
// Das einfachste Value Element
|
||||||
|
|
||||||
|
|
||||||
|
var key: String = initKey
|
||||||
|
var value: String = initValue
|
||||||
|
var lastUpdated: Long = System.currentTimeMillis()
|
||||||
|
var createdAt: Long = System.currentTimeMillis()
|
||||||
|
var lastAcccessed: Long = System.currentTimeMillis()
|
||||||
|
var currentVersion: Long = 0
|
||||||
|
var versions = mutableListOf<ValueVersion>()
|
||||||
|
|
||||||
|
fun getValueValue(): String {
|
||||||
|
this.lastAcccessed = System.currentTimeMillis()
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCurrentValueVersion(): Long {
|
||||||
|
return this.currentVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setValueValue(newValue: String) {
|
||||||
|
versions.add(ValueVersion(this.value , this.currentVersion))
|
||||||
|
this.value = newValue
|
||||||
|
this.lastUpdated = System.currentTimeMillis()
|
||||||
|
this.currentVersion += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVersion(version: Long): String? {
|
||||||
|
val ver = versions.find { it.version == version }
|
||||||
|
return ver?.getValueValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.calvin.erfmann.theGoodStuff
|
||||||
|
|
||||||
|
class ValueVersion(initValue: String, initVersion: Long) {
|
||||||
|
|
||||||
|
// Eine Version eines Values
|
||||||
|
|
||||||
|
var value: String = initValue
|
||||||
|
var lastAcccessed: Long = 1
|
||||||
|
var version : Long = initVersion
|
||||||
|
|
||||||
|
fun getValueValue(): String {
|
||||||
|
this.lastAcccessed = System.currentTimeMillis()
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user