fix(ui): 表单校验加固与对话框生命周期
- 端口 1~65535(0 不是可连接端口);接收上限/最大报文长度/主题别名上限拒 0——MQTT 5 规范定义为 Protocol Error,broker 直接断连且值会被持久化,表现为每次连接都失败 - 遗嘱主题 trim 后入库,并拒绝包含通配符 + / #(通配符只用于订阅过滤器) - 16 个被校验字段订阅输入变更:编辑即清空字段错误,红字不再滞后于实际输入(此前错误只在保存时登记,改好值后红字仍挂着) - 传输切换只按「上一个方式」的默认端口覆盖端口:与全部默认端口比较会把用户手输的 8084/8883 误判为默认值,WSS:9443 → TCP → WSS 往返丢端口 - 五个对话框 open() 统一加 has_active_dialog 守卫:对话框是栈式叠加,多入口重复 open 会压入第二个模态,同 id 控件在两层间串台 - 阿里云预设对话框表单预填即选中第一项:此前 selected=None,打开即保存会 push 一条内容相同的重复预设 - OptionDelegate::position 按框架契约实现(按值反查索引):恒返回 None 会让 set_selected_value 失效、下拉显示空 - 非 Windows 不渲染置顶按钮:set_topmost 恒 false,显示必然报错的按钮只会让人困惑 - 主题键名对齐 gpui-component 的 serde rename:font.size / radius.lg 此前被静默忽略,14px 紧凑排版从未生效(实际渲染 16px) - aliyun.rs DeviceName 空值校验;Cargo.toml 声明 Apache-2.0(与关于页一致)
This commit is contained in:
+30
-1
@@ -44,7 +44,9 @@ impl AliyunAuthMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
// Debug 手写脱敏:AccessKeySecret 与 DeviceSecret 是长期凭据,一次 `{:?}` 格式化
|
||||
// 就会把它们送进 stderr 与 Sentry(panic 钩子会一并上报),因此不能 derive。
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct AliyunPreset {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
@@ -152,6 +154,10 @@ impl AliyunPreset {
|
||||
{
|
||||
return Err("Group ID、AccessKey ID 与 AccessKey Secret 不能为空".into());
|
||||
}
|
||||
// 空 DeviceName 会拼出 "group@@@" 这样的非法 clientId,服务端报错难懂
|
||||
if self.device_id.trim().is_empty() {
|
||||
return Err("Device ID 不能为空".into());
|
||||
}
|
||||
conn.host = self.host();
|
||||
let client_id = format!("{}@@@{}", self.group_id, self.device_id);
|
||||
let password = Self::signature(&self.access_key_secret, &client_id)?;
|
||||
@@ -164,6 +170,10 @@ impl AliyunPreset {
|
||||
if self.product_key.is_empty() || self.device_secret.is_empty() {
|
||||
return Err("一机一密需要 ProductKey 与 DeviceSecret (设备密钥)".into());
|
||||
}
|
||||
// 空 DeviceName 会拼出 "pk.|securemode=..." 与 "&pk",服务端报错难懂
|
||||
if self.device_id.trim().is_empty() {
|
||||
return Err("一机一密需要 DeviceName(设备名)".into());
|
||||
}
|
||||
conn.host = self.host();
|
||||
// 物联网平台「一机一密」直连三元组(见模块文档),DeviceName 复用 device_id
|
||||
let device_name = self.device_id.trim();
|
||||
@@ -189,3 +199,22 @@ impl Default for AliyunPreset {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AliyunPreset {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AliyunPreset")
|
||||
.field("id", &self.id)
|
||||
.field("name", &self.name)
|
||||
.field("group", &self.group)
|
||||
.field("instance_id", &self.instance_id)
|
||||
.field("region", &self.region)
|
||||
.field("auth_mode", &self.auth_mode)
|
||||
.field("access_key_id", &self.access_key_id)
|
||||
.field("access_key_secret", &"<redacted>")
|
||||
.field("group_id", &self.group_id)
|
||||
.field("device_id", &self.device_id)
|
||||
.field("product_key", &self.product_key)
|
||||
.field("device_secret", &"<redacted>")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user