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;
|
||||
}
|
||||
|
||||
.items-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -1,54 +1,2 @@
|
||||
// Package htmx contains the routes for the WebUI 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,76 +8,72 @@ import (
|
||||
"git.staur.ca/stobbsm/clustvirt/lib/log"
|
||||
"git.staur.ca/stobbsm/clustvirt/router"
|
||||
"git.staur.ca/stobbsm/clustvirt/view"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
var Htmx = &htmx{
|
||||
router: chi.NewRouter(),
|
||||
routes: []router.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/cluster",
|
||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := view.ClusterInfo(c).Render(context.Background(), w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusBadRequest).Send()
|
||||
}
|
||||
var Htmx = router.Routes{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/cluster",
|
||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := view.ClusterInfo(c).Render(context.Background(), w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusBadRequest).Send()
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/host/{hostname}",
|
||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
host, err := c.GetHost(chi.URLParam(r, "hostname"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusBadRequest).
|
||||
Send()
|
||||
}
|
||||
if err = view.HostInfo(host).Render(context.Background(), w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusInternalServerError).
|
||||
Send()
|
||||
}
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/host/{hostname}",
|
||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
host, err := c.GetHost(r.PathValue("hostname"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusBadRequest).
|
||||
Send()
|
||||
}
|
||||
},
|
||||
if err = view.HostInfo(host).Render(context.Background(), w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusInternalServerError).
|
||||
Send()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/host/{hostname}/stats",
|
||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
host, err := c.GetHost(chi.URLParam(r, "hostname"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusBadRequest).
|
||||
Send()
|
||||
}
|
||||
if err = view.HostStats(host).Render(context.Background(), w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusInternalServerError).
|
||||
Send()
|
||||
}
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/host/{hostname}/stats",
|
||||
Handler: func(c *cluster.Cluster) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
host, err := c.GetHost(r.PathValue("hostname"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusBadRequest).
|
||||
Send()
|
||||
}
|
||||
},
|
||||
if err = view.HostStats(host).Render(context.Background(), w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Error("htmx.Handler").
|
||||
Str("uri", r.RequestURI).
|
||||
Err(err).
|
||||
Int("statusCode", http.StatusInternalServerError).
|
||||
Send()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ var (
|
||||
BasicAuth = middleware.BasicAuth
|
||||
Compress = middleware.Compress
|
||||
Heartbeat = middleware.Heartbeat
|
||||
NoCache = middleware.NoCache
|
||||
//NoCache = middleware.NoCache
|
||||
Profiler = middleware.Profiler
|
||||
RealIP = middleware.RealIP
|
||||
Recoverer = middleware.Recoverer
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
// Logger uses the in package log module to handle route logging
|
||||
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) {
|
||||
start := time.Now()
|
||||
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"
|
||||
|
||||
"git.staur.ca/stobbsm/clustvirt/cluster"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
// Types that are shared among routers
|
||||
@ -12,7 +11,8 @@ import (
|
||||
// SubRouter defines an interface to be able to add a subrouter to a
|
||||
// chi router
|
||||
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
|
||||
|
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() {
|
||||
// fileserver for static content
|
||||
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
|
||||
s.r.Get("/", templ.Handler(layouts.Manager("ClustVirt", "libvirt made simple")).ServeHTTP)
|
||||
s.r.Get("/about", templ.Handler(static.About()).ServeHTTP)
|
||||
s.mux.Handle("GET /", templ.Handler(layouts.Manager("ClustVirt", "libvirt made simple")))
|
||||
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/htmx"
|
||||
"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 {
|
||||
bindAddr string
|
||||
ssl bool
|
||||
c *cluster.Cluster
|
||||
r chi.Router
|
||||
bindAddr string
|
||||
ssl bool
|
||||
c *cluster.Cluster
|
||||
middleware []middleware.Middleware
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
// New creates a new HTTP Server instance.
|
||||
@ -39,7 +42,7 @@ func (s *Server) Start() {
|
||||
Dur("upTime", time.Since(tstart)).
|
||||
Msg("http server stopped")
|
||||
}()
|
||||
s.r = chi.NewRouter()
|
||||
s.mux = http.NewServeMux()
|
||||
|
||||
indev, _ := os.LookupEnv("CLUSTVIRT_DEV")
|
||||
indev = strings.ToLower(indev)
|
||||
@ -52,17 +55,18 @@ func (s *Server) Start() {
|
||||
fallthrough
|
||||
case "on":
|
||||
s.AddMiddleware(middleware.NoCache)
|
||||
s.r.Get("/_/debug", middleware.Profiler().ServeHTTP)
|
||||
s.mux.Handle("GET /_/debug", middleware.Profiler())
|
||||
}
|
||||
s.AddMiddleware(middleware.Logger)
|
||||
// Add routes
|
||||
s.baseRoutes()
|
||||
if err := s.AddSubRouter(htmx.Htmx); err != nil {
|
||||
if err := s.AddSubRouter("/htmx", htmx.Htmx); err != nil {
|
||||
log.Error("server.Start").
|
||||
Str("subroute", "htmx").
|
||||
Err(err).Send()
|
||||
}
|
||||
// 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").
|
||||
Str("bindaddr", s.bindAddr).
|
||||
Err(err).Send()
|
||||
@ -70,11 +74,25 @@ func (s *Server) Start() {
|
||||
}
|
||||
|
||||
// AddMiddleware appends middleware to the chi router
|
||||
func (s *Server) AddMiddleware(m ...func(h http.Handler) http.Handler) {
|
||||
s.r.Use(m...)
|
||||
func (s *Server) AddMiddleware(m ...middleware.Middleware) {
|
||||
s.middleware = append(s.middleware, m...)
|
||||
}
|
||||
|
||||
// AddSubRouter attachs a SubRouter using it's MountTo method
|
||||
func (s *Server) AddSubRouter(sr router.SubRouter) error {
|
||||
return sr.MountTo(s.c, s.r)
|
||||
// AddSubRouter attachs a SubRouter using it's MountTo method. This method
|
||||
// needs the path prefix and the defined routes
|
||||
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