diff --git a/android/TradeBotMonitor/README.md b/android/TradeBotMonitor/README.md index 55800f6..45550b2 100644 --- a/android/TradeBotMonitor/README.md +++ b/android/TradeBotMonitor/README.md @@ -33,10 +33,16 @@ android/TradeBotMonitor/app/build/outputs/apk/debug/app-debug.apk В настройках приложения укажи адрес API, например: ```text -http://192.168.0.185:8787 +https://tb.kusoft.xyz ``` -Важно: сейчас Docker на Pi пробрасывает порт как `127.0.0.1:8787:8787`, то есть API доступен только на самом Pi. Для телефона нужен безопасный доступ по LAN/VPN/reverse proxy или осознанное изменение проброса порта. +Этот адрес установлен в приложении по умолчанию. Если в настройках ввести просто `tb.kusoft.xyz`, приложение само добавит `https://`. + +Если домен защищён авторизацией, в поле `API auth` можно указать: + +- `login:password` — приложение отправит HTTP Basic; +- `Basic ...` — готовый Basic header; +- `Bearer ...` или просто токен — приложение отправит Bearer. ## Переобучение diff --git a/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/AppPrefs.kt b/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/AppPrefs.kt index 1fb7bd1..856df6f 100644 --- a/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/AppPrefs.kt +++ b/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/AppPrefs.kt @@ -5,9 +5,16 @@ import android.content.Context class AppPrefs(context: Context) { private val prefs = context.getSharedPreferences("tradebot_monitor", Context.MODE_PRIVATE) + init { + val saved = prefs.getString("api_base_url", null)?.trim() + if (saved.isNullOrBlank() || saved == LEGACY_PI_API_BASE_URL) { + prefs.edit().putString("api_base_url", DEFAULT_API_BASE_URL).apply() + } + } + var apiBaseUrl: String - get() = prefs.getString("api_base_url", "http://192.168.0.185:8787") ?: "http://192.168.0.185:8787" - set(value) = prefs.edit().putString("api_base_url", value.trim().trimEnd('/')).apply() + get() = normalizeBaseUrl(prefs.getString("api_base_url", DEFAULT_API_BASE_URL) ?: DEFAULT_API_BASE_URL) + set(value) = prefs.edit().putString("api_base_url", normalizeBaseUrl(value)).apply() var commandToken: String get() = prefs.getString("command_token", "") ?: "" @@ -32,4 +39,19 @@ class AppPrefs(context: Context) { var retrainIntervalHours: Int get() = prefs.getInt("retrain_interval_hours", 6) set(value) = prefs.edit().putInt("retrain_interval_hours", value.coerceAtLeast(1)).apply() + + private fun normalizeBaseUrl(value: String): String { + val trimmed = value.trim().trimEnd('/') + if (trimmed.isBlank()) return DEFAULT_API_BASE_URL + return if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) { + trimmed + } else { + "https://$trimmed" + } + } + + private companion object { + const val DEFAULT_API_BASE_URL = "https://tb.kusoft.xyz" + const val LEGACY_PI_API_BASE_URL = "http://192.168.0.185:8787" + } } diff --git a/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/MainActivity.kt b/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/MainActivity.kt index 44b2fab..26578b3 100644 --- a/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/MainActivity.kt +++ b/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/MainActivity.kt @@ -245,7 +245,7 @@ class MainActivity : Activity() { val data = snapshot val box = scrollContent() val apiInput = input("Адрес API бота", prefs.apiBaseUrl) - val tokenInput = input("Токен команд, если включим защиту API", prefs.commandToken).apply { + val tokenInput = input("API auth: token, Bearer, Basic или login:password", prefs.commandToken).apply { inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD } box.addView(panel("Подключение", LinearLayout(this).apply { diff --git a/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/TradeBotApi.kt b/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/TradeBotApi.kt index 3d5bf3b..f3657ae 100644 --- a/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/TradeBotApi.kt +++ b/android/TradeBotMonitor/app/src/main/java/xyz/kusoft/tradebotmonitor/TradeBotApi.kt @@ -1,5 +1,6 @@ package xyz.kusoft.tradebotmonitor +import android.util.Base64 import org.json.JSONArray import org.json.JSONObject import java.io.BufferedReader @@ -63,9 +64,7 @@ class TradeBotApi( connectTimeout = 6000 readTimeout = 9000 setRequestProperty("Accept", "application/json") - if (token.isNotBlank()) { - setRequestProperty("X-TradeBot-Token", token) - } + applyAuthHeaders(this, token) if (method == "POST") { doOutput = true setRequestProperty("Content-Type", "application/json") @@ -229,9 +228,7 @@ class RetrainCommandClient( doOutput = true setRequestProperty("Content-Type", "application/json") setRequestProperty("Accept", "application/json,text/plain,*/*") - if (token.isNotBlank()) { - setRequestProperty("X-TradeBot-Token", token) - } + applyAuthHeaders(this, token) outputStream.use { stream -> stream.write("{\"source\":\"android\"}".toByteArray(StandardCharsets.UTF_8)) } } val code = connection.responseCode @@ -244,3 +241,19 @@ class RetrainCommandClient( return text.ifBlank { "Команда retrain отправлена" } } } + +private fun applyAuthHeaders(connection: HttpURLConnection, token: String) { + val value = token.trim() + if (value.isBlank()) return + connection.setRequestProperty("X-TradeBot-Token", value) + val authorization = when { + value.startsWith("Basic ", ignoreCase = true) -> value + value.startsWith("Bearer ", ignoreCase = true) -> value + ":" in value -> { + val encoded = Base64.encodeToString(value.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP) + "Basic $encoded" + } + else -> "Bearer $value" + } + connection.setRequestProperty("Authorization", authorization) +}