mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
fix: Wavelog sync via v1 API logbook_get_worked_grids (3.0.2 has no API v2)
This commit is contained in:
1 parent
eb1a5d51a2
commit
13df0dfcec
4 files changed
+40
-36
No files matched your search
+35
-31
@@ -20,64 +20,68 @@ package com.rtbishop.look4sat.core.data.repository
|
||||
import com.rtbishop.look4sat.core.domain.repository.IWavelogRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* Fetches the list of worked gridsquares from a self-hosted Wavelog instance.
|
||||
*
|
||||
* Uses the Wavelog API v2 endpoint (Wavelog >= 3.1.0):
|
||||
* GET {base}/index.php/api/v2/lookup?grid=all
|
||||
* Authorization: Bearer wl2_...
|
||||
* Response: {"data": {"grids": ["JN47", "JO30"], "count": 2}, ...}
|
||||
* Uses the Wavelog v1 API endpoint (verified against Wavelog 3.0.2):
|
||||
* POST {base}/index.php/api/logbook_get_worked_grids
|
||||
* Body: {"key": "<api key>", "logbook_id": "<logbook id>"}
|
||||
* Response 201: JSON array of 4-char gridsquares, e.g. ["JN47","JO30"]
|
||||
* (see Wavelog Api.php logbook_get_worked_grids / Api_model::get_grids_worked_in_logbook)
|
||||
*
|
||||
* Uses a plain HttpURLConnection (bearer auth header) — IRemoteSource does not
|
||||
* support custom headers, and no feature module touches this class directly.
|
||||
* The token field carries "<api_key>:<logbook_id>" so one credential line
|
||||
* configures both values (the dialog explains this to the user).
|
||||
*/
|
||||
class WavelogRepository : IWavelogRepository {
|
||||
|
||||
/**
|
||||
* Fetch worked grids. Returns the grid set, or null on any failure
|
||||
* (network error, non-200, malformed response, bad token).
|
||||
*/
|
||||
override suspend fun fetchWorkedGrids(url: String, token: String): Set<String>? = withContext(Dispatchers.IO) {
|
||||
if (url.isBlank() || token.isBlank()) return@withContext null
|
||||
val parts = token.trim().split(":")
|
||||
val apiKey = parts.getOrNull(0)?.trim().orEmpty()
|
||||
val logbookId = parts.getOrNull(1)?.trim().orEmpty()
|
||||
if (url.isBlank() || apiKey.isBlank() || logbookId.isBlank()) return@withContext null
|
||||
val base = url.trim().trimEnd('/')
|
||||
val requestUrl = "$base/index.php/api/v2/lookup?grid=all"
|
||||
val requestUrl = "$base/index.php/api/logbook_get_worked_grids"
|
||||
try {
|
||||
// IRemoteSource.getNetworkStream does not support custom headers, so
|
||||
// run the bearer-token request directly here via java.net (simple GET,
|
||||
// no OkHttp dependency added to this module's public API).
|
||||
val connection = java.net.URL(requestUrl).openConnection() as java.net.HttpURLConnection
|
||||
connectionTimeouts(connection)
|
||||
connection.setRequestProperty("Authorization", "Bearer ${token.trim()}")
|
||||
connection.requestMethod = "GET"
|
||||
connection.connectTimeout = 10_000
|
||||
connection.readTimeout = 15_000
|
||||
connection.requestMethod = "POST"
|
||||
connection.doOutput = true
|
||||
connection.setRequestProperty("Content-Type", "application/json")
|
||||
val body = JSONObject().apply {
|
||||
put("key", apiKey)
|
||||
put("logbook_id", logbookId)
|
||||
}.toString().toByteArray()
|
||||
connection.outputStream.use { it.write(body) }
|
||||
val code = connection.responseCode
|
||||
if (code != 200) {
|
||||
if (code !in 200..299) {
|
||||
connection.disconnect()
|
||||
return@withContext null
|
||||
}
|
||||
val body = connection.inputStream.bufferedReader().use { it.readText() }
|
||||
val response = connection.inputStream.bufferedReader().use { it.readText() }
|
||||
connection.disconnect()
|
||||
parseWorkedGrids(body)
|
||||
parseWorkedGrids(response)
|
||||
} catch (e: Exception) {
|
||||
println("WavelogRepository fetch failure: $e")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun connectionTimeouts(connection: java.net.HttpURLConnection) {
|
||||
connection.connectTimeout = 10_000
|
||||
connection.readTimeout = 15_000
|
||||
}
|
||||
|
||||
internal fun parseWorkedGrids(body: String): Set<String>? {
|
||||
return try {
|
||||
val obj = JSONObject(body)
|
||||
val data = obj.optJSONObject("data") ?: return null
|
||||
val grids = data.optJSONArray("grids") ?: return null
|
||||
val array = when {
|
||||
body.trimStart().startsWith("[") -> JSONArray(body)
|
||||
else -> {
|
||||
// Tolerate {"grids": [...]} wrappers too
|
||||
JSONObject(body).optJSONArray("grids") ?: return null
|
||||
}
|
||||
}
|
||||
val result = mutableSetOf<String>()
|
||||
for (i in 0 until grids.length()) {
|
||||
val grid = grids.optString(i, "").trim().uppercase()
|
||||
for (i in 0 until array.length()) {
|
||||
val grid = array.optString(i, "").trim().uppercase()
|
||||
// Keep only well-formed 4-character Maidenhead squares (e.g. OL62)
|
||||
if (grid.length == 4 && grid[0] in 'A'..'R' && grid[1] in 'A'..'R'
|
||||
&& grid[2] in '0'..'9' && grid[3] in '0'..'9'
|
||||
|
||||
Reference in new issue
Block a user