github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/daemon/routes_support.go (about) 1 // Copyright 2021 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 5 package daemonPkg 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "io" 11 "net/http" 12 "time" 13 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 15 "github.com/gorilla/mux" 16 "golang.org/x/time/rate" 17 ) 18 19 // RespondWithError marshals an err into JSON and returns the bytes 20 // back to the caller httpStatus HTTP error status code 21 func RespondWithError(w http.ResponseWriter, httpStatus int, err error) { 22 type ErrorResponse struct { 23 Errors []string `json:"errors,omitempty"` 24 } 25 marshalled, _ := json.MarshalIndent(ErrorResponse{Errors: []string{err.Error()}}, "", " ") 26 w.WriteHeader(httpStatus) 27 _, _ = w.Write(marshalled) 28 } 29 30 // NewRouter Creates a new router given the routes array 31 func NewRouter(silent bool) *mux.Router { 32 router := mux.NewRouter().StrictSlash(true) 33 router.Use(CorsHandler) 34 router. 35 Methods("OPTIONS"). 36 Handler(OptionsHandler) 37 router.Use(ContentTypeHandler) 38 39 for _, route := range routes { 40 var handler http.Handler 41 handler = route.HandlerFunc 42 handler = Logger(silent, handler, route.Name) 43 router. 44 Methods(route.Method). 45 Path(route.Pattern). 46 Name(route.Name). 47 Handler(handler) 48 } 49 50 return router 51 } 52 53 func addCorsHeaders(w http.ResponseWriter) { 54 w.Header().Set("Access-Control-Allow-Origin", "*") 55 w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") 56 w.Header().Set("Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, OPTIONS") 57 } 58 59 var OptionsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 60 addCorsHeaders(w) 61 }) 62 63 // CorsHandler handles CORS requests 64 func CorsHandler(next http.Handler) http.Handler { 65 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 66 addCorsHeaders(w) 67 next.ServeHTTP(w, r) 68 }) 69 } 70 71 // ContentTypeHandler sets correct Content-Type header on response 72 func ContentTypeHandler(next http.Handler) http.Handler { 73 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 74 requestedFormat := r.URL.Query().Get("fmt") 75 76 var contentType string 77 switch requestedFormat { 78 case "txt": 79 contentType = "text/plain" 80 case "csv": 81 contentType = "text/csv" 82 default: 83 contentType = "application/json" 84 } 85 86 w.Header().Set("Content-Type", contentType) 87 next.ServeHTTP(w, r) 88 }) 89 } 90 91 var nProcessed int 92 93 // Logger sends information to the server's console 94 func Logger(silent bool, inner http.Handler, name string) http.Handler { 95 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 96 var limiter = rate.NewLimiter(1, 3) 97 if !limiter.Allow() { 98 http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) 99 return 100 } 101 start := time.Now() 102 if silent { 103 w := logger.GetLoggerWriter() 104 defer logger.SetLoggerWriter(w) 105 logger.SetLoggerWriter(io.Discard) 106 } 107 inner.ServeHTTP(w, r) 108 if !silent { 109 t := "" 110 if isTestModeServer(r) { 111 t = "-test" 112 } 113 msg := fmt.Sprintf("%d %s%s %s %s %s", nProcessed, r.Method, t, r.RequestURI, name, time.Since(start)) 114 logger.Info(msg) 115 } 116 nProcessed++ 117 }) 118 } 119 120 // isTestModeServer return true if we are running from the testing harness 121 func isTestModeServer(r *http.Request) bool { 122 return r.Header.Get("User-Agent") == "testRunner" 123 }