Compare commits
4 Commits
074c7b3d56
...
getting-th
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b77f2fa371 | ||
|
|
7a100c96c9 | ||
|
|
ccd5225c3b | ||
|
|
8207d09703 |
@@ -1,5 +1,7 @@
|
||||
plugins {
|
||||
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"
|
||||
@@ -11,6 +13,22 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
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 {
|
||||
@@ -18,4 +36,24 @@ tasks.test {
|
||||
}
|
||||
kotlin {
|
||||
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
|
||||
|
||||
//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"/>.
|
||||
println("i = $i")
|
||||
}
|
||||
|
||||
|
||||
import org.calvin.erfmann.api.apiServ
|
||||
|
||||
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