feat: add :backend:config module with ConfigApiClient (Step 3)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0ede5c690b
commit
ea460f7ef0
5 changed files with 129 additions and 0 deletions
|
|
@ -64,6 +64,8 @@ dependencies {
|
||||||
implementation(project(":core:domain"))
|
implementation(project(":core:domain"))
|
||||||
implementation(project(":core:data"))
|
implementation(project(":core:data"))
|
||||||
implementation(project(":core:ui"))
|
implementation(project(":core:ui"))
|
||||||
|
implementation(project(":backend:api"))
|
||||||
|
implementation(project(":backend:config"))
|
||||||
|
|
||||||
implementation(platform(libs.compose.bom))
|
implementation(platform(libs.compose.bom))
|
||||||
implementation(libs.compose.ui)
|
implementation(libs.compose.ui)
|
||||||
|
|
|
||||||
45
android/backend/config/build.gradle.kts
Normal file
45
android/backend/config/build.gradle.kts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.library)
|
||||||
|
alias(libs.plugins.compose.compiler)
|
||||||
|
alias(libs.plugins.serialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "io.clawdroid.backend.config"
|
||||||
|
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":backend:api"))
|
||||||
|
implementation(project(":core:ui"))
|
||||||
|
|
||||||
|
implementation(libs.ktor.client.okhttp)
|
||||||
|
implementation(libs.ktor.client.content.negotiation)
|
||||||
|
implementation(libs.ktor.serialization.json)
|
||||||
|
implementation(libs.serialization.json)
|
||||||
|
|
||||||
|
implementation(platform(libs.compose.bom))
|
||||||
|
implementation(libs.compose.ui)
|
||||||
|
implementation(libs.compose.material3)
|
||||||
|
implementation(libs.navigation.compose)
|
||||||
|
|
||||||
|
implementation(libs.koin.android)
|
||||||
|
implementation(libs.koin.compose)
|
||||||
|
|
||||||
|
implementation(libs.coroutines.android)
|
||||||
|
|
||||||
|
debugImplementation(libs.compose.ui.tooling)
|
||||||
|
}
|
||||||
2
android/backend/config/src/main/AndroidManifest.xml
Normal file
2
android/backend/config/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package io.clawdroid.backend.config
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.engine.okhttp.OkHttp
|
||||||
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.put
|
||||||
|
import io.ktor.client.request.setBody
|
||||||
|
import io.ktor.client.statement.HttpResponse
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.http.contentType
|
||||||
|
import io.ktor.http.isSuccess
|
||||||
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ConfigSchema(val sections: List<SchemaSection>)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SchemaSection(val key: String, val label: String, val fields: List<SchemaField>)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SchemaField(
|
||||||
|
val key: String,
|
||||||
|
val label: String,
|
||||||
|
val type: String,
|
||||||
|
val secret: Boolean = false,
|
||||||
|
val default: JsonElement = Json.parseToJsonElement("null"),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SaveConfigResult(
|
||||||
|
val status: String? = null,
|
||||||
|
val restart: Boolean = false,
|
||||||
|
val error: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
class ConfigApiClient : Closeable {
|
||||||
|
private val baseUrl: String get() = "http://127.0.0.1:18790"
|
||||||
|
|
||||||
|
private val client = HttpClient(OkHttp) {
|
||||||
|
install(ContentNegotiation) {
|
||||||
|
json(Json { ignoreUnknownKeys = true; isLenient = true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getSchema(): ConfigSchema {
|
||||||
|
return client.get("$baseUrl/api/config/schema").ensureSuccess().body()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getConfig(): JsonObject {
|
||||||
|
return client.get("$baseUrl/api/config").ensureSuccess().body()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveConfig(config: JsonObject): SaveConfigResult {
|
||||||
|
return client.put("$baseUrl/api/config") {
|
||||||
|
contentType(ContentType.Application.Json)
|
||||||
|
setBody(config)
|
||||||
|
}.ensureSuccess().body()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
client.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun HttpResponse.ensureSuccess(): HttpResponse {
|
||||||
|
if (!status.isSuccess()) {
|
||||||
|
val error = runCatching { body<SaveConfigResult>().error }.getOrNull()
|
||||||
|
throw IOException("HTTP ${status.value}: ${error ?: "request failed"}")
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,3 +19,4 @@ include(":core:domain")
|
||||||
include(":core:data")
|
include(":core:data")
|
||||||
include(":core:ui")
|
include(":core:ui")
|
||||||
include(":backend:api")
|
include(":backend:api")
|
||||||
|
include(":backend:config")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue