diff --git a/controllers/authentication.go b/controllers/authentication.go index a58b71d..4830a2d 100644 --- a/controllers/authentication.go +++ b/controllers/authentication.go @@ -468,6 +468,32 @@ func AppLogin(c *core.Context) *core.Response { return c.Response.Template("login.html", tmplData) } +// Show basic app login +func AppSession(c *core.Context) *core.Response { + + var session = new(utils.SessionUser) + + hassession := session.Init(c) + + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{} + + if hassession { + + 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 { 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..c792ba5 --- /dev/null +++ b/utils/session.go @@ -0,0 +1,178 @@ +// 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 ( + "crypto/md5" + "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{}) + + 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() + 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("session%v_%v", userID, userAgent) + s.hashedSessionKey = fmt.Sprintf("%v", fmt.Sprintf("%x", md5.Sum([]byte(sessionKey)))) + value, err := c.GetCache().Get(s.hashedSessionKey) + + if err != nil { + s.values = make(map[string]interface{}) + s.authenticated = true + s.userID = userID + + if len(value) > 0 { + err := json.Unmarshal([]byte(value), &s.values) + if err != nil { + + } + } else { + + } + } + + 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.values = make(map[string]interface{}) + s.mu.Unlock() + return s.Save() +} + +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) + + } + + s.mu.RUnlock() + + if len(value) > 0 { + s.context.GetCache().Set(s.hashedSessionKey, value) + } + + return nil +} + +func (s *SessionUser) GetUserID() uint { + + return s.userID +}