package com.bitcointxoko.gudariwallet.api import com.bitcointxoko.gudariwallet.BuildConfig import com.bitcointxoko.gudariwallet.security.SecretStore import com.bitcointxoko.gudariwallet.util.WalletConstants import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit object LNbitsClient { // Single shared client — used by both Retrofit and WebSocket val httpClient: OkHttpClient = OkHttpClient.Builder() .connectTimeout(WalletConstants.CONNECT_TIMEOUT_S, TimeUnit.SECONDS) .readTimeout(WalletConstants.READ_TIMEOUT_S, TimeUnit.SECONDS) .writeTimeout(WalletConstants.WRITE_TIMEOUT_S, TimeUnit.SECONDS) .apply { if (BuildConfig.DEBUG) { addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }) } } .build() fun create(secrets: SecretStore): LNbitsApi { // Auth header interceptor — reads invoice key per-request (unchanged) // val authInterceptor = Interceptor { chain -> // val key = runBlocking { secrets.invoiceKey() } // chain.proceed( // chain.request().newBuilder() // .header("X-Api-Key", key) // .build() // ) // } // Dynamic base URL interceptor — rewrites the placeholder host to the // real base URL at request time, not at Retrofit construction time. // This means credentials only need to be in DataStore by the time the // first HTTP request fires, not at app startup. val dynamicBaseUrlInterceptor = Interceptor { chain -> val realBaseUrl = runBlocking { secrets.baseUrl() } .ifBlank { "https://placeholder.invalid" } .trimEnd('/') val realHttpUrl = realBaseUrl.toHttpUrl() val originalUrl = chain.request().url val newUrl = originalUrl.newBuilder() .scheme(realHttpUrl.scheme) .host(realHttpUrl.host) .port(realHttpUrl.port) .build() chain.proceed( chain.request().newBuilder() .url(newUrl) .build() ) } val client = httpClient.newBuilder() // .addInterceptor(authInterceptor) .addInterceptor(dynamicBaseUrlInterceptor) .apply { // Logging goes LAST — after URL rewrite — so logs show the real URL if (BuildConfig.DEBUG) { addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }) } } .build() return Retrofit.Builder() .baseUrl("https://placeholder.invalid/") // structurally required by Retrofit; // always overwritten by the interceptor .client(client) .addConverterFactory(GsonConverterFactory.create(GsonProvider.gson)) .build() .create(LNbitsApi::class.java) } }