chore: remove unused meldestelle-portal module
- Deleted obsolete `meldestelle-portal` module, including all associated screens, configurations, tests, and assets. - Includes removal of Compose multiplatform dependencies in `build.gradle.kts`. - Cleaned up redundant files such as `AppPreview`, `AuthStatusScreen`, `DashboardScreen`, and associated core implementations. - Streamlined module references in `settings.gradle.kts`. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
@@ -23,6 +23,7 @@ kotlin {
|
||||
commonMain.dependencies {
|
||||
// Depend on core domain for User/Role types used by navigation API
|
||||
implementation(projects.frontend.core.domain)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
}
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
|
||||
+18
-1
@@ -28,8 +28,13 @@ sealed class AppScreen(val route: String) {
|
||||
data object Funktionaere : AppScreen("/funktionaere")
|
||||
data object Meisterschaften : AppScreen("/meisterschaften")
|
||||
data object Cups : AppScreen("/cups")
|
||||
data object StammdatenImport : AppScreen("/stammdaten/import")
|
||||
|
||||
companion object {
|
||||
private val VERANSTALTUNG_DETAIL = Regex("/veranstaltung/(\\d+)$")
|
||||
private val TURNIER_DETAIL = Regex("/veranstaltung/(\\d+)/turnier/(\\d+)$")
|
||||
private val TURNIER_NEU = Regex("/veranstaltung/(\\d+)/turnier/neu$")
|
||||
|
||||
fun fromRoute(route: String): AppScreen {
|
||||
return when (route) {
|
||||
Routes.HOME -> Landing
|
||||
@@ -49,7 +54,19 @@ sealed class AppScreen(val route: String) {
|
||||
"/funktionaere" -> Funktionaere
|
||||
"/meisterschaften" -> Meisterschaften
|
||||
"/cups" -> Cups
|
||||
else -> Landing // Default fallback
|
||||
"/stammdaten/import" -> StammdatenImport
|
||||
else -> {
|
||||
TURNIER_DETAIL.matchEntire(route)?.destructured?.let { (vId, tId) ->
|
||||
return TurnierDetail(vId.toLong(), tId.toLong())
|
||||
}
|
||||
TURNIER_NEU.matchEntire(route)?.destructured?.let { (vId) ->
|
||||
return TurnierNeu(vId.toLong())
|
||||
}
|
||||
VERANSTALTUNG_DETAIL.matchEntire(route)?.destructured?.let { (id) ->
|
||||
return VeranstaltungDetail(id.toLong())
|
||||
}
|
||||
Landing // Default fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -1,9 +1,18 @@
|
||||
package at.mocode.frontend.core.navigation
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Minimal navigation abstraction used by core navigation components.
|
||||
* The actual implementation lives in shells/apps and delegates to the app's router.
|
||||
* Navigations-Abstraktion für alle Shells.
|
||||
* Die konkrete Implementierung liegt in der jeweiligen Shell (z.B. DesktopNavigationPort).
|
||||
*/
|
||||
interface NavigationPort {
|
||||
/** Aktuell angezeigter Screen als reaktiver State. */
|
||||
val currentScreen: StateFlow<AppScreen>
|
||||
|
||||
/** Navigation via Route-String (z.B. für Deep-Links). */
|
||||
fun navigateTo(route: String)
|
||||
|
||||
/** Typsichere Navigation direkt via AppScreen-Objekt. */
|
||||
fun navigateToScreen(screen: AppScreen)
|
||||
}
|
||||
|
||||
+14
-99
@@ -11,18 +11,20 @@ import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
/**
|
||||
* Simple token provider interface so the core network module does not depend on auth-feature.
|
||||
* Schnittstelle zur Token-Bereitstellung – entkoppelt core-network von core-auth.
|
||||
*/
|
||||
interface TokenProvider {
|
||||
fun getAccessToken(): String?
|
||||
}
|
||||
|
||||
/**
|
||||
* Koin module providing HttpClients.
|
||||
* Koin-Modul mit zwei HttpClient-Instanzen:
|
||||
* - "baseHttpClient": Roh-Client für Auth/Keycloak (kein Token-Header)
|
||||
* - "apiClient": Konfigurierter Client für das API-Gateway (Auth-Header, Retry, Timeout)
|
||||
*/
|
||||
val networkModule = module {
|
||||
|
||||
// 1. Base Client (Raw, for Auth/Keycloak)
|
||||
// 1. Basis-Client (für Auth-Endpunkte, ohne Bearer-Token)
|
||||
single(named("baseHttpClient")) {
|
||||
HttpClient {
|
||||
install(ContentNegotiation) {
|
||||
@@ -39,48 +41,27 @@ val networkModule = module {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. API Client (Configured for Gateway & Auth Header)
|
||||
// 2. API-Client (Gateway, mit Bearer-Token, Retry & Timeout)
|
||||
single(named("apiClient")) {
|
||||
// Resolve TokenProvider lazily to avoid circular dependency issues during init
|
||||
// We use a provider lambda to get the TokenProvider instance when needed
|
||||
// This avoids resolving it immediately during module definition
|
||||
val koinScope = this@single
|
||||
|
||||
HttpClient {
|
||||
// JSON (kotlinx) configuration
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
)
|
||||
json(Json { ignoreUnknownKeys = true; isLenient = true; encodeDefaults = true })
|
||||
}
|
||||
|
||||
// Request timeouts
|
||||
install(HttpTimeout) {
|
||||
requestTimeoutMillis = 15_000
|
||||
connectTimeoutMillis = 10_000
|
||||
socketTimeoutMillis = 15_000
|
||||
}
|
||||
|
||||
// Automatic simple retry on network exceptions and 5xx
|
||||
install(HttpRequestRetry) {
|
||||
maxRetries = 3
|
||||
retryIf { _, response ->
|
||||
val s = response.status.value
|
||||
s == 0 || s >= 500
|
||||
}
|
||||
retryIf { _, response -> response.status.value.let { it == 0 || it >= 500 } }
|
||||
exponentialDelay()
|
||||
}
|
||||
|
||||
// Base URL configuration
|
||||
defaultRequest {
|
||||
val base = NetworkConfig.baseUrl.trimEnd('/')
|
||||
url(base)
|
||||
url(NetworkConfig.baseUrl.trimEnd('/'))
|
||||
}
|
||||
|
||||
// Logging for development
|
||||
install(Logging) {
|
||||
logger = object : Logger {
|
||||
override fun log(message: String) {
|
||||
@@ -90,79 +71,13 @@ val networkModule = module {
|
||||
level = LogLevel.INFO
|
||||
}
|
||||
}.also { client ->
|
||||
// Dynamic Auth Header Injection via HttpSend plugin
|
||||
// This ensures we get the CURRENT token for each request
|
||||
// Bearer-Token pro Request dynamisch injizieren (lazy, damit kein Circular-Dependency)
|
||||
client.plugin(HttpSend).intercept { request ->
|
||||
try {
|
||||
// Resolve TokenProvider dynamically from Koin scope
|
||||
// This assumes Koin is initialized and accessible
|
||||
// Since we are inside a Koin component, we should be able to get it?
|
||||
// No, 'this' here is HttpSendScope.
|
||||
|
||||
// We need to capture the Koin scope or use GlobalContext if necessary,
|
||||
// BUT better: we inject the TokenProvider into the module definition lambda
|
||||
// and use it here.
|
||||
|
||||
// However, `get<TokenProvider>()` might fail if not yet registered.
|
||||
// Let's try to resolve it safely.
|
||||
|
||||
// The issue with the previous code was likely that `get<TokenProvider>()` was called
|
||||
// during module definition time (or bean creation time), and if it wasn't ready or
|
||||
// if it was null (due to try-catch), the interceptor logic was skipped or broken.
|
||||
|
||||
// Let's try to get it from the Koin instance that created this client.
|
||||
// But we are inside `single { ... }`.
|
||||
|
||||
// We can capture the `Scope` from the `single` block.
|
||||
// val scope = this // Koin Scope
|
||||
|
||||
// But we can't easily pass `scope` into `intercept`.
|
||||
|
||||
// Let's try to resolve TokenProvider lazily using a lazy delegate or similar.
|
||||
// Or just resolve it inside the interceptor if we can access Koin.
|
||||
|
||||
// Since we are in `single`, we can get the provider.
|
||||
// The previous error `TypeError: this.getToken_wiq2bn_k$ is not a function`
|
||||
// was in AuthModule, which we fixed.
|
||||
|
||||
// The current error `Error_0: Fail to fetch` is a CORS error on the network level,
|
||||
// NOT a JS runtime error in the interceptor (unless the interceptor causes it).
|
||||
|
||||
// Wait, the logs show:
|
||||
// [baseClient] REQUEST: .../token
|
||||
// Access to fetch at ... blocked by CORS policy
|
||||
|
||||
// This confirms it is a CORS issue on the Keycloak server side or the browser side.
|
||||
// The JS error `TypeError` is GONE in the latest log!
|
||||
|
||||
// So the interceptor logic in NetworkModule might be fine, or at least not the cause of the CORS error.
|
||||
// But let's make it robust anyway.
|
||||
|
||||
// We will use a safe lazy resolution pattern.
|
||||
} catch (_: Exception) {
|
||||
// ignore
|
||||
}
|
||||
execute(request)
|
||||
}
|
||||
|
||||
// Re-applying the logic with proper Koin resolution
|
||||
val koinScope = this@single
|
||||
|
||||
client.plugin(HttpSend).intercept { request ->
|
||||
try {
|
||||
// Attempt to resolve TokenProvider from the capturing scope
|
||||
val tokenProvider = try {
|
||||
koinScope.get<TokenProvider>()
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
val token = tokenProvider?.getAccessToken()
|
||||
if (token != null) {
|
||||
request.header("Authorization", "Bearer $token")
|
||||
}
|
||||
val token = koinScope.get<TokenProvider>().getAccessToken()
|
||||
if (token != null) request.header("Authorization", "Bearer $token")
|
||||
} catch (e: Exception) {
|
||||
println("[apiClient] Error injecting auth header: $e")
|
||||
println("[apiClient] Auth-Header konnte nicht gesetzt werden: $e")
|
||||
}
|
||||
execute(request)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user