refactor: enhance platform configuration, database schema handling, and Keycloak setup
Improved `PlatformConfig` API base URL resolution with enhanced logging and fallback logic. Revised database initialization with version checks, schema migration, and error handling. Updated Keycloak configuration to enable `Direct Access Grants` and refine CORS/redirect settings. Adjusted Webpack proxy settings for correct API routing.
This commit is contained in:
+12
-2
@@ -43,9 +43,19 @@ class AuthApiClient(
|
||||
formParameters = Parameters.build {
|
||||
append("grant_type", "password")
|
||||
append("client_id", clientId)
|
||||
if (!clientSecret.isNullOrBlank()) {
|
||||
|
||||
// IMPORTANT: Only send client_secret if it's NOT a public client (like 'web-app')
|
||||
// Keycloak rejects requests from public clients that contain a client_secret.
|
||||
// We check if the client ID suggests a public client or if secret is explicitly provided.
|
||||
// For now, we rely on the fact that 'web-app' is public and should NOT have a secret sent.
|
||||
|
||||
// Logic: If clientId is 'web-app', we force ignore the secret, or we rely on caller to pass null.
|
||||
// Since AppConstants might still have the secret for 'postman-client', we need to be careful.
|
||||
|
||||
if (!clientSecret.isNullOrBlank() && clientId != "web-app") {
|
||||
append("client_secret", clientSecret)
|
||||
}
|
||||
|
||||
append("username", username)
|
||||
append("password", password)
|
||||
}
|
||||
@@ -89,7 +99,7 @@ class AuthApiClient(
|
||||
formParameters = Parameters.build {
|
||||
append("grant_type", "refresh_token")
|
||||
append("client_id", clientId)
|
||||
if (!clientSecret.isNullOrBlank()) {
|
||||
if (!clientSecret.isNullOrBlank() && clientId != "web-app") {
|
||||
append("client_secret", clientSecret)
|
||||
}
|
||||
append("refresh_token", refreshToken)
|
||||
|
||||
+3
-1
@@ -28,9 +28,11 @@ val authModule = module {
|
||||
|
||||
// Bridge to core network TokenProvider without adding a hard dependency there
|
||||
single<TokenProvider> {
|
||||
// We need to capture the AuthTokenManager instance to avoid issues with 'this' context in JS
|
||||
val tokenManager = get<AuthTokenManager>()
|
||||
object : TokenProvider {
|
||||
override fun getAccessToken(): String? {
|
||||
return get<AuthTokenManager>().getToken()
|
||||
return tokenManager.getToken()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-1
@@ -5,6 +5,8 @@ import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -14,6 +16,7 @@ import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@@ -26,6 +29,7 @@ fun LoginScreen(
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
val passwordFocusRequester = remember { FocusRequester() }
|
||||
var passwordVisible by remember { mutableStateOf(false) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -75,7 +79,19 @@ fun LoginScreen(
|
||||
enabled = !uiState.isLoading,
|
||||
isError = uiState.passwordError != null,
|
||||
supportingText = uiState.passwordError?.let { { Text(it) } },
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
trailingIcon = {
|
||||
val image = if (passwordVisible)
|
||||
Icons.Filled.Visibility
|
||||
else
|
||||
Icons.Filled.VisibilityOff
|
||||
|
||||
val description = if (passwordVisible) "Passwort verbergen" else "Passwort anzeigen"
|
||||
|
||||
IconButton(onClick = { passwordVisible = !passwordVisible }) {
|
||||
Icon(imageVector = image, description)
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Done
|
||||
|
||||
+4
-1
@@ -114,7 +114,10 @@ class LoginViewModel(
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// Fire-and-forget sync call; Bearer token added by Ktor Auth plugin
|
||||
apiClient.post("/api/members/sync")
|
||||
// IMPORTANT: Use relative path (no leading slash) so Ktor appends it to baseUrl
|
||||
// baseUrl is http://localhost:8080/api (JS) or http://localhost:8081 (JVM)
|
||||
// Result: http://localhost:8080/api/members/sync -> Proxy -> http://localhost:8081/api/members/sync
|
||||
apiClient.post("members/sync")
|
||||
} catch (_: Exception) {
|
||||
// Non-fatal: Wir zeigen Sync-Fehler im Login nicht an
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user