restful-api.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package restful_api
  2. import (
  3. "encoding/json"
  4. "github.com/go-chi/chi/v5"
  5. "github.com/go-chi/chi/v5/middleware"
  6. "github.com/go-playground/validator/v10"
  7. "github.com/v2fly/v2ray-core/v4/common/net"
  8. "github.com/v2fly/v2ray-core/v4/transport/internet"
  9. "net/http"
  10. "strings"
  11. )
  12. func JSONResponse(w http.ResponseWriter, data interface{}, code int) {
  13. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  14. w.Header().Set("X-Content-Type-Options", "nosniff")
  15. w.WriteHeader(code)
  16. _ = json.NewEncoder(w).Encode(data)
  17. }
  18. var validate *validator.Validate
  19. type StatsUser struct {
  20. uuid string `validate:"required_without=email,uuid4"`
  21. email string `validate:"required_without=uuid,email"`
  22. }
  23. type StatsUserResponse struct {
  24. Uplink int64 `json:"uplink"`
  25. Downlink int64 `json:"downlink"`
  26. }
  27. func (rs *restfulService) statsUser(w http.ResponseWriter, r *http.Request) {
  28. query := r.URL.Query()
  29. statsUser := &StatsUser{
  30. uuid: query.Get("uuid"),
  31. email: query.Get("email"),
  32. }
  33. if err := validate.Struct(statsUser); err != nil {
  34. JSONResponse(w, http.StatusText(422), 422)
  35. }
  36. response := &StatsUserResponse{
  37. Uplink: 0,
  38. Downlink: 0,
  39. }
  40. JSONResponse(w, response, 200)
  41. }
  42. type Stats struct {
  43. tag string `validate:"required,alpha,min=1,max=255"`
  44. }
  45. type StatsBound struct { // Better name?
  46. Uplink int64 `json:"uplink"`
  47. Downlink int64 `json:"downlink"`
  48. }
  49. type StatsResponse struct {
  50. Inbound StatsBound `json:"inbound"`
  51. Outbound StatsBound `json:"outbound"`
  52. }
  53. func (rs *restfulService) statsRequest(w http.ResponseWriter, r *http.Request) {
  54. stats := &Stats{
  55. tag: r.URL.Query().Get("tag"),
  56. }
  57. if err := validate.Struct(stats); err != nil {
  58. JSONResponse(w, http.StatusText(422), 422)
  59. }
  60. response := StatsResponse{
  61. Inbound: StatsBound{
  62. Uplink: 1,
  63. Downlink: 1,
  64. },
  65. Outbound: StatsBound{
  66. Uplink: 1,
  67. Downlink: 1,
  68. }}
  69. JSONResponse(w, response, 200)
  70. }
  71. func (rs *restfulService) TokenAuthMiddleware(next http.Handler) http.Handler {
  72. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  73. auth := r.Header.Get("Authorization")
  74. const prefix = "Bearer "
  75. if !strings.HasPrefix(auth, prefix) {
  76. JSONResponse(w, http.StatusText(403), 403)
  77. return
  78. }
  79. auth = strings.TrimPrefix(auth, prefix)
  80. if auth != rs.config.AuthToken {
  81. JSONResponse(w, http.StatusText(403), 403)
  82. return
  83. }
  84. next.ServeHTTP(w, r)
  85. })
  86. }
  87. func (rs *restfulService) start() error {
  88. r := chi.NewRouter()
  89. r.Use(rs.TokenAuthMiddleware)
  90. r.Use(middleware.Heartbeat("/ping"))
  91. r.Route("/v1", func(r chi.Router) {
  92. r.Get("/stats/user", rs.statsUser)
  93. r.Get("/stats", rs.statsRequest)
  94. })
  95. var listener net.Listener
  96. var err error
  97. address := net.ParseAddress(rs.config.ListenAddr)
  98. switch {
  99. case address.Family().IsIP():
  100. listener, err = internet.ListenSystem(rs.ctx, &net.TCPAddr{IP: address.IP(), Port: int(rs.config.ListenPort)}, nil)
  101. case strings.EqualFold(address.Domain(), "localhost"):
  102. listener, err = internet.ListenSystem(rs.ctx, &net.TCPAddr{IP: net.IP{127, 0, 0, 1}, Port: int(rs.config.ListenPort)}, nil)
  103. default:
  104. return newError("restful api cannot listen on the address: ", address)
  105. }
  106. if err != nil {
  107. return newError("restful api cannot listen on the port ", rs.config.ListenPort).Base(err)
  108. }
  109. go func() {
  110. err := http.Serve(listener, r)
  111. if err != nil {
  112. newError("unable to serve restful api").WriteToLog()
  113. }
  114. }()
  115. return nil
  116. }