restful-api.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package restful_api
  2. import (
  3. "net/http"
  4. "strings"
  5. "github.com/go-chi/chi/v5"
  6. "github.com/go-chi/chi/v5/middleware"
  7. "github.com/go-chi/render"
  8. "github.com/go-playground/validator/v10"
  9. core "github.com/v2fly/v2ray-core/v4"
  10. "github.com/v2fly/v2ray-core/v4/common/net"
  11. "github.com/v2fly/v2ray-core/v4/transport/internet"
  12. )
  13. var validate *validator.Validate
  14. type StatsBound struct { // Better name?
  15. Uplink int64 `json:"uplink"`
  16. Downlink int64 `json:"downlink"`
  17. }
  18. func (rs *restfulService) tagStats(w http.ResponseWriter, r *http.Request) {
  19. boundType := chi.URLParam(r, "bound_type")
  20. tag := chi.URLParam(r, "tag")
  21. if validate.Var(boundType, "required,oneof=inbounds outbounds") != nil ||
  22. validate.Var(tag, "required,min=1,max=255") != nil {
  23. render.Status(r, http.StatusUnprocessableEntity)
  24. render.JSON(w, r, render.M{})
  25. return
  26. }
  27. bound := boundType[:len(boundType)-1]
  28. upCounter := rs.stats.GetCounter(bound + ">>>" + tag + ">>>traffic>>>uplink")
  29. downCounter := rs.stats.GetCounter(bound + ">>>" + tag + ">>>traffic>>>downlink")
  30. if upCounter == nil || downCounter == nil {
  31. render.Status(r, http.StatusNotFound)
  32. render.JSON(w, r, render.M{})
  33. return
  34. }
  35. render.JSON(w, r, &StatsBound{
  36. Uplink: upCounter.Value(),
  37. Downlink: downCounter.Value(),
  38. })
  39. }
  40. func (rs *restfulService) version(w http.ResponseWriter, r *http.Request) {
  41. render.JSON(w, r, render.M{"version": core.Version()})
  42. }
  43. func (rs *restfulService) TokenAuthMiddleware(next http.Handler) http.Handler {
  44. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  45. header := r.Header.Get("Authorization")
  46. text := strings.SplitN(header, " ", 2)
  47. hasInvalidHeader := text[0] != "Bearer"
  48. hasInvalidSecret := len(text) != 2 || text[1] != rs.config.AuthToken
  49. if hasInvalidHeader || hasInvalidSecret {
  50. render.Status(r, http.StatusUnauthorized)
  51. render.JSON(w, r, render.M{})
  52. return
  53. }
  54. next.ServeHTTP(w, r)
  55. })
  56. }
  57. func (rs *restfulService) start() error {
  58. r := chi.NewRouter()
  59. r.Use(middleware.Heartbeat("/ping"))
  60. validate = validator.New()
  61. r.Route("/v1", func(r chi.Router) {
  62. r.Get("/{bound_type}/{tag}/stats", rs.tagStats)
  63. })
  64. r.Get("/version", rs.version)
  65. var listener net.Listener
  66. var err error
  67. address := net.ParseAddress(rs.config.ListenAddr)
  68. switch {
  69. case address.Family().IsIP():
  70. listener, err = internet.ListenSystem(rs.ctx, &net.TCPAddr{IP: address.IP(), Port: int(rs.config.ListenPort)}, nil)
  71. case strings.EqualFold(address.Domain(), "localhost"):
  72. listener, err = internet.ListenSystem(rs.ctx, &net.TCPAddr{IP: net.IP{127, 0, 0, 1}, Port: int(rs.config.ListenPort)}, nil)
  73. default:
  74. return newError("restful api cannot listen on the address: ", address)
  75. }
  76. if err != nil {
  77. return newError("restful api cannot listen on the port ", rs.config.ListenPort).Base(err)
  78. }
  79. go func() {
  80. err := http.Serve(listener, r)
  81. if err != nil {
  82. newError("unable to serve restful api").WriteToLog()
  83. }
  84. }()
  85. return nil
  86. }