(fix) Konfiguration-Setup Umbau zu SCS
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
# Konfigurationsmanagement
|
||||
|
||||
Dieses Dokument beschreibt, wie die Konfiguration des Meldestelle-Projekts verwaltet wird.
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Projekt verwendet einen mehrschichtigen Konfigurationsansatz, um verschiedene Umgebungen (Entwicklung, Test, Staging, Produktion) zu unterstützen. Die Konfiguration kann über folgende Quellen bereitgestellt werden:
|
||||
|
||||
1. Umgebungsvariablen (höchste Priorität)
|
||||
2. Umgebungsspezifische Konfigurationsdateien (.properties)
|
||||
3. Basis-Konfigurationsdatei (application.properties)
|
||||
4. Standardwerte im Code (niedrigste Priorität)
|
||||
|
||||
## Konfigurationsquellen
|
||||
|
||||
### Umgebungsvariablen
|
||||
|
||||
Umgebungsvariablen haben die höchste Priorität und überschreiben alle anderen Konfigurationen. Sie werden typischerweise verwendet, um sensible Informationen wie Passwörter oder umgebungsspezifische Werte zu setzen.
|
||||
|
||||
Beispiel:
|
||||
```bash
|
||||
# Umgebung festlegen
|
||||
export APP_ENV=PRODUCTION
|
||||
|
||||
# Datenbank-Konfiguration
|
||||
export DB_HOST=db.example.com
|
||||
export DB_PORT=5432
|
||||
export DB_NAME=meldestelle_db
|
||||
export DB_USER=db_user
|
||||
export DB_PASSWORD=secret_password
|
||||
|
||||
# Server-Konfiguration
|
||||
export API_PORT=8081
|
||||
```
|
||||
|
||||
### Konfigurationsdateien
|
||||
|
||||
Das Projekt verwendet .properties-Dateien im `/config`-Verzeichnis. Die folgenden Dateien werden geladen (in dieser Reihenfolge):
|
||||
|
||||
1. `application.properties` - Basiseinstellungen für alle Umgebungen
|
||||
2. Umgebungsspezifische Datei - abhängig von `APP_ENV`:
|
||||
- `application-dev.properties` - Entwicklungsumgebung (Standard)
|
||||
- `application-test.properties` - Testumgebung
|
||||
- `application-staging.properties` - Staging-Umgebung
|
||||
- `application-prod.properties` - Produktionsumgebung
|
||||
|
||||
## Umgebungen
|
||||
|
||||
Das Projekt unterstützt folgende Umgebungen:
|
||||
|
||||
| Umgebung | Beschreibung | Typische Verwendung |
|
||||
|----------|-------------|--------------------|
|
||||
| DEVELOPMENT | Lokale Entwicklungsumgebung | Lokale Entwicklung, Debug-Modus aktiv |
|
||||
| TEST | Testumgebung | Automatisierte Tests, Integrationstests |
|
||||
| STAGING | Vorabproduktionsumgebung | Manuelle Tests, UAT, Demos |
|
||||
| PRODUCTION | Produktionsumgebung | Live-System |
|
||||
|
||||
Die aktuelle Umgebung wird über die Umgebungsvariable `APP_ENV` festgelegt. Wenn diese Variable nicht gesetzt ist, wird standardmäßig `DEVELOPMENT` verwendet.
|
||||
|
||||
## Konfigurationsstruktur
|
||||
|
||||
Die Konfiguration ist in mehrere Kategorien unterteilt:
|
||||
|
||||
### AppInfo
|
||||
|
||||
Allgemeine Anwendungsinformationen:
|
||||
|
||||
```properties
|
||||
app.name=Meldestelle
|
||||
app.version=1.0.0
|
||||
app.description=Pferdesport Meldestelle System
|
||||
```
|
||||
|
||||
### Server
|
||||
|
||||
Server-Konfiguration:
|
||||
|
||||
```properties
|
||||
server.port=8081
|
||||
server.host=0.0.0.0
|
||||
server.workers=4
|
||||
server.cors.enabled=true
|
||||
server.cors.allowedOrigins=*
|
||||
```
|
||||
|
||||
### Datenbank
|
||||
|
||||
Datenbank-Konfiguration:
|
||||
|
||||
```properties
|
||||
database.host=localhost
|
||||
database.port=5432
|
||||
database.name=meldestelle_db
|
||||
database.username=meldestelle_user
|
||||
database.password=secure_password_change_me
|
||||
database.maxPoolSize=10
|
||||
database.autoMigrate=true
|
||||
```
|
||||
|
||||
### Sicherheit
|
||||
|
||||
Sicherheitseinstellungen (JWT, etc.):
|
||||
|
||||
```properties
|
||||
security.jwt.secret=your-secret-key
|
||||
security.jwt.issuer=meldestelle-api
|
||||
security.jwt.audience=meldestelle-clients
|
||||
security.jwt.realm=meldestelle
|
||||
security.jwt.expirationInMinutes=1440
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
Logging-Konfiguration:
|
||||
|
||||
```properties
|
||||
logging.level=INFO
|
||||
logging.requests=true
|
||||
logging.responses=false
|
||||
```
|
||||
|
||||
## Verwendung im Code
|
||||
|
||||
Die Konfiguration wird über die zentrale `AppConfig`-Klasse bereitgestellt:
|
||||
|
||||
```kotlin
|
||||
import at.mocode.shared.config.AppConfig
|
||||
|
||||
// Verwendung der Konfiguration
|
||||
fun example() {
|
||||
// Umgebung prüfen
|
||||
if (AppConfig.environment.isDevelopment()) {
|
||||
println("Debug-Modus aktiv")
|
||||
}
|
||||
|
||||
// Server-Port abrufen
|
||||
val port = AppConfig.server.port
|
||||
|
||||
// Datenbank-Konfiguration
|
||||
val dbConfig = AppConfig.database
|
||||
|
||||
// JWT-Secret
|
||||
val jwtSecret = AppConfig.security.jwt.secret
|
||||
}
|
||||
```
|
||||
|
||||
## Konfiguration für Docker
|
||||
|
||||
Bei Verwendung von Docker werden Umgebungsvariablen in der `.env`-Datei und im `docker-compose.yml` definiert:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
server:
|
||||
environment:
|
||||
- APP_ENV=PRODUCTION
|
||||
- DB_HOST=db
|
||||
- DB_PORT=5432
|
||||
- DB_NAME=${POSTGRES_DB}
|
||||
- DB_USER=${POSTGRES_USER}
|
||||
- DB_PASSWORD=${POSTGRES_PASSWORD}
|
||||
```
|
||||
|
||||
## Beste Praktiken
|
||||
|
||||
1. **Sensible Daten**: Speichern Sie niemals sensible Daten wie Passwörter oder API-Schlüssel direkt in Konfigurationsdateien, die in die Versionskontrolle eingecheckt werden. Verwenden Sie stattdessen Umgebungsvariablen.
|
||||
|
||||
2. **Umgebungsspezifische Konfiguration**: Verwenden Sie umgebungsspezifische Konfigurationsdateien nur für Werte, die sich zwischen den Umgebungen unterscheiden.
|
||||
|
||||
3. **Standardwerte**: Geben Sie für alle Konfigurationsparameter sinnvolle Standardwerte an, damit die Anwendung auch funktioniert, wenn nicht alle Konfigurationen explizit gesetzt sind.
|
||||
|
||||
4. **Validierung**: Validieren Sie kritische Konfigurationen beim Anwendungsstart, um Fehler frühzeitig zu erkennen.
|
||||
|
||||
5. **Dokumentation**: Halten Sie die Dokumentation der Konfigurationsparameter aktuell, damit neue Teammitglieder die Anwendung leicht konfigurieren können.
|
||||
@@ -1,38 +1,30 @@
|
||||
package at.mocode.gateway
|
||||
|
||||
import at.mocode.gateway.config.MigrationSetup
|
||||
import at.mocode.shared.config.AppConfig
|
||||
import at.mocode.shared.database.DatabaseConfig
|
||||
import at.mocode.shared.database.DatabaseFactory
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun main() {
|
||||
// Konfiguration laden (wird automatisch beim ersten Zugriff auf AppConfig initialisiert)
|
||||
val config = AppConfig
|
||||
|
||||
// Datenbank initialisieren
|
||||
val databaseConfig = DatabaseConfig.fromEnv()
|
||||
DatabaseFactory.init(databaseConfig)
|
||||
DatabaseFactory.init(config.database)
|
||||
|
||||
// Migrationen ausführen
|
||||
MigrationSetup.runMigrations()
|
||||
|
||||
// Server starten
|
||||
embeddedServer(Netty, port = System.getenv("API_PORT")?.toIntOrNull() ?: 8081) {
|
||||
configureApplication()
|
||||
embeddedServer(Netty, port = config.server.port, host = config.server.host) {
|
||||
module()
|
||||
}.start(wait = true)
|
||||
}
|
||||
|
||||
fun Application.configureApplication() {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
|
||||
routing {
|
||||
get("/health") {
|
||||
call.respond(mapOf("status" to "OK"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package at.mocode.gateway
|
||||
|
||||
import at.mocode.shared.config.AppConfig
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.calllogging.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.plugins.cors.routing.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.module() {
|
||||
val config = AppConfig
|
||||
|
||||
// ContentNegotiation installieren
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
|
||||
// CORS installieren, wenn aktiviert
|
||||
if (config.server.cors.enabled) {
|
||||
install(CORS) {
|
||||
if (config.server.cors.allowedOrigins.contains("*")) {
|
||||
anyHost()
|
||||
} else {
|
||||
config.server.cors.allowedOrigins.forEach { allowHost(it, schemes = listOf("http", "https")) }
|
||||
}
|
||||
allowHeader(HttpHeaders.ContentType)
|
||||
allowHeader(HttpHeaders.Authorization)
|
||||
allowMethod(HttpMethod.Options)
|
||||
allowMethod(HttpMethod.Get)
|
||||
allowMethod(HttpMethod.Post)
|
||||
allowMethod(HttpMethod.Put)
|
||||
allowMethod(HttpMethod.Delete)
|
||||
}
|
||||
}
|
||||
|
||||
// Call-Logging installieren
|
||||
if (config.logging.logRequests) {
|
||||
install(CallLogging)
|
||||
}
|
||||
|
||||
routing {
|
||||
// Hauptrouten
|
||||
get("/") {
|
||||
call.respondText(
|
||||
"${config.appInfo.name} API v${config.appInfo.version} (${config.environment})",
|
||||
ContentType.Text.Plain
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Entwicklungsumgebung spezifische Konfiguration
|
||||
|
||||
# Server-Einstellungen
|
||||
server.port=8081
|
||||
|
||||
# Datenbank-Einstellungen
|
||||
database.host=localhost
|
||||
database.port=5432
|
||||
|
||||
# Logging-Einstellungen
|
||||
logging.level=DEBUG
|
||||
logging.requests=true
|
||||
logging.responses=true
|
||||
@@ -0,0 +1,16 @@
|
||||
# Produktionsumgebung spezifische Konfiguration
|
||||
|
||||
# Server-Einstellungen
|
||||
server.port=8081
|
||||
server.workers=4
|
||||
server.cors.allowedOrigins=https://meldestelle.at,https://app.meldestelle.at
|
||||
|
||||
# Datenbank-Einstellungen
|
||||
database.host=db
|
||||
database.port=5432
|
||||
database.maxPoolSize=20
|
||||
|
||||
# Logging-Einstellungen
|
||||
logging.level=INFO
|
||||
logging.requests=true
|
||||
logging.responses=false
|
||||
@@ -0,0 +1,16 @@
|
||||
# Staging-Umgebung spezifische Konfiguration
|
||||
|
||||
# Server-Einstellungen
|
||||
server.port=8081
|
||||
server.workers=2
|
||||
server.cors.allowedOrigins=https://staging.meldestelle.at
|
||||
|
||||
# Datenbank-Einstellungen
|
||||
database.host=db
|
||||
database.port=5432
|
||||
database.name=meldestelle_staging_db
|
||||
|
||||
# Logging-Einstellungen
|
||||
logging.level=INFO
|
||||
logging.requests=true
|
||||
logging.responses=false
|
||||
@@ -0,0 +1,14 @@
|
||||
# Testumgebung spezifische Konfiguration
|
||||
|
||||
# Server-Einstellungen
|
||||
server.port=8082
|
||||
|
||||
# Datenbank-Einstellungen
|
||||
database.host=localhost
|
||||
database.port=5432
|
||||
database.name=meldestelle_test_db
|
||||
|
||||
# Logging-Einstellungen
|
||||
logging.level=DEBUG
|
||||
logging.requests=true
|
||||
logging.responses=true
|
||||
@@ -0,0 +1,32 @@
|
||||
# Allgemeine Anwendungseinstellungen
|
||||
app.name=Meldestelle
|
||||
app.version=1.0.0
|
||||
app.description=Pferdesport Meldestelle System
|
||||
|
||||
# Server-Einstellungen
|
||||
server.port=8081
|
||||
server.host=0.0.0.0
|
||||
server.workers=4
|
||||
server.cors.enabled=true
|
||||
server.cors.allowedOrigins=*
|
||||
|
||||
# Datenbank-Einstellungen
|
||||
database.host=localhost
|
||||
database.port=5432
|
||||
database.name=meldestelle_db
|
||||
database.username=meldestelle_user
|
||||
database.password=secure_password_change_me
|
||||
database.maxPoolSize=10
|
||||
database.autoMigrate=true
|
||||
|
||||
# Sicherheits-Einstellungen
|
||||
security.jwt.secret=default-jwt-secret-key-please-change-in-production
|
||||
security.jwt.issuer=meldestelle-api
|
||||
security.jwt.audience=meldestelle-clients
|
||||
security.jwt.realm=meldestelle
|
||||
security.jwt.expirationInMinutes=1440
|
||||
|
||||
# Logging-Einstellungen
|
||||
logging.level=INFO
|
||||
logging.requests=true
|
||||
logging.responses=false
|
||||
+14
-14
@@ -1,16 +1,14 @@
|
||||
package at.mocode.events.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.SparteE
|
||||
import at.mocode.events.domain.model.Veranstaltung
|
||||
import at.mocode.events.domain.repository.VeranstaltungRepository
|
||||
import at.mocode.enums.SparteE
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
|
||||
/**
|
||||
@@ -22,21 +20,21 @@ import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): Veranstaltung? {
|
||||
return VeranstaltungTable.select { VeranstaltungTable.id eq id }
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.id eq id }
|
||||
.map { rowToVeranstaltung(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<Veranstaltung> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return VeranstaltungTable.select { VeranstaltungTable.name like searchPattern }
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.name like searchPattern }
|
||||
.orderBy(VeranstaltungTable.startDatum, SortOrder.DESC)
|
||||
.limit(limit)
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByVeranstalterVereinId(vereinId: Uuid, activeOnly: Boolean): List<Veranstaltung> {
|
||||
val query = VeranstaltungTable.select { VeranstaltungTable.veranstalterVereinId eq vereinId }
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.veranstalterVereinId eq vereinId }
|
||||
|
||||
return if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
@@ -47,9 +45,9 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
}
|
||||
|
||||
override suspend fun findByDateRange(startDate: LocalDate, endDate: LocalDate, activeOnly: Boolean): List<Veranstaltung> {
|
||||
val query = VeranstaltungTable.select {
|
||||
val query = VeranstaltungTable.selectAll().where {
|
||||
(VeranstaltungTable.startDatum greaterEq startDate) and
|
||||
(VeranstaltungTable.endDatum lessEq endDate)
|
||||
(VeranstaltungTable.endDatum lessEq endDate)
|
||||
}
|
||||
|
||||
return if (activeOnly) {
|
||||
@@ -61,7 +59,7 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
}
|
||||
|
||||
override suspend fun findByStartDate(date: LocalDate, activeOnly: Boolean): List<Veranstaltung> {
|
||||
val query = VeranstaltungTable.select { VeranstaltungTable.startDatum eq date }
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.startDatum eq date }
|
||||
|
||||
return if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
@@ -72,14 +70,14 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<Veranstaltung> {
|
||||
return VeranstaltungTable.select { VeranstaltungTable.istAktiv eq true }
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.istAktiv eq true }
|
||||
.orderBy(VeranstaltungTable.startDatum, SortOrder.DESC)
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToVeranstaltung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findPublicEvents(activeOnly: Boolean): List<Veranstaltung> {
|
||||
val query = VeranstaltungTable.select { VeranstaltungTable.istOeffentlich eq true }
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.istOeffentlich eq true }
|
||||
|
||||
return if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
@@ -94,7 +92,9 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
val updatedVeranstaltung = veranstaltung.copy(updatedAt = now)
|
||||
|
||||
// Check if record exists
|
||||
val existingRecord = VeranstaltungTable.select { VeranstaltungTable.id eq veranstaltung.veranstaltungId }.singleOrNull()
|
||||
val existingRecord = VeranstaltungTable.selectAll()
|
||||
.where { VeranstaltungTable.id eq veranstaltung.veranstaltungId }
|
||||
.singleOrNull()
|
||||
|
||||
return if (existingRecord != null) {
|
||||
// Update existing record
|
||||
@@ -118,12 +118,12 @@ class VeranstaltungRepositoryImpl : VeranstaltungRepository {
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return VeranstaltungTable.select { VeranstaltungTable.istAktiv eq true }
|
||||
return VeranstaltungTable.selectAll().where { VeranstaltungTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
override suspend fun countByVeranstalterVereinId(vereinId: Uuid, activeOnly: Boolean): Long {
|
||||
val query = VeranstaltungTable.select { VeranstaltungTable.veranstalterVereinId eq vereinId }
|
||||
val query = VeranstaltungTable.selectAll().where { VeranstaltungTable.veranstalterVereinId eq vereinId }
|
||||
|
||||
return if (activeOnly) {
|
||||
query.andWhere { VeranstaltungTable.istAktiv eq true }
|
||||
|
||||
+4
-4
@@ -41,7 +41,7 @@ class HorseRepositoryImpl : HorseRepository {
|
||||
}
|
||||
|
||||
override suspend fun findByOepsNummer(oepsNummer: String): DomPferd? {
|
||||
return HorseTable.select { HorseTable.oepsNummer eq oepsNummer }
|
||||
return HorseTable.selectAll().where { HorseTable.oepsNummer eq oepsNummer }
|
||||
.map { rowToDomPferd(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
@@ -224,17 +224,17 @@ class HorseRepositoryImpl : HorseRepository {
|
||||
}
|
||||
|
||||
override suspend fun existsByFeiNummer(feiNummer: String): Boolean {
|
||||
return HorseTable.select { HorseTable.feiNummer eq feiNummer }
|
||||
return HorseTable.selectAll().where { HorseTable.feiNummer eq feiNummer }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return HorseTable.select { HorseTable.istAktiv eq true }
|
||||
return HorseTable.selectAll().where { HorseTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
override suspend fun countByOwnerId(ownerId: Uuid, activeOnly: Boolean): Long {
|
||||
val query = HorseTable.select { HorseTable.besitzerId eq ownerId }
|
||||
val query = HorseTable.selectAll().where { HorseTable.besitzerId eq ownerId }
|
||||
|
||||
return if (activeOnly) {
|
||||
query.andWhere { HorseTable.istAktiv eq true }
|
||||
|
||||
+13
-19
@@ -6,8 +6,6 @@ import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
|
||||
/**
|
||||
* PostgreSQL implementation of LandRepository using Exposed ORM.
|
||||
@@ -18,29 +16,29 @@ import org.jetbrains.exposed.sql.SortOrder
|
||||
class LandRepositoryImpl : LandRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): LandDefinition? {
|
||||
return LandTable.select { LandTable.id eq id }
|
||||
return LandTable.selectAll().where { LandTable.id eq id }
|
||||
.singleOrNull()
|
||||
?.toLandDefinition()
|
||||
}
|
||||
|
||||
override suspend fun findByIsoAlpha2Code(isoAlpha2Code: String): LandDefinition? {
|
||||
return LandTable.select { LandTable.isoAlpha2Code eq isoAlpha2Code }
|
||||
return LandTable.selectAll().where { LandTable.isoAlpha2Code eq isoAlpha2Code }
|
||||
.singleOrNull()
|
||||
?.toLandDefinition()
|
||||
}
|
||||
|
||||
override suspend fun findByIsoAlpha3Code(isoAlpha3Code: String): LandDefinition? {
|
||||
return LandTable.select { LandTable.isoAlpha3Code eq isoAlpha3Code }
|
||||
return LandTable.selectAll().where { LandTable.isoAlpha3Code eq isoAlpha3Code }
|
||||
.singleOrNull()
|
||||
?.toLandDefinition()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<LandDefinition> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return LandTable.select {
|
||||
return LandTable.selectAll().where {
|
||||
(LandTable.nameGerman like searchPattern) or
|
||||
(LandTable.nameEnglish like searchPattern) or
|
||||
(LandTable.nameLocal like searchPattern)
|
||||
(LandTable.nameEnglish like searchPattern) or
|
||||
(LandTable.nameLocal like searchPattern)
|
||||
}
|
||||
.orderBy(LandTable.sortierReihenfolge)
|
||||
.limit(limit)
|
||||
@@ -48,7 +46,7 @@ class LandRepositoryImpl : LandRepository {
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(orderBySortierung: Boolean): List<LandDefinition> {
|
||||
val query = LandTable.select { LandTable.isActive eq true }
|
||||
val query = LandTable.selectAll().where { LandTable.isActive eq true }
|
||||
|
||||
return if (orderBySortierung) {
|
||||
query.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameGerman to SortOrder.ASC)
|
||||
@@ -58,17 +56,13 @@ class LandRepositoryImpl : LandRepository {
|
||||
}
|
||||
|
||||
override suspend fun findEuMembers(): List<LandDefinition> {
|
||||
return LandTable.select {
|
||||
(LandTable.isActive eq true) and (LandTable.isEuMember eq true)
|
||||
}
|
||||
return LandTable.selectAll().where { (LandTable.isActive eq true) and (LandTable.isEuMember eq true) }
|
||||
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameGerman to SortOrder.ASC)
|
||||
.map { it.toLandDefinition() }
|
||||
}
|
||||
|
||||
override suspend fun findEwrMembers(): List<LandDefinition> {
|
||||
return LandTable.select {
|
||||
(LandTable.isActive eq true) and (LandTable.isEwrMember eq true)
|
||||
}
|
||||
return LandTable.selectAll().where { (LandTable.isActive eq true) and (LandTable.isEwrMember eq true) }
|
||||
.orderBy(LandTable.sortierReihenfolge to SortOrder.ASC, LandTable.nameGerman to SortOrder.ASC)
|
||||
.map { it.toLandDefinition() }
|
||||
}
|
||||
@@ -77,7 +71,7 @@ class LandRepositoryImpl : LandRepository {
|
||||
val now = Clock.System.now()
|
||||
|
||||
// Check if record exists
|
||||
val existingRecord = LandTable.select { LandTable.id eq land.landId }.singleOrNull()
|
||||
val existingRecord = LandTable.selectAll().where { LandTable.id eq land.landId }.singleOrNull()
|
||||
|
||||
return if (existingRecord != null) {
|
||||
// Update existing record
|
||||
@@ -126,17 +120,17 @@ class LandRepositoryImpl : LandRepository {
|
||||
}
|
||||
|
||||
override suspend fun existsByIsoAlpha2Code(isoAlpha2Code: String): Boolean {
|
||||
return LandTable.select { LandTable.isoAlpha2Code eq isoAlpha2Code }
|
||||
return LandTable.selectAll().where { LandTable.isoAlpha2Code eq isoAlpha2Code }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByIsoAlpha3Code(isoAlpha3Code: String): Boolean {
|
||||
return LandTable.select { LandTable.isoAlpha3Code eq isoAlpha3Code }
|
||||
return LandTable.selectAll().where { LandTable.isoAlpha3Code eq isoAlpha3Code }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return LandTable.select { LandTable.isActive eq true }.count()
|
||||
return LandTable.selectAll().where { LandTable.isActive eq true }.count()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ class UserAuthorizationService(
|
||||
for (roleType in roles) {
|
||||
// Find the role by type
|
||||
val rolle = rolleRepository.findByTyp(roleType)
|
||||
if (rolle != null && rolle.rolleId != null) {
|
||||
if (rolle != null) {
|
||||
// Get role permissions
|
||||
val rolleBerechtigungen = rolleBerechtigungRepository.findByRolleId(rolle.rolleId)
|
||||
.filter { it.istAktiv }
|
||||
|
||||
+12
-16
@@ -1,20 +1,16 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
// Import table definition and extension functions
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import at.mocode.members.domain.model.DomBerechtigung
|
||||
import at.mocode.members.domain.repository.BerechtigungRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.toKotlinInstant
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
|
||||
// Import table definition and extension functions
|
||||
import at.mocode.members.infrastructure.repository.BerechtigungTable
|
||||
import at.mocode.members.infrastructure.repository.insertOrUpdate
|
||||
import at.mocode.members.infrastructure.repository.toLocalDateTime
|
||||
import at.mocode.members.infrastructure.repository.toInstant
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.update
|
||||
|
||||
/**
|
||||
* Exposed-based implementation of BerechtigungRepository.
|
||||
@@ -45,35 +41,35 @@ class BerechtigungRepositoryImpl : BerechtigungRepository {
|
||||
}
|
||||
|
||||
override suspend fun findById(berechtigungId: Uuid): DomBerechtigung? {
|
||||
return BerechtigungTable.select { BerechtigungTable.id eq berechtigungId }
|
||||
return BerechtigungTable.selectAll().where { BerechtigungTable.id eq berechtigungId }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByTyp(berechtigungTyp: BerechtigungE): DomBerechtigung? {
|
||||
return BerechtigungTable.select { BerechtigungTable.berechtigungTyp eq berechtigungTyp }
|
||||
return BerechtigungTable.selectAll().where { BerechtigungTable.berechtigungTyp eq berechtigungTyp }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(name: String): List<DomBerechtigung> {
|
||||
val searchPattern = "%$name%"
|
||||
return BerechtigungTable.select { BerechtigungTable.name like searchPattern }
|
||||
return BerechtigungTable.selectAll().where { BerechtigungTable.name like searchPattern }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByRessource(ressource: String): List<DomBerechtigung> {
|
||||
return BerechtigungTable.select { BerechtigungTable.ressource eq ressource }
|
||||
return BerechtigungTable.selectAll().where { BerechtigungTable.ressource eq ressource }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByAktion(aktion: String): List<DomBerechtigung> {
|
||||
return BerechtigungTable.select { BerechtigungTable.aktion eq aktion }
|
||||
return BerechtigungTable.selectAll().where { BerechtigungTable.aktion eq aktion }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(): List<DomBerechtigung> {
|
||||
return BerechtigungTable.select { BerechtigungTable.istAktiv eq true }
|
||||
return BerechtigungTable.selectAll().where { BerechtigungTable.istAktiv eq true }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
@@ -103,7 +99,7 @@ class BerechtigungRepositoryImpl : BerechtigungRepository {
|
||||
}
|
||||
|
||||
override suspend fun existsByTyp(berechtigungTyp: BerechtigungE): Boolean {
|
||||
return BerechtigungTable.select { BerechtigungTable.berechtigungTyp eq berechtigungTyp }
|
||||
return BerechtigungTable.selectAll().where { BerechtigungTable.berechtigungTyp eq berechtigungTyp }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
|
||||
+13
-18
@@ -1,20 +1,15 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
// Import table definition and extension functions
|
||||
import at.mocode.members.domain.model.DomPerson
|
||||
import at.mocode.members.domain.repository.PersonRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.toKotlinInstant
|
||||
import kotlinx.datetime.toKotlinLocalDate
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
|
||||
// Import table definition and extension functions
|
||||
import at.mocode.members.infrastructure.repository.PersonTable
|
||||
import at.mocode.members.infrastructure.repository.insertOrUpdate
|
||||
import at.mocode.members.infrastructure.repository.toLocalDateTime
|
||||
import at.mocode.members.infrastructure.repository.toInstant
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
import org.jetbrains.exposed.sql.or
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
|
||||
/**
|
||||
* Exposed-based implementation of PersonRepository.
|
||||
@@ -25,34 +20,34 @@ import at.mocode.members.infrastructure.repository.toInstant
|
||||
class PersonRepositoryImpl : PersonRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): DomPerson? {
|
||||
return PersonTable.select { PersonTable.id eq id }
|
||||
return PersonTable.selectAll().where { PersonTable.id eq id }
|
||||
.map { rowToDomPerson(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsSatzNr(oepsSatzNr: String): DomPerson? {
|
||||
return PersonTable.select { PersonTable.oepsSatzNr eq oepsSatzNr }
|
||||
return PersonTable.selectAll().where { PersonTable.oepsSatzNr eq oepsSatzNr }
|
||||
.map { rowToDomPerson(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByStammVereinId(vereinId: Uuid): List<DomPerson> {
|
||||
return PersonTable.select { PersonTable.stammVereinId eq vereinId }
|
||||
return PersonTable.selectAll().where { PersonTable.stammVereinId eq vereinId }
|
||||
.map { rowToDomPerson(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<DomPerson> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return PersonTable.select {
|
||||
return PersonTable.selectAll().where {
|
||||
(PersonTable.nachname like searchPattern) or
|
||||
(PersonTable.vorname like searchPattern)
|
||||
(PersonTable.vorname like searchPattern)
|
||||
}
|
||||
.limit(limit)
|
||||
.map { rowToDomPerson(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<DomPerson> {
|
||||
return PersonTable.select { PersonTable.istAktiv eq true }
|
||||
return PersonTable.selectAll().where { PersonTable.istAktiv eq true }
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToDomPerson(it) }
|
||||
}
|
||||
@@ -100,12 +95,12 @@ class PersonRepositoryImpl : PersonRepository {
|
||||
}
|
||||
|
||||
override suspend fun existsByOepsSatzNr(oepsSatzNr: String): Boolean {
|
||||
return PersonTable.select { PersonTable.oepsSatzNr eq oepsSatzNr }
|
||||
return PersonTable.selectAll().where { PersonTable.oepsSatzNr eq oepsSatzNr }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return PersonTable.select { PersonTable.istAktiv eq true }
|
||||
return PersonTable.selectAll().where { PersonTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
|
||||
+14
-22
@@ -1,19 +1,12 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
// Import table definition and extension functions
|
||||
import at.mocode.members.domain.model.DomVerein
|
||||
import at.mocode.members.domain.repository.VereinRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.toKotlinInstant
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
|
||||
// Import table definition and extension functions
|
||||
import at.mocode.members.infrastructure.repository.VereinTable
|
||||
import at.mocode.members.infrastructure.repository.insertOrUpdate
|
||||
import at.mocode.members.infrastructure.repository.toLocalDateTime
|
||||
import at.mocode.members.infrastructure.repository.toInstant
|
||||
|
||||
/**
|
||||
* Exposed-based implementation of VereinRepository.
|
||||
@@ -24,48 +17,48 @@ import at.mocode.members.infrastructure.repository.toInstant
|
||||
class VereinRepositoryImpl : VereinRepository {
|
||||
|
||||
override suspend fun findById(id: Uuid): DomVerein? {
|
||||
return VereinTable.select { VereinTable.id eq id }
|
||||
return VereinTable.selectAll().where { VereinTable.id eq id }
|
||||
.map { rowToDomVerein(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsVereinsNr(oepsVereinsNr: String): DomVerein? {
|
||||
return VereinTable.select { VereinTable.oepsVereinsNr eq oepsVereinsNr }
|
||||
return VereinTable.selectAll().where { VereinTable.oepsVereinsNr eq oepsVereinsNr }
|
||||
.map { rowToDomVerein(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(searchTerm: String, limit: Int): List<DomVerein> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return VereinTable.select {
|
||||
return VereinTable.selectAll().where {
|
||||
(VereinTable.name like searchPattern) or
|
||||
(VereinTable.kuerzel like searchPattern)
|
||||
(VereinTable.kuerzel like searchPattern)
|
||||
}
|
||||
.limit(limit)
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByBundeslandId(bundeslandId: Uuid): List<DomVerein> {
|
||||
return VereinTable.select { VereinTable.bundeslandId eq bundeslandId }
|
||||
return VereinTable.selectAll().where { VereinTable.bundeslandId eq bundeslandId }
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByLandId(landId: Uuid): List<DomVerein> {
|
||||
return VereinTable.select { VereinTable.landId eq landId }
|
||||
return VereinTable.selectAll().where { VereinTable.landId eq landId }
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(limit: Int, offset: Int): List<DomVerein> {
|
||||
return VereinTable.select { VereinTable.istAktiv eq true }
|
||||
return VereinTable.selectAll().where { VereinTable.istAktiv eq true }
|
||||
.limit(limit, offset.toLong())
|
||||
.map { rowToDomVerein(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByLocation(searchTerm: String, limit: Int): List<DomVerein> {
|
||||
val searchPattern = "%$searchTerm%"
|
||||
return VereinTable.select {
|
||||
return VereinTable.selectAll().where {
|
||||
(VereinTable.ort like searchPattern) or
|
||||
(VereinTable.plz like searchPattern)
|
||||
(VereinTable.plz like searchPattern)
|
||||
}
|
||||
.limit(limit)
|
||||
.map { rowToDomVerein(it) }
|
||||
@@ -104,19 +97,18 @@ class VereinRepositoryImpl : VereinRepository {
|
||||
}
|
||||
|
||||
override suspend fun existsByOepsVereinsNr(oepsVereinsNr: String): Boolean {
|
||||
return VereinTable.select { VereinTable.oepsVereinsNr eq oepsVereinsNr }
|
||||
return VereinTable.selectAll().where { VereinTable.oepsVereinsNr eq oepsVereinsNr }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
override suspend fun countActive(): Long {
|
||||
return VereinTable.select { VereinTable.istAktiv eq true }
|
||||
return VereinTable.selectAll().where { VereinTable.istAktiv eq true }
|
||||
.count()
|
||||
}
|
||||
|
||||
override suspend fun countActiveByBundeslandId(bundeslandId: Uuid): Long {
|
||||
return VereinTable.select {
|
||||
(VereinTable.istAktiv eq true) and (VereinTable.bundeslandId eq bundeslandId)
|
||||
}
|
||||
return VereinTable.selectAll()
|
||||
.where { (VereinTable.istAktiv eq true) and (VereinTable.bundeslandId eq bundeslandId) }
|
||||
.count()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
package at.mocode.shared.config
|
||||
|
||||
import at.mocode.shared.database.DatabaseConfig
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
* Zentrale Konfigurationsverwaltung für die Anwendung.
|
||||
* Lädt Konfigurationen aus verschiedenen Quellen (Umgebungsvariablen, Property-Dateien).
|
||||
*/
|
||||
object AppConfig {
|
||||
// Aktuelle Umgebung
|
||||
val environment: AppEnvironment = AppEnvironment.current()
|
||||
|
||||
// Anwendungs-Informationen
|
||||
val appInfo = AppInfoConfig()
|
||||
|
||||
// Server-Konfiguration
|
||||
val server = ServerConfig()
|
||||
|
||||
// Sicherheits-Konfiguration
|
||||
val security = SecurityConfig()
|
||||
|
||||
// Logging-Konfiguration
|
||||
val logging = LoggingConfig()
|
||||
|
||||
// Datenbank-Konfiguration (wird nach dem Laden der Properties initialisiert)
|
||||
val database: DatabaseConfig
|
||||
|
||||
init {
|
||||
// Lade Umgebungsspezifische Properties
|
||||
val props = loadProperties()
|
||||
|
||||
// Konfiguriere Komponenten mit Properties
|
||||
appInfo.configure(props)
|
||||
server.configure(props)
|
||||
security.configure(props)
|
||||
logging.configure(props)
|
||||
|
||||
// Datenbank-Konfiguration mit Properties initialisieren
|
||||
database = DatabaseConfig.fromEnv(props)
|
||||
|
||||
// Log Konfigurationsinformationen
|
||||
if (!AppEnvironment.isProduction()) {
|
||||
println("=== Anwendungskonfiguration ===")
|
||||
println("Umgebung: $environment")
|
||||
println("App: ${appInfo.name} v${appInfo.version}")
|
||||
println("Server: Port ${server.port}, ${server.workers} Worker")
|
||||
println("Datenbank: ${database.jdbcUrl}")
|
||||
println("===============================\n")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt die Properties für die aktuelle Umgebung.
|
||||
*/
|
||||
private fun loadProperties(): Properties {
|
||||
val props = Properties()
|
||||
|
||||
// Lade Basis-Properties
|
||||
loadPropertiesFile("application.properties", props)
|
||||
|
||||
// Lade umgebungsspezifische Properties
|
||||
val envFile = when (environment) {
|
||||
AppEnvironment.DEVELOPMENT -> "application-dev.properties"
|
||||
AppEnvironment.TEST -> "application-test.properties"
|
||||
AppEnvironment.STAGING -> "application-staging.properties"
|
||||
AppEnvironment.PRODUCTION -> "application-prod.properties"
|
||||
}
|
||||
|
||||
loadPropertiesFile(envFile, props)
|
||||
|
||||
return props
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt eine Property-Datei, wenn sie existiert.
|
||||
*/
|
||||
private fun loadPropertiesFile(filename: String, props: Properties) {
|
||||
val resourceStream = javaClass.classLoader.getResourceAsStream(filename)
|
||||
if (resourceStream != null) {
|
||||
props.load(resourceStream)
|
||||
resourceStream.close()
|
||||
} else {
|
||||
// Versuche aus dem Dateisystem zu laden
|
||||
val file = File("config/$filename")
|
||||
if (file.exists()) {
|
||||
file.inputStream().use { props.load(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Wert einer Property zurück, wobei die Priorität wie folgt ist:
|
||||
* 1. Umgebungsvariable
|
||||
* 2. Property aus Datei
|
||||
* 3. Standardwert
|
||||
*/
|
||||
fun getProperty(key: String, defaultValue: String? = null): String? {
|
||||
val envKey = key.replace('.', '_').uppercase()
|
||||
return System.getenv(envKey) ?: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für Anwendungsinformationen.
|
||||
*/
|
||||
class AppInfoConfig {
|
||||
var name: String = "Meldestelle"
|
||||
var version: String = "1.0.0"
|
||||
var description: String = "Pferdesport Meldestelle System"
|
||||
|
||||
fun configure(props: Properties) {
|
||||
name = props.getProperty("app.name", name)
|
||||
version = props.getProperty("app.version", version)
|
||||
description = props.getProperty("app.description", description)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für den Server.
|
||||
*/
|
||||
class ServerConfig {
|
||||
var port: Int = System.getenv("API_PORT")?.toIntOrNull() ?: 8081
|
||||
var host: String = System.getenv("API_HOST") ?: "0.0.0.0"
|
||||
var workers: Int = Runtime.getRuntime().availableProcessors()
|
||||
var cors: CorsConfig = CorsConfig()
|
||||
|
||||
fun configure(props: Properties) {
|
||||
port = props.getProperty("server.port")?.toIntOrNull() ?: port
|
||||
host = props.getProperty("server.host") ?: host
|
||||
workers = props.getProperty("server.workers")?.toIntOrNull() ?: workers
|
||||
|
||||
// CORS Konfiguration
|
||||
cors.enabled = props.getProperty("server.cors.enabled")?.toBoolean() ?: cors.enabled
|
||||
props.getProperty("server.cors.allowedOrigins")?.split(",")?.map { it.trim() }?.let {
|
||||
cors.allowedOrigins = it
|
||||
}
|
||||
}
|
||||
|
||||
class CorsConfig {
|
||||
var enabled: Boolean = true
|
||||
var allowedOrigins: List<String> = listOf("*")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für die Sicherheit.
|
||||
*/
|
||||
class SecurityConfig {
|
||||
var jwt = JwtConfig()
|
||||
|
||||
fun configure(props: Properties) {
|
||||
// JWT Konfiguration
|
||||
jwt.secret = System.getenv("JWT_SECRET") ?: props.getProperty("security.jwt.secret") ?: jwt.secret
|
||||
jwt.issuer = System.getenv("JWT_ISSUER") ?: props.getProperty("security.jwt.issuer") ?: jwt.issuer
|
||||
jwt.audience = System.getenv("JWT_AUDIENCE") ?: props.getProperty("security.jwt.audience") ?: jwt.audience
|
||||
jwt.realm = System.getenv("JWT_REALM") ?: props.getProperty("security.jwt.realm") ?: jwt.realm
|
||||
|
||||
props.getProperty("security.jwt.expirationInMinutes")?.toLongOrNull()?.let {
|
||||
jwt.expirationInMinutes = it
|
||||
}
|
||||
}
|
||||
|
||||
class JwtConfig {
|
||||
var secret: String = "default-jwt-secret-key-please-change-in-production"
|
||||
var issuer: String = "meldestelle-api"
|
||||
var audience: String = "meldestelle-clients"
|
||||
var realm: String = "meldestelle"
|
||||
var expirationInMinutes: Long = 60 * 24 // 24 Stunden
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration für das Logging.
|
||||
*/
|
||||
class LoggingConfig {
|
||||
var level: String = if (AppEnvironment.isProduction()) "INFO" else "DEBUG"
|
||||
var logRequests: Boolean = true
|
||||
var logResponses: Boolean = !AppEnvironment.isProduction()
|
||||
|
||||
fun configure(props: Properties) {
|
||||
level = props.getProperty("logging.level") ?: level
|
||||
logRequests = props.getProperty("logging.requests")?.toBoolean() ?: logRequests
|
||||
logResponses = props.getProperty("logging.responses")?.toBoolean() ?: logResponses
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package at.mocode.shared.config
|
||||
|
||||
/**
|
||||
* Aufzählung der verschiedenen Anwendungsumgebungen.
|
||||
*/
|
||||
enum class AppEnvironment {
|
||||
DEVELOPMENT, // Lokale Entwicklungsumgebung
|
||||
TEST, // Testumgebung (CI/CD, Integrationstests)
|
||||
STAGING, // Vorabproduktionsumgebung
|
||||
PRODUCTION; // Produktionsumgebung
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Ermittelt die aktuelle Umgebung basierend auf der APP_ENV Umgebungsvariable.
|
||||
*
|
||||
* @return Die aktuelle Umgebung (Standardmäßig DEVELOPMENT wenn nicht definiert)
|
||||
*/
|
||||
fun current(): AppEnvironment {
|
||||
val envName = System.getenv("APP_ENV")?.uppercase() ?: "DEVELOPMENT"
|
||||
return try {
|
||||
valueOf(envName)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
println("Warnung: Unbekannte Umgebung '$envName', verwende DEVELOPMENT")
|
||||
DEVELOPMENT
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Entwicklungsumgebung ist.
|
||||
*/
|
||||
fun isDevelopment() = current() == DEVELOPMENT
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Testumgebung ist.
|
||||
*/
|
||||
fun isTest() = current() == TEST
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Staging-Umgebung ist.
|
||||
*/
|
||||
fun isStaging() = current() == STAGING
|
||||
|
||||
/**
|
||||
* Prüft, ob die aktuelle Umgebung die Produktionsumgebung ist.
|
||||
*/
|
||||
fun isProduction() = current() == PRODUCTION
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,45 @@
|
||||
package at.mocode.shared.database
|
||||
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
* Konfiguration für die Datenbankverbindung.
|
||||
* Parameter werden aus Umgebungsvariablen gelesen oder Standardwerte verwendet.
|
||||
* Parameter werden aus Umgebungsvariablen oder Property-Dateien gelesen.
|
||||
*/
|
||||
data class DatabaseConfig(
|
||||
val jdbcUrl: String,
|
||||
val username: String,
|
||||
val password: String,
|
||||
val driverClassName: String = "org.postgresql.Driver",
|
||||
val maxPoolSize: Int = 10
|
||||
val maxPoolSize: Int = 10,
|
||||
val autoMigrate: Boolean = true
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Erstellt eine Datenbank-Konfiguration aus Umgebungsvariablen.
|
||||
* Erstellt eine Datenbank-Konfiguration aus Umgebungsvariablen und Properties.
|
||||
* Wenn keine Umgebungsvariablen gefunden werden, werden Standardwerte für die Entwicklung verwendet.
|
||||
*/
|
||||
fun fromEnv(): DatabaseConfig {
|
||||
val host = System.getenv("DB_HOST") ?: "localhost"
|
||||
val port = System.getenv("DB_PORT") ?: "5432"
|
||||
val database = System.getenv("DB_NAME") ?: "meldestelle_db"
|
||||
val username = System.getenv("DB_USER") ?: "meldestelle_user"
|
||||
val password = System.getenv("DB_PASSWORD") ?: "secure_password_change_me"
|
||||
fun fromEnv(props: Properties = Properties()): DatabaseConfig {
|
||||
// Priorität: Umgebungsvariablen > Properties > Standardwerte
|
||||
val host = System.getenv("DB_HOST") ?: props.getProperty("database.host") ?: "localhost"
|
||||
val port = System.getenv("DB_PORT") ?: props.getProperty("database.port") ?: "5432"
|
||||
val database = System.getenv("DB_NAME") ?: props.getProperty("database.name") ?: "meldestelle_db"
|
||||
val username = System.getenv("DB_USER") ?: props.getProperty("database.username") ?: "meldestelle_user"
|
||||
val password = System.getenv("DB_PASSWORD") ?: props.getProperty("database.password") ?: "secure_password_change_me"
|
||||
val maxPoolSize = System.getenv("DB_MAX_POOL_SIZE")?.toIntOrNull()
|
||||
?: props.getProperty("database.maxPoolSize")?.toIntOrNull()
|
||||
?: 10
|
||||
val autoMigrate = System.getenv("DB_AUTO_MIGRATE")?.toBoolean()
|
||||
?: props.getProperty("database.autoMigrate")?.toBoolean()
|
||||
?: true
|
||||
|
||||
return DatabaseConfig(
|
||||
jdbcUrl = "jdbc:postgresql://$host:$port/$database",
|
||||
username = username,
|
||||
password = password
|
||||
password = password,
|
||||
driverClassName = "org.postgresql.Driver",
|
||||
maxPoolSize = maxPoolSize,
|
||||
autoMigrate = autoMigrate
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user