merge migrate_chi_to_new_nethttp
no longer using chi for routing
This commit is contained in:
parent
8e62f7e271
commit
5dfffc496b
@ -962,10 +962,6 @@ span>a:visited {
|
|||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.items-end {
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.items-center {
|
.items-center {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,2 @@
|
|||||||
// Package htmx contains the routes for the WebUI HTMX
|
// Package htmx contains the routes for the WebUI HTMX
|
||||||
package htmx
|
package htmx
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.staur.ca/stobbsm/clustvirt/cluster"
|
|
||||||
"git.staur.ca/stobbsm/clustvirt/lib/log"
|
|
||||||
"git.staur.ca/stobbsm/clustvirt/router"
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type htmx struct {
|
|
||||||
router chi.Router
|
|
||||||
routes []router.Route
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *htmx) MountTo(c *cluster.Cluster, rr chi.Router) error {
|
|
||||||
var errs []error
|
|
||||||
for _, r := range h.routes {
|
|
||||||
switch r.Method {
|
|
||||||
case http.MethodTrace:
|
|
||||||
h.router.Trace(r.Path, r.Handler(c))
|
|
||||||
case http.MethodOptions:
|
|
||||||
h.router.Options(r.Path, r.Handler(c))
|
|
||||||
case http.MethodConnect:
|
|
||||||
h.router.Connect(r.Path, r.Handler(c))
|
|
||||||
case http.MethodHead:
|
|
||||||
h.router.Head(r.Path, r.Handler(c))
|
|
||||||
case http.MethodGet:
|
|
||||||
h.router.Get(r.Path, r.Handler(c))
|
|
||||||
case http.MethodPost:
|
|
||||||
h.router.Post(r.Path, r.Handler(c))
|
|
||||||
case http.MethodPut:
|
|
||||||
h.router.Put(r.Path, r.Handler(c))
|
|
||||||
case http.MethodPatch:
|
|
||||||
h.router.Patch(r.Path, r.Handler(c))
|
|
||||||
case http.MethodDelete:
|
|
||||||
h.router.Delete(r.Path, r.Handler(c))
|
|
||||||
default:
|
|
||||||
err := fmt.Errorf("method: %s: %w", r.Method, router.ErrMethodNotExist)
|
|
||||||
errs = append(errs, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Info("htmx.MoutnTo").
|
|
||||||
Str("Route.Path", r.Path).
|
|
||||||
Str("Route.Method", r.Method).
|
|
||||||
Msg("route registered")
|
|
||||||
}
|
|
||||||
rr.Mount("/htmx", h.router)
|
|
||||||
return errors.Join(errs...)
|
|
||||||
}
|
|
||||||
|
@ -8,12 +8,9 @@ import (
|
|||||||
"git.staur.ca/stobbsm/clustvirt/lib/log"
|
"git.staur.ca/stobbsm/clustvirt/lib/log"
|
||||||
"git.staur.ca/stobbsm/clustvirt/router"
|
"git.staur.ca/stobbsm/clustvirt/router"
|
||||||
"git.staur.ca/stobbsm/clustvirt/view"
|
"git.staur.ca/stobbsm/clustvirt/view"
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Htmx = &htmx{
|
var Htmx = router.Routes{
|
||||||
router: chi.NewRouter(),
|
|
||||||
routes: []router.Route{
|
|
||||||
{
|
{
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
Path: "/cluster",
|
Path: "/cluster",
|
||||||
@ -34,7 +31,7 @@ var Htmx = &htmx{
|
|||||||
Path: "/host/{hostname}",
|
Path: "/host/{hostname}",
|
||||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
host, err := c.GetHost(chi.URLParam(r, "hostname"))
|
host, err := c.GetHost(r.PathValue("hostname"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
log.Error("htmx.Handler").
|
log.Error("htmx.Handler").
|
||||||
@ -59,7 +56,7 @@ var Htmx = &htmx{
|
|||||||
Path: "/host/{hostname}/stats",
|
Path: "/host/{hostname}/stats",
|
||||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
host, err := c.GetHost(chi.URLParam(r, "hostname"))
|
host, err := c.GetHost(r.PathValue("hostname"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
log.Error("htmx.Handler").
|
log.Error("htmx.Handler").
|
||||||
@ -79,5 +76,4 @@ var Htmx = &htmx{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ var (
|
|||||||
BasicAuth = middleware.BasicAuth
|
BasicAuth = middleware.BasicAuth
|
||||||
Compress = middleware.Compress
|
Compress = middleware.Compress
|
||||||
Heartbeat = middleware.Heartbeat
|
Heartbeat = middleware.Heartbeat
|
||||||
NoCache = middleware.NoCache
|
//NoCache = middleware.NoCache
|
||||||
Profiler = middleware.Profiler
|
Profiler = middleware.Profiler
|
||||||
RealIP = middleware.RealIP
|
RealIP = middleware.RealIP
|
||||||
Recoverer = middleware.Recoverer
|
Recoverer = middleware.Recoverer
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
// Logger uses the in package log module to handle route logging
|
// Logger uses the in package log module to handle route logging
|
||||||
func Logger(next http.Handler) http.Handler {
|
func Logger(next http.Handler) http.Handler {
|
||||||
|
log.Info("logger.Logger").Str("middleware", "Logger").Msg("middleware loaded")
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
|
7
router/middleware/middleware.go
Normal file
7
router/middleware/middleware.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Middleware is a function that is meant to be chained with other functions
|
||||||
|
// through an http request lifecycle
|
||||||
|
type Middleware func(http.Handler) http.Handler
|
22
router/middleware/nocache.go
Normal file
22
router/middleware/nocache.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.staur.ca/stobbsm/clustvirt/lib/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NoCache adds headers indicating the browser shouldn't cache
|
||||||
|
// any of the responses. Useful for debugging, should not be used
|
||||||
|
// on production
|
||||||
|
func NoCache(next http.Handler) http.Handler {
|
||||||
|
log.Info("router.middleware").Str("middleware", "NoCache").Msg("middleware loaded")
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Cache-Control", "no-cache, no-store, no-transform, must-revalidate, private, max-age=0")
|
||||||
|
w.Header().Add("Expires", time.Unix(0, 0).UTC().Format(http.TimeFormat))
|
||||||
|
w.Header().Add("Pragma", "no-cache")
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
@ -4,7 +4,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.staur.ca/stobbsm/clustvirt/cluster"
|
"git.staur.ca/stobbsm/clustvirt/cluster"
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Types that are shared among routers
|
// Types that are shared among routers
|
||||||
@ -12,7 +11,8 @@ import (
|
|||||||
// SubRouter defines an interface to be able to add a subrouter to a
|
// SubRouter defines an interface to be able to add a subrouter to a
|
||||||
// chi router
|
// chi router
|
||||||
type SubRouter interface {
|
type SubRouter interface {
|
||||||
MountTo(*cluster.Cluster, chi.Router) error
|
// MountTo needs the path prefix, cluster handle and the mux to add to
|
||||||
|
MountTo(string, *cluster.Cluster, *http.ServeMux) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route defines a route that should be added to a chi router or
|
// Route defines a route that should be added to a chi router or
|
||||||
|
57
router/routes.go
Normal file
57
router/routes.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.staur.ca/stobbsm/clustvirt/cluster"
|
||||||
|
"git.staur.ca/stobbsm/clustvirt/lib/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Routes []Route
|
||||||
|
|
||||||
|
func addprefix(b, p string) string { return fmt.Sprintf("%s%s", b, p) }
|
||||||
|
func trace(p string) string { return fmt.Sprintf("TRACE %s", p) }
|
||||||
|
func options(p string) string { return fmt.Sprintf("OPTIONS %s", p) }
|
||||||
|
func connect(p string) string { return fmt.Sprintf("CONNECT %s", p) }
|
||||||
|
func head(p string) string { return fmt.Sprintf("HEAD %s", p) }
|
||||||
|
func get(p string) string { return fmt.Sprintf("GET %s", p) }
|
||||||
|
func post(p string) string { return fmt.Sprintf("POST %s", p) }
|
||||||
|
func put(p string) string { return fmt.Sprintf("PUT %s", p) }
|
||||||
|
func patch(p string) string { return fmt.Sprintf("PATCH %s", p) }
|
||||||
|
func delete(p string) string { return fmt.Sprintf("DELETE %s", p) }
|
||||||
|
|
||||||
|
func (rte Routes) MountTo(prefix string, c *cluster.Cluster, mux *http.ServeMux) error {
|
||||||
|
var errs []error
|
||||||
|
for _, r := range rte {
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodTrace:
|
||||||
|
mux.Handle(trace(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodOptions:
|
||||||
|
mux.Handle(options(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodConnect:
|
||||||
|
mux.Handle(connect(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodHead:
|
||||||
|
mux.Handle(head(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodGet:
|
||||||
|
mux.Handle(get(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodPost:
|
||||||
|
mux.Handle(post(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodPut:
|
||||||
|
mux.Handle(put(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodPatch:
|
||||||
|
mux.Handle(patch(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
case http.MethodDelete:
|
||||||
|
mux.Handle(delete(addprefix(prefix, r.Path)), r.Handler(c))
|
||||||
|
default:
|
||||||
|
mux.Handle(addprefix(prefix, r.Path), r.Handler(c))
|
||||||
|
}
|
||||||
|
log.Info("Routes.MoutnTo").
|
||||||
|
Str("prefix", prefix).
|
||||||
|
Str("Route.Path", r.Path).
|
||||||
|
Str("Route.Method", r.Method).
|
||||||
|
Msg("route registered")
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
@ -13,9 +13,9 @@ import (
|
|||||||
func (s *Server) baseRoutes() {
|
func (s *Server) baseRoutes() {
|
||||||
// fileserver for static content
|
// fileserver for static content
|
||||||
fs := http.StripPrefix("/static", http.FileServer(http.Dir("public")))
|
fs := http.StripPrefix("/static", http.FileServer(http.Dir("public")))
|
||||||
s.r.Get("/static/*", fs.ServeHTTP)
|
s.mux.Handle("GET /static/*", fs)
|
||||||
|
|
||||||
// Root defaults to the cluster view
|
// Root defaults to the cluster view
|
||||||
s.r.Get("/", templ.Handler(layouts.Manager("ClustVirt", "libvirt made simple")).ServeHTTP)
|
s.mux.Handle("GET /", templ.Handler(layouts.Manager("ClustVirt", "libvirt made simple")))
|
||||||
s.r.Get("/about", templ.Handler(static.About()).ServeHTTP)
|
s.mux.Handle("GET /about", templ.Handler(static.About()))
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,18 @@ import (
|
|||||||
"git.staur.ca/stobbsm/clustvirt/router"
|
"git.staur.ca/stobbsm/clustvirt/router"
|
||||||
"git.staur.ca/stobbsm/clustvirt/router/htmx"
|
"git.staur.ca/stobbsm/clustvirt/router/htmx"
|
||||||
"git.staur.ca/stobbsm/clustvirt/router/middleware"
|
"git.staur.ca/stobbsm/clustvirt/router/middleware"
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server represents an HTTP server that uses chi for routes
|
// Server represents an HTTP server that uses net/http ServeMux to route requests
|
||||||
|
// Originally done with chi, but a migration to the new net/http patterns seems like
|
||||||
|
// a good choice to reduce dependencies, but does mean middleware needs to be implemented
|
||||||
|
// in the server
|
||||||
type Server struct {
|
type Server struct {
|
||||||
bindAddr string
|
bindAddr string
|
||||||
ssl bool
|
ssl bool
|
||||||
c *cluster.Cluster
|
c *cluster.Cluster
|
||||||
r chi.Router
|
middleware []middleware.Middleware
|
||||||
|
mux *http.ServeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new HTTP Server instance.
|
// New creates a new HTTP Server instance.
|
||||||
@ -39,7 +42,7 @@ func (s *Server) Start() {
|
|||||||
Dur("upTime", time.Since(tstart)).
|
Dur("upTime", time.Since(tstart)).
|
||||||
Msg("http server stopped")
|
Msg("http server stopped")
|
||||||
}()
|
}()
|
||||||
s.r = chi.NewRouter()
|
s.mux = http.NewServeMux()
|
||||||
|
|
||||||
indev, _ := os.LookupEnv("CLUSTVIRT_DEV")
|
indev, _ := os.LookupEnv("CLUSTVIRT_DEV")
|
||||||
indev = strings.ToLower(indev)
|
indev = strings.ToLower(indev)
|
||||||
@ -52,17 +55,18 @@ func (s *Server) Start() {
|
|||||||
fallthrough
|
fallthrough
|
||||||
case "on":
|
case "on":
|
||||||
s.AddMiddleware(middleware.NoCache)
|
s.AddMiddleware(middleware.NoCache)
|
||||||
s.r.Get("/_/debug", middleware.Profiler().ServeHTTP)
|
s.mux.Handle("GET /_/debug", middleware.Profiler())
|
||||||
}
|
}
|
||||||
|
s.AddMiddleware(middleware.Logger)
|
||||||
// Add routes
|
// Add routes
|
||||||
s.baseRoutes()
|
s.baseRoutes()
|
||||||
if err := s.AddSubRouter(htmx.Htmx); err != nil {
|
if err := s.AddSubRouter("/htmx", htmx.Htmx); err != nil {
|
||||||
log.Error("server.Start").
|
log.Error("server.Start").
|
||||||
Str("subroute", "htmx").
|
Str("subroute", "htmx").
|
||||||
Err(err).Send()
|
Err(err).Send()
|
||||||
}
|
}
|
||||||
// Start the server
|
// Start the server
|
||||||
if err := http.ListenAndServe(s.bindAddr, s.r); err != nil {
|
if err := http.ListenAndServe(s.bindAddr, s); err != nil {
|
||||||
log.Error("router.Server.Start").
|
log.Error("router.Server.Start").
|
||||||
Str("bindaddr", s.bindAddr).
|
Str("bindaddr", s.bindAddr).
|
||||||
Err(err).Send()
|
Err(err).Send()
|
||||||
@ -70,11 +74,25 @@ func (s *Server) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddMiddleware appends middleware to the chi router
|
// AddMiddleware appends middleware to the chi router
|
||||||
func (s *Server) AddMiddleware(m ...func(h http.Handler) http.Handler) {
|
func (s *Server) AddMiddleware(m ...middleware.Middleware) {
|
||||||
s.r.Use(m...)
|
s.middleware = append(s.middleware, m...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSubRouter attachs a SubRouter using it's MountTo method
|
// AddSubRouter attachs a SubRouter using it's MountTo method. This method
|
||||||
func (s *Server) AddSubRouter(sr router.SubRouter) error {
|
// needs the path prefix and the defined routes
|
||||||
return sr.MountTo(s.c, s.r)
|
func (s *Server) AddSubRouter(pfx string, sr router.SubRouter) error {
|
||||||
|
return sr.MountTo(pfx, s.c, s.mux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements the Handler interface
|
||||||
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.chain().ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) chain() http.Handler {
|
||||||
|
h := http.Handler(s.mux)
|
||||||
|
for i := range s.middleware {
|
||||||
|
h = s.middleware[len(s.middleware)-1-i](h)
|
||||||
|
}
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user