前端重构: - 重构Layout为左侧导航+顶栏的现代管理后台布局 - 重构设备管理页面(Devices.vue):左侧分组面板+右侧设备列表 - 重构设备详情(DeviceDetail.vue):集成硬件资产/软件资产/变更记录标签页 - 移除独立资产管理页面,功能合并至设备详情 - 重构Dashboard/登录/设置/告警/水印/上网管控等页面样式 - 新增全局CSS变量和统一样式系统 - 添加分组管理UI:新建/重命名/删除分组,移动设备到分组 后端完善: - 新增分组CRUD API(groups.rs):创建/重命名/删除分组,设备分组移动 - 客户端硬件采集:完善GPU/主板/序列号/磁盘信息采集(Windows PowerShell) - 客户端软件采集:通过Windows注册表读取已安装软件列表 - 新增SoftwareAssetReport消息类型(0x09)及处理链路 - 数据库新增upsert_software方法处理软件资产存储 - 服务端推送软件资产配置给新注册设备 - 修复密码修改功能,添加旧密码验证
135 lines
4.2 KiB
Rust
135 lines
4.2 KiB
Rust
use anyhow::Result;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::path::Path;
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct AppConfig {
|
|
pub server: ServerConfig,
|
|
pub database: DatabaseConfig,
|
|
pub auth: AuthConfig,
|
|
pub retention: RetentionConfig,
|
|
#[serde(default)]
|
|
pub notify: NotifyConfig,
|
|
/// Token required for device registration. Empty = any token accepted.
|
|
#[serde(default)]
|
|
pub registration_token: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct ServerConfig {
|
|
pub http_addr: String,
|
|
pub tcp_addr: String,
|
|
/// Allowed CORS origins. Empty = same-origin only (no CORS headers).
|
|
#[serde(default)]
|
|
pub cors_origins: Vec<String>,
|
|
/// Optional TLS configuration for the TCP listener.
|
|
#[serde(default)]
|
|
pub tls: Option<TlsConfig>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct TlsConfig {
|
|
/// Path to the server certificate (PEM format)
|
|
pub cert_path: String,
|
|
/// Path to the server private key (PEM format)
|
|
pub key_path: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct DatabaseConfig {
|
|
pub path: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct AuthConfig {
|
|
pub jwt_secret: String,
|
|
#[serde(default = "default_access_ttl")]
|
|
pub access_token_ttl_secs: u64,
|
|
#[serde(default = "default_refresh_ttl")]
|
|
pub refresh_token_ttl_secs: u64,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RetentionConfig {
|
|
#[serde(default = "default_status_history_days")]
|
|
pub status_history_days: u32,
|
|
#[serde(default = "default_usb_events_days")]
|
|
pub usb_events_days: u32,
|
|
#[serde(default = "default_asset_changes_days")]
|
|
pub asset_changes_days: u32,
|
|
#[serde(default = "default_alert_records_days")]
|
|
pub alert_records_days: u32,
|
|
#[serde(default = "default_audit_log_days")]
|
|
pub audit_log_days: u32,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
|
pub struct NotifyConfig {
|
|
#[serde(default)]
|
|
pub smtp: Option<SmtpConfig>,
|
|
#[serde(default)]
|
|
pub webhook_urls: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct SmtpConfig {
|
|
pub host: String,
|
|
pub port: u16,
|
|
pub username: String,
|
|
pub password: String,
|
|
pub from: String,
|
|
}
|
|
|
|
impl AppConfig {
|
|
pub async fn load(path: &str) -> Result<Self> {
|
|
if Path::new(path).exists() {
|
|
let content = tokio::fs::read_to_string(path).await?;
|
|
let config: AppConfig = toml::from_str(&content)?;
|
|
Ok(config)
|
|
} else {
|
|
let config = default_config();
|
|
// Write default config for reference
|
|
let toml_str = toml::to_string_pretty(&config)?;
|
|
tokio::fs::write(path, &toml_str).await?;
|
|
tracing::warn!("Created default config at {}", path);
|
|
Ok(config)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn default_access_ttl() -> u64 { 1800 } // 30 minutes
|
|
fn default_refresh_ttl() -> u64 { 604800 } // 7 days
|
|
fn default_status_history_days() -> u32 { 7 }
|
|
fn default_usb_events_days() -> u32 { 90 }
|
|
fn default_asset_changes_days() -> u32 { 365 }
|
|
fn default_alert_records_days() -> u32 { 90 }
|
|
fn default_audit_log_days() -> u32 { 365 }
|
|
|
|
pub fn default_config() -> AppConfig {
|
|
AppConfig {
|
|
server: ServerConfig {
|
|
http_addr: "0.0.0.0:9998".into(),
|
|
tcp_addr: "0.0.0.0:9999".into(),
|
|
cors_origins: vec![],
|
|
tls: None,
|
|
},
|
|
database: DatabaseConfig {
|
|
path: "./csm.db".into(),
|
|
},
|
|
auth: AuthConfig {
|
|
jwt_secret: uuid::Uuid::new_v4().to_string(),
|
|
access_token_ttl_secs: default_access_ttl(),
|
|
refresh_token_ttl_secs: default_refresh_ttl(),
|
|
},
|
|
retention: RetentionConfig {
|
|
status_history_days: default_status_history_days(),
|
|
usb_events_days: default_usb_events_days(),
|
|
asset_changes_days: default_asset_changes_days(),
|
|
alert_records_days: default_alert_records_days(),
|
|
audit_log_days: default_audit_log_days(),
|
|
},
|
|
notify: NotifyConfig::default(),
|
|
registration_token: uuid::Uuid::new_v4().to_string(),
|
|
}
|
|
}
|