Small fixes

This commit is contained in:
spsobole 2021-05-29 15:14:15 -06:00
parent baada0dae5
commit e59a973228
4 changed files with 187 additions and 24 deletions

View File

@ -5,8 +5,9 @@ import (
)
type AuthData struct {
User string
Group string
User string
Group string
Properties map[string]string
}
type AuthManager interface {

View File

@ -5,6 +5,7 @@ import (
"io/ioutil"
"net/http"
"strconv"
"strings"
"git.thirdmartini.com/pub/snap/auth"
)
@ -19,9 +20,11 @@ type Context struct {
}
type SnapContent struct {
User string
Theme string
Content interface{}
User string
UserProperties map[string]string
Theme string
Meta map[string]string
Content interface{}
}
func (c *Context) GetRequest() *http.Request {
@ -43,14 +46,32 @@ func (c *Context) Error(code int, msg string) {
c.srv.renderError(c.w, code, msg)
}
func (c *Context) ErrorObject(code int, obj interface{}) {
msg, err := json.Marshal(obj)
if err != nil {
c.Error(http.StatusInternalServerError, "Internal Server Error")
return
}
c.Error(code, string(msg))
}
func (c *Context) Reply(msg string) {
c.srv.reply(c.w, msg)
}
func (c *Context) ReplyWithHeaders(msg string, kv map[string]string) {
h := c.w.Header()
for k, v := range kv {
h.Add(k, v)
}
c.srv.reply(c.w, msg)
}
func (c *Context) ReplyObject(obj interface{}) {
msg, err := json.Marshal(obj)
if err != nil {
c.Error(400, "Internal Server Error")
c.Error(http.StatusInternalServerError, "Internal Server Error")
return
}
c.Reply(string(msg))
}
@ -72,11 +93,38 @@ func (c *Context) RenderEx(tmpl string, content interface{}) {
cnt := SnapContent{
User: c.GetUser(),
Theme: c.srv.theme,
Meta: c.srv.meta,
Content: content,
}
c.srv.render(c.w, tmpl, &cnt)
}
func (c *Context) RenderWithMeta(tmpl string, meta map[string]string, content interface{}) {
// Merge metas
if meta == nil {
meta = make(map[string]string)
}
for k, v := range c.srv.meta {
if _, ok := meta[k]; !ok {
meta[k] = v
}
}
cnt := SnapContent{
User: c.GetUser(),
Theme: c.srv.theme,
Meta: meta,
Content: content,
}
if c.auth != nil {
cnt.UserProperties = c.auth.Properties
}
c.srv.render(c.w, tmpl, &cnt)
}
func (c *Context) GetVar(k string) (string, bool) {
v, ok := c.vars[k]
return v, ok
@ -90,6 +138,18 @@ func (c *Context) GetVarDefault(k string, def string) string {
return v
}
func (c *Context) GetVarAsSlice(k string, delim string, def []string) []string {
v, ok := c.vars[k]
if !ok {
return def
}
r := strings.Split(v, delim)
if len(r) == 1 && r[0] == "" {
return def
}
return r
}
func (c *Context) ParseForm() error {
return c.r.ParseForm()
}
@ -109,6 +169,15 @@ func (c *Context) FormValueUint64(k string) uint64 {
return val
}
func (c *Context) FormValueAsSlice(k string, delim string) []string {
v := c.r.FormValue(k)
r := strings.Split(v, delim)
if len(r) == 1 && r[0] == "" {
return []string{}
}
return r
}
func (c *Context) Redirect(url string) {
http.Redirect(c.w, c.r, url, http.StatusSeeOther)
}
@ -117,6 +186,11 @@ func (c *Context) QueryValue(key string) string {
return c.r.URL.Query().Get(key)
}
func (c *Context) QueryHas(key string) bool {
_, ok := c.r.URL.Query()[key]
return ok
}
func (c *Context) QueryValueWithDefault(key string, def string) string {
val := c.r.URL.Query().Get(key)
if val == "" {

117
server.go
View File

@ -20,15 +20,17 @@ import (
)
type Server struct {
address string
theme string
debug bool
fs http.FileSystem
auth auth.Authenticator
router *mux.Router
templates string
cachedTmpl *template.Template
address string
theme string
debug bool
fs http.FileSystem
auth auth.Authenticator
router *mux.Router
templates string
cachedTmpl *template.Template
templateFuncs template.FuncMap
meta map[string]string
// testModeEnabled if test mode is enabled we will not render the template but jsut return the
// json object
testModeEnabled bool
@ -39,6 +41,14 @@ type SnapBaseContent struct {
Content interface{}
}
func noescape(str string) template.HTML {
return template.HTML(str)
}
var builtinFuncMap = template.FuncMap{
"noescape": noescape,
}
func (s *Server) makeContext(auth *auth.AuthData, w http.ResponseWriter, r *http.Request) *Context {
c := &Context{
r: r,
@ -50,9 +60,30 @@ func (s *Server) makeContext(auth *auth.AuthData, w http.ResponseWriter, r *http
return c
}
func (s *Server) withLoginHandler(auth auth.Authenticator, loginHandler func(c *Context) bool, handle func(c *Context)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if s.debug {
log.Debug("authenticated request with login ui handler: ", r.RequestURI)
}
rec, ok := auth.DoAuth(w, r)
if !ok {
log.Debug("authenticated request with login ui handler to login ", r.RequestURI)
c := s.makeContext(rec, w, r)
if loginHandler(c) {
handle(c)
}
} else {
c := s.makeContext(rec, w, r)
handle(c)
}
}
}
func (s *Server) authenticated(auth auth.Authenticator, redirect string, handle func(c *Context)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Debug("authenticated request: ", r.RequestURI)
if s.debug {
log.Debug("authenticated request: ", r.RequestURI)
}
rec, ok := auth.DoAuth(w, r)
if !ok {
@ -70,7 +101,9 @@ func (s *Server) authenticated(auth auth.Authenticator, redirect string, handle
func (s *Server) wrapper(handle func(c *Context)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Debug("request: ", r.RequestURI)
if s.debug {
log.Debug("request: ", r.RequestURI)
}
c := s.makeContext(nil, w, r)
if s.auth != nil {
@ -119,7 +152,7 @@ func (s *Server) parseTemplates(t *template.Template, filenames ...string) (*tem
// works. Otherwise we create a new template associated with t.
var tmpl *template.Template
if t == nil {
t = template.New(name)
t = template.New(name).Funcs(s.templateFuncs)
}
if name == t.Name() {
tmpl = t
@ -165,7 +198,7 @@ func Walk(fs http.FileSystem, base string, walkFunc func(path string, info os.Fi
}
func (s *Server) LoadTemplatesFS(fs http.FileSystem, base string) (*template.Template, error) {
tmpl := template.New("")
tmpl := template.New("").Funcs(s.templateFuncs)
err := Walk(fs, base, func(path string, info os.FileInfo, err error) error {
if strings.Contains(path, ".html") {
@ -236,6 +269,13 @@ func (s *Server) HandleFuncAuthenticated(path, redirect string, f func(c *Contex
return s.router.HandleFunc(path, s.authenticated(s.auth, redirect, f))
}
func (s *Server) HandleFuncAuthenticatedWithLogin(path string, loginHandler func(c *Context) bool, contentHandler func(c *Context)) *mux.Route {
if s.auth == nil {
return nil
}
return s.router.HandleFunc(path, s.withLoginHandler(s.auth, loginHandler, contentHandler))
}
func (s *Server) HandleFuncCustomAuth(auth auth.Authenticator, path, redirect string, f func(c *Context)) *mux.Route {
if auth == nil {
log.Warn("Nil auth on", path)
@ -318,6 +358,45 @@ func (s *Server) WithDebug(debugURL string) *Server {
return s
}
func (s *Server) WithMetadata(meta map[string]string) *Server {
s.meta = meta
return s
}
func (s *Server) WithTemplateFuncs(funcs template.FuncMap) *Server {
for k, f := range funcs {
s.templateFuncs[k] = f
}
return s
}
func (s *Server) WithAuth(auth auth.Authenticator) *Server {
s.auth = auth
return s
}
func (s *Server) WithHealthCheck(version, date string, status func() (bool, string)) {
s.HandleFunc("/_health", func(c *Context) {
ok, msg := status()
hc := struct {
Version string
Date string
Status string
}{
Version: version,
Date: date,
Status: msg,
}
if ok {
c.ReplyObject(&hc)
return
}
c.ErrorObject(http.StatusServiceUnavailable, &hc)
})
}
func (s *Server) Dump() {
fmt.Printf(" Theme: %s\n", s.theme)
fmt.Printf(" Templates: %s\n", s.templates)
@ -325,12 +404,14 @@ func (s *Server) Dump() {
func New(address string, path string, auth auth.Authenticator) *Server {
s := Server{
router: mux.NewRouter(),
auth: auth,
address: address,
fs: http.FileSystem(http.Dir(path)),
templates: "/templates",
theme: "/static/css/default.css",
router: mux.NewRouter(),
auth: auth,
address: address,
fs: http.FileSystem(http.Dir(path)),
templates: "/templates",
templateFuncs: builtinFuncMap,
theme: "/static/css/default.css",
meta: make(map[string]string),
}
return &s
}

7
utility.go Normal file
View File

@ -0,0 +1,7 @@
package snap
func Redirect(to string) func(c *Context) {
return func(c *Context) {
c.Redirect(to)
}
}