package handlers import ( "encoding/json" "fanqie/internal/config" "fanqie/internal/fqapi" "fmt" "net/http" "sync/atomic" "time" ) var ( startTime = time.Now() deviceHeader string // 请求统计 totalRequests int64 searchRequests int64 detailRequests int64 catalogRequests int64 contentRequests int64 mangaRequests int64 audioRequests int64 videoRequests int64 categoryRequests int64 // 统计保存定时器 statsSaveTicker *time.Ticker ) // 统计函数 func incrementTotal() { atomic.AddInt64(&totalRequests, 1) } func incrementSearch() { atomic.AddInt64(&searchRequests, 1) } func incrementDetail() { atomic.AddInt64(&detailRequests, 1) } func incrementCatalog() { atomic.AddInt64(&catalogRequests, 1) } func incrementContent() { atomic.AddInt64(&contentRequests, 1) } func incrementManga() { atomic.AddInt64(&mangaRequests, 1) } func incrementAudio() { atomic.AddInt64(&audioRequests, 1) } func incrementVideo() { atomic.AddInt64(&videoRequests, 1) } func incrementCategory() { atomic.AddInt64(&categoryRequests, 1) } func getTotal() int64 { return atomic.LoadInt64(&totalRequests) } func getSearch() int64 { return atomic.LoadInt64(&searchRequests) } func getDetail() int64 { return atomic.LoadInt64(&detailRequests) } func getCatalog() int64 { return atomic.LoadInt64(&catalogRequests) } func getContent() int64 { return atomic.LoadInt64(&contentRequests) } func getManga() int64 { return atomic.LoadInt64(&mangaRequests) } func getAudio() int64 { return atomic.LoadInt64(&audioRequests) } func getVideo() int64 { return atomic.LoadInt64(&videoRequests) } func getCategory() int64 { return atomic.LoadInt64(&categoryRequests) } // SetDeviceHeader 设置设备header func SetDeviceHeader(header string) { deviceHeader = header } // LoadStatistics 从数据库加载统计数据 func LoadStatistics() { stats, err := fqapi.LoadStatistics() if err != nil { fmt.Printf("加载统计数据失败: %v\n", err) return } atomic.StoreInt64(&totalRequests, stats["total"]) atomic.StoreInt64(&searchRequests, stats["search"]) atomic.StoreInt64(&detailRequests, stats["detail"]) atomic.StoreInt64(&catalogRequests, stats["catalog"]) atomic.StoreInt64(&contentRequests, stats["content"]) atomic.StoreInt64(&mangaRequests, stats["manga"]) atomic.StoreInt64(&audioRequests, stats["audio"]) atomic.StoreInt64(&videoRequests, stats["video"]) atomic.StoreInt64(&categoryRequests, stats["category"]) } // SaveStatistics 保存统计数据到数据库 func SaveStatistics() { stats := map[string]int64{ "total": atomic.LoadInt64(&totalRequests), "search": atomic.LoadInt64(&searchRequests), "detail": atomic.LoadInt64(&detailRequests), "catalog": atomic.LoadInt64(&catalogRequests), "content": atomic.LoadInt64(&contentRequests), "manga": atomic.LoadInt64(&mangaRequests), "audio": atomic.LoadInt64(&audioRequests), "video": atomic.LoadInt64(&videoRequests), "category": atomic.LoadInt64(&categoryRequests), } if err := fqapi.SaveStatistics(stats); err != nil { fmt.Printf("保存统计数据失败: %v\n", err) } } // StartStatisticsSaver 启动统计数据定期保存 func StartStatisticsSaver() { // 每30秒保存一次统计数据 statsSaveTicker = time.NewTicker(30 * time.Second) go func() { for range statsSaveTicker.C { SaveStatistics() } }() } // StopStatisticsSaver 停止统计数据定期保存 func StopStatisticsSaver() { if statsSaveTicker != nil { statsSaveTicker.Stop() // 最后保存一次 SaveStatistics() } } // RequireAccessKey 验证访问密钥中间件 func RequireAccessKey(handler http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cfg := config.GetConfig() // 如果未配置 access_key 或为空,则不启用验证 if cfg == nil || cfg.Server.AccessKey == "" { handler(w, r) return } // 从查询参数或 Header 中获取 key key := r.URL.Query().Get("key") if key == "" { key = r.Header.Get("X-Access-Key") } // 验证密钥 if key != cfg.Server.AccessKey { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusUnauthorized) fmt.Fprintf(w, `{"code": -1, "msg": "访问被拒绝:无效的访问密钥"}`) return } // 验证通过,继续处理请求 handler(w, r) } } // HandleMain 主页处理 func HandleMain(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") response := map[string]interface{}{ "code": 0, "message": "success", "data": nil, } json.NewEncoder(w).Encode(response) } // HandleSelfInfo 设备信息处理 func HandleSelfInfo(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") // 获取设备池统计信息 pool := fqapi.GetDeviceKeyPool() poolStats := pool.GetStats() response := map[string]interface{}{ "code": 0, "message": "SUCCESS", "data": map[string]interface{}{ "device_pool_enabled": pool.IsEnabled(), "device_pool_stats": poolStats, }, } json.NewEncoder(w).Encode(response) }