mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
v0.4.3: fix IdC token refresh with form-urlencoded format, add debug logging
This commit is contained in:
@@ -7,7 +7,8 @@
|
||||
"Bash(cargo test:*)",
|
||||
"Bash(cargo build:*)",
|
||||
"Bash(npm run check:*)",
|
||||
"Bash(npm run:*)"
|
||||
"Bash(npm run:*)",
|
||||
"Bash(tree:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "proxycast",
|
||||
"private": true,
|
||||
"version": "0.4.2",
|
||||
"version": "0.4.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
Generated
+76
-1
@@ -8,6 +8,18 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
@@ -810,6 +822,18 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
@@ -1332,12 +1356,30 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
@@ -1865,6 +1907,17 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
@@ -2789,7 +2842,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast"
|
||||
version = "0.4.1"
|
||||
version = "0.4.3"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"axum",
|
||||
@@ -2799,6 +2852,7 @@ dependencies = [
|
||||
"md5",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
@@ -2807,6 +2861,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -3058,6 +3113,20 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
@@ -4513,6 +4582,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "urlpattern"
|
||||
version = "0.3.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "proxycast"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
description = "AI API Proxy Desktop App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
@@ -29,6 +29,8 @@ futures = "0.3"
|
||||
async-stream = "0.3"
|
||||
regex = "1"
|
||||
md5 = "0.7"
|
||||
urlencoding = "2"
|
||||
rusqlite = { version = "0.31", features = ["bundled"] }
|
||||
|
||||
[features]
|
||||
default = ["custom-protocol"]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// MCP Servers DAO - 数据访问对象
|
||||
// TODO: 实现 mcp_servers 表的 CRUD 操作
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod mcp;
|
||||
pub mod prompts;
|
||||
pub mod providers;
|
||||
@@ -0,0 +1,2 @@
|
||||
// Prompts DAO - 数据访问对象
|
||||
// TODO: 实现 prompts 表的 CRUD 操作
|
||||
@@ -0,0 +1,2 @@
|
||||
// Providers DAO - 数据访问对象
|
||||
// TODO: 实现 providers 表的 CRUD 操作
|
||||
@@ -0,0 +1,46 @@
|
||||
use rusqlite::Connection;
|
||||
use serde_json::Value;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// 从旧的 JSON 配置迁移数据到 SQLite
|
||||
pub fn migrate_from_json(
|
||||
conn: &Connection,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// 检查是否已经迁移过
|
||||
let migrated: bool = conn
|
||||
.query_row(
|
||||
"SELECT value FROM settings WHERE key = 'migrated_from_json'",
|
||||
[],
|
||||
|row| row.get::<_, String>(0),
|
||||
)
|
||||
.map(|v| v == "true")
|
||||
.unwrap_or(false);
|
||||
|
||||
if migrated {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 读取旧配置文件
|
||||
let home = dirs::home_dir().ok_or("Cannot find home directory")?;
|
||||
let config_path = home.join(".proxycast").join("config.json");
|
||||
|
||||
if config_path.exists() {
|
||||
let content = std::fs::read_to_string(&config_path)?;
|
||||
let config: Value = serde_json::from_str(&content)?;
|
||||
|
||||
// TODO: 解析旧配置并插入到数据库
|
||||
// 这里需要根据实际的旧配置格式来实现
|
||||
|
||||
// 备份旧配置
|
||||
let backup_path = home.join(".proxycast").join("config.json.backup");
|
||||
std::fs::copy(&config_path, &backup_path)?;
|
||||
}
|
||||
|
||||
// 标记迁移完成
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO settings (key, value) VALUES ('migrated_from_json', 'true')",
|
||||
[],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
pub mod dao;
|
||||
pub mod migration;
|
||||
pub mod schema;
|
||||
|
||||
use rusqlite::Connection;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub type DbConnection = Arc<Mutex<Connection>>;
|
||||
|
||||
/// 获取数据库文件路径
|
||||
pub fn get_db_path() -> PathBuf {
|
||||
let home = dirs::home_dir().expect("Cannot find home directory");
|
||||
let db_dir = home.join(".proxycast");
|
||||
std::fs::create_dir_all(&db_dir).expect("Cannot create .proxycast directory");
|
||||
db_dir.join("proxycast.db")
|
||||
}
|
||||
|
||||
/// 初始化数据库连接
|
||||
pub fn init_database() -> Result<DbConnection, rusqlite::Error> {
|
||||
let db_path = get_db_path();
|
||||
let conn = Connection::open(&db_path)?;
|
||||
|
||||
// 创建表结构
|
||||
schema::create_tables(&conn)?;
|
||||
|
||||
Ok(Arc::new(Mutex::new(conn)))
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use rusqlite::Connection;
|
||||
|
||||
pub fn create_tables(conn: &Connection) -> Result<(), rusqlite::Error> {
|
||||
// Providers 表
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS providers (
|
||||
id TEXT NOT NULL,
|
||||
app_type TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
settings_config TEXT NOT NULL,
|
||||
category TEXT,
|
||||
icon TEXT,
|
||||
icon_color TEXT,
|
||||
notes TEXT,
|
||||
created_at INTEGER,
|
||||
sort_index INTEGER,
|
||||
is_current INTEGER DEFAULT 0,
|
||||
PRIMARY KEY (id, app_type)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
// MCP 服务器表
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS mcp_servers (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
server_config TEXT NOT NULL,
|
||||
description TEXT,
|
||||
enabled_proxycast INTEGER DEFAULT 0,
|
||||
enabled_claude INTEGER DEFAULT 0,
|
||||
enabled_codex INTEGER DEFAULT 0,
|
||||
enabled_gemini INTEGER DEFAULT 0,
|
||||
created_at INTEGER
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
// Prompts 表
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS prompts (
|
||||
id TEXT NOT NULL,
|
||||
app_type TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
description TEXT,
|
||||
is_current INTEGER DEFAULT 0,
|
||||
created_at INTEGER,
|
||||
PRIMARY KEY (id, app_type)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
// 设置表
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod config;
|
||||
mod converter;
|
||||
mod database;
|
||||
mod logger;
|
||||
mod models;
|
||||
mod providers;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AppType {
|
||||
ProxyCast,
|
||||
Claude,
|
||||
Codex,
|
||||
Gemini,
|
||||
}
|
||||
|
||||
impl AppType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
AppType::ProxyCast => "proxycast",
|
||||
AppType::Claude => "claude",
|
||||
AppType::Codex => "codex",
|
||||
AppType::Gemini => "gemini",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for AppType {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"proxycast" => Ok(AppType::ProxyCast),
|
||||
"claude" => Ok(AppType::Claude),
|
||||
"codex" => Ok(AppType::Codex),
|
||||
"gemini" => Ok(AppType::Gemini),
|
||||
_ => Err(format!("Invalid app type: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AppType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct McpServer {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub server_config: Value,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
#[serde(default)]
|
||||
pub enabled_proxycast: bool,
|
||||
#[serde(default)]
|
||||
pub enabled_claude: bool,
|
||||
#[serde(default)]
|
||||
pub enabled_codex: bool,
|
||||
#[serde(default)]
|
||||
pub enabled_gemini: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub created_at: Option<i64>,
|
||||
}
|
||||
|
||||
impl McpServer {
|
||||
pub fn new(id: String, name: String, server_config: Value) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
server_config,
|
||||
description: None,
|
||||
enabled_proxycast: false,
|
||||
enabled_claude: false,
|
||||
enabled_codex: false,
|
||||
enabled_gemini: false,
|
||||
created_at: Some(chrono::Utc::now().timestamp()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,18 @@
|
||||
pub mod anthropic;
|
||||
pub mod app_type;
|
||||
pub mod codewhisperer;
|
||||
pub mod mcp_model;
|
||||
pub mod openai;
|
||||
pub mod prompt_model;
|
||||
pub mod provider_model;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use anthropic::*;
|
||||
pub use app_type::AppType;
|
||||
#[allow(unused_imports)]
|
||||
pub use codewhisperer::*;
|
||||
pub use mcp_model::McpServer;
|
||||
#[allow(unused_imports)]
|
||||
pub use openai::*;
|
||||
pub use prompt_model::Prompt;
|
||||
pub use provider_model::Provider;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Prompt {
|
||||
pub id: String,
|
||||
pub app_type: String,
|
||||
pub name: String,
|
||||
pub content: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
#[serde(default)]
|
||||
pub is_current: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub created_at: Option<i64>,
|
||||
}
|
||||
|
||||
impl Prompt {
|
||||
pub fn new(id: String, app_type: String, name: String, content: String) -> Self {
|
||||
Self {
|
||||
id,
|
||||
app_type,
|
||||
name,
|
||||
content,
|
||||
description: None,
|
||||
is_current: false,
|
||||
created_at: Some(chrono::Utc::now().timestamp()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Provider {
|
||||
pub id: String,
|
||||
pub app_type: String,
|
||||
pub name: String,
|
||||
pub settings_config: Value,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub category: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub icon: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub icon_color: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub notes: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub created_at: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub sort_index: Option<i32>,
|
||||
#[serde(default)]
|
||||
pub is_current: bool,
|
||||
}
|
||||
|
||||
impl Provider {
|
||||
pub fn new(id: String, app_type: String, name: String, settings_config: Value) -> Self {
|
||||
Self {
|
||||
id,
|
||||
app_type,
|
||||
name,
|
||||
settings_config,
|
||||
category: None,
|
||||
icon: None,
|
||||
icon_color: None,
|
||||
notes: None,
|
||||
created_at: Some(chrono::Utc::now().timestamp()),
|
||||
sort_index: None,
|
||||
is_current: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,13 @@ impl KiroProvider {
|
||||
if tokio::fs::try_exists(&path).await.unwrap_or(false) {
|
||||
let content = tokio::fs::read_to_string(&path).await?;
|
||||
let creds: KiroCredentials = serde_json::from_str(&content)?;
|
||||
tracing::info!(
|
||||
"[KIRO] Main file loaded: has_access={}, has_refresh={}, has_client_id={}, auth_method={:?}",
|
||||
creds.access_token.is_some(),
|
||||
creds.refresh_token.is_some(),
|
||||
creds.client_id.is_some(),
|
||||
creds.auth_method
|
||||
);
|
||||
merge_credentials(&mut merged, &creds);
|
||||
}
|
||||
|
||||
@@ -114,6 +121,12 @@ impl KiroProvider {
|
||||
{
|
||||
if let Ok(content) = tokio::fs::read_to_string(&file_path).await {
|
||||
if let Ok(creds) = serde_json::from_str::<KiroCredentials>(&content) {
|
||||
tracing::info!(
|
||||
"[KIRO] Extra file {:?}: has_client_id={}, has_client_secret={}",
|
||||
file_path.file_name(),
|
||||
creds.client_id.is_some(),
|
||||
creds.client_secret.is_some()
|
||||
);
|
||||
merge_credentials(&mut merged, &creds);
|
||||
}
|
||||
}
|
||||
@@ -121,6 +134,15 @@ impl KiroProvider {
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"[KIRO] Final merged: has_access={}, has_refresh={}, has_client_id={}, has_client_secret={}, auth_method={:?}",
|
||||
merged.access_token.is_some(),
|
||||
merged.refresh_token.is_some(),
|
||||
merged.client_id.is_some(),
|
||||
merged.client_secret.is_some(),
|
||||
merged.auth_method
|
||||
);
|
||||
|
||||
self.credentials = merged;
|
||||
Ok(())
|
||||
}
|
||||
@@ -161,25 +183,43 @@ impl KiroProvider {
|
||||
.to_lowercase();
|
||||
let refresh_url = self.get_refresh_url();
|
||||
|
||||
let body = if auth_method == "idc" {
|
||||
serde_json::json!({
|
||||
"refreshToken": refresh_token,
|
||||
"clientId": self.credentials.client_id,
|
||||
"clientSecret": self.credentials.client_secret,
|
||||
"grantType": "refresh_token"
|
||||
})
|
||||
} else {
|
||||
serde_json::json!({ "refreshToken": refresh_token })
|
||||
};
|
||||
let resp = if auth_method == "idc" {
|
||||
// AWS OIDC endpoint requires form-urlencoded format
|
||||
let client_id = self
|
||||
.credentials
|
||||
.client_id
|
||||
.as_ref()
|
||||
.ok_or("No client_id for IdC refresh")?;
|
||||
let client_secret = self
|
||||
.credentials
|
||||
.client_secret
|
||||
.as_ref()
|
||||
.ok_or("No client_secret for IdC refresh")?;
|
||||
|
||||
let resp = self
|
||||
.client
|
||||
.post(&refresh_url)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.json(&body)
|
||||
.send()
|
||||
.await?;
|
||||
let form_body = format!(
|
||||
"grant_type=refresh_token&client_id={}&client_secret={}&refresh_token={}",
|
||||
urlencoding::encode(client_id),
|
||||
urlencoding::encode(client_secret),
|
||||
urlencoding::encode(refresh_token)
|
||||
);
|
||||
|
||||
self.client
|
||||
.post(&refresh_url)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Accept", "application/json")
|
||||
.body(form_body)
|
||||
.send()
|
||||
.await?
|
||||
} else {
|
||||
let body = serde_json::json!({ "refreshToken": refresh_token });
|
||||
self.client
|
||||
.post(&refresh_url)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.json(&body)
|
||||
.send()
|
||||
.await?
|
||||
};
|
||||
|
||||
if !resp.status().is_success() {
|
||||
let status = resp.status();
|
||||
@@ -188,13 +228,20 @@ impl KiroProvider {
|
||||
}
|
||||
|
||||
let data: serde_json::Value = resp.json().await?;
|
||||
|
||||
// AWS OIDC returns snake_case, social endpoint returns camelCase
|
||||
let new_token = data["accessToken"]
|
||||
.as_str()
|
||||
.or_else(|| data["access_token"].as_str())
|
||||
.ok_or("No access token in response")?;
|
||||
|
||||
self.credentials.access_token = Some(new_token.to_string());
|
||||
|
||||
if let Some(rt) = data["refreshToken"].as_str() {
|
||||
// Handle both camelCase and snake_case response formats
|
||||
if let Some(rt) = data["refreshToken"]
|
||||
.as_str()
|
||||
.or_else(|| data["refresh_token"].as_str())
|
||||
{
|
||||
self.credentials.refresh_token = Some(rt.to_string());
|
||||
}
|
||||
if let Some(arn) = data["profileArn"].as_str() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "ProxyCast",
|
||||
"version": "0.4.2",
|
||||
"version": "0.4.3",
|
||||
"identifier": "com.proxycast.app",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
Reference in New Issue
Block a user