switched to chi, also:
* updated healthcheck a bit * added maintenance mode config toggle * added recovery handler
This commit is contained in:
@@ -1,26 +1,29 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s *server) handleHealthCheck() http.HandlerFunc {
|
||||
type response struct {
|
||||
Status string `json:"status"`
|
||||
Now string `json:"now"`
|
||||
Uptime string `json:"uptime"`
|
||||
Version string `json:"version"`
|
||||
Appname string `json:"appname"`
|
||||
Status string `json:"status"`
|
||||
Now string `json:"now"`
|
||||
UptimeSeconds int64 `json:"uptime_seconds"`
|
||||
UptimeHuman string `json:"uptime_human"`
|
||||
Version string `json:"version"`
|
||||
Appname string `json:"appname"`
|
||||
Maintenance bool `json:"maintenance_mode"`
|
||||
}
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
resp := &response{
|
||||
Status: "ok",
|
||||
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
||||
Uptime: fmt.Sprintf("%f", s.uptime().Seconds()),
|
||||
Appname: s.appname,
|
||||
Version: s.version,
|
||||
Status: "ok",
|
||||
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
||||
UptimeSeconds: int64(s.uptime().Seconds()),
|
||||
UptimeHuman: s.uptime().String(),
|
||||
Maintenance: s.maintenance(),
|
||||
Appname: s.appname,
|
||||
Version: s.version,
|
||||
}
|
||||
s.respondJSON(w, req, resp, 200)
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
// spooky action at a distance!
|
||||
// this populates the environment
|
||||
@@ -40,7 +40,7 @@ type server struct {
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
httpServer *http.Server
|
||||
router *mux.Router
|
||||
router *chi.Mux
|
||||
}
|
||||
|
||||
func NewServer(options ...func(s *server)) *server {
|
||||
@@ -157,6 +157,10 @@ func (s *server) uptime() time.Duration {
|
||||
return time.Since(s.startupTime)
|
||||
}
|
||||
|
||||
func (s *server) maintenance() bool {
|
||||
return viper.GetBool("MAINTENANCE_MODE")
|
||||
}
|
||||
|
||||
func (s *server) configure() {
|
||||
viper.SetConfigName(s.appname)
|
||||
viper.SetConfigType("yaml")
|
||||
@@ -168,6 +172,7 @@ func (s *server) configure() {
|
||||
viper.AutomaticEnv()
|
||||
|
||||
viper.SetDefault("DEBUG", "false")
|
||||
viper.SetDefault("MAINTENANCE_MODE", "false")
|
||||
viper.SetDefault("PORT", "8080")
|
||||
viper.SetDefault("DBURL", "")
|
||||
|
||||
@@ -182,7 +187,7 @@ func (s *server) configure() {
|
||||
}
|
||||
}
|
||||
|
||||
// if viper.GetBool("debug") {
|
||||
// if viper.GetBool("DEBUG") {
|
||||
// pp.Print(viper.AllSettings())
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
)
|
||||
|
||||
// the following is from
|
||||
@@ -43,6 +45,7 @@ func (s *server) LoggingMiddleware() func(http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
lrw := NewLoggingResponseWriter(w)
|
||||
ctx := r.Context()
|
||||
defer func() {
|
||||
latency := time.Since(start)
|
||||
s.log.Info().
|
||||
@@ -50,11 +53,12 @@ func (s *server) LoggingMiddleware() func(http.Handler) http.Handler {
|
||||
Str("method", r.Method).
|
||||
Str("url", r.URL.String()).
|
||||
Str("useragent", r.UserAgent()).
|
||||
Str("request_id", ctx.Value(middleware.RequestIDKey).(string)).
|
||||
Str("referer", r.Referer()).
|
||||
Str("proto", r.Proto).
|
||||
Str("remoteIP", ipFromHostPort(r.RemoteAddr)).
|
||||
Int("status", lrw.statusCode).
|
||||
Dur("latency", latency).
|
||||
Int("latency_ms", int(latency.Milliseconds())).
|
||||
Send()
|
||||
|
||||
}()
|
||||
|
||||
@@ -1,50 +1,71 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
)
|
||||
|
||||
func (s *server) routes() {
|
||||
s.router = mux.NewRouter()
|
||||
s.router = chi.NewRouter()
|
||||
|
||||
authMiddleware := s.AuthMiddleware()
|
||||
|
||||
s.router.HandleFunc("/", s.handleIndex()).Methods("GET")
|
||||
|
||||
// if you want to use a general purpose middleware (http.Handler
|
||||
// wrapper) on a specific HandleFunc route, you need to take the
|
||||
// .ServeHTTP of the http.Handler to get its HandleFunc, viz:
|
||||
s.router.HandleFunc(
|
||||
"/login",
|
||||
authMiddleware(s.handleLogin()).ServeHTTP,
|
||||
).Methods("GET")
|
||||
|
||||
s.router.HandleFunc(
|
||||
"/.well-known/healthcheck.json",
|
||||
s.handleHealthCheck(),
|
||||
).Methods("GET")
|
||||
|
||||
// route that panics for testing
|
||||
// CHANGEME remove this
|
||||
s.router.HandleFunc(
|
||||
"/panic",
|
||||
s.handlePanic(),
|
||||
).Methods("GET")
|
||||
|
||||
// the Gorilla mux .Use() takes a http.Handler wrapper func, like
|
||||
// the mux .Use() takes a http.Handler wrapper func, like
|
||||
// most things that deal with "middlewares" like alice et c, and
|
||||
// will call ServeHTTP on it. These middlewares applied by the mux
|
||||
// (you can .Use() more than one) will be applied to every request
|
||||
// into the service.
|
||||
|
||||
s.router.Use(middleware.RequestID)
|
||||
s.router.Use(s.LoggingMiddleware())
|
||||
// timeout for request context
|
||||
s.router.Use(middleware.Timeout(60 * time.Second))
|
||||
|
||||
// this adds a sentry reporting middleware if and only if
|
||||
// sentry is enabled via setting of SENTRY_DSN in env.
|
||||
// this was at the bottom, but chi requires *all* middlewares
|
||||
// applied before any routes are, so now it's up here.
|
||||
// unfortunately this cannot coexist with the normal chi Recoverer
|
||||
// handler which prints a nice stack trace to the console
|
||||
if s.sentryEnabled {
|
||||
// Options docs at
|
||||
// https://docs.sentry.io/platforms/go/guides/http/
|
||||
sentryHandler := sentryhttp.New(sentryhttp.Options{})
|
||||
s.router.Use(sentryHandler.Handle)
|
||||
// FYI: the sentry panic-catcher seems to set the response
|
||||
// code to 200.
|
||||
} else {
|
||||
// FYI: the chi Recoverer middleware sets the response code
|
||||
// on panics to 500.
|
||||
s.router.Use(middleware.Recoverer)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ROUTES
|
||||
// complete docs: https://github.com/go-chi/chi
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
s.router.Get("/", s.handleIndex())
|
||||
|
||||
// if you want to use a general purpose middleware (http.Handler
|
||||
// wrapper) on a specific HandleFunc route, you need to take the
|
||||
// .ServeHTTP of the http.Handler to get its HandleFunc, viz:
|
||||
authMiddleware := s.AuthMiddleware()
|
||||
s.router.Get(
|
||||
"/login",
|
||||
authMiddleware(s.handleLogin()).ServeHTTP,
|
||||
)
|
||||
|
||||
s.router.Get(
|
||||
"/.well-known/healthcheck.json",
|
||||
s.handleHealthCheck(),
|
||||
)
|
||||
|
||||
// route that panics for testing
|
||||
// CHANGEME remove this
|
||||
s.router.Get(
|
||||
"/panic",
|
||||
s.handlePanic(),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user