fix: sync gateway pairing persistence and proxy null clears

This commit is contained in:
Chummy 2026-02-19 17:51:08 +08:00
parent f1ca73d3d2
commit 916c0c823b
4 changed files with 80 additions and 14 deletions

View file

@ -224,6 +224,7 @@ fn has_supervised_channels(config: &Config) -> bool {
irc,
lark,
dingtalk,
linq,
qq,
..
} = &config.channels_config;
@ -240,6 +241,7 @@ fn has_supervised_channels(config: &Config) -> bool {
|| irc.is_some()
|| lark.is_some()
|| dingtalk.is_some()
|| linq.is_some()
|| qq.is_some()
}

View file

@ -653,11 +653,16 @@ async fn persist_pairing_tokens(config: Arc<Mutex<Config>>, pairing: &PairingGua
let paired_tokens = pairing.tokens();
// This is needed because parking_lot's guard is not Send so we clone the inner
// this should be removed once async mutexes are used everywhere
let mut cfg = { config.lock().clone() };
cfg.gateway.paired_tokens = paired_tokens;
cfg.save()
let mut updated_cfg = { config.lock().clone() };
updated_cfg.gateway.paired_tokens = paired_tokens;
updated_cfg
.save()
.await
.context("Failed to persist paired tokens to config.toml")
.context("Failed to persist paired tokens to config.toml")?;
// Keep shared runtime config in sync with persisted tokens.
*config.lock() = updated_cfg;
Ok(())
}
/// Webhook request body
@ -1410,7 +1415,9 @@ mod tests {
assert!(guard.is_authenticated(&token));
let shared_config = Arc::new(Mutex::new(config));
persist_pairing_tokens(shared_config, &guard).await.unwrap();
persist_pairing_tokens(shared_config.clone(), &guard)
.await
.unwrap();
let saved = tokio::fs::read_to_string(config_path).await.unwrap();
let parsed: Config = toml::from_str(&saved).unwrap();
@ -1418,6 +1425,10 @@ mod tests {
let persisted = &parsed.gateway.paired_tokens[0];
assert_eq!(persisted.len(), 64);
assert!(persisted.chars().all(|c| c.is_ascii_hexdigit()));
let in_memory = shared_config.lock();
assert_eq!(in_memory.gateway.paired_tokens.len(), 1);
assert_eq!(&in_memory.gateway.paired_tokens[0], persisted);
}
#[test]

View file

@ -74,6 +74,7 @@ fn has_launchable_channels(channels: &ChannelsConfig) -> bool {
irc,
lark,
dingtalk,
linq,
qq,
..
} = channels;
@ -90,6 +91,7 @@ fn has_launchable_channels(channels: &ChannelsConfig) -> bool {
|| irc.is_some()
|| lark.is_some()
|| dingtalk.is_some()
|| linq.is_some()
|| qq.is_some()
}

View file

@ -189,20 +189,41 @@ impl ProxyConfigTool {
})?;
}
if let MaybeSet::Set(update) = Self::parse_optional_string_update(args, "http_proxy")? {
match Self::parse_optional_string_update(args, "http_proxy")? {
MaybeSet::Set(update) => {
proxy.http_proxy = Some(update);
touched_proxy_url = true;
}
MaybeSet::Null => {
proxy.http_proxy = None;
touched_proxy_url = true;
}
MaybeSet::Unset => {}
}
if let MaybeSet::Set(update) = Self::parse_optional_string_update(args, "https_proxy")? {
match Self::parse_optional_string_update(args, "https_proxy")? {
MaybeSet::Set(update) => {
proxy.https_proxy = Some(update);
touched_proxy_url = true;
}
MaybeSet::Null => {
proxy.https_proxy = None;
touched_proxy_url = true;
}
MaybeSet::Unset => {}
}
if let MaybeSet::Set(update) = Self::parse_optional_string_update(args, "all_proxy")? {
match Self::parse_optional_string_update(args, "all_proxy")? {
MaybeSet::Set(update) => {
proxy.all_proxy = Some(update);
touched_proxy_url = true;
}
MaybeSet::Null => {
proxy.all_proxy = None;
touched_proxy_url = true;
}
MaybeSet::Unset => {}
}
if let Some(no_proxy_raw) = args.get("no_proxy") {
proxy.no_proxy = Self::parse_string_list(no_proxy_raw, "no_proxy")?;
@ -494,4 +515,34 @@ mod tests {
assert!(get_result.output.contains("provider.openai"));
assert!(get_result.output.contains("services"));
}
#[tokio::test]
async fn set_null_proxy_url_clears_existing_value() {
let tmp = TempDir::new().unwrap();
let tool = ProxyConfigTool::new(test_config(&tmp).await, test_security());
let set_result = tool
.execute(json!({
"action": "set",
"http_proxy": "http://127.0.0.1:7890"
}))
.await
.unwrap();
assert!(set_result.success, "{:?}", set_result.error);
let clear_result = tool
.execute(json!({
"action": "set",
"http_proxy": null
}))
.await
.unwrap();
assert!(clear_result.success, "{:?}", clear_result.error);
let get_result = tool.execute(json!({"action": "get"})).await.unwrap();
assert!(get_result.success);
let parsed: Value = serde_json::from_str(&get_result.output).unwrap();
assert!(parsed["proxy"]["http_proxy"].is_null());
assert!(parsed["runtime_proxy"]["http_proxy"].is_null());
}
}