api.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "strconv"
  7. "strings"
  8. "AI-Status-Light/internal/database"
  9. )
  10. type Server struct {
  11. db *database.DB
  12. server *http.Server
  13. }
  14. type Response struct {
  15. Code int `json:"code"`
  16. Message string `json:"message"`
  17. Data interface{} `json:"data,omitempty"`
  18. }
  19. func New(db *database.DB, addr string) *Server {
  20. s := &Server{db: db}
  21. mux := http.NewServeMux()
  22. mux.HandleFunc("/api/mqtt", s.handleMQTT)
  23. mux.HandleFunc("/api/mqtt/", s.handleMQTTByID)
  24. mux.HandleFunc("/api/health", s.handleHealth)
  25. s.server = &http.Server{
  26. Addr: addr,
  27. Handler: corsMiddleware(mux),
  28. }
  29. return s
  30. }
  31. func (s *Server) Start() error {
  32. return s.server.ListenAndServe()
  33. }
  34. func corsMiddleware(next http.Handler) http.Handler {
  35. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  36. w.Header().Set("Access-Control-Allow-Origin", "*")
  37. w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
  38. w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
  39. if r.Method == "OPTIONS" {
  40. w.WriteHeader(http.StatusOK)
  41. return
  42. }
  43. next.ServeHTTP(w, r)
  44. })
  45. }
  46. func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
  47. writeJSON(w, http.StatusOK, Response{Code: 0, Message: "ok"})
  48. }
  49. func (s *Server) handleMQTT(w http.ResponseWriter, r *http.Request) {
  50. switch r.Method {
  51. case http.MethodGet:
  52. s.listMQTTConfigs(w, r)
  53. case http.MethodPost:
  54. s.createMQTTConfig(w, r)
  55. default:
  56. writeJSON(w, http.StatusMethodNotAllowed, Response{Code: -1, Message: "方法不允许"})
  57. }
  58. }
  59. func (s *Server) handleMQTTByID(w http.ResponseWriter, r *http.Request) {
  60. idStr := strings.TrimPrefix(r.URL.Path, "/api/mqtt/")
  61. id, err := strconv.Atoi(idStr)
  62. if err != nil {
  63. writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "无效的 ID"})
  64. return
  65. }
  66. switch r.Method {
  67. case http.MethodGet:
  68. s.getMQTTConfig(w, id)
  69. case http.MethodPut:
  70. s.updateMQTTConfig(w, r, id)
  71. case http.MethodDelete:
  72. s.deleteMQTTConfig(w, id)
  73. default:
  74. writeJSON(w, http.StatusMethodNotAllowed, Response{Code: -1, Message: "方法不允许"})
  75. }
  76. }
  77. func (s *Server) listMQTTConfigs(w http.ResponseWriter, r *http.Request) {
  78. configs, err := s.db.ListMQTTConfigs()
  79. if err != nil {
  80. writeJSON(w, http.StatusInternalServerError, Response{Code: -1, Message: err.Error()})
  81. return
  82. }
  83. writeJSON(w, http.StatusOK, Response{Code: 0, Message: "ok", Data: configs})
  84. }
  85. func (s *Server) createMQTTConfig(w http.ResponseWriter, r *http.Request) {
  86. var cfg database.MQTTConfig
  87. if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
  88. writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "无效的请求体"})
  89. return
  90. }
  91. if cfg.Broker == "" {
  92. writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "broker 不能为空"})
  93. return
  94. }
  95. if cfg.ClientID == "" {
  96. cfg.ClientID = "opencode-monitor"
  97. }
  98. if cfg.Topic == "" {
  99. cfg.Topic = "opencode/status"
  100. }
  101. if err := s.db.SaveMQTTConfig(&cfg); err != nil {
  102. writeJSON(w, http.StatusInternalServerError, Response{Code: -1, Message: err.Error()})
  103. return
  104. }
  105. writeJSON(w, http.StatusCreated, Response{Code: 0, Message: "创建成功", Data: cfg})
  106. }
  107. func (s *Server) getMQTTConfig(w http.ResponseWriter, id int) {
  108. configs, err := s.db.ListMQTTConfigs()
  109. if err != nil {
  110. writeJSON(w, http.StatusInternalServerError, Response{Code: -1, Message: err.Error()})
  111. return
  112. }
  113. for _, cfg := range configs {
  114. if cfg.ID == id {
  115. writeJSON(w, http.StatusOK, Response{Code: 0, Message: "ok", Data: cfg})
  116. return
  117. }
  118. }
  119. writeJSON(w, http.StatusNotFound, Response{Code: -1, Message: "配置不存在"})
  120. }
  121. func (s *Server) updateMQTTConfig(w http.ResponseWriter, r *http.Request, id int) {
  122. var cfg database.MQTTConfig
  123. if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
  124. writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "无效的请求体"})
  125. return
  126. }
  127. cfg.ID = id
  128. if cfg.Broker == "" {
  129. writeJSON(w, http.StatusBadRequest, Response{Code: -1, Message: "broker 不能为空"})
  130. return
  131. }
  132. if cfg.ClientID == "" {
  133. cfg.ClientID = "opencode-monitor"
  134. }
  135. if cfg.Topic == "" {
  136. cfg.Topic = "opencode/status"
  137. }
  138. if err := s.db.SaveMQTTConfig(&cfg); err != nil {
  139. writeJSON(w, http.StatusInternalServerError, Response{Code: -1, Message: err.Error()})
  140. return
  141. }
  142. writeJSON(w, http.StatusOK, Response{Code: 0, Message: "更新成功", Data: cfg})
  143. }
  144. func (s *Server) deleteMQTTConfig(w http.ResponseWriter, id int) {
  145. if err := s.db.DeleteMQTTConfig(id); err != nil {
  146. writeJSON(w, http.StatusInternalServerError, Response{Code: -1, Message: err.Error()})
  147. return
  148. }
  149. writeJSON(w, http.StatusOK, Response{Code: 0, Message: "删除成功"})
  150. }
  151. func writeJSON(w http.ResponseWriter, statusCode int, data interface{}) {
  152. w.Header().Set("Content-Type", "application/json")
  153. w.WriteHeader(statusCode)
  154. json.NewEncoder(w).Encode(data)
  155. }
  156. func (s *Server) GetAddr() string {
  157. return s.server.Addr
  158. }