fix(server): Read database config directly from environment variables
This commit is contained in:
@@ -16,3 +16,4 @@ captures
|
|||||||
!*.xcodeproj/project.xcworkspace/
|
!*.xcodeproj/project.xcworkspace/
|
||||||
!*.xcworkspace/contents.xcworkspacedata
|
!*.xcworkspace/contents.xcworkspacedata
|
||||||
**/xcshareddata/WorkspaceSettings.xcsettings
|
**/xcshareddata/WorkspaceSettings.xcsettings
|
||||||
|
/.env
|
||||||
|
|||||||
+15
@@ -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"]
|
||||||
@@ -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,6 +9,9 @@ ktor-tests = "2.3.13"
|
|||||||
logback = "1.5.18"
|
logback = "1.5.18"
|
||||||
junit-jupiter = "5.12.0"
|
junit-jupiter = "5.12.0"
|
||||||
junit-jupiter-version = "5.8.1"
|
junit-jupiter-version = "5.8.1"
|
||||||
|
exposed = "0.52.0"
|
||||||
|
postgresql = "42.7.3"
|
||||||
|
hikari = "5.1.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
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" }
|
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" }
|
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]
|
[plugins]
|
||||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
||||||
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
|||||||
@@ -22,4 +22,16 @@ dependencies {
|
|||||||
testImplementation(libs.jupiter.junit.jupiter)
|
testImplementation(libs.jupiter.junit.jupiter)
|
||||||
implementation(libs.ktor.server.config.yaml)
|
implementation(libs.ktor.server.config.yaml)
|
||||||
testImplementation(libs.junit.junit.jupiter)
|
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
|
package at.mocode
|
||||||
|
|
||||||
|
import at.mocode.plugins.configureDatabase
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.netty.*
|
import io.ktor.server.netty.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
@@ -10,6 +11,11 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Application.module() {
|
fun Application.module() {
|
||||||
|
|
||||||
|
// Als Erstes die Datenbank konfigurieren:
|
||||||
|
configureDatabase()
|
||||||
|
|
||||||
|
// Danach deine anderen Konfigurationen (Routing etc.):
|
||||||
routing {
|
routing {
|
||||||
get("/") {
|
get("/") {
|
||||||
call.respondText("Ktor: ${Greeting().greet()}")
|
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,5 +1,4 @@
|
|||||||
# Grundkonfiguration für Ktor in YAML
|
# Grundkonfiguration für Ktor in YAML
|
||||||
|
|
||||||
ktor:
|
ktor:
|
||||||
deployment:
|
deployment:
|
||||||
# Der Port, auf dem der Server lauschen soll
|
# Der Port, auf dem der Server lauschen soll
|
||||||
@@ -9,11 +8,7 @@ ktor:
|
|||||||
# watch:
|
# watch:
|
||||||
# - classes
|
# - classes
|
||||||
# - resources
|
# - resources
|
||||||
|
|
||||||
application:
|
application:
|
||||||
# Hier wird Ktor gesagt, welche Funktion die Konfiguration enthält
|
# 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:
|
modules:
|
||||||
- at.mocode.ApplicationKt.module
|
- at.mocode.ApplicationKt.module
|
||||||
# Wenn Application.kt direkt unter at.mocode liegt:
|
|
||||||
# - at.mocode.ApplicationKt.module
|
|
||||||
Reference in New Issue
Block a user