fixing web-app
This commit is contained in:
+1
-1
@@ -94,7 +94,7 @@ temp-data/
|
|||||||
**/temp/
|
**/temp/
|
||||||
**/.temp/
|
**/.temp/
|
||||||
# Exception: Allow temp/ping-service for Docker builds
|
# Exception: Allow temp/ping-service for Docker builds
|
||||||
!temp/ping-service/
|
!services/ping/ping-service/
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
# Gradle wrapper executable (keep gradle wrapper jar)
|
# Gradle wrapper executable (keep gradle wrapper jar)
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
|
||||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
|
||||||
alias(libs.plugins.kotlinSerialization)
|
|
||||||
alias(libs.plugins.composeMultiplatform)
|
|
||||||
alias(libs.plugins.composeCompiler)
|
|
||||||
// alias(libs.plugins.composeHotReload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Project version configuration
|
|
||||||
version = "1.0.0"
|
|
||||||
group = "at.mocode"
|
|
||||||
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
|
|
||||||
// Configure JVM toolchain for all JVM targets
|
|
||||||
jvmToolchain(21)
|
|
||||||
|
|
||||||
jvm()
|
|
||||||
|
|
||||||
js {
|
|
||||||
browser()
|
|
||||||
binaries.executable()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalWasmDsl::class)
|
|
||||||
wasmJs {
|
|
||||||
browser()
|
|
||||||
binaries.executable()
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
commonMain.dependencies {
|
|
||||||
|
|
||||||
implementation(projects.client.shared)
|
|
||||||
// Core Compose Dependencies - minimiert für kleinere Bundle-Größe
|
|
||||||
implementation(compose.runtime)
|
|
||||||
implementation(compose.foundation)
|
|
||||||
implementation(compose.material3)
|
|
||||||
implementation(compose.ui)
|
|
||||||
implementation(compose.components.resources)
|
|
||||||
// UiToolingPreview nur für Development, nicht für Production WASM
|
|
||||||
implementation(compose.components.uiToolingPreview)
|
|
||||||
|
|
||||||
implementation(libs.androidx.lifecycle.viewmodelCompose)
|
|
||||||
implementation(libs.androidx.lifecycle.runtimeCompose)
|
|
||||||
|
|
||||||
// HTTP client dependencies for ping-service - optimiert
|
|
||||||
implementation(libs.ktor.client.core)
|
|
||||||
implementation(libs.ktor.client.contentNegotiation)
|
|
||||||
implementation(libs.ktor.client.serialization.kotlinx.json)
|
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
|
||||||
implementation(libs.kotlinx.serialization.json)
|
|
||||||
}
|
|
||||||
commonTest.dependencies {
|
|
||||||
implementation(libs.kotlin.test)
|
|
||||||
}
|
|
||||||
jvmMain.dependencies {
|
|
||||||
implementation(compose.desktop.currentOs)
|
|
||||||
implementation(libs.kotlinx.coroutines.swing)
|
|
||||||
implementation(libs.ktor.client.cio)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compose.desktop {
|
|
||||||
application {
|
|
||||||
mainClass = "at.mocode.MainKt"
|
|
||||||
|
|
||||||
nativeDistributions {
|
|
||||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
|
|
||||||
packageName = "Meldestelle"
|
|
||||||
packageVersion = "1.0.0"
|
|
||||||
|
|
||||||
// Application metadata
|
|
||||||
description = "Pferdesport Meldestelle System - Client Application"
|
|
||||||
copyright = "© 2025 Meldestelle Project"
|
|
||||||
vendor = "at.mocode"
|
|
||||||
|
|
||||||
// Platform-specific configurations
|
|
||||||
linux {
|
|
||||||
iconFile.set(project.file("src/commonMain/resources/icon.png"))
|
|
||||||
packageName = "meldestelle"
|
|
||||||
debMaintainer = "stefan@mocode.at"
|
|
||||||
menuGroup = "Office"
|
|
||||||
}
|
|
||||||
|
|
||||||
windows {
|
|
||||||
iconFile.set(project.file("src/commonMain/resources/icon.ico"))
|
|
||||||
menuGroup = "Meldestelle"
|
|
||||||
upgradeUuid = "61DAB35E-17CB-43B8-8A72-39876CF0E021"
|
|
||||||
}
|
|
||||||
|
|
||||||
macOS {
|
|
||||||
iconFile.set(project.file("src/commonMain/resources/icon.icns"))
|
|
||||||
bundleID = "at.mocode.meldestelle"
|
|
||||||
packageBuildVersion = "1.0.0"
|
|
||||||
packageVersion = "1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes.release.proguard {
|
|
||||||
configurationFiles.from(project.file("compose-desktop.pro"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<vector
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="450dp"
|
|
||||||
android:height="450dp"
|
|
||||||
android:viewportWidth="64"
|
|
||||||
android:viewportHeight="64">
|
|
||||||
<path
|
|
||||||
android:pathData="M56.25,18V46L32,60 7.75,46V18L32,4Z"
|
|
||||||
android:fillColor="#6075f2"/>
|
|
||||||
<path
|
|
||||||
android:pathData="m41.5,26.5v11L32,43V60L56.25,46V18Z"
|
|
||||||
android:fillColor="#6b57ff"/>
|
|
||||||
<path
|
|
||||||
android:pathData="m32,43 l-9.5,-5.5v-11L7.75,18V46L32,60Z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:centerX="23.131"
|
|
||||||
android:centerY="18.441"
|
|
||||||
android:gradientRadius="42.132"
|
|
||||||
android:type="radial">
|
|
||||||
<item
|
|
||||||
android:offset="0"
|
|
||||||
android:color="#FF5383EC"/>
|
|
||||||
<item
|
|
||||||
android:offset="0.867"
|
|
||||||
android:color="#FF7F52FF"/>
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:pathData="M22.5,26.5 L32,21 41.5,26.5 56.25,18 32,4 7.75,18Z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:startX="44.172"
|
|
||||||
android:startY="4.377"
|
|
||||||
android:endX="17.973"
|
|
||||||
android:endY="34.035"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:offset="0"
|
|
||||||
android:color="#FF33C3FF"/>
|
|
||||||
<item
|
|
||||||
android:offset="0.878"
|
|
||||||
android:color="#FF5383EC"/>
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:pathData="m32,21 l9.526,5.5v11L32,43 22.474,37.5v-11z"
|
|
||||||
android:fillColor="#000000"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
package at.mocode
|
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.*
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import at.mocode.ui.PingViewModel
|
|
||||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Preview
|
|
||||||
fun App() {
|
|
||||||
MaterialTheme {
|
|
||||||
val viewModel: PingViewModel = viewModel()
|
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.background(MaterialTheme.colorScheme.background)
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp)
|
|
||||||
.verticalScroll(rememberScrollState()),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Header
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Meldestelle - Ping Service Client",
|
|
||||||
style = MaterialTheme.typography.headlineMedium
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "Trace-Bullet Implementation",
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.secondary
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action Buttons
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "API Tests",
|
|
||||||
style = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.simplePing() },
|
|
||||||
enabled = !uiState.isLoading,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Text("Simple Ping")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.enhancedPing() },
|
|
||||||
enabled = !uiState.isLoading,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Text("Enhanced Ping")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.healthCheck() },
|
|
||||||
enabled = !uiState.isLoading,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Text("Health Check")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.enhancedPing(simulate = true) },
|
|
||||||
enabled = !uiState.isLoading,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
colors = ButtonDefaults.buttonColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text("Test Failure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loading Indicator
|
|
||||||
if (uiState.isLoading) {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error Display
|
|
||||||
uiState.error?.let { error ->
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.errorContainer
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = error,
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
color = MaterialTheme.colorScheme.onErrorContainer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Results Display
|
|
||||||
uiState.lastPingResponse?.let { response ->
|
|
||||||
ResultCard("Simple Ping Result", response)
|
|
||||||
}
|
|
||||||
|
|
||||||
uiState.lastEnhancedResponse?.let { response ->
|
|
||||||
ResultCard("Enhanced Ping Result", response)
|
|
||||||
}
|
|
||||||
|
|
||||||
uiState.lastHealthResponse?.let { response ->
|
|
||||||
ResultCard("Health Check Result", response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ResultCard(title: String, data: Any) {
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Text(
|
|
||||||
text = data.toString(),
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package at.mocode.ui
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import at.mocode.model.EnhancedPingResponse
|
|
||||||
import at.mocode.model.HealthResponse
|
|
||||||
import at.mocode.model.PingResponse
|
|
||||||
import at.mocode.service.PingService
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
data class PingUiState(
|
|
||||||
val isLoading: Boolean = false,
|
|
||||||
val lastPingResponse: PingResponse? = null,
|
|
||||||
val lastEnhancedResponse: EnhancedPingResponse? = null,
|
|
||||||
val lastHealthResponse: HealthResponse? = null,
|
|
||||||
val error: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
class PingViewModel : ViewModel() {
|
|
||||||
|
|
||||||
private val pingService = PingService()
|
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(PingUiState())
|
|
||||||
val uiState: StateFlow<PingUiState> = _uiState.asStateFlow()
|
|
||||||
|
|
||||||
fun simplePing() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
_uiState.value = _uiState.value.copy(isLoading = true, error = null)
|
|
||||||
|
|
||||||
pingService.ping()
|
|
||||||
.onSuccess { response ->
|
|
||||||
_uiState.value = _uiState.value.copy(
|
|
||||||
isLoading = false,
|
|
||||||
lastPingResponse = response
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.onFailure { exception ->
|
|
||||||
_uiState.value = _uiState.value.copy(
|
|
||||||
isLoading = false,
|
|
||||||
error = "Ping failed: ${exception.message}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun enhancedPing(simulate: Boolean = false) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
_uiState.value = _uiState.value.copy(isLoading = true, error = null)
|
|
||||||
|
|
||||||
pingService.enhancedPing(simulate)
|
|
||||||
.onSuccess { response ->
|
|
||||||
_uiState.value = _uiState.value.copy(
|
|
||||||
isLoading = false,
|
|
||||||
lastEnhancedResponse = response
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.onFailure { exception ->
|
|
||||||
_uiState.value = _uiState.value.copy(
|
|
||||||
isLoading = false,
|
|
||||||
error = "Enhanced ping failed: ${exception.message}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun healthCheck() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
_uiState.value = _uiState.value.copy(isLoading = true, error = null)
|
|
||||||
|
|
||||||
pingService.health()
|
|
||||||
.onSuccess { response ->
|
|
||||||
_uiState.value = _uiState.value.copy(
|
|
||||||
isLoading = false,
|
|
||||||
lastHealthResponse = response
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.onFailure { exception ->
|
|
||||||
_uiState.value = _uiState.value.copy(
|
|
||||||
isLoading = false,
|
|
||||||
error = "Health check failed: ${exception.message}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package at.mocode
|
|
||||||
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class ComposeAppCommonTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun example() {
|
|
||||||
assertEquals(3, 1 + 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package at.mocode
|
|
||||||
|
|
||||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.window.Window
|
|
||||||
import androidx.compose.ui.window.application
|
|
||||||
|
|
||||||
fun main() = application {
|
|
||||||
Window(
|
|
||||||
onCloseRequest = ::exitApplication,
|
|
||||||
title = "Meldestelle",
|
|
||||||
) {
|
|
||||||
App()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun AppDesktopPreview() {
|
|
||||||
App()
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package at.mocode
|
|
||||||
|
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
||||||
import androidx.compose.ui.window.ComposeViewport
|
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
|
||||||
fun main() {
|
|
||||||
ComposeViewport {
|
|
||||||
App()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Meldestelle</title>
|
|
||||||
<link type="text/css" rel="stylesheet" href="styles.css">
|
|
||||||
<script type="application/javascript" src="composeApp.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
html, body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Temporary workaround for [KT-80582](https://youtrack.jetbrains.com/issue/KT-80582)
|
|
||||||
*
|
|
||||||
* This file should be safe to be removed once the ticket is closed and the project is updated to Kotlin version which solves that issue.
|
|
||||||
*/
|
|
||||||
config.watchOptions = config.watchOptions || {
|
|
||||||
ignored: ["**/*.kt", "**/node_modules"]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.devServer) {
|
|
||||||
config.devServer.static = config.devServer.static.map(file => {
|
|
||||||
if (typeof file === "string") {
|
|
||||||
return {
|
|
||||||
directory: file,
|
|
||||||
watch: false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlinMultiplatform)
|
|
||||||
alias(libs.plugins.kotlinSerialization)
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
|
|
||||||
// Configure JVM toolchain for all JVM targets
|
|
||||||
jvmToolchain(21)
|
|
||||||
|
|
||||||
jvm()
|
|
||||||
|
|
||||||
js {
|
|
||||||
browser()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalWasmDsl::class)
|
|
||||||
wasmJs {
|
|
||||||
browser()
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
commonMain.dependencies {
|
|
||||||
// HTTP Client dependencies for ping-service
|
|
||||||
implementation(libs.ktor.client.core)
|
|
||||||
implementation(libs.ktor.client.contentNegotiation)
|
|
||||||
implementation(libs.ktor.client.serialization.kotlinx.json)
|
|
||||||
implementation(libs.kotlinx.serialization.json)
|
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
|
||||||
}
|
|
||||||
|
|
||||||
jvmMain.dependencies {
|
|
||||||
implementation(libs.ktor.client.cio)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsMain.dependencies {
|
|
||||||
implementation(libs.ktor.client.js)
|
|
||||||
}
|
|
||||||
|
|
||||||
commonTest.dependencies {
|
|
||||||
implementation(libs.kotlin.test)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package at.mocode.service
|
|
||||||
|
|
||||||
import at.mocode.model.EnhancedPingResponse
|
|
||||||
import at.mocode.model.HealthResponse
|
|
||||||
import at.mocode.model.PingResponse
|
|
||||||
import io.ktor.client.*
|
|
||||||
import io.ktor.client.call.*
|
|
||||||
import io.ktor.client.plugins.*
|
|
||||||
import io.ktor.client.plugins.contentnegotiation.*
|
|
||||||
import io.ktor.client.request.*
|
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
|
|
||||||
class PingService {
|
|
||||||
|
|
||||||
private val client = HttpClient {
|
|
||||||
install(ContentNegotiation) {
|
|
||||||
json(Json {
|
|
||||||
ignoreUnknownKeys = true
|
|
||||||
isLenient = true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
install(HttpTimeout) {
|
|
||||||
requestTimeoutMillis = 10000
|
|
||||||
connectTimeoutMillis = 5000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val baseUrl = getBaseUrl()
|
|
||||||
|
|
||||||
suspend fun ping(): Result<PingResponse> = runCatching {
|
|
||||||
client.get("$baseUrl/ping").body<PingResponse>()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun enhancedPing(simulate: Boolean = false): Result<EnhancedPingResponse> = runCatching {
|
|
||||||
// Fallback: Use simple ping and enhance response locally
|
|
||||||
val response = client.get("$baseUrl/ping").body<PingResponse>()
|
|
||||||
EnhancedPingResponse(
|
|
||||||
status = response.status,
|
|
||||||
timestamp = response.timestamp,
|
|
||||||
service = response.service,
|
|
||||||
circuitBreakerState = if (simulate) "OPEN" else "CLOSED",
|
|
||||||
responseTime = 100L
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun health(): Result<HealthResponse> = runCatching {
|
|
||||||
// Fallback: Use simple ping to determine health
|
|
||||||
val response = client.get("$baseUrl/ping").body<PingResponse>()
|
|
||||||
HealthResponse(
|
|
||||||
status = response.status,
|
|
||||||
timestamp = response.timestamp,
|
|
||||||
service = response.service,
|
|
||||||
healthy = response.status == "pong"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun testFailure(): Result<EnhancedPingResponse> = runCatching {
|
|
||||||
// Simulate failure for testing
|
|
||||||
throw RuntimeException("Simulated failure for testing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Platform-specific base URL
|
|
||||||
expect fun getBaseUrl(): String
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
|
alias(libs.plugins.kotlinSerialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "at.mocode"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
js {
|
||||||
|
browser()
|
||||||
|
}
|
||||||
|
// Keep WASM for dev since sources already present
|
||||||
|
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
|
||||||
|
wasmJs {
|
||||||
|
browser()
|
||||||
|
}
|
||||||
|
|
||||||
|
jvmToolchain(21)
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.services.ping.pingApi)
|
||||||
|
|
||||||
|
implementation(libs.ktor.client.core)
|
||||||
|
implementation(libs.ktor.client.contentNegotiation)
|
||||||
|
implementation(libs.ktor.client.serialization.kotlinx.json)
|
||||||
|
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val commonTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.kotlin.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jvmMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.ktor.client.cio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package at.mocode.model
|
||||||
|
|
||||||
|
// Deprecated local DTOs are replaced by typealiases to the shared API contract.
|
||||||
|
// This preserves binary/source compatibility for existing imports while enforcing SSoT.
|
||||||
|
|
||||||
|
typealias PingResponse = at.mocode.ping.api.PingResponse
|
||||||
|
|
||||||
|
typealias EnhancedPingResponse = at.mocode.ping.api.EnhancedPingResponse
|
||||||
|
|
||||||
|
typealias HealthResponse = at.mocode.ping.api.HealthResponse
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package at.mocode.ping.client
|
||||||
|
|
||||||
|
import at.mocode.ping.api.EnhancedPingResponse
|
||||||
|
import at.mocode.ping.api.HealthResponse
|
||||||
|
import at.mocode.ping.api.PingApi
|
||||||
|
import at.mocode.ping.api.PingResponse
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.parameter
|
||||||
|
import at.mocode.service.getBaseUrl
|
||||||
|
|
||||||
|
class PingApiClient(
|
||||||
|
private val client: HttpClient,
|
||||||
|
baseUrl: String = getBaseUrl()
|
||||||
|
) : PingApi {
|
||||||
|
private val base = "$baseUrl/api/ping"
|
||||||
|
|
||||||
|
override suspend fun simplePing(): PingResponse = client.get("$base/simple").body()
|
||||||
|
|
||||||
|
override suspend fun enhancedPing(simulate: Boolean): EnhancedPingResponse =
|
||||||
|
client.get("$base/enhanced") { parameter("simulate", simulate) }.body()
|
||||||
|
|
||||||
|
override suspend fun healthCheck(): HealthResponse = client.get("$base/health").body()
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package at.mocode.service
|
||||||
|
|
||||||
|
import at.mocode.model.EnhancedPingResponse
|
||||||
|
import at.mocode.model.HealthResponse
|
||||||
|
import at.mocode.model.PingResponse
|
||||||
|
import at.mocode.ping.client.PingApiClient
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
|
import io.ktor.client.plugins.contentnegotiation.*
|
||||||
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
@Deprecated("Use PingApiClient directly for new code")
|
||||||
|
class PingService(
|
||||||
|
private val client: HttpClient = HttpClient {
|
||||||
|
install(ContentNegotiation) {
|
||||||
|
json(Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
isLenient = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
install(HttpTimeout) {
|
||||||
|
requestTimeoutMillis = 10000
|
||||||
|
connectTimeoutMillis = 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
private val api = PingApiClient(client)
|
||||||
|
|
||||||
|
suspend fun ping(): Result<PingResponse> = runCatching { api.simplePing() }
|
||||||
|
|
||||||
|
suspend fun enhancedPing(simulate: Boolean = false): Result<EnhancedPingResponse> =
|
||||||
|
runCatching { api.enhancedPing(simulate) }
|
||||||
|
|
||||||
|
suspend fun health(): Result<HealthResponse> = runCatching { api.healthCheck() }
|
||||||
|
|
||||||
|
suspend fun testFailure(): Result<EnhancedPingResponse> = runCatching {
|
||||||
|
throw RuntimeException("Simulated failure for testing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Platform-specific base URL required by PingApiClient via getBaseUrl()
|
||||||
|
expect fun getBaseUrl(): String
|
||||||
@@ -24,11 +24,12 @@ services:
|
|||||||
SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DOCKER:-docker}
|
SPRING_PROFILES_ACTIVE: ${DOCKER_SPRING_PROFILES_DOCKER:-docker}
|
||||||
container_name: meldestelle-ping-service
|
container_name: meldestelle-ping-service
|
||||||
environment:
|
environment:
|
||||||
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-dev}
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-docker}
|
||||||
SERVER_PORT: ${PING_SERVICE_PORT:-8082}
|
SERVER_PORT: ${PING_SERVICE_PORT:-8082}
|
||||||
CONSUL_HOST: consul
|
CONSUL_HOST: consul
|
||||||
CONSUL_PORT: ${CONSUL_PORT:-8500}
|
CONSUL_PORT: ${CONSUL_PORT:-8500}
|
||||||
CONSUL_ENABLED: true
|
CONSUL_ENABLED: ${CONSUL_ENABLED:-true}
|
||||||
|
SPRING_CLOUD_CONSUL_DISCOVERY_ENABLED: ${CONSUL_ENABLED:-true}
|
||||||
DB_HOST: postgres
|
DB_HOST: postgres
|
||||||
DB_PORT: 5432
|
DB_PORT: 5432
|
||||||
DB_NAME: ${POSTGRES_DB:-meldestelle}
|
DB_NAME: ${POSTGRES_DB:-meldestelle}
|
||||||
@@ -41,8 +42,15 @@ services:
|
|||||||
- "${PING_SERVICE_PORT:-8082}:8082"
|
- "${PING_SERVICE_PORT:-8082}:8082"
|
||||||
networks:
|
networks:
|
||||||
- meldestelle-network
|
- meldestelle-network
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
consul:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD", "curl", "--fail", "http://localhost:${PING_SERVICE_PORT:-8082}/actuator/health" ]
|
test: [ "CMD", "curl", "--fail", "http://localhost:8082/actuator/health/readiness" ]
|
||||||
interval: 15s
|
interval: 15s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ COPY gradle/ gradle/
|
|||||||
COPY platform/ platform/
|
COPY platform/ platform/
|
||||||
|
|
||||||
# Copy client directories (required by settings.gradle.kts)
|
# Copy client directories (required by settings.gradle.kts)
|
||||||
COPY client/ client/
|
COPY clients/ clients/
|
||||||
|
|
||||||
# Copy core directories (required by settings.gradle.kts)
|
# Copy core directories (required by settings.gradle.kts)
|
||||||
COPY core/ core/
|
COPY core/ core/
|
||||||
@@ -64,19 +64,19 @@ COPY docs/ docs/
|
|||||||
# Copy root build configuration
|
# Copy root build configuration
|
||||||
COPY build.gradle.kts ./
|
COPY build.gradle.kts ./
|
||||||
|
|
||||||
# Copy ping-service specific files last (changes most frequently)
|
# Copy ping modules (changes most frequently)
|
||||||
COPY temp/ping-service/build.gradle.kts temp/ping-service/
|
COPY services/ping/ping-api/ services/ping/ping-api/
|
||||||
COPY temp/ping-service/src/ temp/ping-service/src/
|
COPY services/ping/ping-service/ services/ping/ping-service/
|
||||||
|
|
||||||
# Download and cache dependencies in a separate layer with build cache
|
# Download and cache dependencies in a separate layer with build cache
|
||||||
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
|
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
|
||||||
--mount=type=cache,target=/home/gradle/.gradle/wrapper \
|
--mount=type=cache,target=/home/gradle/.gradle/wrapper \
|
||||||
./gradlew :temp:ping-service:dependencies --no-daemon --info
|
./gradlew :services:ping:ping-service:dependencies --no-daemon --info
|
||||||
|
|
||||||
# Build the application with optimizations and build cache
|
# Build the application with optimizations and build cache
|
||||||
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
|
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
|
||||||
--mount=type=cache,target=/home/gradle/.gradle/wrapper \
|
--mount=type=cache,target=/home/gradle/.gradle/wrapper \
|
||||||
./gradlew :temp:ping-service:bootJar --no-daemon --info \
|
./gradlew :services:ping:ping-service:bootJar --no-daemon --info \
|
||||||
-Pspring.profiles.active=${SPRING_PROFILES_ACTIVE}
|
-Pspring.profiles.active=${SPRING_PROFILES_ACTIVE}
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
@@ -133,7 +133,7 @@ RUN apk update && \
|
|||||||
|
|
||||||
# Copy the built JAR from builder stage with proper ownership
|
# Copy the built JAR from builder stage with proper ownership
|
||||||
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} \
|
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} \
|
||||||
/workspace/temp/ping-service/build/libs/*.jar app.jar
|
/workspace/services/ping/ping-service/build/libs/*.jar app.jar
|
||||||
|
|
||||||
# Switch to non-root user
|
# Switch to non-root user
|
||||||
USER ${APP_USER}
|
USER ${APP_USER}
|
||||||
|
|||||||
+18
-891
File diff suppressed because it is too large
Load Diff
@@ -454,4 +454,4 @@ spec:
|
|||||||
|
|
||||||
**Letzte Aktualisierung**: 25. Juli 2025
|
**Letzte Aktualisierung**: 25. Juli 2025
|
||||||
|
|
||||||
Für weitere Informationen zur Gesamtarchitektur siehe [README.md](../README.md).
|
Für weitere Informationen zur Gesamtarchitektur siehe [README.md](../../README.md).
|
||||||
@@ -530,4 +530,4 @@ spec:
|
|||||||
|
|
||||||
**Letzte Aktualisierung**: 25. Juli 2025
|
**Letzte Aktualisierung**: 25. Juli 2025
|
||||||
|
|
||||||
Für weitere Informationen zur Gesamtarchitektur siehe [README.md](../README.md).
|
Für weitere Informationen zur Gesamtarchitektur siehe [README.md](../../README.md).
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user