Point Android monitor to public bot API
This commit is contained in:
@@ -33,10 +33,16 @@ android/TradeBotMonitor/app/build/outputs/apk/debug/app-debug.apk
|
|||||||
В настройках приложения укажи адрес API, например:
|
В настройках приложения укажи адрес API, например:
|
||||||
|
|
||||||
```text
|
```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.
|
||||||
|
|
||||||
## Переобучение
|
## Переобучение
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,16 @@ import android.content.Context
|
|||||||
class AppPrefs(context: Context) {
|
class AppPrefs(context: Context) {
|
||||||
private val prefs = context.getSharedPreferences("tradebot_monitor", Context.MODE_PRIVATE)
|
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
|
var apiBaseUrl: String
|
||||||
get() = prefs.getString("api_base_url", "http://192.168.0.185:8787") ?: "http://192.168.0.185:8787"
|
get() = normalizeBaseUrl(prefs.getString("api_base_url", DEFAULT_API_BASE_URL) ?: DEFAULT_API_BASE_URL)
|
||||||
set(value) = prefs.edit().putString("api_base_url", value.trim().trimEnd('/')).apply()
|
set(value) = prefs.edit().putString("api_base_url", normalizeBaseUrl(value)).apply()
|
||||||
|
|
||||||
var commandToken: String
|
var commandToken: String
|
||||||
get() = prefs.getString("command_token", "") ?: ""
|
get() = prefs.getString("command_token", "") ?: ""
|
||||||
@@ -32,4 +39,19 @@ class AppPrefs(context: Context) {
|
|||||||
var retrainIntervalHours: Int
|
var retrainIntervalHours: Int
|
||||||
get() = prefs.getInt("retrain_interval_hours", 6)
|
get() = prefs.getInt("retrain_interval_hours", 6)
|
||||||
set(value) = prefs.edit().putInt("retrain_interval_hours", value.coerceAtLeast(1)).apply()
|
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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -245,7 +245,7 @@ class MainActivity : Activity() {
|
|||||||
val data = snapshot
|
val data = snapshot
|
||||||
val box = scrollContent()
|
val box = scrollContent()
|
||||||
val apiInput = input("Адрес API бота", prefs.apiBaseUrl)
|
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
|
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||||
}
|
}
|
||||||
box.addView(panel("Подключение", LinearLayout(this).apply {
|
box.addView(panel("Подключение", LinearLayout(this).apply {
|
||||||
|
|||||||
+19
-6
@@ -1,5 +1,6 @@
|
|||||||
package xyz.kusoft.tradebotmonitor
|
package xyz.kusoft.tradebotmonitor
|
||||||
|
|
||||||
|
import android.util.Base64
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
@@ -63,9 +64,7 @@ class TradeBotApi(
|
|||||||
connectTimeout = 6000
|
connectTimeout = 6000
|
||||||
readTimeout = 9000
|
readTimeout = 9000
|
||||||
setRequestProperty("Accept", "application/json")
|
setRequestProperty("Accept", "application/json")
|
||||||
if (token.isNotBlank()) {
|
applyAuthHeaders(this, token)
|
||||||
setRequestProperty("X-TradeBot-Token", token)
|
|
||||||
}
|
|
||||||
if (method == "POST") {
|
if (method == "POST") {
|
||||||
doOutput = true
|
doOutput = true
|
||||||
setRequestProperty("Content-Type", "application/json")
|
setRequestProperty("Content-Type", "application/json")
|
||||||
@@ -229,9 +228,7 @@ class RetrainCommandClient(
|
|||||||
doOutput = true
|
doOutput = true
|
||||||
setRequestProperty("Content-Type", "application/json")
|
setRequestProperty("Content-Type", "application/json")
|
||||||
setRequestProperty("Accept", "application/json,text/plain,*/*")
|
setRequestProperty("Accept", "application/json,text/plain,*/*")
|
||||||
if (token.isNotBlank()) {
|
applyAuthHeaders(this, token)
|
||||||
setRequestProperty("X-TradeBot-Token", token)
|
|
||||||
}
|
|
||||||
outputStream.use { stream -> stream.write("{\"source\":\"android\"}".toByteArray(StandardCharsets.UTF_8)) }
|
outputStream.use { stream -> stream.write("{\"source\":\"android\"}".toByteArray(StandardCharsets.UTF_8)) }
|
||||||
}
|
}
|
||||||
val code = connection.responseCode
|
val code = connection.responseCode
|
||||||
@@ -244,3 +241,19 @@ class RetrainCommandClient(
|
|||||||
return text.ifBlank { "Команда retrain отправлена" }
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user