fix(server): Read database config directly from environment variables

This commit is contained in:
2025-04-18 22:01:20 +02:00
parent b999cb5832
commit 0f68084f49
8 changed files with 182 additions and 6 deletions
+1
View File
@@ -16,3 +16,4 @@ captures
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
**/xcshareddata/WorkspaceSettings.xcsettings
/.env
+15
View File
@@ -0,0 +1,15 @@
# ----------- Stage 1: Build Stage -----------
FROM gradle:8.13-jdk21 AS build
WORKDIR /home/gradle/src
COPY build.gradle.kts settings.gradle.kts gradle.properties ./
COPY gradle ./gradle
COPY shared ./shared
COPY server ./server
RUN gradle :server:shadowJar --no-configure-on-demand
# ----------- Stage 2: Runtime Stage -----------
FROM openjdk:21-slim-bookworm AS runtime
WORKDIR /app
COPY --from=build /home/gradle/src/server/build/libs/*.jar ./app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
+70
View File
@@ -0,0 +1,70 @@
services:
server:
build:
context: . # Baut mit Dockerfile im Root
image: meldestelle/server:latest
container_name: meldestelle-server
restart: unless-stopped
ports:
- "8080:8080"
environment:
- DB_USER=${POSTGRES_USER}
- DB_PASSWORD=${POSTGRES_PASSWORD}
- DB_NAME=${POSTGRES_DB}
- DB_HOST=db
- DB_PORT=5432
depends_on:
db:
condition: service_healthy
networks:
- meldestelle-net
# PostgreSQL Datenbank (Service-Name 'db')
db:
image: postgres:16-alpine # Spezifische Version
container_name: meldestelle-db
restart: unless-stopped
environment:
# Liest Werte aus .env
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
# PGDATA nicht nötig, Standard verwenden
volumes:
# Benanntes Volume für Daten auf Standardpfad
- postgres_data:/var/lib/postgresql/data
networks:
- meldestelle-net # <--- Muss zum Netzwerk-Namen passen
healthcheck: # Wichtig für depends_on
test: [ "CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}" ] # Doppelte $$ beachten!
interval: 10s
timeout: 5s
retries: 5
# ports: # Nur bei Bedarf freigeben, z.B. für lokalen Zugriff
# - "127.0.0.1:54321:5432" # Host-Port 54321 → Container-Port 5432
# Optional: PgAdmin Service
# pgadmin:
# image: dpage/pgadmin4:latest # Oder spezifische Version
# container_name: meldestelle-pgadmin
# restart: unless-stopped
# environment:
# # Werte aus .env lesen (oder Defaults nutzen)
# PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-admin@example.com}
# PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-DeinSicheresPgAdminPasswort!} # UNBEDINGT IN .env SETZEN!
# PGADMIN_CONFIG_SERVER_MODE: 'False'
# volumes:
# - pgadmin_data:/var/lib/pgadmin # Benanntes Volume
# ports:
# # Port 5050 auf dem Host (nur localhost) → Port 80 im Container
# - "${PGADMIN_PORT:-127.0.0.1:5050}:80"
# networks:
# - meldestelle-net # <--- Muss zum Netzwerk-Namen passen
# depends_on: # PgAdmin braucht die DB
# - db
networks:
meldestelle-net:
driver: bridge
volumes:
postgres_data: # <--- Konsistenter Name
# pgadmin_data: # <--- Konsistenter Name
+9
View File
@@ -9,6 +9,9 @@ ktor-tests = "2.3.13"
logback = "1.5.18"
junit-jupiter = "5.12.0"
junit-jupiter-version = "5.8.1"
exposed = "0.52.0"
postgresql = "42.7.3"
hikari = "5.1.0"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
@@ -29,6 +32,12 @@ jupiter-junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", v
ktor-server-config-yaml = { module = "io.ktor:ktor-server-config-yaml", version.ref = "ktor" }
junit-junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit-jupiter-version" }
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
postgresql-driver = { module = "org.postgresql:postgresql", version.ref = "postgresql" }
hikari-cp = { module = "com.zaxxer:HikariCP", version.ref = "hikari" }
[plugins]
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+12
View File
@@ -22,4 +22,16 @@ dependencies {
testImplementation(libs.jupiter.junit.jupiter)
implementation(libs.ktor.server.config.yaml)
testImplementation(libs.junit.junit.jupiter)
// Exposed für Datenbankzugriff (Core, DAO-Pattern, JDBC-Implementierung)
implementation(libs.exposed.core)
implementation(libs.exposed.dao)
implementation(libs.exposed.jdbc)
// JDBC Treiber für PostgreSQL (nur zur Laufzeit benötigt)
runtimeOnly(libs.postgresql.driver)
// HikariCP für Connection Pooling
implementation(libs.hikari.cp)
}
@@ -1,5 +1,6 @@
package at.mocode
import at.mocode.plugins.configureDatabase
import io.ktor.server.application.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
@@ -10,6 +11,11 @@ fun main(args: Array<String>) {
}
fun Application.module() {
// Als Erstes die Datenbank konfigurieren:
configureDatabase()
// Danach deine anderen Konfigurationen (Routing etc.):
routing {
get("/") {
call.respondText("Ktor: ${Greeting().greet()}")
@@ -0,0 +1,68 @@
package at.mocode.plugins
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.ktor.server.application.*
import org.jetbrains.exposed.sql.Database
import org.slf4j.LoggerFactory
fun Application.configureDatabase() {
val log = LoggerFactory.getLogger("DatabaseInitialization")
log.info("Initializing database connection from environment variables...")
// Lese Konfiguration direkt aus Umgebungsvariablen,
// die von Docker Compose (aus .env) gesetzt werden.
val dbHost = System.getenv("DB_HOST") ?: "db" // Fallback auf 'db', falls nicht gesetzt
val dbPort = System.getenv("DB_PORT") ?: "5432"
val dbName = System.getenv("DB_NAME")
?: error("Database name (DB_NAME) not set in environment") // Fehler, wenn nicht gesetzt
val dbUser = System.getenv("DB_USER")
?: error("Database user (DB_USER) not set in environment") // Fehler, wenn nicht gesetzt
val dbPassword = System.getenv("DB_PASSWORD")
?: error("Database password (DB_PASSWORD) not set in environment") // Fehler, wenn nicht gesetzt
val driverClassName = "org.postgresql.Driver" // Ist für Postgres fix
// Pool Size auch optional aus Env Var lesen
val maxPoolSize = System.getenv("DB_POOL_SIZE")?.toIntOrNull() ?: 10
// Baue die JDBC URL zusammen
val jdbcURL = "jdbc:postgresql://$dbHost:$dbPort/$dbName"
log.info("Attempting to connect to database at URL: {}", jdbcURL) // Logge die URL (ohne User/Passwort!)
// Konfiguriere HikariCP mit den Werten aus der Umgebung
val hikariConfig = HikariConfig().apply {
this.driverClassName = driverClassName
this.jdbcUrl = jdbcURL
this.username = dbUser
this.password = dbPassword
this.maximumPoolSize = maxPoolSize
// Hier könnten weitere HikariCP-Optimierungen hin
try {
this.validate() // Prüft die Konfiguration frühzeitig
} catch (e: Exception) {
log.error("HikariCP configuration validation failed!", e)
throw e // Wirft den Fehler weiter, damit die App nicht startet
}
}
// Erstelle DataSource und verbinde Exposed
try {
val dataSource = HikariDataSource(hikariConfig)
Database.connect(dataSource)
log.info("Database connection pool initialized successfully!")
} catch (e: Exception) {
log.error("Failed to initialize database connection pool!", e)
// Optional: Hier entscheiden, ob die App trotzdem starten soll oder nicht.
// Aktuell würde sie bei Fehlern hier abstürzen (was oft gewünscht ist).
throw e
}
// --- TODO für den NÄCHSTEN Schritt ---
// Hier kommt später die Logik zum Erstellen der Tabellen hin,
// z.B. innerhalb einer Transaktion:
// transaction {
// SchemaUtils.create(TurniereTable) // Erstellt die Tabelle, wenn sie nicht existiert
// }
// ------------------------------------
}
+1 -6
View File
@@ -1,5 +1,4 @@
# Grundkonfiguration für Ktor in YAML
ktor:
deployment:
# Der Port, auf dem der Server lauschen soll
@@ -9,11 +8,7 @@ ktor:
# watch:
# - classes
# - resources
application:
# Hier wird Ktor gesagt, welche Funktion die Konfiguration enthält
# PASSE DEN PFAD AN, falls deine Application.kt oder module() anders heißt/liegt!
modules:
- at.mocode.ApplicationKt.module
# Wenn Application.kt direkt unter at.mocode liegt:
# - at.mocode.ApplicationKt.module
- at.mocode.ApplicationKt.module