server.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package api
  2. import (
  3. "context"
  4. "net/http"
  5. "strconv"
  6. "clash-speed-test/internal/config"
  7. "clash-speed-test/internal/core"
  8. "clash-speed-test/internal/database"
  9. "clash-speed-test/internal/logger"
  10. "github.com/gin-gonic/gin"
  11. "gorm.io/gorm"
  12. )
  13. type Server struct {
  14. config *config.Config
  15. db *gorm.DB
  16. speedTester *core.SpeedTester
  17. scheduler *core.Scheduler
  18. router *gin.Engine
  19. server *http.Server
  20. }
  21. func NewServer(cfg *config.Config, db *gorm.DB, speedTester *core.SpeedTester, scheduler *core.Scheduler) *Server {
  22. gin.SetMode(gin.ReleaseMode)
  23. router := gin.New()
  24. router.Use(gin.Recovery())
  25. server := &Server{
  26. config: cfg,
  27. db: db,
  28. speedTester: speedTester,
  29. scheduler: scheduler,
  30. router: router,
  31. }
  32. server.setupRoutes()
  33. return server
  34. }
  35. func (s *Server) setupRoutes() {
  36. // 健康检查
  37. s.router.GET("/health", s.healthCheck)
  38. // API路由组
  39. api := s.router.Group("/api")
  40. {
  41. // 节点管理
  42. api.GET("/nodes", s.getNodes)
  43. api.POST("/nodes", s.createNode)
  44. api.PUT("/nodes/:id", s.updateNode)
  45. api.DELETE("/nodes/:id", s.deleteNode)
  46. // 测速相关
  47. api.GET("/speed-test", s.getSpeedTestResults)
  48. api.POST("/speed-test/trigger", s.triggerSpeedTest)
  49. api.GET("/speed-test/history/:nodeId", s.getNodeTestHistory)
  50. // 系统状态
  51. api.GET("/status", s.getSystemStatus)
  52. }
  53. // 静态文件服务
  54. s.router.Static("/static", "./static")
  55. s.router.LoadHTMLGlob("templates/*")
  56. // 主页
  57. s.router.GET("/", s.index)
  58. }
  59. // 启动服务器
  60. func (s *Server) Start() error {
  61. addr := s.config.Server.Host + ":" + strconv.Itoa(s.config.Server.Port)
  62. s.server = &http.Server{
  63. Addr: addr,
  64. Handler: s.router,
  65. }
  66. return s.server.ListenAndServe()
  67. }
  68. // 关闭服务器
  69. func (s *Server) Shutdown(ctx context.Context) error {
  70. return s.server.Shutdown(ctx)
  71. }
  72. // 健康检查
  73. func (s *Server) healthCheck(c *gin.Context) {
  74. c.JSON(http.StatusOK, gin.H{
  75. "status": "ok",
  76. "service": "clash-speed-test",
  77. "version": "1.0.0",
  78. })
  79. }
  80. // 主页
  81. func (s *Server) index(c *gin.Context) {
  82. c.HTML(http.StatusOK, "index.html", gin.H{
  83. "title": "Clash Speed Test",
  84. })
  85. }
  86. // 获取所有节点
  87. func (s *Server) getNodes(c *gin.Context) {
  88. var nodes []database.Node
  89. if err := s.db.Find(&nodes).Error; err != nil {
  90. logger.Error("获取节点列表失败", map[string]interface{}{
  91. "error": err.Error(),
  92. })
  93. c.JSON(http.StatusInternalServerError, gin.H{"error": "获取节点列表失败"})
  94. return
  95. }
  96. c.JSON(http.StatusOK, gin.H{
  97. "data": nodes,
  98. })
  99. }
  100. // 创建节点
  101. func (s *Server) createNode(c *gin.Context) {
  102. var node database.Node
  103. if err := c.ShouldBindJSON(&node); err != nil {
  104. c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
  105. return
  106. }
  107. if err := s.db.Create(&node).Error; err != nil {
  108. logger.Error("创建节点失败", map[string]interface{}{
  109. "error": err.Error(),
  110. })
  111. c.JSON(http.StatusInternalServerError, gin.H{"error": "创建节点失败"})
  112. return
  113. }
  114. c.JSON(http.StatusCreated, gin.H{
  115. "data": node,
  116. })
  117. }
  118. // 更新节点
  119. func (s *Server) updateNode(c *gin.Context) {
  120. id, err := strconv.ParseUint(c.Param("id"), 10, 32)
  121. if err != nil {
  122. c.JSON(http.StatusBadRequest, gin.H{"error": "无效的节点ID"})
  123. return
  124. }
  125. var node database.Node
  126. if err := s.db.First(&node, id).Error; err != nil {
  127. c.JSON(http.StatusNotFound, gin.H{"error": "节点不存在"})
  128. return
  129. }
  130. var updateData database.Node
  131. if err := c.ShouldBindJSON(&updateData); err != nil {
  132. c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
  133. return
  134. }
  135. if err := s.db.Model(&node).Updates(updateData).Error; err != nil {
  136. logger.Error("更新节点失败", map[string]interface{}{
  137. "error": err.Error(),
  138. })
  139. c.JSON(http.StatusInternalServerError, gin.H{"error": "更新节点失败"})
  140. return
  141. }
  142. c.JSON(http.StatusOK, gin.H{
  143. "data": node,
  144. })
  145. }
  146. // 删除节点
  147. func (s *Server) deleteNode(c *gin.Context) {
  148. id, err := strconv.ParseUint(c.Param("id"), 10, 32)
  149. if err != nil {
  150. c.JSON(http.StatusBadRequest, gin.H{"error": "无效的节点ID"})
  151. return
  152. }
  153. if err := s.db.Delete(&database.Node{}, id).Error; err != nil {
  154. logger.Error("删除节点失败", map[string]interface{}{
  155. "error": err.Error(),
  156. })
  157. c.JSON(http.StatusInternalServerError, gin.H{"error": "删除节点失败"})
  158. return
  159. }
  160. c.JSON(http.StatusOK, gin.H{"message": "节点删除成功"})
  161. }
  162. // 获取测速结果
  163. func (s *Server) getSpeedTestResults(c *gin.Context) {
  164. limit := 50
  165. if limitStr := c.Query("limit"); limitStr != "" {
  166. if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
  167. limit = l
  168. }
  169. }
  170. results, err := database.GetRecentTestResults(limit)
  171. if err != nil {
  172. logger.Error("获取测速结果失败", map[string]interface{}{
  173. "error": err.Error(),
  174. })
  175. c.JSON(http.StatusInternalServerError, gin.H{"error": "获取测速结果失败"})
  176. return
  177. }
  178. c.JSON(http.StatusOK, gin.H{
  179. "data": results,
  180. })
  181. }
  182. // 手动触发测速
  183. func (s *Server) triggerSpeedTest(c *gin.Context) {
  184. s.scheduler.TriggerSpeedTest()
  185. c.JSON(http.StatusOK, gin.H{
  186. "message": "测速任务已触发",
  187. })
  188. }
  189. // 获取节点测试历史
  190. func (s *Server) getNodeTestHistory(c *gin.Context) {
  191. nodeID, err := strconv.ParseUint(c.Param("nodeId"), 10, 32)
  192. if err != nil {
  193. c.JSON(http.StatusBadRequest, gin.H{"error": "无效的节点ID"})
  194. return
  195. }
  196. limit := 20
  197. if limitStr := c.Query("limit"); limitStr != "" {
  198. if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
  199. limit = l
  200. }
  201. }
  202. results, err := database.GetNodeTestHistory(uint(nodeID), limit)
  203. if err != nil {
  204. logger.Error("获取节点测试历史失败", map[string]interface{}{
  205. "error": err.Error(),
  206. })
  207. c.JSON(http.StatusInternalServerError, gin.H{"error": "获取节点测试历史失败"})
  208. return
  209. }
  210. c.JSON(http.StatusOK, gin.H{
  211. "data": results,
  212. })
  213. }
  214. // 获取系统状态
  215. func (s *Server) getSystemStatus(c *gin.Context) {
  216. // 获取节点统计
  217. var totalNodes, activeNodes int64
  218. s.db.Model(&database.Node{}).Count(&totalNodes)
  219. s.db.Model(&database.Node{}).Where("is_active = ?", true).Count(&activeNodes)
  220. // 获取测试结果统计
  221. var totalTests, successTests int64
  222. s.db.Model(&database.TestResult{}).Count(&totalTests)
  223. s.db.Model(&database.TestResult{}).Where("is_success = ?", true).Count(&successTests)
  224. // 计算平均延迟
  225. var avgLatency float64
  226. s.db.Model(&database.TestResult{}).
  227. Where("is_success = ? AND latency IS NOT NULL", true).
  228. Select("AVG(latency)").
  229. Scan(&avgLatency)
  230. // 计算成功率
  231. var successRate float64
  232. if totalTests > 0 {
  233. successRate = float64(successTests) / float64(totalTests) * 100
  234. }
  235. c.JSON(http.StatusOK, gin.H{
  236. "total_nodes": totalNodes,
  237. "active_nodes": activeNodes,
  238. "avg_latency": int(avgLatency),
  239. "success_rate": int(successRate),
  240. "scheduler_running": s.scheduler.IsRunning(),
  241. })
  242. }