web增加恢复解析统计图表

This commit is contained in:
Alex Yang
2025-11-25 16:28:24 +08:00
parent aea162a616
commit 6154764091
19 changed files with 154764 additions and 335 deletions

View File

@@ -970,22 +970,83 @@ func (m *ShieldManager) GetStats() map[string]interface{} {
// loadStatsData 从文件加载计数数据
func (m *ShieldManager) loadStatsData() {
if m.config.StatsFile == "" {
logger.Info("Shield统计文件路径未配置跳过加载")
return
}
// 检查文件是否存在
data, err := ioutil.ReadFile(m.config.StatsFile)
// 获取绝对路径以避免工作目录问题
statsFilePath, err := filepath.Abs(m.config.StatsFile)
if err != nil {
if !os.IsNotExist(err) {
logger.Error("读取Shield计数数据文件失败", "error", err)
logger.Error("获取Shield统计文件绝对路径失败", "path", m.config.StatsFile, "error", err)
return
}
logger.Debug("尝试加载Shield统计数据", "file", statsFilePath)
// 检查文件是否存在
fileInfo, err := os.Stat(statsFilePath)
if err != nil {
if os.IsNotExist(err) {
logger.Info("Shield统计文件不存在将创建新文件", "file", statsFilePath)
// 初始化空的计数数据
m.rulesMutex.Lock()
m.blockedDomainsCount = make(map[string]int)
m.resolvedDomainsCount = make(map[string]int)
m.rulesMutex.Unlock()
// 尝试立即保存一个有效的空文件
m.saveStatsData()
} else {
logger.Error("检查Shield统计文件失败", "file", statsFilePath, "error", err)
}
return
}
// 检查文件大小
if fileInfo.Size() == 0 {
logger.Warn("Shield统计文件为空将重新初始化", "file", statsFilePath)
m.rulesMutex.Lock()
m.blockedDomainsCount = make(map[string]int)
m.resolvedDomainsCount = make(map[string]int)
m.rulesMutex.Unlock()
m.saveStatsData()
return
}
// 读取文件内容
data, err := ioutil.ReadFile(statsFilePath)
if err != nil {
logger.Error("读取Shield计数数据文件失败", "file", statsFilePath, "error", err)
return
}
// 检查数据长度
if len(data) == 0 {
logger.Warn("读取到的Shield统计数据为空", "file", statsFilePath)
return
}
// 尝试解析JSON
var statsData ShieldStatsData
err = json.Unmarshal(data, &statsData)
if err != nil {
logger.Error("解析Shield计数数据失败", "error", err)
// 记录更详细的错误信息包括数据前50个字符
dataSample := string(data)
if len(dataSample) > 50 {
dataSample = dataSample[:50] + "..."
}
logger.Error("解析Shield计数数据失败",
"file", statsFilePath,
"error", err,
"data_length", len(data),
"data_sample", dataSample)
// 重置为默认空数据
m.rulesMutex.Lock()
m.blockedDomainsCount = make(map[string]int)
m.resolvedDomainsCount = make(map[string]int)
m.rulesMutex.Unlock()
// 尝试保存一个有效的空文件
m.saveStatsData()
return
}
@@ -993,26 +1054,38 @@ func (m *ShieldManager) loadStatsData() {
m.rulesMutex.Lock()
if statsData.BlockedDomainsCount != nil {
m.blockedDomainsCount = statsData.BlockedDomainsCount
} else {
m.blockedDomainsCount = make(map[string]int)
}
if statsData.ResolvedDomainsCount != nil {
m.resolvedDomainsCount = statsData.ResolvedDomainsCount
} else {
m.resolvedDomainsCount = make(map[string]int)
}
m.rulesMutex.Unlock()
logger.Info("Shield计数数据加载成功")
logger.Info("Shield计数数据加载成功", "blocked_entries", len(m.blockedDomainsCount), "resolved_entries", len(m.resolvedDomainsCount))
}
// saveStatsData 保存计数数据到文件
func (m *ShieldManager) saveStatsData() {
if m.config.StatsFile == "" {
logger.Debug("Shield统计文件路径未配置跳过保存")
return
}
// 获取绝对路径以避免工作目录问题
statsFilePath, err := filepath.Abs(m.config.StatsFile)
if err != nil {
logger.Error("获取Shield统计文件绝对路径失败", "path", m.config.StatsFile, "error", err)
return
}
// 创建数据目录
statsDir := filepath.Dir(m.config.StatsFile)
err := os.MkdirAll(statsDir, 0755)
statsDir := filepath.Dir(statsFilePath)
err = os.MkdirAll(statsDir, 0755)
if err != nil {
logger.Error("创建Shield统计数据目录失败", "error", err)
logger.Error("创建Shield统计数据目录失败", "dir", statsDir, "error", err)
return
}
@@ -1040,14 +1113,24 @@ func (m *ShieldManager) saveStatsData() {
return
}
// 写入文件
err = ioutil.WriteFile(m.config.StatsFile, jsonData, 0644)
// 使用临时文件先写入,然后重命名,避免文件损坏
tempFilePath := statsFilePath + ".tmp"
err = ioutil.WriteFile(tempFilePath, jsonData, 0644)
if err != nil {
logger.Error("保存Shield计数数据到文件失败", "error", err)
logger.Error("写入临时Shield统计文件失败", "file", tempFilePath, "error", err)
return
}
logger.Info("Shield计数数据保存成功")
// 原子操作重命名文件
err = os.Rename(tempFilePath, statsFilePath)
if err != nil {
logger.Error("重命名Shield统计文件失败", "temp", tempFilePath, "dest", statsFilePath, "error", err)
// 尝试清理临时文件
os.Remove(tempFilePath)
return
}
logger.Info("Shield计数数据保存成功", "file", statsFilePath, "blocked_entries", len(statsData.BlockedDomainsCount), "resolved_entries", len(statsData.ResolvedDomainsCount))
}
// startAutoSaveStats 启动计数数据自动保存功能