package handlers import ( _ "embed" "fanqie/internal/algos" "fanqie/internal/config" "fanqie/internal/fqapi" "fmt" "html/template" "net/http" "time" ) // 状态页模板(embed 内嵌,动态数据由 html/template 自动转义) // //go:embed templates/status.html var statusHTML string var statusTmpl = template.Must(template.New("status").Parse(statusHTML)) // HandleStatus 状态页面处理 func HandleStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") uptime := time.Since(startTime) // 格式化运行时间 days := int(uptime.Hours() / 24) hours := int(uptime.Hours()) % 24 minutes := int(uptime.Minutes()) % 60 seconds := int(uptime.Seconds()) % 60 var uptimeStr string if days > 0 { uptimeStr = fmt.Sprintf("%d天 %d小时 %d分钟 %d秒", days, hours, minutes, seconds) } else if hours > 0 { uptimeStr = fmt.Sprintf("%d小时 %d分钟 %d秒", hours, minutes, seconds) } else if minutes > 0 { uptimeStr = fmt.Sprintf("%d分钟 %d秒", minutes, seconds) } else { uptimeStr = fmt.Sprintf("%d秒", seconds) } // 获取签名信息 currentSign := algos.GlobalSignatureSwitcher.GetCurrentMethod() // 生成签名详情显示 var signatureDetails string if currentSign == "sign2" { signatureDetails = "X-Gorgon + X-Khronos" } else { signatureDetails = "X-Gorgon + X-Ladon + X-Argus + X-Khronos" } // 获取统计数据 stats := getStatsSnapshot() // 获取设备密钥池状态 poolStatus := getDeviceKeyPoolStatus() // 获取最近请求(日志未启用时为空) var recent []map[string]interface{} for _, e := range fqapi.GetRecentRequests(20) { recent = append(recent, map[string]interface{}{ "Time": time.Unix(e.Ts, 0).Format("15:04:05"), "IP": e.IP, "Method": e.Method, "Path": e.Path, "Query": truncateStr(e.Query, 80), "Status": e.Status, "IsErr": e.Status >= 400, "Duration": fmt.Sprintf("%dms", e.DurationMs), }) } statusTmpl.Execute(w, map[string]interface{}{ "NowTime": time.Now().Format("2006-01-02 15:04:05"), "Uptime": uptimeStr, "StartTime": startTime.Format("2006-01-02 15:04:05"), "Signature": signatureDetails, "DBTotal": getDBTotalSize(), "DBUsedPct": getDBUsagePercentage(), "DBUsed": getDBUsedSize(), "DBFree": getDBFreeSize(), "PoolStatus": poolStatus["status"], "PoolTotal": poolStatus["total"], "PoolValid": poolStatus["valid"], "PoolInvalid": poolStatus["invalid"], "PoolIndex": poolStatus["current_index"], "StatTotal": stats["total"], "StatSearch": stats["search"], "StatDetail": stats["detail"], "StatCatalog": stats["catalog"], "StatContent": stats["content"], "StatManga": stats["manga"], "StatAudio": stats["audio"], "StatVideo": stats["video"], "StatCategory": stats["category"], "CacheCount": getDBCacheCount(), "CacheStatus": getCacheStatus(), "CleanupStatus": getCleanupStatus(), "CleanupInterval": getCleanupInterval(), "RecentRequests": recent, }) } // truncateStr 截断字符串,超长加省略号 func truncateStr(s string, n int) string { if len(s) <= n { return s } return s[:n] + "…" } // getCacheStatus 获取缓存状态 func getCacheStatus() string { cfg := config.GetConfig() if cfg != nil && cfg.Cache.Enabled { return "已启用" } return "已禁用" } // getCleanupStatus 获取清理状态 func getCleanupStatus() string { cfg := config.GetConfig() if cfg != nil && cfg.Cache.CleanupEnabled { return "已启用" } return "已禁用" } // getCleanupInterval 获取清理间隔 func getCleanupInterval() string { cfg := config.GetConfig() if cfg != nil { seconds := cfg.Cache.CleanupSeconds if seconds == 0 { return "不清理" } else if seconds < 60 { return fmt.Sprintf("%d秒", seconds) } else if seconds < 3600 { return fmt.Sprintf("%d分钟", seconds/60) } else { return fmt.Sprintf("%d小时", seconds/3600) } } return "未知" } // formatBytes 格式化字节大小 func formatBytes(bytes int64) string { const unit = 1024 if bytes < unit { return fmt.Sprintf("%d B", bytes) } div, exp := int64(unit), 0 for n := bytes / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.2f %cB", float64(bytes)/float64(div), "KMGTPE"[exp]) } // getDBCacheCount 获取数据库缓存记录数 func getDBCacheCount() int { stats, err := fqapi.GetDatabaseStats() if err != nil || stats == nil { return 0 } if count, ok := stats["cache_count"].(int); ok { return count } return 0 } // getDBTotalSize 获取数据库总大小 func getDBTotalSize() string { stats, err := fqapi.GetDatabaseStats() if err != nil || stats == nil { return "未知" } if size, ok := stats["total_size_bytes"].(int64); ok { return formatBytes(size) } return "未知" } // getDBUsedSize 获取数据库已用空间 func getDBUsedSize() string { stats, err := fqapi.GetDatabaseStats() if err != nil || stats == nil { return "未知" } if size, ok := stats["used_size_bytes"].(int64); ok { return formatBytes(size) } return "未知" } // getDBFreeSize 获取数据库空闲空间(带颜色提示) func getDBFreeSize() string { stats, err := fqapi.GetDatabaseStats() if err != nil || stats == nil { return "未知" } freeSize, okFree := stats["free_size_bytes"].(int64) totalSize, okTotal := stats["total_size_bytes"].(int64) if !okFree { return "未知" } if freeSize == 0 { return "0 B" } freeSizeStr := formatBytes(freeSize) // 如果有总大小,计算并显示百分比 if okTotal && totalSize > 0 { percentage := float64(freeSize) * 100 / float64(totalSize) return fmt.Sprintf("%s (%.1f%%)", freeSizeStr, percentage) } return freeSizeStr } // getDBUsagePercentage 获取数据库使用率 func getDBUsagePercentage() float64 { stats, err := fqapi.GetDatabaseStats() if err != nil || stats == nil { return 0 } usedSize, okUsed := stats["used_size_bytes"].(int64) totalSize, okTotal := stats["total_size_bytes"].(int64) if !okUsed || !okTotal || totalSize == 0 { return 0 } return float64(usedSize) * 100 / float64(totalSize) } // getDeviceKeyPoolStatus 获取设备密钥池状态 func getDeviceKeyPoolStatus() map[string]interface{} { pool := fqapi.GetDeviceKeyPool() if !pool.IsEnabled() { return map[string]interface{}{ "status": "未启用", "total": 0, "valid": 0, "invalid": 0, "current_index": 0, } } stats := pool.GetStats() return map[string]interface{}{ "status": "已启用", "total": stats["total"], "valid": stats["valid"], "invalid": stats["invalid"], "current_index": stats["current_index"], } }