package handlers import ( "encoding/json" "fanqie/internal/algos" "fanqie/internal/config" "fanqie/internal/fqapi" "fmt" "net/http" "time" ) // HandleVacuum 处理数据库压缩请求 func HandleVacuum(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") // 只允许 POST 请求 if r.Method != http.MethodPost { json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "message": "仅支持 POST 请求", }) return } // 执行 VACUUM err := fqapi.VacuumDatabase() if err != nil { json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "message": "压缩失败: " + err.Error(), }) return } json.NewEncoder(w).Encode(map[string]interface{}{ "success": true, "message": "数据库压缩完成", }) } // 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() html := fmt.Sprintf(` 番茄小说API - 运行状态

番茄小说API

运行状态监控面板

● 运行状态
服务器时间 %s
运行时长 %s
启动时间 %s
签名方法 %s
数据库大小 %s
已用空间 %s
空闲空间 %s 立即压缩
◉ 设备密钥池
池状态 %s
总设备数 %d
有效设备 %d 可用
无效设备 %d
当前索引 %d
■ 请求统计
总请求数 %d
搜索请求 %d
详情请求 %d
目录请求 %d
内容请求 %d
漫画请求 %d
音频请求 %d
视频请求 %d
分类请求 %d
◆ 缓存统计
正文缓存数 %d
正文缓存 %s
自动清理 %s
清理间隔 %s
◈ API 接口文档
书籍相关
GET/search
◆ 搜索书籍
query 必需 搜索关键字
offset 可选 偏移量,默认0
tab_type 可选 类型,默认3
GET/detail
◆ 获取书籍详情
book_id 必需 书籍ID
GET/catalog
◆ 获取书籍目录
book_id 必需 书籍ID
GET/multi-detail
◆ 批量获取书籍详情
book_id 必需 书籍ID列表,逗号分隔
内容相关
GET/content
◆ 获取章节正文内容
item_id 必需 章节ID,支持路径参数或查询参数
text_mode 可选 纯文本模式:1/true=纯文本,0/false=HTML
image_mode 可选 图片处理:1/true=保留图片,0/false=移除图片
review 可选 段评功能:1=启用,0=禁用
GET/multi-content
◆ 批量获取多个章节正文
book_id 必需 书籍ID
item_ids 必需 章节ID列表,多个ID用逗号分隔,如:123,456,789
text_mode 可选 纯文本模式:1/true=纯文本,0/false=HTML
image_mode 可选 图片处理:1/true=保留图片,0/false=移除图片
ignore_failed 可选 忽略失败章节:1/true=忽略,0/false=不忽略
GET/item-summary
◆ 获取章节摘要信息
book_id 必需 书籍ID
item_ids 必需 章节ID列表,逗号分隔
段评相关
GET/review
◆ 获取段落评论、回复
item_id 必需 章节ID
book_id 必需 书籍ID
para 必需 段落索引(从0开始)
comment_id 可选 评论ID(获取回复时需要)
GET/review_html
◆ 获取段落评论HTML页面
item_id 必需 章节ID
book_id 必需 书籍ID
para 必需 段落索引(从0开始)
多媒体
GET/manga
◆ 获取漫画内容
item_ids 必需 章节ID
output 可选 输出格式:url/proxy/data_uri,默认url
GET/audio
◆ 获取音频信息
item_ids 必需 章节ID
tone_id 可选 音色ID,默认1
GET/video
◆ 获取视频信息
item_ids 必需 章节ID
分类
GET/category-front
◆ 获取分类首页
tab 可选 标签页,默认0
GET/category-landing
◆ 获取分类详情页
category_id 必需 分类ID
offset 可选 偏移量,默认0
limit 可选 每页数量,默认20
系统
GET/status
◆ 系统状态页面(当前页面)
GET/self-info
◆ 设备信息(包含敏感数据)
`, time.Now().Format("2006-01-02 15:04:05"), uptimeStr, startTime.Format("2006-01-02 15:04:05"), signatureDetails, getDBTotalSize(), getDBUsedSize(), getDBFreeSize(), poolStatus["status"], poolStatus["total"], poolStatus["valid"], poolStatus["invalid"], poolStatus["current_index"], stats["total"], stats["search"], stats["detail"], stats["catalog"], stats["content"], stats["manga"], stats["audio"], stats["video"], stats["category"], getDBCacheCount(), getCacheStatus(), getCleanupStatus(), getCleanupInterval(), ) fmt.Fprint(w, html) } // 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"], } }