Files
markbase/markbase-core/src/audit.rs
Warren 1300a4e223
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能:
-  Categories/Series双视图管理(category_view.rs + import_markdown.rs)
-  FUSE Multi-Volume支持(tree_type参数)
-  SSH/SFTP/SCP/rsync协议完整实现(4042行)
-  NFS/SMB Module Phase 1-3完成
-  Archive Module Phase 1-4完成(2916行)
-  Download Center API完整实现
-  S3兼容API实现(560行)

Git配置修正:
-  删除错误origin(gitea.momentry.ddns.net)
-  删除m5max128(指向机器名)
-  设置origin = m5max128gitea.momentry.ddns.net/admin/markbase
-  设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase

数据清理:
-  删除38个临时SQLite(保留accusys.sqlite、demo.sqlite)
-  删除.bak、test_*.bin、调试脚本等临时文件
-  删除临时目录(build/、download files/、raid_test/等)
-  更新.gitignore排除临时文件

架构优化:
- 52个文件修改,2434行新增,4739行删除
- Workspace成员整合(16个crate)
- 数据库状态:accusys.sqlite保留(主demo测试)

远程同步:
-  准备推送到m5max128gitea(远程Gitea)
-  准备推送到m4minigitea(本地Gitea)
2026-06-12 12:59:54 +08:00

132 lines
3.4 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditLogEntry {
timestamp: DateTime<Utc>,
operation: String,
config_type: String,
key: String,
old_value: String,
new_value: String,
user: String,
ip_address: Option<String>,
}
pub struct AuditLogger {
log_path: PathBuf,
}
impl AuditLogger {
pub fn new(log_path: &str) -> Self {
Self {
log_path: PathBuf::from(log_path),
}
}
pub fn default() -> Self {
Self::new("logs/config_audit.log")
}
pub fn log_config_change(
&self,
config_type: &str,
key: &str,
old_value: &str,
new_value: &str,
user: &str,
ip_address: Option<&str>,
) -> anyhow::Result<()> {
let entry = AuditLogEntry {
timestamp: Utc::now(),
operation: "edit".to_string(),
config_type: config_type.to_string(),
key: key.to_string(),
old_value: old_value.to_string(),
new_value: new_value.to_string(),
user: user.to_string(),
ip_address: ip_address.map(|s| s.to_string()),
};
self.write_entry(&entry)?;
log::info!(
"Audit: {} config {} changed from '{}' to '{}' by {}",
config_type,
key,
old_value,
new_value,
user
);
Ok(())
}
pub fn log_config_validate(
&self,
config_type: &str,
result: &str,
user: &str,
ip_address: Option<&str>,
) -> anyhow::Result<()> {
let entry = AuditLogEntry {
timestamp: Utc::now(),
operation: "validate".to_string(),
config_type: config_type.to_string(),
key: "validation".to_string(),
old_value: "".to_string(),
new_value: result.to_string(),
user: user.to_string(),
ip_address: ip_address.map(|s| s.to_string()),
};
self.write_entry(&entry)?;
Ok(())
}
fn write_entry(&self, entry: &AuditLogEntry) -> anyhow::Result<()> {
// Create logs directory if not exists
if let Some(parent) = self.log_path.parent() {
if !parent.exists() {
std::fs::create_dir_all(parent)?;
}
}
// Open file in append mode
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(&self.log_path)?;
// Write JSON line
let json = serde_json::to_string(entry)?;
file.write_all(json.as_bytes())?;
file.write_all(b"\n")?;
Ok(())
}
pub fn read_recent_entries(&self, limit: usize) -> anyhow::Result<Vec<AuditLogEntry>> {
if !self.log_path.exists() {
return Ok(Vec::new());
}
let content = std::fs::read_to_string(&self.log_path)?;
let entries: Vec<AuditLogEntry> = content
.lines()
.filter_map(|line| serde_json::from_str(line).ok())
.collect();
// Return last N entries
let start = if entries.len() > limit {
entries.len() - limit
} else {
0
};
Ok(entries[start..].to_vec())
}
}