api.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package ssmapi
  2. import (
  3. "net/http"
  4. C "github.com/sagernet/sing-box/constant"
  5. "github.com/sagernet/sing/common/logger"
  6. sHTTP "github.com/sagernet/sing/protocol/http"
  7. "github.com/go-chi/chi/v5"
  8. "github.com/go-chi/render"
  9. )
  10. type APIServer struct {
  11. logger logger.Logger
  12. traffic *TrafficManager
  13. user *UserManager
  14. }
  15. func NewAPIServer(logger logger.Logger, traffic *TrafficManager, user *UserManager) *APIServer {
  16. return &APIServer{
  17. logger: logger,
  18. traffic: traffic,
  19. user: user,
  20. }
  21. }
  22. func (s *APIServer) Route(r chi.Router) {
  23. r.Route("/server/v1", func(r chi.Router) {
  24. r.Use(func(handler http.Handler) http.Handler {
  25. return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
  26. s.logger.Debug(request.Method, " ", request.RequestURI, " ", sHTTP.SourceAddress(request))
  27. handler.ServeHTTP(writer, request)
  28. })
  29. })
  30. r.Get("/", s.getServerInfo)
  31. r.Get("/users", s.listUser)
  32. r.Post("/users", s.addUser)
  33. r.Get("/users/{username}", s.getUser)
  34. r.Put("/users/{username}", s.updateUser)
  35. r.Delete("/users/{username}", s.deleteUser)
  36. r.Get("/stats", s.getStats)
  37. })
  38. }
  39. func (s *APIServer) getServerInfo(writer http.ResponseWriter, request *http.Request) {
  40. render.JSON(writer, request, render.M{
  41. "server": "sing-box " + C.Version,
  42. "apiVersion": "v1",
  43. })
  44. }
  45. type UserObject struct {
  46. UserName string `json:"username"`
  47. Password string `json:"uPSK,omitempty"`
  48. DownlinkBytes int64 `json:"downlinkBytes"`
  49. UplinkBytes int64 `json:"uplinkBytes"`
  50. DownlinkPackets int64 `json:"downlinkPackets"`
  51. UplinkPackets int64 `json:"uplinkPackets"`
  52. TCPSessions int64 `json:"tcpSessions"`
  53. UDPSessions int64 `json:"udpSessions"`
  54. }
  55. func (s *APIServer) listUser(writer http.ResponseWriter, request *http.Request) {
  56. render.JSON(writer, request, render.M{
  57. "users": s.user.List(),
  58. })
  59. }
  60. func (s *APIServer) addUser(writer http.ResponseWriter, request *http.Request) {
  61. var addRequest struct {
  62. UserName string `json:"username"`
  63. Password string `json:"uPSK"`
  64. }
  65. err := render.DecodeJSON(request.Body, &addRequest)
  66. if err != nil {
  67. render.Status(request, http.StatusBadRequest)
  68. render.PlainText(writer, request, err.Error())
  69. return
  70. }
  71. err = s.user.Add(addRequest.UserName, addRequest.Password)
  72. if err != nil {
  73. render.Status(request, http.StatusBadRequest)
  74. render.PlainText(writer, request, err.Error())
  75. return
  76. }
  77. writer.WriteHeader(http.StatusCreated)
  78. }
  79. func (s *APIServer) getUser(writer http.ResponseWriter, request *http.Request) {
  80. userName := chi.URLParam(request, "username")
  81. if userName == "" {
  82. writer.WriteHeader(http.StatusBadRequest)
  83. return
  84. }
  85. uPSK, loaded := s.user.Get(userName)
  86. if !loaded {
  87. writer.WriteHeader(http.StatusNotFound)
  88. return
  89. }
  90. user := UserObject{
  91. UserName: userName,
  92. Password: uPSK,
  93. }
  94. s.traffic.ReadUser(&user)
  95. render.JSON(writer, request, user)
  96. }
  97. func (s *APIServer) updateUser(writer http.ResponseWriter, request *http.Request) {
  98. userName := chi.URLParam(request, "username")
  99. if userName == "" {
  100. writer.WriteHeader(http.StatusBadRequest)
  101. return
  102. }
  103. var updateRequest struct {
  104. Password string `json:"uPSK"`
  105. }
  106. err := render.DecodeJSON(request.Body, &updateRequest)
  107. if err != nil {
  108. render.Status(request, http.StatusBadRequest)
  109. render.PlainText(writer, request, err.Error())
  110. return
  111. }
  112. _, loaded := s.user.Get(userName)
  113. if !loaded {
  114. writer.WriteHeader(http.StatusNotFound)
  115. return
  116. }
  117. err = s.user.Update(userName, updateRequest.Password)
  118. if err != nil {
  119. render.Status(request, http.StatusBadRequest)
  120. render.PlainText(writer, request, err.Error())
  121. return
  122. }
  123. writer.WriteHeader(http.StatusNoContent)
  124. }
  125. func (s *APIServer) deleteUser(writer http.ResponseWriter, request *http.Request) {
  126. userName := chi.URLParam(request, "username")
  127. if userName == "" {
  128. writer.WriteHeader(http.StatusBadRequest)
  129. return
  130. }
  131. _, loaded := s.user.Get(userName)
  132. if !loaded {
  133. writer.WriteHeader(http.StatusNotFound)
  134. return
  135. }
  136. err := s.user.Delete(userName)
  137. if err != nil {
  138. render.Status(request, http.StatusBadRequest)
  139. render.PlainText(writer, request, err.Error())
  140. return
  141. }
  142. writer.WriteHeader(http.StatusNoContent)
  143. }
  144. func (s *APIServer) getStats(writer http.ResponseWriter, request *http.Request) {
  145. requireClear := request.URL.Query().Get("clear") == "true"
  146. users := s.user.List()
  147. s.traffic.ReadUsers(users, requireClear)
  148. for i := range users {
  149. users[i].Password = ""
  150. }
  151. uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal(requireClear)
  152. render.JSON(writer, request, render.M{
  153. "uplinkBytes": uplinkBytes,
  154. "downlinkBytes": downlinkBytes,
  155. "uplinkPackets": uplinkPackets,
  156. "downlinkPackets": downlinkPackets,
  157. "tcpSessions": tcpSessions,
  158. "udpSessions": udpSessions,
  159. "users": users,
  160. })
  161. }