feat: 初始化项目基础架构和核心功能

- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件
- 实现前端Vue3项目结构:路由、登录页面、设备管理页面
- 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等
- 实现客户端监控模块:系统状态收集、资产收集
- 实现服务端基础API和插件系统
- 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等
- 实现前端设备状态展示和基本交互
- 添加使用时长统计和水印功能插件
This commit is contained in:
iven
2026-04-05 00:57:51 +08:00
commit fd6fb5cca0
87 changed files with 19576 additions and 0 deletions

134
crates/server/src/config.rs Normal file
View File

@@ -0,0 +1,134 @@
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:8080".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(),
}
}