rest.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package api
  2. import (
  3. "fmt"
  4. "google.golang.org/protobuf/encoding/protojson"
  5. "net/http"
  6. "strings"
  7. "github.com/asaskevich/govalidator"
  8. "github.com/cad/ovpm/api/pb"
  9. "github.com/cad/ovpm/bundle"
  10. assetfs "github.com/elazarl/go-bindata-assetfs"
  11. "github.com/go-openapi/runtime/middleware"
  12. "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
  13. "github.com/sirupsen/logrus"
  14. "golang.org/x/net/context"
  15. "google.golang.org/grpc"
  16. )
  17. // NewRESTServer returns a new REST server.
  18. func NewRESTServer(grpcPort string) (http.Handler, context.CancelFunc, error) {
  19. mux := http.NewServeMux()
  20. ctx := context.Background()
  21. ctx, cancel := context.WithCancel(ctx)
  22. if !govalidator.IsNumeric(grpcPort) {
  23. return nil, cancel, fmt.Errorf("grpcPort should be numeric")
  24. }
  25. endPoint := fmt.Sprintf("localhost:%s", grpcPort)
  26. ctx = NewOriginTypeContext(ctx, OriginTypeREST)
  27. gmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
  28. MarshalOptions: protojson.MarshalOptions{
  29. UseProtoNames: true,
  30. EmitUnpopulated: true,
  31. },
  32. UnmarshalOptions: protojson.UnmarshalOptions{
  33. DiscardUnknown: true,
  34. },
  35. }))
  36. opts := []grpc.DialOption{grpc.WithInsecure()}
  37. err := pb.RegisterVPNServiceHandlerFromEndpoint(ctx, gmux, endPoint, opts)
  38. if err != nil {
  39. return nil, cancel, err
  40. }
  41. err = pb.RegisterUserServiceHandlerFromEndpoint(ctx, gmux, endPoint, opts)
  42. if err != nil {
  43. return nil, cancel, err
  44. }
  45. err = pb.RegisterNetworkServiceHandlerFromEndpoint(ctx, gmux, endPoint, opts)
  46. if err != nil {
  47. return nil, cancel, err
  48. }
  49. err = pb.RegisterAuthServiceHandlerFromEndpoint(ctx, gmux, endPoint, opts)
  50. if err != nil {
  51. return nil, cancel, err
  52. }
  53. mux.HandleFunc("/api/specs/", specsHandler)
  54. mware := middleware.Redoc(middleware.RedocOpts{
  55. BasePath: "/api/docs/",
  56. SpecURL: "/api/specs/user.swagger.json",
  57. Path: "user",
  58. }, gmux)
  59. mware = middleware.Redoc(middleware.RedocOpts{
  60. BasePath: "/api/docs/",
  61. SpecURL: "/api/specs/vpn.swagger.json",
  62. Path: "vpn",
  63. }, mware)
  64. mware = middleware.Redoc(middleware.RedocOpts{
  65. BasePath: "/api/docs/",
  66. SpecURL: "/api/specs/network.swagger.json",
  67. Path: "network",
  68. }, mware)
  69. mware = middleware.Redoc(middleware.RedocOpts{
  70. BasePath: "/api/docs/",
  71. SpecURL: "/api/specs/auth.swagger.json",
  72. Path: "auth",
  73. }, mware)
  74. mux.Handle("/api/", mware)
  75. mux.Handle("/", http.FileServer(
  76. &assetfs.AssetFS{Asset: bundle.Asset, AssetDir: bundle.AssetDir, Prefix: "bundle"}))
  77. return allowCORS(mux), cancel, nil
  78. }
  79. func specsHandler(w http.ResponseWriter, r *http.Request) {
  80. w.Header().Set("Content-Type", "application/json")
  81. switch r.URL.Path {
  82. case "/api/specs/user.swagger.json":
  83. userData, err := bundle.Asset("bundle/user.swagger.json")
  84. if err != nil {
  85. logrus.Warn(err)
  86. }
  87. w.Write(userData)
  88. case "/api/specs/network.swagger.json":
  89. networkData, err := bundle.Asset("bundle/network.swagger.json")
  90. if err != nil {
  91. logrus.Warn(err)
  92. }
  93. w.Write(networkData)
  94. case "/api/specs/vpn.swagger.json":
  95. vpnData, err := bundle.Asset("bundle/vpn.swagger.json")
  96. if err != nil {
  97. logrus.Warn(err)
  98. }
  99. w.Write(vpnData)
  100. case "/api/specs/auth.swagger.json":
  101. vpnData, err := bundle.Asset("bundle/auth.swagger.json")
  102. if err != nil {
  103. logrus.Warn(err)
  104. }
  105. w.Write(vpnData)
  106. }
  107. }
  108. func webuiHandler(w http.ResponseWriter, r *http.Request) {
  109. switch r.URL.Path {
  110. case "/bundle.js":
  111. userData, err := bundle.Asset("bundle/bundle.js")
  112. if err != nil {
  113. logrus.Warn(err)
  114. }
  115. w.Header().Set("Content-Type", "application/javascript")
  116. w.Write(userData)
  117. case "/js/mui.min.js":
  118. userData, err := bundle.Asset("bundle/js/mui.min.js")
  119. if err != nil {
  120. logrus.Warn(err)
  121. }
  122. w.Header().Set("Content-Type", "application/javascript")
  123. w.Write(userData)
  124. case "/css/bootstrap.min.css":
  125. userData, err := bundle.Asset("bundle/css/bootstrap.min.css")
  126. if err != nil {
  127. logrus.Warn(err)
  128. }
  129. w.Header().Set("Content-Type", "text/css")
  130. w.Write(userData)
  131. case "/css/mui.min.css":
  132. userData, err := bundle.Asset("bundle/css/mui.min.css")
  133. if err != nil {
  134. logrus.Warn(err)
  135. }
  136. w.Header().Set("Content-Type", "text/css")
  137. w.Write(userData)
  138. case "/fonts/glyphicons-halflings-regular.woff":
  139. userData, err := bundle.Asset("bundle/glyphicons-halflings-regular.woff")
  140. if err != nil {
  141. logrus.Warn(err)
  142. }
  143. w.Header().Set("Content-Type", "application/font-woff")
  144. w.Write(userData)
  145. default:
  146. networkData, err := bundle.Asset("bundle/index.html")
  147. if err != nil {
  148. logrus.Warn(err)
  149. }
  150. w.Write(networkData)
  151. }
  152. }
  153. func preflightHandler(w http.ResponseWriter, r *http.Request) {
  154. headers := []string{"Content-Type", "Accept", "Authorization"}
  155. w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ","))
  156. methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"}
  157. w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ","))
  158. w.Header().Set("Access-Control-Expose-Headers", "Access-Control-Allow-Origin")
  159. logrus.Debugf("rest: preflight request for %s", r.URL.Path)
  160. return
  161. }
  162. func allowCORS(h http.Handler) http.Handler {
  163. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  164. if origin := r.Header.Get("Origin"); origin != "" {
  165. w.Header().Set("Access-Control-Allow-Origin", origin)
  166. if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" {
  167. preflightHandler(w, r)
  168. return
  169. }
  170. }
  171. h.ServeHTTP(w, r)
  172. })
  173. }