From a0a4b5698dc175b94f41bc18389aa39c3fe1a466 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Mon, 18 May 2026 09:40:22 +0800 Subject: [PATCH] fix: add User-Agent/Referer headers, GET index first for session cookie, handle non-JSON login response --- .../kotlin/com/bricks/mp/sage/SageClient.kt | 53 ++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/shared/src/commonMain/kotlin/com/bricks/mp/sage/SageClient.kt b/shared/src/commonMain/kotlin/com/bricks/mp/sage/SageClient.kt index 414883c..bcc1211 100644 --- a/shared/src/commonMain/kotlin/com/bricks/mp/sage/SageClient.kt +++ b/shared/src/commonMain/kotlin/com/bricks/mp/sage/SageClient.kt @@ -16,9 +16,10 @@ import kotlinx.serialization.json.* * Sage 客户端 - 处理登录、Session 管理和 UI 加载 * * Sage 使用 Cookie Session 认证: - * 1. POST /rbac/user/userpassword_login.dspy 登录获取 session cookie - * 2. 后续请求自动携带 cookie - * 3. GET /xxx.ui 获取 JSON 格式的 UI 描述 + * 1. GET 首页获取初始 session cookie + * 2. POST /rbac/user/userpassword_login.dspy 登录 + * 3. 后续请求自动携带 cookie + * 4. GET /xxx.ui 获取 JSON 格式的 UI 描述 */ class SageClient { @@ -54,45 +55,55 @@ class SageClient { suspend fun login(username: String, password: String): Boolean = mutex.withLock { _loginError.value = null try { + // Step 1: GET the index page to obtain initial session cookies + // Sage requires a valid session cookie before accepting login POST + val indexResponse = client.get("$baseUrl/") + println("[Sage] GET / status: ${indexResponse.status}") + + // Step 2: POST login val url = "$baseUrl/rbac/user/userpassword_login.dspy" val encodedUser = java.net.URLEncoder.encode(username, "UTF-8") val encodedPass = java.net.URLEncoder.encode(password, "UTF-8") val formBody = "username=$encodedUser&passwd=$encodedPass" val response = client.post(url) { + header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36") + header(HttpHeaders.Referrer, "$baseUrl/") contentType(ContentType.Application.FormUrlEncoded) setBody(formBody) } val body = response.bodyAsText() println("[Sage] Login response status: ${response.status}") - println("[Sage] Login response body: ${body.take(200)}") + println("[Sage] Login response body: ${body.take(300)}") + + // Check if response is JSON + if (!body.trimStart().startsWith("{")) { + // Non-JSON response - likely an error page or auth failure + if (response.status.value == 401 || response.status.value == 403) { + _loginError.value = "登录失败: 服务器拒绝请求 (${response.status.value})" + } else { + _loginError.value = "登录失败: 服务器返回非JSON响应 (${response.status.value})" + } + println("[Sage] Non-JSON login response: ${body.take(200)}") + return@withLock false + } // Sage 返回 UiMessage 格式: {"widgettype": "Message", "options": {...}} - // 或者返回错误: {"widgettype": "Error", "options": {...}} val json = try { Json.parseToJsonElement(body).jsonObject } catch (e: Exception) { - _loginError.value = "登录响应格式错误: ${e.message}" + _loginError.value = "登录响应解析错误: ${e.message}" return@withLock false } val widgetType = json["widgettype"]?.jsonPrimitive?.content if (widgetType == "Message" || widgetType == "UiMessage") { - // 检查是否有 session cookie val cookies = cookieStorage.get(URLBuilder(baseUrl).build()) - if (cookies.isNotEmpty()) { - println("[Sage] Login successful, got ${cookies.size} cookies") - _isLoggedIn.value = true - true - } else { - // 即使没有 cookie,如果服务器返回成功也算登录成功 - // 有些部署可能使用 token 而非 cookie - println("[Sage] Login successful (no cookies)") - _isLoggedIn.value = true - true - } + println("[Sage] Login successful, cookies: ${cookies.size}") + _isLoggedIn.value = true + true } else { // 错误消息 val options = json["options"]?.jsonObject ?: JsonObject(emptyMap()) @@ -118,7 +129,9 @@ class SageClient { val url = if (path.startsWith("http")) path else "$baseUrl/${path.trimStart('/')}" println("[Sage] Fetching UI: $url") - val response = client.get(url) + val response = client.get(url) { + header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36") + } val body = response.bodyAsText() if (!response.status.isSuccess()) { @@ -151,6 +164,7 @@ class SageClient { url { params.forEach { (k, v) -> parameters.append(k, v) } } + header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36") when { jsonBody != null -> { contentType(ContentType.Application.Json) @@ -169,6 +183,7 @@ class SageClient { url { params.forEach { (k, v) -> parameters.append(k, v) } } + header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36") } }