#4 DB-Integration in Server - Gerüst mit Exposed+HikariCP+Postgres steht
This commit is contained in:
1 parent
60f70ece4a
commit
0e46bf6cf9
14 files changed
+195
-49
No files matched your search
@@ -4,13 +4,13 @@ import net.keinekekse.version.Version
|
||||
|
||||
object VERSION: Version {
|
||||
override val version = "1.0.0"
|
||||
override val fullVersion = "1.0.0+build.221"
|
||||
override val fullVersion = "1.0.0+build.222"
|
||||
override val major = 1
|
||||
override val minor = 0
|
||||
override val patch = 0
|
||||
override val preRelease = ""
|
||||
override val buildMetadata = "build.221"
|
||||
override val buildDate = "2026-02-06T22:31:36.863331Z"
|
||||
override val buildMetadata = "build.222"
|
||||
override val buildDate = "2026-02-07T16:26:28.842311Z"
|
||||
override val displayName = "Books App"
|
||||
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
||||
override val name = "books"
|
||||
|
||||
@@ -8,19 +8,20 @@ composeAppVersion = "1.0.0"
|
||||
androidx-lifecycle = "2.9.6"
|
||||
composeHotReload = "1.0.0"
|
||||
composeMultiplatform = "1.10.0"
|
||||
exposed = "1.0.0"
|
||||
hikari = "7.0.2"
|
||||
junit = "4.13.2"
|
||||
kotlin = "2.3.0"
|
||||
kotlin-wrappers-css = "2025.6.4"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
kotlinx-html = "0.12.0"
|
||||
koin-bom = "4.2.0-RC1"
|
||||
kotlinLogging = "7.0.14"
|
||||
koin-plugin = "0.3.0"
|
||||
ktor = "3.4.0"
|
||||
logback = "1.5.26"
|
||||
kotlinLogging = "7.0.14"
|
||||
logback = "1.5.28"
|
||||
material3 = "1.10.0-alpha05"
|
||||
settings = "1.3.0"
|
||||
dataStore = "1.2.0"
|
||||
postgres = "42.7.9"
|
||||
uiDesktop = "1.7.0"
|
||||
|
||||
[libraries]
|
||||
@@ -55,6 +56,9 @@ ktor-serverHtmlBuilder = { module = "io.ktor:ktor-server-html-builder", version.
|
||||
ktor-serverHtmx = { module = "io.ktor:ktor-server-htmx", version.ref = "ktor" }
|
||||
ktor-htmxHtml = { module = "io.ktor:ktor-htmx-html", version.ref = "ktor" }
|
||||
ktor-serverContentNegotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
|
||||
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common-jvm", version.ref = "ktor" }
|
||||
ktor-server-status-pages = { module = "io.ktor:ktor-server-status-pages-jvm", version.ref = "ktor" }
|
||||
|
||||
ktor-serverTestHost = { module = "io.ktor:ktor-server-test-host-jvm", version.ref = "ktor" }
|
||||
ktor-client = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
||||
ktor-clientAuth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" }
|
||||
@@ -84,10 +88,11 @@ koin-test = { module = "io.insert-koin:koin-test" }
|
||||
#koin-compose-viewmodel-navigation3 = { module = "io.insert-koin:koin-compose-viewmodel-navigation3" }
|
||||
# ----
|
||||
|
||||
# Settings vorerst ohne externe Hilfe
|
||||
#multiplatformSettings = { module = "com.russhwolf:multiplatform-settings", version.ref = "settings" }
|
||||
#androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "dataStore" }
|
||||
#androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "dataStore" }
|
||||
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgres" }
|
||||
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
|
||||
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
|
||||
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
|
||||
hikaricp = { module = "com.zaxxer:HikariCP", version.ref = "hikari" }
|
||||
|
||||
[bundles]
|
||||
kotlin-compose = [
|
||||
|
||||
@@ -31,6 +31,8 @@ dependencies {
|
||||
implementation(libs.ktor.serverAuthJwt)
|
||||
implementation(libs.ktor.serverContentNegotiation)
|
||||
implementation(libs.ktor.serializationJson)
|
||||
implementation(libs.ktor.server.host.common)
|
||||
implementation(libs.ktor.server.status.pages)
|
||||
|
||||
implementation(project.dependencies.platform(libs.koin.bom))
|
||||
implementation(libs.koin.logger.slf4j)
|
||||
@@ -38,6 +40,12 @@ dependencies {
|
||||
implementation(libs.koin.ktor)
|
||||
// implementation("io.insert-koin:koin-logger-slf4j:4.1.2-Beta1")
|
||||
|
||||
implementation(libs.postgresql)
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.hikaricp)
|
||||
|
||||
testImplementation(libs.ktor.serverTestHost)
|
||||
testImplementation(libs.kotlin.testJunit)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#auto generated
|
||||
#Sat Feb 07 20:02:00 CET 2026
|
||||
build.number=27
|
||||
#Sun Feb 08 01:38:22 CET 2026
|
||||
build.number=35
|
||||
@@ -3,9 +3,13 @@ package net.keinekekse.app
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.ktor.server.application.*
|
||||
import net.keinekekse.app.server.VERSION
|
||||
import net.keinekekse.app.server.config.configureDatabase
|
||||
import net.keinekekse.app.server.config.configureKoin
|
||||
import net.keinekekse.app.server.config.configureRouting
|
||||
import net.keinekekse.app.server.config.configureSecurity
|
||||
import net.keinekekse.app.server.testdb.db.BookTable
|
||||
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
@@ -26,4 +30,10 @@ fun Application.module() {
|
||||
configureKoin()
|
||||
configureSecurity()
|
||||
configureRouting()
|
||||
configureDatabase()
|
||||
|
||||
// create if not exists
|
||||
transaction {
|
||||
SchemaUtils.create(BookTable)
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,14 @@ package net.keinekekse.app.server
|
||||
import net.keinekekse.version.Version
|
||||
|
||||
object VERSION: Version {
|
||||
override val version = "0.1.0-beta.1+build.27"
|
||||
override val fullVersion = "0.1.0-beta.1+build.27"
|
||||
override val version = "0.1.0-beta.1+build.35"
|
||||
override val fullVersion = "0.1.0-beta.1+build.35"
|
||||
override val major = 0
|
||||
override val minor = 1
|
||||
override val patch = 0
|
||||
override val preRelease = "beta.1"
|
||||
override val buildMetadata = "build.27"
|
||||
override val buildDate = "2026-02-07T19:02:00.909145Z"
|
||||
override val buildMetadata = "build.35"
|
||||
override val buildDate = "2026-02-08T00:38:22.418116Z"
|
||||
override val displayName = "kAS"
|
||||
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
||||
override val name = "server"
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.keinekekse.app.server.config
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import io.ktor.server.application.*
|
||||
import org.jetbrains.exposed.v1.core.DatabaseConfig
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
|
||||
fun Application.configureDatabase() {
|
||||
|
||||
val config = HikariConfig().apply {
|
||||
// jdbcUrl = "jdbc:mysql://$host:$port/$db"
|
||||
dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
|
||||
username = getKeyFromConfiguration("postgres.user")
|
||||
password = getKeyFromConfiguration("postgres.password")
|
||||
|
||||
addDataSourceProperty("serverName", getKeyFromConfiguration("postgres.host", "localhost"))
|
||||
addDataSourceProperty("portNumber", getKeyFromConfiguration("postgres.port", "5432"))
|
||||
addDataSourceProperty(
|
||||
"databaseName",
|
||||
getKeyFromConfiguration("postgres.database", username)
|
||||
)
|
||||
|
||||
maximumPoolSize = 6
|
||||
isReadOnly = false
|
||||
transactionIsolation = "TRANSACTION_SERIALIZABLE"
|
||||
}
|
||||
|
||||
val dataSource = HikariDataSource(config)
|
||||
|
||||
Database.connect(
|
||||
datasource = dataSource,
|
||||
databaseConfig = DatabaseConfig {
|
||||
// set other parameters here
|
||||
}
|
||||
)
|
||||
|
||||
// Class.forName("org.postgresql.Driver")
|
||||
// val host = getKeyFromConfiguration("postgres.host", "localhost")
|
||||
// val port = getKeyFromConfiguration("postgres.port", "5432")
|
||||
// val user = getKeyFromConfiguration("postgres.user")
|
||||
// val password = getKeyFromConfiguration("postgres.password")
|
||||
// val database = getKeyFromConfiguration("postgres.database", user)
|
||||
//
|
||||
// // "jdbc:postgresql://host:5432/database",
|
||||
// return DriverManager.getConnection("jdbc:postgresql://$host:$port/$database", user, password)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.keinekekse.app.server.config
|
||||
|
||||
import io.ktor.server.application.*
|
||||
|
||||
fun Application.getKeyFromConfiguration(key: String, default: String? = null): String {
|
||||
val value = environment.config.propertyOrNull(key)?.getString() ?: default
|
||||
// ohne die Konfig-Params geht nix, hier ist nur noch Abbruch möglich
|
||||
return value
|
||||
?: throw IllegalStateException("Configuration key '$key' not found, server start aborted.")
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.auth.jwt.*
|
||||
import io.ktor.server.config.*
|
||||
import io.ktor.server.response.*
|
||||
import java.net.URI
|
||||
|
||||
@@ -14,25 +13,13 @@ private val logger = KotlinLogging.logger {}
|
||||
|
||||
fun Application.configureSecurity() {
|
||||
|
||||
fun getFromConfig(key: String): String {
|
||||
try {
|
||||
return environment.config.property("ktor.conf.$key").getString()
|
||||
} catch (e: ApplicationConfigurationException) {
|
||||
// ohne die Konfig-Params geht nix (und defaults sind hier sinnlos), hier ist nur noch Abbruch möglich
|
||||
throw IllegalStateException(
|
||||
"Configuration key '$key' not found, server start aborted.",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Kommunikation funktioniert nur im internen Containernetzwerk,
|
||||
// d.h. Container- bzw. Pod-Name + interner Port
|
||||
val keycloakInternalUrl = getFromConfig("keycloakInternalUrl")
|
||||
val keycloakUrl = getFromConfig("keycloakUrl")
|
||||
val keycloakRealm = getFromConfig("keycloakRealm")
|
||||
val keycloakClientId = getFromConfig("keycloakClientId")
|
||||
val keycloakCertUrl = getFromConfig("keycloakCertUrl")
|
||||
val keycloakInternalUrl = getKeyFromConfiguration("keycloak.internalUrl")
|
||||
val keycloakUrl = getKeyFromConfiguration("keycloak.url")
|
||||
val keycloakRealm = getKeyFromConfiguration("keycloak.realm")
|
||||
val keycloakClientId = getKeyFromConfiguration("keycloak.clientId")
|
||||
val keycloakCertUrl = getKeyFromConfiguration("keycloak.certUrl")
|
||||
|
||||
val certUrl = "$keycloakInternalUrl/$keycloakRealm/$keycloakCertUrl"
|
||||
val issuer = "$keycloakUrl/$keycloakRealm"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.keinekekse.app.server.testdb.db
|
||||
|
||||
import net.keinekekse.app.server.testdb.model.Book
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.LongIdTable
|
||||
import org.jetbrains.exposed.v1.dao.LongEntity
|
||||
import org.jetbrains.exposed.v1.dao.LongEntityClass
|
||||
|
||||
object BookTable : LongIdTable("book") {
|
||||
var title = varchar("title", 50)
|
||||
var author = varchar("author", 50)
|
||||
var series = varchar("series", 50)
|
||||
var seriesIndex = integer("seriesIndex")
|
||||
}
|
||||
|
||||
class BookDAO(id: EntityID<Long>) : LongEntity(id) {
|
||||
companion object : LongEntityClass<BookDAO>(BookTable)
|
||||
|
||||
var title by BookTable.title
|
||||
var author by BookTable.author
|
||||
var series by BookTable.series
|
||||
var seriesIndex by BookTable.seriesIndex
|
||||
}
|
||||
|
||||
fun daoToModel(dao: BookDAO) = Book(dao.title, dao.author, dao.series, dao.seriesIndex)
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.keinekekse.app.server.testdb.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Book(
|
||||
var title: String = "",
|
||||
var author: String = "",
|
||||
var series: String = "",
|
||||
var seriesIndex: Int = 0,
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.keinekekse.app.server.testdb.model
|
||||
|
||||
interface BookRepository {
|
||||
suspend fun allBooks(): List<Book>
|
||||
suspend fun bookById(id: Long): Book?
|
||||
suspend fun booksBySeries(series: String): List<Book>
|
||||
suspend fun bookByTitle(title: String): List<Book>
|
||||
suspend fun bookByAuthor(author: String): List<Book>
|
||||
suspend fun addTBook(book: Book)
|
||||
suspend fun removeBook(id: Long): Boolean
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.keinekekse.app.server.testdb.model
|
||||
|
||||
class BookRepositoryImpl : BookRepository {
|
||||
override suspend fun allBooks(): List<Book> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun bookById(id: Long): Book? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun booksBySeries(series: String): List<Book> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun bookByTitle(title: String): List<Book> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun bookByAuthor(author: String): List<Book> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun addTBook(book: Book) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun removeBook(id: Long): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,19 @@ ktor {
|
||||
# ApplicationKt.module() übernimmt die Konfiguration
|
||||
modules = [ net.keinekekse.app.ApplicationKt.module ]
|
||||
}
|
||||
conf {
|
||||
# eigene Parameter -> ktor.conf.<name>
|
||||
#
|
||||
keycloakInternalUrl = "http://kec:8080/realms"
|
||||
keycloakUrl = "https://kec.keinekekse.net/realms"
|
||||
keycloakRealm = "app-server-realm"
|
||||
keycloakClientId = "pkce-book-app"
|
||||
keycloakCertUrl = "protocol/openid-connect/certs"
|
||||
# DB-Params werden vom Container als env vars gesetzt
|
||||
dbHost = ${DB_HOST}
|
||||
dbPort = ${DB_PORT}
|
||||
dbUser = ${DB_USER}
|
||||
dbPassword = ${DB_PASSWORD}
|
||||
}
|
||||
}
|
||||
keycloak {
|
||||
internalUrl = "http://kec:8080/realms"
|
||||
url = "https://kec.keinekekse.net/realms"
|
||||
realm = "app-server-realm"
|
||||
clientId = "pkce-book-app"
|
||||
certUrl = "protocol/openid-connect/certs"
|
||||
}
|
||||
postgres {
|
||||
# DB-Params werden vom Container als env vars gesetzt
|
||||
host = ${DB_HOST}
|
||||
port = ${DB_PORT}
|
||||
database = ${DB_DATABASE}
|
||||
user = ${DB_USER}
|
||||
password = ${DB_PASSWORD}
|
||||
}
|
||||
Reference in new issue
Block a user