feat: aether-proxy 远程配置下发、连通性测试与 setup TUI

- 后端新增远程配置管理 API (PUT /config) 和连通性测试 API (POST /test)
- 前端新增远程配置编辑对话框和节点连通性测试按钮
- aether-proxy 支持通过心跳接收并热加载远程配置 (端口白名单、日志级别、心跳间隔、时间戳容差)
- aether-proxy 新增 TOML 配置文件支持和交互式 setup TUI
- aether-proxy 心跳 404 时自动重注册节点
- plain proxy 响应改为流式传输,减少内存缓冲
- 新增 remote_config 和 config_version 数据库字段及迁移
This commit is contained in:
fawney19
2026-02-07 19:20:09 +08:00
parent 3b8398b2e5
commit 31bc452374
18 changed files with 2771 additions and 113 deletions

View File

@@ -36,10 +36,15 @@ impl std::fmt::Display for AuthError {
///
/// Expected format: `Basic base64(hmac:{timestamp}.{signature})`
/// where signature = hex(HMAC-SHA256(hmac_key, "{timestamp}\n{node_id}"))
///
/// `timestamp_tolerance` is accepted separately so the caller can supply
/// the value from [`DynamicConfig`](crate::runtime::DynamicConfig) (which
/// may be updated remotely).
pub fn validate_proxy_auth(
proxy_auth_header: Option<&str>,
config: &Config,
node_id: &str,
timestamp_tolerance: u64,
) -> Result<(), AuthError> {
let header = proxy_auth_header.ok_or(AuthError::MissingHeader)?;
@@ -83,7 +88,7 @@ pub fn validate_proxy_auth(
timestamp - now
};
if diff > config.timestamp_tolerance {
if diff > timestamp_tolerance {
return Err(AuthError::TimestampExpired);
}
@@ -146,7 +151,7 @@ mod tests {
fn test_valid_auth() {
let config = make_config();
let header = make_valid_auth(&config, "node-1");
assert!(validate_proxy_auth(Some(&header), &config, "node-1").is_ok());
assert!(validate_proxy_auth(Some(&header), &config, "node-1", config.timestamp_tolerance).is_ok());
}
#[test]
@@ -154,7 +159,7 @@ mod tests {
let config = make_config();
let header = make_valid_auth(&config, "node-1");
assert!(matches!(
validate_proxy_auth(Some(&header), &config, "node-2"),
validate_proxy_auth(Some(&header), &config, "node-2", config.timestamp_tolerance),
Err(AuthError::SignatureMismatch)
));
}
@@ -163,7 +168,7 @@ mod tests {
fn test_missing_header() {
let config = make_config();
assert!(matches!(
validate_proxy_auth(None, &config, "node-1"),
validate_proxy_auth(None, &config, "node-1", config.timestamp_tolerance),
Err(AuthError::MissingHeader)
));
}
@@ -175,7 +180,7 @@ mod tests {
let header = format!("Basic {}", encoded);
let config = make_config();
assert!(matches!(
validate_proxy_auth(Some(&header), &config, "node-1"),
validate_proxy_auth(Some(&header), &config, "node-1", config.timestamp_tolerance),
Err(AuthError::InvalidUsername)
));
}