diff options
Diffstat (limited to 'cmd/monfront/server.go')
-rw-r--r-- | cmd/monfront/server.go | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/cmd/monfront/server.go b/cmd/monfront/server.go new file mode 100644 index 0000000..2ada1d0 --- /dev/null +++ b/cmd/monfront/server.go @@ -0,0 +1,153 @@ +package main + +import ( + "compress/gzip" + "database/sql" + "encoding/json" + "fmt" + "html/template" + "io" + "log" + "net" + "net/http" + "strings" + "time" +) + +type ( + server struct { + listen net.Listener + db *sql.DB + h *http.ServeMux + tmpl *template.Template + auth func(c *Context) error // authentication + autho func(c *Context) error // authorization + } + + handleFunc func(c *Context) + + Context struct { + // internal maintenance stuff + w http.ResponseWriter + r *http.Request + tmpl *template.Template + db *sql.DB + + User string `json:"-"` + Filter *filter `json:"-"` + CanEdit bool `json:"-"` // has user permission to edit stuff? + + Title string `json:"title,omitempty"` + CurrentPath string `json:"-"` + Error string `json:"error,omitempty"` + Mappings map[int]map[int]MapEntry `json:"mappings,omitempty"` + Commands map[string]int `json:"commands,omitempty"` + Checks []check `json:"checks,omitempty"` + CheckDetails *checkDetails `json:"check_details,omitempty"` + Groups []group `json:"groups,omitempty"` + Unhandled bool `json:"-"` // set this flag when unhandled was called + } +) + +func newServer(l net.Listener, db *sql.DB, tmpl *template.Template, auth func(c *Context) error, autho func(c *Context) error) *server { + s := &server{ + listen: l, + db: db, + tmpl: tmpl, + h: http.NewServeMux(), + auth: auth, + autho: autho, + } + return s +} + +func (s *server) ListenAndServe() error { + server := http.Server{Handler: s.h} + return server.Serve(s.listen) +} + +func (s *server) Handle(path string, fun handleFunc) { + s.h.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + c := &Context{ + w: w, + r: r, + tmpl: s.tmpl, + db: s.db, + } + if err := s.auth(c); err != nil { + return + } + if err := s.autho(c); err != nil { + return + } + fun(c) + return + }) +} + +func (s *server) HandleStatic(path string, h func(w http.ResponseWriter, r *http.Request)) { + s.h.HandleFunc(path, h) +} + +// Render calls the template with the given name to +// render the appropiate content. +// In case of an error, a error message is automatically pushed +// to the client. +func (c *Context) Render(t string) error { + var w io.Writer = c.w + if strings.Contains(c.r.Header.Get("Accept-Encoding"), "gzip") { + gz, err := gzip.NewWriterLevel(w, 5) + if err != nil { + log.Printf("could not create gzip writer: %s", err) + return fmt.Errorf("could not create gzip writer: %s", err) + } + defer gz.Close() + w = gz + c.w.Header().Set("Content-Encoding", "gzip") + } + + if c.r.Header.Get("Accept") == "application/json" { + c.w.Header().Set("Content-Type", "application/json") + enc := json.NewEncoder(w) + enc.SetIndent("", "") // disable indentation to save traffic + if err := enc.Encode(c); err != nil { + c.w.WriteHeader(http.StatusInternalServerError) + c.w.Write([]byte("could not write json output")) + log.Printf("could not write json output: %s", err) + return err + } + return nil + } + + if err := c.tmpl.ExecuteTemplate(w, t, c); err != nil { + c.w.WriteHeader(http.StatusInternalServerError) + c.w.Write([]byte("problem with a template")) + log.Printf("could not execute template: %s", err) + return err + } + return nil +} + +// Get a cookie value. +func (c *Context) GetCookieVal(name string) string { + cook, err := c.r.Cookie(name) + if err == http.ErrNoCookie { + return "" + } + return cook.Value +} + +// Set a new key value cookie with a deadline. +func (c *Context) SetCookie(name, val string, expire time.Time) { + cook := http.Cookie{ + Name: name, + Value: val, + Expires: expire, + Secure: true, + SameSite: http.SameSiteStrictMode, + HttpOnly: true, + Path: "/", + } + http.SetCookie(c.w, &cook) + return +} |