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  }