From b8f8c995c1c4bd19284b139c4b9f54c9e299f28f Mon Sep 17 00:00:00 2001 From: lrodham Date: Sat, 7 Apr 2018 14:37:07 +0100 Subject: [PATCH] added sessions and flash storage --- Gopkg.lock | 124 +++++++++++++++++++++++++++++++----------- Gopkg.toml | 4 ++ default_context.go | 21 ++++++- default_router.go | 6 ++ flash.go | 57 +++++++++++++++++++ middleware/session.go | 16 ++++++ route.go | 3 +- router.go | 1 + session.go | 52 ++++++++++++++++++ tuu.go | 18 ++++-- 10 files changed, 263 insertions(+), 39 deletions(-) create mode 100644 flash.go create mode 100644 middleware/session.go create mode 100644 session.go diff --git a/Gopkg.lock b/Gopkg.lock index d55222a..eef95bf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -13,12 +13,6 @@ packages = ["crdb"] revision = "59c0560478b705bf9bd12f9252224a0fad7c87df" -[[projects]] - name = "github.com/davecgh/go-spew" - packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - [[projects]] name = "github.com/daviddengcn/go-colortext" packages = ["."] @@ -63,13 +57,23 @@ [[projects]] name = "github.com/gobuffalo/plush" - packages = [".","ast","lexer","parser","token"] + packages = [ + ".", + "ast", + "lexer", + "parser", + "token" + ] revision = "a3df688902c0ccd9fa712ed5a1da6b78e7efab0d" version = "v3.6.12" [[projects]] name = "github.com/gobuffalo/tags" - packages = [".","form","form/bootstrap"] + packages = [ + ".", + "form", + "form/bootstrap" + ] revision = "ab52126649ee51d858d9525f60bc1618592433b0" version = "v2.0.0" @@ -81,7 +85,10 @@ [[projects]] name = "github.com/gobuffalo/validate" - packages = [".","validators"] + packages = [ + ".", + "validators" + ] revision = "42d8db6e06e617cdedcc7a849d6690a2cb5a8d28" version = "v2.0.0" @@ -92,15 +99,24 @@ version = "v1.1" [[projects]] - name = "github.com/gorilla/mux" + name = "github.com/gorilla/securecookie" packages = ["."] - revision = "53c1911da2b537f792e7cafcb446b05ffe33b996" - version = "v1.6.1" + revision = "e59506cc896acb7f7bf732d4fdf5e25f7ccd8983" + version = "v1.1.1" + +[[projects]] + name = "github.com/gorilla/sessions" + packages = ["."] + revision = "ca9ada44574153444b00d3fd9c8559e4cc95f896" + version = "v1.1" [[projects]] branch = "master" name = "github.com/jmoiron/sqlx" - packages = [".","reflectx"] + packages = [ + ".", + "reflectx" + ] revision = "cf35089a197953c69420c8d0cecda90809764b1d" [[projects]] @@ -112,13 +128,20 @@ [[projects]] branch = "master" name = "github.com/lib/pq" - packages = [".","oid"] + packages = [ + ".", + "oid" + ] revision = "88edab0803230a3898347e77b474f8c1820a1f20" [[projects]] branch = "master" name = "github.com/markbates/going" - packages = ["defaults","randx","wait"] + packages = [ + "defaults", + "randx", + "wait" + ] revision = "0576708c56cea02331f864fe6e157ac7841923e4" [[projects]] @@ -129,7 +152,13 @@ [[projects]] name = "github.com/markbates/pop" - packages = [".","associations","columns","fizz","fizz/translators"] + packages = [ + ".", + "associations", + "columns", + "fizz", + "fizz/translators" + ] revision = "f230643ae0f8fa01e9444c2fb857ba1f1a78eb1f" version = "v4.0.0" @@ -142,7 +171,36 @@ [[projects]] branch = "master" name = "github.com/mattn/anko" - packages = ["ast","builtins","builtins/encoding/json","builtins/errors","builtins/flag","builtins/fmt","builtins/github.com/daviddengcn/go-colortext","builtins/io","builtins/io/ioutil","builtins/math","builtins/math/big","builtins/math/rand","builtins/net","builtins/net/http","builtins/net/url","builtins/os","builtins/os/exec","builtins/os/signal","builtins/path","builtins/path/filepath","builtins/regexp","builtins/runtime","builtins/sort","builtins/strconv","builtins/strings","builtins/time","parser","vm"] + packages = [ + "ast", + "builtins", + "builtins/encoding/json", + "builtins/errors", + "builtins/flag", + "builtins/fmt", + "builtins/github.com/daviddengcn/go-colortext", + "builtins/io", + "builtins/io/ioutil", + "builtins/math", + "builtins/math/big", + "builtins/math/rand", + "builtins/net", + "builtins/net/http", + "builtins/net/url", + "builtins/os", + "builtins/os/exec", + "builtins/os/signal", + "builtins/path", + "builtins/path/filepath", + "builtins/regexp", + "builtins/runtime", + "builtins/sort", + "builtins/strconv", + "builtins/strings", + "builtins/time", + "parser", + "vm" + ] revision = "d5441ca3f0c7e071a9ede864f9db2cc652690f42" [[projects]] @@ -181,12 +239,6 @@ revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" -[[projects]] - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - [[projects]] name = "github.com/russross/blackfriday" packages = ["."] @@ -219,7 +271,12 @@ [[projects]] branch = "master" name = "github.com/shurcooL/go" - packages = ["parserutil","printerutil","reflectfind","reflectsource"] + packages = [ + "parserutil", + "printerutil", + "reflectfind", + "reflectsource" + ] revision = "364c5ae8518b51f5fda954762864d6f4d5c30c89" [[projects]] @@ -276,12 +333,6 @@ packages = ["."] revision = "bd320f5d308e1a3c4314c678d8227a0d72574ae7" -[[projects]] - name = "github.com/stretchr/testify" - packages = ["assert","require"] - revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" - version = "v1.2.1" - [[projects]] branch = "master" name = "golang.org/x/crypto" @@ -291,7 +342,11 @@ [[projects]] branch = "master" name = "golang.org/x/net" - packages = ["context","html","html/atom"] + packages = [ + "context", + "html", + "html/atom" + ] revision = "ae89d30ce0c63142b652837da33d782e2b0a9b25" [[projects]] @@ -303,7 +358,10 @@ [[projects]] branch = "master" name = "golang.org/x/sys" - packages = ["unix","windows"] + packages = [ + "unix", + "windows" + ] revision = "c28acc882ebcbfbe8ce9f0f14b9ac26ee138dd51" [[projects]] @@ -315,6 +373,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "686f5e2aed8b743dfbc53e74a1e48185702059972b7a587db36af49ec0eeea8f" + inputs-digest = "a9de62d6e42d4a73e9888b54ce7f31b165f2709ba75b468104413487da78238a" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index c85374a..8c79bca 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -20,3 +20,7 @@ [[constraint]] name = "github.com/sirupsen/logrus" version = "1.0.5" + +[[constraint]] + name = "github.com/gorilla/sessions" + version = "1.1.0" diff --git a/default_context.go b/default_context.go index 8853ab3..f27aff5 100644 --- a/default_context.go +++ b/default_context.go @@ -12,7 +12,7 @@ import ( "github.com/sirupsen/logrus" ) -func newContext(r Route, res http.ResponseWriter, req *http.Request) *DefaultContext { +func (a *App) newContext(r Route, res http.ResponseWriter, req *http.Request) *DefaultContext { data := make(map[string]interface{}) data["path"] = r.Path data["env"] = r.Env @@ -23,6 +23,13 @@ func newContext(r Route, res http.ResponseWriter, req *http.Request) *DefaultCon params.Set(k, v) } + sessionStore, _ := a.cfg.SessionStore.Get(req, a.cfg.SessionName) + session := &Session{ + Session: sessionStore, + req: req, + res: res, + } + return &DefaultContext{ response: res, request: req, @@ -30,6 +37,8 @@ func newContext(r Route, res http.ResponseWriter, req *http.Request) *DefaultCon data: data, env: r.Env, logger: r.Logger, + session: session, + flash: newFlash(session), } } @@ -42,6 +51,8 @@ type DefaultContext struct { data map[string]interface{} env string logger *logrus.Logger + session *Session + flash *Flash } // Response returns the original Response for the request. @@ -125,3 +136,11 @@ func (d *DefaultContext) Env() string { func (d *DefaultContext) Logger() *logrus.Logger { return d.logger } + +func (d *DefaultContext) Session() *Session { + return d.session +} + +func (d *DefaultContext) Flash() *Flash { + return d.flash +} diff --git a/default_router.go b/default_router.go index 5ff5b67..ad10a4e 100644 --- a/default_router.go +++ b/default_router.go @@ -22,6 +22,11 @@ type DefaultRouter struct { Routes []*Route StaticRoutes []*StaticRoute Options *RouterOptions + app *App +} + +func (r *DefaultRouter) SetApp(app *App) { + r.app = app } func (r *DefaultRouter) GET(path string, h Handler) { @@ -67,6 +72,7 @@ func (r *DefaultRouter) addRoute(m, p string, h Handler) { Env: r.Options.Env, Middleware: r.Options.MiddlewareStack, Logger: r.Options.Logger, + app: r.app, }) } diff --git a/flash.go b/flash.go new file mode 100644 index 0000000..2a940ed --- /dev/null +++ b/flash.go @@ -0,0 +1,57 @@ +package tuu + +import "encoding/json" + +// flashKey is the prefix inside the Session. +const flashKey = "_flash_" + +//Flash is a struct that helps with the operations over flash messages. +type Flash struct { + data map[string][]string +} + +//Delete removes a particular key from the Flash. +func (f Flash) Delete(key string) { + delete(f.data, key) +} + +//Clear removes all keys from the Flash. +func (f *Flash) Clear() { + f.data = map[string][]string{} +} + +//Set allows to set a list of values into a particular key. +func (f Flash) Set(key string, values []string) { + f.data[key] = values +} + +//Add adds a flash value for a flash key, if the key already has values the list for that value grows. +func (f Flash) Add(key, value string) { + if len(f.data[key]) == 0 { + f.data[key] = []string{value} + return + } + + f.data[key] = append(f.data[key], value) +} + +//Persist the flash inside the session. +func (f Flash) persist(session *Session) { + b, _ := json.Marshal(f.data) + session.Set(flashKey, b) + session.Save() +} + +//newFlash creates a new Flash and loads the session data inside its data. +func newFlash(session *Session) *Flash { + result := &Flash{ + data: map[string][]string{}, + } + + if session.Session != nil { + if f := session.Get(flashKey); f != nil { + json.Unmarshal(f.([]byte), &result.data) + } + } + return result +} diff --git a/middleware/session.go b/middleware/session.go new file mode 100644 index 0000000..0550b3e --- /dev/null +++ b/middleware/session.go @@ -0,0 +1,16 @@ +package middleware + +import ( + "github.com/lukerodham/tuu" +) + +func SessionSaver(next tuu.Handler) Handler { + return func(c tuu.Context) error { + err := next(c) + if err != nil { + return err + } + + return c.Session().Save() + } +} diff --git a/route.go b/route.go index ea48e9b..4befd5a 100644 --- a/route.go +++ b/route.go @@ -14,12 +14,13 @@ type Route struct { Env string Middleware MiddlewareStack Logger *logrus.Logger + app *App } func (r *Route) ServeHTTP(res http.ResponseWriter, req *http.Request) { defer gcontext.Clear(req) - c := newContext(*r, res, req) + c := r.app.newContext(*r, res, req) err := r.Middleware.handler(r)(c) diff --git a/router.go b/router.go index 184b031..460fba8 100644 --- a/router.go +++ b/router.go @@ -5,6 +5,7 @@ import "net/http" type Handler func(Context) error type Router interface { + SetApp(app *App) GET(path string, h Handler) POST(path string, h Handler) Static(path string, root http.FileSystem) diff --git a/session.go b/session.go new file mode 100644 index 0000000..6a7c087 --- /dev/null +++ b/session.go @@ -0,0 +1,52 @@ +package tuu + +import ( + "net/http" + + "github.com/gorilla/sessions" +) + +// Session wraps the "github.com/gorilla/sessions" API +// in something a little cleaner and a bit more useable. +type Session struct { + Session *sessions.Session + req *http.Request + res http.ResponseWriter +} + +// Save the current session. +func (s *Session) Save() error { + return s.Session.Save(s.req, s.res) +} + +// Get a value from the current session. +func (s *Session) Get(name interface{}) interface{} { + return s.Session.Values[name] +} + +// GetOnce gets a value from the current session and then deletes it. +func (s *Session) GetOnce(name interface{}) interface{} { + if x, ok := s.Session.Values[name]; ok { + s.Delete(name) + return x + } + return nil +} + +// Set a value onto the current session. If a value with that name +// already exists it will be overridden with the new value. +func (s *Session) Set(name, value interface{}) { + s.Session.Values[name] = value +} + +// Delete a value from the current session. +func (s *Session) Delete(name interface{}) { + delete(s.Session.Values, name) +} + +// Clear the current session +func (s *Session) Clear() { + for k := range s.Session.Values { + s.Delete(k) + } +} diff --git a/tuu.go b/tuu.go index c857720..e98cfa9 100644 --- a/tuu.go +++ b/tuu.go @@ -9,16 +9,26 @@ import ( "os/signal" "github.com/gorilla/mux" + "github.com/gorilla/sessions" ) type Config struct { - IPAddr string - Port string - Env string + IPAddr string + Port string + Env string + SessionName string + SessionStore sessions.Store } func New(r Router, cfg Config) *App { - return &App{router: r, cfg: cfg} + cfg.SessionName = "_tuu_session" + // TODO: make session secret actual, secret. + cfg.SessionStore = sessions.NewCookieStore([]byte("secret")) + app := &App{router: r, cfg: cfg} + + r.SetApp(app) + + return app } type App struct {