diff --git a/controllers/authentication.go b/controllers/authentication.go index a58b71d..25c7b31 100644 --- a/controllers/authentication.go +++ b/controllers/authentication.go @@ -22,8 +22,6 @@ import ( "git.smarteching.com/goffee/cup/utils" "github.com/google/uuid" "gorm.io/gorm" - - "git.smarteching.com/goffee/core/template/components" ) func Signup(c *core.Context) *core.Response { @@ -223,6 +221,12 @@ func Signin(c *core.Context) *core.Response { userAgent := c.GetUserAgent() hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent) err = c.GetCache().Set(hashedCacheKey, token) + + // delete data from old sessions + sessionKey := fmt.Sprintf("sess_%v", userAgent) + hashedSessionKey := utils.CreateAuthTokenHashedCacheKey(user.ID, sessionKey) + _ = c.GetCache().Delete(hashedSessionKey) + if err != nil { c.GetLogger().Error(err.Error()) if TemplateEnable { @@ -448,43 +452,3 @@ func Signout(c *core.Context) *core.Response { "message": "signed out successfully", })) } - -// Show basic app login -func AppLogin(c *core.Context) *core.Response { - - // first, include all compoments - // first, include all compoments - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Card title", - CardBody: "Loerm ipsum at deim", - }, - } - return c.Response.Template("login.html", tmplData) -} - -// Show basic app sample -func AppSample(c *core.Context) *core.Response { - - // first, include all compoments - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Protected page", - CardBody: "If you can see this page, your are loggedin", - }, - } - //fmt.Printf("Outside cookie user is: %s", user.Email) - - return c.Response.Template("app.html", tmplData) - -} diff --git a/controllers/sample.go b/controllers/sample.go index 1243d5d..bc06390 100644 --- a/controllers/sample.go +++ b/controllers/sample.go @@ -5,8 +5,11 @@ package controllers import ( + "fmt" + "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/core/template/components" + "git.smarteching.com/goffee/cup/utils" ) // Show basic template @@ -28,3 +31,103 @@ func Sample(c *core.Context) *core.Response { return c.Response.Template("basic.html", tmplData) } + +// Show basic app login +func AppLogin(c *core.Context) *core.Response { + + // first, include all compoments + // first, include all compoments + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{ + PageCard: components.PageCard{ + CardTitle: "Card title", + CardBody: "Loerm ipsum at deim", + }, + } + return c.Response.Template("login.html", tmplData) +} + +// Show basic app login +func AppSession(c *core.Context) *core.Response { + + var session = new(utils.SessionUser) + + // true if session is active + hassession := session.Init(c) + + //session.Set("numberdos", 66) + + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{} + + if hassession { + + sesiondata := "" + cardtitle := fmt.Sprintf("Session user id: %v", session.GetUserID()) + numberdos, ok := session.Get("numberdos") + + if numberdos != nil { + numberdos = numberdos.(float64) + 10 + } else { + numberdos = 10 + } + + session.Set("numberdos", numberdos) + + if ok { + sesiondata = fmt.Sprintf("OK, Session numberdos has %v", numberdos) + } else { + sesiondata = fmt.Sprintf("No ok, session numberdos has %v", numberdos) + } + + // delete single + //session.Delete("numberdos") + + // delete all data + //session.Flush() + + tmplData = templateData{ + PageCard: components.PageCard{ + CardTitle: cardtitle, + CardBody: sesiondata, + }, + } + + return c.Response.Template("appsession.html", tmplData) + + } else { + + return c.Response.Template("login.html", tmplData) + + } + +} + +// Show basic app sample +func AppSample(c *core.Context) *core.Response { + + // first, include all compoments + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{ + PageCard: components.PageCard{ + CardTitle: "Protected page", + CardBody: "If you can see this page, your are loggedin", + }, + } + //fmt.Printf("Outside cookie user is: %s", user.Email) + + return c.Response.Template("app.html", tmplData) + +} diff --git a/controllers/themedemo.go b/controllers/themedemo.go index 1373052..22ba5a5 100644 --- a/controllers/themedemo.go +++ b/controllers/themedemo.go @@ -114,10 +114,10 @@ func Themeform(c *core.Context) *core.Response { optionc.Value = "buenosaires" optionc.Label = "Buenos Aires" allOptionsc = append(allOptionsc, optionc) - optionc.ID = "sogas" - optionc.Name = "sogas" - optionc.Value = "Sogamoso" - optionc.Label = "Sogamoso" + optionc.ID = "london" + optionc.Name = "london" + optionc.Value = "london" + optionc.Label = "London" //optionc.IsChecked = true allOptionsc = append(allOptionsc, optionc) diff --git a/routes.go b/routes.go index 18c72be..610fbcb 100644 --- a/routes.go +++ b/routes.go @@ -41,4 +41,7 @@ func registerRoutes() { controller.Get("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) controller.Post("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) + + controller.Get("/appsession", controllers.AppSession) + controller.Post("/appsession", controllers.AppSession) } diff --git a/storage/templates/appsession.html b/storage/templates/appsession.html new file mode 100644 index 0000000..32364f0 --- /dev/null +++ b/storage/templates/appsession.html @@ -0,0 +1,12 @@ + + + {{template "page_head" "Sample page test session vars"}} + +
+
+ {{template "page_card" .PageCard}} +
+
+ {{template "page_footer"}} + + \ No newline at end of file diff --git a/utils/session.go b/utils/session.go new file mode 100644 index 0000000..27e202d --- /dev/null +++ b/utils/session.go @@ -0,0 +1,170 @@ +// Copyright (c) 2024 Zeni Kim +// Use of this source code is governed by MIT-style +// license that can be found in the LICENSE file. + +package utils + +import ( + "errors" + "fmt" + "sync" + "time" + + "encoding/json" + + "git.smarteching.com/goffee/core" + "git.smarteching.com/goffee/cup/models" + "gorm.io/gorm" +) + +type SessionUser struct { + mu sync.RWMutex + context *core.Context + userID uint + hashedSessionKey string + authenticated bool + sessionStart time.Time + values map[string]interface{} +} + +// start the struct +func (s *SessionUser) Init(c *core.Context) bool { + + // check session cookie + pass := true + token := "" + s.context = c + + payload := make(map[string]interface{}) + // get cookie + usercookie, err := c.GetCookie() + if err != nil { + + } + + token = usercookie.Token + + if token == "" { + + pass = false + + } else { + + payload, err = c.GetJWT().DecodeToken(token) + + if err != nil { + + pass = false + + } else { + + userID := uint(c.CastToInt(payload["userID"])) + userAgent := c.GetUserAgent() + + // get data from redis + hashedCacheKey := CreateAuthTokenHashedCacheKey(userID, userAgent) + cachedToken, err := c.GetCache().Get(hashedCacheKey) + + if err != nil { + pass = false + } else if cachedToken != token { + pass = false + } else { + var user models.User + res := c.GetGorm().Where("id = ?", userID).First(&user) + if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) { + pass = false + } + // if have session start the struct + if pass { + userAgent := c.GetUserAgent() + sessionKey := fmt.Sprintf("sess_%v", userAgent) + s.hashedSessionKey = CreateAuthTokenHashedCacheKey(userID, sessionKey) + + s.values = make(map[string]interface{}) + s.authenticated = true + s.userID = userID + value, _ := c.GetCache().Get(s.hashedSessionKey) + + if len(value) > 0 { + _ = json.Unmarshal([]byte(value), &s.values) + } + + return true + + } else { + + s.hashedSessionKey = "" + s.authenticated = false + s.userID = 0 + return false + } + } + } + } + + return false +} + +func (s *SessionUser) Set(key string, value interface{}) error { + s.mu.Lock() + s.values[key] = value + s.mu.Unlock() + return s.Save() +} + +func (s *SessionUser) Get(key string) (interface{}, bool) { + s.mu.RLock() + defer s.mu.RUnlock() + val, ok := s.values[key] + return val, ok +} + +func (s *SessionUser) Delete(key string) interface{} { + s.mu.RLock() + v, ok := s.values[key] + s.mu.RUnlock() + if ok { + s.mu.Lock() + delete(s.values, key) + s.mu.Unlock() + } + s.Save() + return v +} + +func (s *SessionUser) Flush() error { + s.mu.Lock() + s.context.GetCache().Delete(s.hashedSessionKey) + s.mu.Unlock() + return nil +} + +func (s *SessionUser) Save() error { + + var value string + + s.mu.RLock() + + if len(s.values) > 0 { + buf, err := json.Marshal(&s.values) + if err != nil { + s.mu.RUnlock() + return err + } + value = string(buf) + } + + if len(value) > 0 { + s.context.GetCache().Set(s.hashedSessionKey, value) + } else { + s.context.GetCache().Delete(s.hashedSessionKey) + } + s.mu.RUnlock() + return nil +} + +func (s *SessionUser) GetUserID() uint { + + return s.userID +}