feat: 初始化项目基础架构和核心功能
- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件 - 实现前端Vue3项目结构:路由、登录页面、设备管理页面 - 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等 - 实现客户端监控模块:系统状态收集、资产收集 - 实现服务端基础API和插件系统 - 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等 - 实现前端设备状态展示和基本交互 - 添加使用时长统计和水印功能插件
This commit is contained in:
118
crates/server/src/alert.rs
Normal file
118
crates/server/src/alert.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
use crate::AppState;
|
||||
use tracing::{info, warn, error};
|
||||
|
||||
/// Background task for data cleanup and alert processing
|
||||
pub async fn cleanup_task(state: AppState) {
|
||||
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(3600));
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
// Cleanup old status history
|
||||
if let Err(e) = sqlx::query(
|
||||
"DELETE FROM device_status_history WHERE reported_at < datetime('now', ?)"
|
||||
)
|
||||
.bind(format!("-{} days", state.config.retention.status_history_days))
|
||||
.execute(&state.db)
|
||||
.await
|
||||
{
|
||||
error!("Failed to cleanup status history: {}", e);
|
||||
}
|
||||
|
||||
// Cleanup old USB events
|
||||
if let Err(e) = sqlx::query(
|
||||
"DELETE FROM usb_events WHERE event_time < datetime('now', ?)"
|
||||
)
|
||||
.bind(format!("-{} days", state.config.retention.usb_events_days))
|
||||
.execute(&state.db)
|
||||
.await
|
||||
{
|
||||
error!("Failed to cleanup USB events: {}", e);
|
||||
}
|
||||
|
||||
// Cleanup handled alert records
|
||||
if let Err(e) = sqlx::query(
|
||||
"DELETE FROM alert_records WHERE handled = 1 AND triggered_at < datetime('now', ?)"
|
||||
)
|
||||
.bind(format!("-{} days", state.config.retention.alert_records_days))
|
||||
.execute(&state.db)
|
||||
.await
|
||||
{
|
||||
error!("Failed to cleanup alert records: {}", e);
|
||||
}
|
||||
|
||||
// Mark devices as offline if no heartbeat for 2 minutes
|
||||
if let Err(e) = sqlx::query(
|
||||
"UPDATE devices SET status = 'offline' WHERE status = 'online' AND last_heartbeat < datetime('now', '-2 minutes')"
|
||||
)
|
||||
.execute(&state.db)
|
||||
.await
|
||||
{
|
||||
error!("Failed to mark stale devices offline: {}", e);
|
||||
}
|
||||
|
||||
// SQLite WAL checkpoint
|
||||
if let Err(e) = sqlx::query("PRAGMA wal_checkpoint(TRUNCATE)")
|
||||
.execute(&state.db)
|
||||
.await
|
||||
{
|
||||
warn!("WAL checkpoint failed: {}", e);
|
||||
}
|
||||
|
||||
info!("Cleanup cycle completed");
|
||||
}
|
||||
}
|
||||
|
||||
/// Send email notification
|
||||
pub async fn send_email(
|
||||
smtp_config: &crate::config::SmtpConfig,
|
||||
to: &str,
|
||||
subject: &str,
|
||||
body: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
use lettre::message::header::ContentType;
|
||||
use lettre::{Message, SmtpTransport, Transport};
|
||||
use lettre::transport::smtp::authentication::Credentials;
|
||||
|
||||
let email = Message::builder()
|
||||
.from(smtp_config.from.parse()?)
|
||||
.to(to.parse()?)
|
||||
.subject(subject)
|
||||
.header(ContentType::TEXT_HTML)
|
||||
.body(body.to_string())?;
|
||||
|
||||
let creds = Credentials::new(
|
||||
smtp_config.username.clone(),
|
||||
smtp_config.password.clone(),
|
||||
);
|
||||
|
||||
let mailer = SmtpTransport::starttls_relay(&smtp_config.host)?
|
||||
.port(smtp_config.port)
|
||||
.credentials(creds)
|
||||
.build();
|
||||
|
||||
mailer.send(&email)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shared HTTP client for webhook notifications.
|
||||
/// Lazily initialized once and reused across calls to benefit from connection pooling.
|
||||
static WEBHOOK_CLIENT: std::sync::OnceLock<reqwest::Client> = std::sync::OnceLock::new();
|
||||
|
||||
fn webhook_client() -> &'static reqwest::Client {
|
||||
WEBHOOK_CLIENT.get_or_init(|| {
|
||||
reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()
|
||||
.unwrap_or_else(|_| reqwest::Client::new())
|
||||
})
|
||||
}
|
||||
|
||||
/// Send webhook notification
|
||||
pub async fn send_webhook(url: &str, payload: &serde_json::Value) -> anyhow::Result<()> {
|
||||
webhook_client().post(url)
|
||||
.json(payload)
|
||||
.send()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user