From bf7119f580989ca98355c9b3866bd5d217db90f3 Mon Sep 17 00:00:00 2001 From: Hartmut Bauer Date: Wed, 11 Feb 2026 01:58:57 +0100 Subject: [PATCH] #6 Tabellen-Definitionen erweitert, weiterer API-call --- server/buildnumber.properties | 4 +- .../net/keinekekse/app/server/Application.kt | 158 ++++++++++-------- .../net/keinekekse/app/server/Version.kt | 8 +- .../keinekekse/app/server/books/booksRoute.kt | 5 +- .../app/server/books/db/ArtistDAO.kt | 7 +- .../app/server/books/db/ArtistRoleDAO.kt | 3 + .../keinekekse/app/server/books/db/BookDAO.kt | 17 +- .../app/server/books/db/SeriesDAO.kt | 5 +- .../server/books/model/BookRepositoryImpl.kt | 6 +- .../app/server/config/AppPrincipal.kt | 5 +- .../keinekekse/app/server/config/routing.kt | 2 +- .../keinekekse/app/server/config/security.kt | 6 +- .../kotlin/net/keinekekse/app/UserRoles.kt | 23 ++- .../net/keinekekse/app/books/model/Book.kt | 3 + .../keinekekse/app/books/model/Languages.kt | 7 + 15 files changed, 158 insertions(+), 101 deletions(-) create mode 100644 shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Languages.kt diff --git a/server/buildnumber.properties b/server/buildnumber.properties index 49d8e02..455d23f 100644 --- a/server/buildnumber.properties +++ b/server/buildnumber.properties @@ -1,3 +1,3 @@ #auto generated -#Tue Feb 10 18:18:54 CET 2026 -build.number=58 +#Tue Feb 10 23:59:14 CET 2026 +build.number=62 diff --git a/server/src/main/kotlin/net/keinekekse/app/server/Application.kt b/server/src/main/kotlin/net/keinekekse/app/server/Application.kt index bd65abc..5b6667e 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/Application.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/Application.kt @@ -35,85 +35,99 @@ fun Application.module() { configureRouting() - /* // DB-Test - transaction { + /* + // DB-Test + transaction { - // create if not exists - SchemaUtils.create( - SeriesTable, - ArtistRolesTable, - ArtistsTable, - ArtistToBookTable, - BooksTable - ) - logger.debug { "Tables created" } - val auth = ArtistRoleDAO.new { role = "Autor" } - ArtistRoleDAO.new { role = "Übersetzer" } + // create if not exists + SchemaUtils.drop( + SeriesTable, + ArtistToBookTable, + ArtistRolesTable, + ArtistsTable, + BooksTable + ) + SchemaUtils.create( + SeriesTable, + ArtistRolesTable, + ArtistsTable, + ArtistToBookTable, + BooksTable + ) + logger.debug { "Tables created" } + val auth = ArtistRoleDAO.new { role = "Autor" } + ArtistRoleDAO.new { role = "Übersetzer" } - val kia1 = ArtistDAO.new { - name = "Aigner" - givenName = "Sebastian" - } - val kia2 = ArtistDAO.new { - name = "Elizarov" - givenName = "Roman" - } - val kia3 = ArtistDAO.new { - name = "Isakova" - givenName = "Svetlana" - } - val kia4 = ArtistDAO.new { - name = "Jemerov" - givenName = "Dmitry" - } + val kia1 = ArtistDAO.new { + name = "Aigner" + givenName = "Sebastian" + } + val kia2 = ArtistDAO.new { + name = "Elizarov" + givenName = "Roman" + } + val kia3 = ArtistDAO.new { + name = "Isakova" + givenName = "Svetlana" + } + val kia4 = ArtistDAO.new { + name = "Jemerov" + givenName = "Dmitry" + } - val kp1 = ArtistDAO.new { - name = "Soshin" - givenName = "Alexey" - } + val kp1 = ArtistDAO.new { + name = "Soshin" + givenName = "Alexey" + } - val kia = BookDAO.new { - title = "Kotlin in Action, 2nd Edition" - seriesIndex = 0 - } - val kp = BookDAO.new { - title = "Kotlin Design Patterns and Best Practices" - seriesIndex = 0 - } + val kia = BookDAO.new { + title = "Kotlin in Action, 2nd Edition" + originalTitle = "" + year = 2024 + seriesIndex = 0 + language = Languages.EN.name + } + val kp = BookDAO.new { + title = "Kotlin Design Patterns and Best Practices, 3rd Edition" + originalTitle = "" + year = 2024 + seriesIndex = 0 + language = Languages.EN.name + } - ArtistToBookDAO.new { - book = kia - role = auth - artist = kia1 - } - ArtistToBookDAO.new { - book = kia - role = auth - artist = kia2 - } - ArtistToBookDAO.new { - book = kia - role = auth - artist = kia3 - } - ArtistToBookDAO.new { - book = kia - role = auth - artist = kia4 - } - ArtistToBookDAO.new { - book = kp - role = auth - artist = kp1 - } + ArtistToBookDAO.new { + book = kia + role = auth + artist = kia1 + } + ArtistToBookDAO.new { + book = kia + role = auth + artist = kia2 + } + ArtistToBookDAO.new { + book = kia + role = auth + artist = kia3 + } + ArtistToBookDAO.new { + book = kia + role = auth + artist = kia4 + } + ArtistToBookDAO.new { + book = kp + role = auth + artist = kp1 + } - val books = BookDAO.all().map(::daoToModel) - books.forEach { book -> - logger.debug { "### ${book.title}:" } - book.artists.forEach { atb -> - logger.debug { "### ${atb.role.role}: ${atb.artist.givenName} ${atb.artist.name}" } - } + val books = BookDAO.all().map(::daoToModel) + books.forEach { book -> + logger.debug { "### ${book.title}:" } + book.artists.forEach { atb -> + logger.debug { "### ${atb.role.role}: ${atb.artist.givenName} ${atb.artist.name}" } } } + } */ } diff --git a/server/src/main/kotlin/net/keinekekse/app/server/Version.kt b/server/src/main/kotlin/net/keinekekse/app/server/Version.kt index 5d3946c..7d99a3f 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/Version.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/Version.kt @@ -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.58" - override val fullVersion = "0.1.0-beta.1+build.58" + override val version = "0.1.0-beta.1+build.62" + override val fullVersion = "0.1.0-beta.1+build.62" override val major = 0 override val minor = 1 override val patch = 0 override val preRelease = "beta.1" - override val buildMetadata = "build.58" - override val buildDate = "2026-02-10T17:18:54.134684Z" + override val buildMetadata = "build.62" + override val buildDate = "2026-02-10T22:59:14.710093Z" override val displayName = "kAS" override val copyright = "Copyright (c) 2026 Hartmut Bauer" override val name = "server" diff --git a/server/src/main/kotlin/net/keinekekse/app/server/books/booksRoute.kt b/server/src/main/kotlin/net/keinekekse/app/server/books/booksRoute.kt index a58737d..c787bd4 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/books/booksRoute.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/books/booksRoute.kt @@ -11,10 +11,9 @@ import org.koin.ktor.ext.inject fun Route.booksRoute() { val repository: BookRepository by inject() - authenticate(UserRoles.BOOKS_USER.roleName) { + authenticate(UserRoles.BOOKS_USER.name) { route("/books") { - get("/books") { -// call.respondText("Test DB route") + get("") { val books = repository.allBooks() call.respond(books) } diff --git a/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistDAO.kt b/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistDAO.kt index 6b77fc7..9143e43 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistDAO.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistDAO.kt @@ -1,13 +1,14 @@ package net.keinekekse.app.server.books.db +import net.keinekekse.app.books.model.Artist import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntityClass object ArtistsTable : IntIdTable("artists") { - var name = varchar("name", 50) - var givenName = varchar("given_name", 50) + var name = varchar("name", 100) + var givenName = varchar("given_name", 100) } class ArtistDAO(id: EntityID) : IntEntity(id) { @@ -17,4 +18,4 @@ class ArtistDAO(id: EntityID) : IntEntity(id) { var givenName by ArtistsTable.givenName } -//fun daoToModel(dao: ArtistDAO) = Artist(dao.name, dao.givenName) +fun daoToModel(dao: ArtistDAO) = Artist(dao.id.value, dao.name, dao.givenName) diff --git a/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistRoleDAO.kt b/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistRoleDAO.kt index c7e607f..9da256d 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistRoleDAO.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/books/db/ArtistRoleDAO.kt @@ -1,5 +1,6 @@ package net.keinekekse.app.server.books.db +import net.keinekekse.app.books.model.ArtistRole import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.dao.IntEntity @@ -14,3 +15,5 @@ class ArtistRoleDAO(id: EntityID) : IntEntity(id) { var role by ArtistRolesTable.role } + +fun daoToModel(dao: ArtistRoleDAO) = ArtistRole(dao.id.value, dao.role) diff --git a/server/src/main/kotlin/net/keinekekse/app/server/books/db/BookDAO.kt b/server/src/main/kotlin/net/keinekekse/app/server/books/db/BookDAO.kt index 65c2ce6..8365b01 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/books/db/BookDAO.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/books/db/BookDAO.kt @@ -7,7 +7,10 @@ import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntityClass object BooksTable : IntIdTable("books") { - var title = varchar("title", 50) + var title = varchar("title", 100) + var originalTitle = varchar("original_title", 100) + var year = integer("year") + var language = varchar("language", 2) var series = optReference("series_id", SeriesTable.id) var seriesIndex = integer("seriesIndex") } @@ -17,6 +20,9 @@ class BookDAO(id: EntityID) : IntEntity(id) { var title by BooksTable.title var seriesIndex by BooksTable.seriesIndex + var originalTitle by BooksTable.originalTitle + var year by BooksTable.year + var language: Languages by BooksTable.language.transform({ it.name }, { Languages.valueOf(it) }) var series by SeriesDAO.Companion optionalReferencedOn BooksTable.series val artists by ArtistToBookDAO referrersOn ArtistToBookTable.bookId @@ -25,9 +31,12 @@ class BookDAO(id: EntityID) : IntEntity(id) { fun daoToModel(dao: BookDAO) = Book( dao.id.value, dao.title, - if (dao.series != null) Series(dao.series!!.id.value, dao.series!!.name) else null, - dao.seriesIndex, - dao.artists.map { + dao.originalTitle, + dao.year, + dao.language, + series = if (dao.series != null) Series(dao.series!!.id.value, dao.series!!.name) else null, + seriesIndex = dao.seriesIndex, + artists = dao.artists.map { ArtistForBook( Artist(it.artist.id.value, it.artist.name, it.artist.givenName), ArtistRole(it.role.id.value, it.role.role) diff --git a/server/src/main/kotlin/net/keinekekse/app/server/books/db/SeriesDAO.kt b/server/src/main/kotlin/net/keinekekse/app/server/books/db/SeriesDAO.kt index ba5c3f5..c1c790a 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/books/db/SeriesDAO.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/books/db/SeriesDAO.kt @@ -1,12 +1,13 @@ package net.keinekekse.app.server.books.db +import net.keinekekse.app.books.model.Series import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntityClass object SeriesTable : IntIdTable("series") { - var name = varchar("name", 50) + var name = varchar("name", 100) } class SeriesDAO(id: EntityID) : IntEntity(id) { @@ -14,3 +15,5 @@ class SeriesDAO(id: EntityID) : IntEntity(id) { var name by SeriesTable.name } + +fun daoToModel(dao: SeriesDAO) = Series(dao.id.value, dao.name) diff --git a/server/src/main/kotlin/net/keinekekse/app/server/books/model/BookRepositoryImpl.kt b/server/src/main/kotlin/net/keinekekse/app/server/books/model/BookRepositoryImpl.kt index 9d3fc0d..7873a4c 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/books/model/BookRepositoryImpl.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/books/model/BookRepositoryImpl.kt @@ -4,7 +4,9 @@ import net.keinekekse.app.books.model.Artist import net.keinekekse.app.books.model.Book import net.keinekekse.app.books.model.Series import net.keinekekse.app.server.books.db.BookDAO +import net.keinekekse.app.server.books.db.BooksTable import net.keinekekse.app.server.books.db.daoToModel +import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.jdbc.transactions.suspendTransaction import org.koin.core.annotation.Single @@ -18,8 +20,8 @@ class BookRepositoryImpl : BookRepository { BookDAO.findById(id)?.let { daoToModel(it) } } - override suspend fun booksBySeries(series: Series): List { - TODO("Not yet implemented") + override suspend fun booksBySeries(series: Series): List = suspendTransaction { + BookDAO.find { BooksTable.series eq series.id }.map(::daoToModel) } override suspend fun bookByTitle(title: String): List { diff --git a/server/src/main/kotlin/net/keinekekse/app/server/config/AppPrincipal.kt b/server/src/main/kotlin/net/keinekekse/app/server/config/AppPrincipal.kt index 1295cd3..9010c45 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/config/AppPrincipal.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/config/AppPrincipal.kt @@ -13,7 +13,10 @@ class AppPrincipal(credential: JWTCredential) : Principal { init { val realmAccess: Map<*, *>? = credential.getClaim("realm_access", Map::class) val list = realmAccess?.get("roles") - roles = if (list is List<*>) list.map { it as String } else emptyList() + roles = if (list is List<*>) list.map { + // für den Vergleich mit der UserRoles-enum umwandeln + (it as String).uppercase().replace("-", "_") + } else emptyList() } override fun getName(): String { diff --git a/server/src/main/kotlin/net/keinekekse/app/server/config/routing.kt b/server/src/main/kotlin/net/keinekekse/app/server/config/routing.kt index d534641..de2157b 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/config/routing.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/config/routing.kt @@ -13,7 +13,7 @@ private val logger = KotlinLogging.logger {} fun Application.configureRouting() { routing { - authenticate(UserRoles.BOOKS_USER.roleName) { + authenticate(UserRoles.BOOKS_USER.name) { get("/") { val principal = call.principal() call.respondText("user: ${principal?.userId}") diff --git a/server/src/main/kotlin/net/keinekekse/app/server/config/security.kt b/server/src/main/kotlin/net/keinekekse/app/server/config/security.kt index f791cbd..85dd2d8 100644 --- a/server/src/main/kotlin/net/keinekekse/app/server/config/security.kt +++ b/server/src/main/kotlin/net/keinekekse/app/server/config/security.kt @@ -27,7 +27,7 @@ fun Application.configureSecurity() { authentication { for (role in UserRoles.entries) { - jwt(role.roleName) { + jwt(role.name) { verifier( UrlJwkProvider(URI(certUrl).toURL()), issuer ) { @@ -39,8 +39,8 @@ fun Application.configureSecurity() { } validate { credential -> val principal = AppPrincipal(credential) - if (principal.roles.contains(role.roleName) || - principal.roles.contains(UserRoles.ADMIN.roleName) + if (principal.roles.contains(role.name) || + principal.roles.contains(UserRoles.ADMIN.name) ) principal else null } } diff --git a/shared/src/commonMain/kotlin/net/keinekekse/app/UserRoles.kt b/shared/src/commonMain/kotlin/net/keinekekse/app/UserRoles.kt index 4ff98d1..96b8516 100644 --- a/shared/src/commonMain/kotlin/net/keinekekse/app/UserRoles.kt +++ b/shared/src/commonMain/kotlin/net/keinekekse/app/UserRoles.kt @@ -1,9 +1,22 @@ package net.keinekekse.app -enum class UserRoles(val roleName: String) { - ADMIN("admin"), - BOOKS_ADMIN("books-admin"), - BOOKS_USER("books-user"), - BOOKS_EDITOR("books-editor") +/** + * Die enum spiegelt die verfügbaren Rollen aus Keycloak wider. + * + * Für den Vergleich wird die Keycloak-Rolle in Großbuchstaben umgewandelt + * und "-" durch "_" ersetzt. + * + * Alle Änderungen auf der Keycloak-Seite müssen hier nachgefahren werden. + * + * Wegen der Art des Vergleichs (für eine Route wird immer nur eine Rolle geprüft) + * ist zu beachten, dass der User jeweils alle benötigten Rollen bekommt. Implizit + * ist nur Admin, der für alle Routen zugelassen ist. + * + */ +enum class UserRoles { + ADMIN, + BOOKS_ADMIN, + BOOKS_USER, + BOOKS_EDITOR } diff --git a/shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Book.kt b/shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Book.kt index 8dcadf7..4ec695f 100644 --- a/shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Book.kt +++ b/shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Book.kt @@ -9,6 +9,9 @@ data class ArtistForBook(val artist: Artist, val role: ArtistRole) data class Book( val id: Int = 0, val title: String = "", + val originalTitle: String = "", + val year: Int = 0, + val language: Languages = Languages.DE, val series: Series? = null, val seriesIndex: Int = 0, diff --git a/shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Languages.kt b/shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Languages.kt new file mode 100644 index 0000000..291efc2 --- /dev/null +++ b/shared/src/commonMain/kotlin/net/keinekekse/app/books/model/Languages.kt @@ -0,0 +1,7 @@ +package net.keinekekse.app.books.model + +enum class Languages { + DE, + EN, + FR, +}