From aa3e82010fb8b4d7052092e50afb616352a4e04a Mon Sep 17 00:00:00 2001 From: Daniel Svitan Date: Sun, 11 May 2025 12:48:41 +0200 Subject: [PATCH] :construction: Starts rework of database --- backend/build.gradle.kts | 2 +- backend/compose.yaml | 14 +++++ backend/src/main/kotlin/Application.kt | 4 +- backend/src/main/kotlin/UsersSchema.kt | 62 ------------------- backend/src/main/kotlin/plugins/Databases.kt | 30 +++++---- .../src/main/kotlin/plugins/Serialization.kt | 12 ++++ .../src/main/kotlin/schemas/UsersSchema.kt | 56 +++++++++++++++++ 7 files changed, 105 insertions(+), 75 deletions(-) create mode 100644 backend/compose.yaml delete mode 100644 backend/src/main/kotlin/UsersSchema.kt create mode 100644 backend/src/main/kotlin/plugins/Serialization.kt create mode 100644 backend/src/main/kotlin/schemas/UsersSchema.kt diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index 1badbbd..e231fa8 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation("io.ktor:ktor-serialization-kotlinx-json") implementation("org.jetbrains.exposed:exposed-core:$exposed_version") implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version") - implementation("com.h2database:h2:$h2_version") + implementation("org.postgresql:postgresql:42.7.2") implementation("io.github.flaxoos:ktor-server-rate-limiting:2.2.1") implementation("io.ktor:ktor-server-compression") implementation("io.ktor:ktor-server-netty") diff --git a/backend/compose.yaml b/backend/compose.yaml new file mode 100644 index 0000000..691c6b6 --- /dev/null +++ b/backend/compose.yaml @@ -0,0 +1,14 @@ +version: "3.3" + +services: + postgres: + image: postgres:17 + restart: on-failure + + environment: + POSTGRES_USER: "user" + POSTGRES_PASSWORD: "password" + POSTGRES_DB: "db" + + ports: + - "5432:5432" diff --git a/backend/src/main/kotlin/Application.kt b/backend/src/main/kotlin/Application.kt index b827da0..32634a1 100644 --- a/backend/src/main/kotlin/Application.kt +++ b/backend/src/main/kotlin/Application.kt @@ -10,6 +10,7 @@ import dev.svitan.plugins.configureHTTP import dev.svitan.plugins.configureMonitoring import dev.svitan.plugins.configureRouting import dev.svitan.plugins.configureSecurity +import dev.svitan.plugins.configureSerialization fun main() { embeddedServer( @@ -25,8 +26,9 @@ fun Application.module() { configureHTTP() configureRouting() - configureDatabases() configureMonitoring() + configureSerialization() configureAdministration() configureSecurity(dotenv) + configureDatabases(dotenv) } diff --git a/backend/src/main/kotlin/UsersSchema.kt b/backend/src/main/kotlin/UsersSchema.kt deleted file mode 100644 index 055a448..0000000 --- a/backend/src/main/kotlin/UsersSchema.kt +++ /dev/null @@ -1,62 +0,0 @@ -package dev.svitan - -import kotlinx.coroutines.Dispatchers -import kotlinx.serialization.Serializable -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction -import org.jetbrains.exposed.sql.transactions.transaction - -@Serializable -data class ExposedUser(val name: String, val age: Int) - -class UserService(database: Database) { - object Users : Table() { - val id = integer("id").autoIncrement() - val name = varchar("name", length = 50) - val age = integer("age") - - override val primaryKey = PrimaryKey(id) - } - - init { - transaction(database) { - SchemaUtils.create(Users) - } - } - - suspend fun create(user: ExposedUser): Int = dbQuery { - Users.insert { - it[name] = user.name - it[age] = user.age - }[Users.id] - } - - suspend fun read(id: Int): ExposedUser? { - return dbQuery { - Users.selectAll() - .where { Users.id eq id } - .map { ExposedUser(it[Users.name], it[Users.age]) } - .singleOrNull() - } - } - - suspend fun update(id: Int, user: ExposedUser) { - dbQuery { - Users.update({ Users.id eq id }) { - it[name] = user.name - it[age] = user.age - } - } - } - - suspend fun delete(id: Int) { - dbQuery { - Users.deleteWhere { Users.id.eq(id) } - } - } - - private suspend fun dbQuery(block: suspend () -> T): T = - newSuspendedTransaction(Dispatchers.IO) { block() } -} - diff --git a/backend/src/main/kotlin/plugins/Databases.kt b/backend/src/main/kotlin/plugins/Databases.kt index 9d2204c..51ed6d9 100644 --- a/backend/src/main/kotlin/plugins/Databases.kt +++ b/backend/src/main/kotlin/plugins/Databases.kt @@ -1,26 +1,34 @@ package dev.svitan.plugins +import dev.svitan.schemas.UserDTO +import dev.svitan.schemas.UserService import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import org.jetbrains.exposed.sql.Database -import dev.svitan.ExposedUser -import dev.svitan.UserService +import io.github.cdimascio.dotenv.Dotenv -fun Application.configureDatabases() { - val database = Database.connect( - url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", - user = "root", - driver = "org.h2.Driver", - password = "", +fun Application.configureDatabases(dotenv: Dotenv) { + val dbHost = dotenv["DB_HOST"] ?: throw Exception("DB_HOST not found") + val dbPort = dotenv["DB_PORT"] ?: throw Exception("DB_PORT not found") + val dbName = dotenv["DB_NAME"] ?: throw Exception("DB_NAME not found") + val dbUser = dotenv["DB_USER"] ?: throw Exception("DB_USER not found") + val dbPassword = dotenv["DB_PASSWORD"] ?: throw Exception("DB_PASSWORD not found") + + Database.connect( + url = "jdbc:postgresql://${dbHost}:${dbPort}/${dbName}", + driver = "org.postgresql.Driver", + user = dbUser, + password = dbPassword ) - val userService = UserService(database) + val userService = UserService() + routing { // Create user post("/users") { - val user = call.receive() + val user = call.receive() val id = userService.create(user) call.respond(HttpStatusCode.Created, id) } @@ -39,7 +47,7 @@ fun Application.configureDatabases() { // Update user put("/users/{id}") { val id = call.parameters["id"]?.toInt() ?: throw IllegalArgumentException("Invalid ID") - val user = call.receive() + val user = call.receive() userService.update(id, user) call.respond(HttpStatusCode.OK) } diff --git a/backend/src/main/kotlin/plugins/Serialization.kt b/backend/src/main/kotlin/plugins/Serialization.kt new file mode 100644 index 0000000..e74998d --- /dev/null +++ b/backend/src/main/kotlin/plugins/Serialization.kt @@ -0,0 +1,12 @@ +package dev.svitan.plugins + +import io.ktor.serialization.kotlinx.json.json +import io.ktor.server.application.Application +import io.ktor.server.application.install +import io.ktor.server.plugins.contentnegotiation.ContentNegotiation + +fun Application.configureSerialization() { + install(ContentNegotiation) { + json() + } +} diff --git a/backend/src/main/kotlin/schemas/UsersSchema.kt b/backend/src/main/kotlin/schemas/UsersSchema.kt new file mode 100644 index 0000000..098268a --- /dev/null +++ b/backend/src/main/kotlin/schemas/UsersSchema.kt @@ -0,0 +1,56 @@ +package dev.svitan.schemas + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.transactions.transaction + +@Serializable +data class UserDTO(val name: String, val age: Int) + +class UserService { + object Users : Table("users") { + val id = integer("id").autoIncrement() + val name = varchar("name", length = 50) + val age = integer("age") + + override val primaryKey = PrimaryKey(id) + } + + init { + transaction { + SchemaUtils.create(Users) + } + } + + fun create(user: UserDTO): Int = + transaction { + Users.insert { + it[name] = user.name + it[age] = user.age + }[Users.id] + } + + fun read(id: Int): UserDTO? = + transaction { + Users.selectAll() + .where { Users.id eq id } + .map { UserDTO(it[Users.name], it[Users.age]) } + .singleOrNull() + } + + fun update(id: Int, user: UserDTO) { + transaction { + Users.update({ Users.id eq id }) { + it[name] = user.name + it[age] = user.age + } + } + } + + fun delete(id: Int) { + transaction { + Users.deleteWhere { Users.id.eq(id) } + } + } +}