fix: add __templates__ and image button coords to KVM form submissions
All checks were successful
CI / fmt (push) Successful in 34s
Publish / frontend (push) Successful in 46s
CI / clippy (push) Successful in 1m21s
CI / check (push) Successful in 1m32s
Publish / backend (push) Successful in 2m44s

The Belkin firmware requires:
1. A hidden __templates__ field in all form POSTs
2. Image button actions submitted as name.x=0&name.y=0 (not name=value)
3. ECG_kvm_powerport_cnt hidden field

Without these, the device returns "Permission denied" on port config
saves. Also fixed the switch endpoint with the same pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 11:50:38 +03:00
parent d503742542
commit 9bd215356b

View File

@@ -93,10 +93,7 @@ fn extract_selected_option(html: &str, name: &str) -> Option<String> {
// Find "selected>" then the text until newline or '<' // Find "selected>" then the text until newline or '<'
let sel_pos = after.find("selected>")?; let sel_pos = after.find("selected>")?;
let text_start = sel_pos + "selected>".len(); let text_start = sel_pos + "selected>".len();
let text_end = after[text_start..] let text_end = after[text_start..].find(['<', '\n']).unwrap_or(0) + text_start;
.find(['<', '\n'])
.unwrap_or(0)
+ text_start;
Some(after[text_start..text_end].trim().to_string()) Some(after[text_start..text_end].trim().to_string())
} }
@@ -175,6 +172,8 @@ pub async fn save_ports(
let cookie = get_cookie(&state).await?; let cookie = get_cookie(&state).await?;
let mut form: Vec<(String, String)> = vec![ let mut form: Vec<(String, String)> = vec![
// Hidden template field required by the firmware
("__templates__".into(), " kvm_port_list kvm".into()),
("ECG_kvm_nr_ports".into(), req.port_count.to_string()), ("ECG_kvm_nr_ports".into(), req.port_count.to_string()),
( (
"ECG_key_pause_duration".into(), "ECG_key_pause_duration".into(),
@@ -182,6 +181,7 @@ pub async fn save_ports(
), ),
("ECG_kvm_portname_cnt".into(), req.port_count.to_string()), ("ECG_kvm_portname_cnt".into(), req.port_count.to_string()),
("ECG_kvm_hotkey_cnt".into(), req.port_count.to_string()), ("ECG_kvm_hotkey_cnt".into(), req.port_count.to_string()),
("ECG_kvm_powerport_cnt".into(), req.port_count.to_string()),
("ECG_kvm_show_in_rc_cnt".into(), req.port_count.to_string()), ("ECG_kvm_show_in_rc_cnt".into(), req.port_count.to_string()),
]; ];
@@ -199,7 +199,9 @@ pub async fn save_ports(
} }
} }
form.push(("action_apply".into(), "Apply".into())); // Image button submits with .x/.y coordinates
form.push(("action_apply.x".into(), "0".into()));
form.push(("action_apply.y".into(), "0".into()));
let resp = state let resp = state
.http_client .http_client
@@ -233,8 +235,13 @@ pub async fn switch_port(
let cookie = get_cookie(&state).await?; let cookie = get_cookie(&state).await?;
let form = [ let form = [
(
"__templates__",
" kvm_port_list rc_preview power_control_ipmi power_control_intern power_control_direct".to_string(),
),
("kvm_active_port_0", req.port.to_string()), ("kvm_active_port_0", req.port.to_string()),
("action_switch_0", "Switch".into()), ("action_switch_0.x", "0".into()),
("action_switch_0.y", "0".into()),
]; ];
state state