From 911c1ce734c9d31144e8258a98d9f962275ead0c Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Mon, 11 May 2026 21:36:48 -0500 Subject: [PATCH 1/5] add new flag COOKIE_SECURE --- .env-dev | 2 +- .env-example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env-dev b/.env-dev index 800ca98..9157a9c 100644 --- a/.env-dev +++ b/.env-dev @@ -29,7 +29,7 @@ COOKIE_SECRET=13d6b4dff8f84a10851021ec8608f814570d562c92fe6b5ec4c9f595bcb3234b ###### JWT ###### ####################################### JWT_SECRET=dkfTgonmgaAdlgkw -JWT_LIFESPAN_MINUTES=4320 # expires after 3 days +JWT_LIFESPAN_MINUTES=1440 # expires after 1 day ####################################### ###### DATABASE ###### diff --git a/.env-example b/.env-example index d0780b5..3639247 100644 --- a/.env-example +++ b/.env-example @@ -28,7 +28,7 @@ CDNEnable=false ###### JWT ###### ####################################### JWT_SECRET=dkfTgonmgaAdlgkw -JWT_LIFESPAN_MINUTES=4320 # expires after 3 days +JWT_LIFESPAN_MINUTES=1440 # expires after 1 day ####################################### ###### DATABASE ###### From f5ab92b1cfbabec7876b47a8702f73e77aef1792 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Sat, 16 May 2026 10:55:02 -0500 Subject: [PATCH 2/5] recover basic login demo paths --- controllers/home.go | 21 ++++++++++++++++++++- routes.go | 8 ++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/controllers/home.go b/controllers/home.go index 0a88e29..ccc7ac2 100644 --- a/controllers/home.go +++ b/controllers/home.go @@ -47,8 +47,27 @@ func WelcomeHome(c *core.Context) *core.Response { } -// Show dashboard + func WelcomeToDashboard(c *core.Context) *core.Response { message := "{\"message\": \"Welcome to Dashboard\"}" return c.Response.Json(message) } + +// 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) +} diff --git a/routes.go b/routes.go index 193da2d..faaf06b 100644 --- a/routes.go +++ b/routes.go @@ -8,6 +8,7 @@ package main import ( "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/cup/controllers" + "git.smarteching.com/goffee/cup/hooks" ) // Register the app controllers @@ -19,7 +20,7 @@ func registerRoutes() { // Define your routes here... controller.Get("/", controllers.WelcomeHome) - + // Uncomment the lines below to enable authentication API controller.Post("/signup", controllers.Signup) controller.Post("/signin", controllers.Signin) @@ -37,5 +38,8 @@ func registerRoutes() { controller.Post("/admin/users/delete", controllers.AdminUsersDelete) controller.Post("/admin/users/deleteconfirm", controllers.AdminUsersDelConfirm) - + controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck) + controller.Get("/signout", controllers.Signout) + controller.Get("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) + controller.Post("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) } From df80e29f6eeba86d3e80b8e1bbee2d6c110f849d Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Mon, 18 May 2026 15:51:12 -0500 Subject: [PATCH 3/5] update to new core session system --- controllers/adminusers.go | 4 +- controllers/authentication.go | 9 +- hooks/auth-check.go | 5 +- utils/helpers.go | 8 -- utils/session.go | 170 ---------------------------------- 5 files changed, 8 insertions(+), 188 deletions(-) delete mode 100644 utils/session.go diff --git a/controllers/adminusers.go b/controllers/adminusers.go index 9ffb762..5870512 100644 --- a/controllers/adminusers.go +++ b/controllers/adminusers.go @@ -21,7 +21,7 @@ func AdminUsersList(c *core.Context) *core.Response { // initiate authority auth := new(utils.Authority) - var session = new(utils.SessionUser) + session := c.GetSession() // true if session is active hassession := session.Init(c) @@ -135,7 +135,7 @@ func AdminUsersAdd(c *core.Context) *core.Response { // initiate authority auth := new(utils.Authority) - var session = new(utils.SessionUser) + session := c.GetSession() // true if session is active hassession := session.Init(c) diff --git a/controllers/authentication.go b/controllers/authentication.go index 9e3eff9..500533d 100644 --- a/controllers/authentication.go +++ b/controllers/authentication.go @@ -19,7 +19,6 @@ import ( "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/cup/events" "git.smarteching.com/goffee/cup/models" - "git.smarteching.com/goffee/cup/utils" "github.com/google/uuid" "gorm.io/gorm" ) @@ -101,7 +100,7 @@ func Signup(c *core.Context) *core.Response { // cache the token userAgent := c.GetUserAgent() - hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent) + hashedCacheKey := core.CreateAuthTokenHashedCacheKey(user.ID, userAgent) err = c.GetCache().Set(hashedCacheKey, token) if err != nil { c.GetLogger().Error(err.Error()) @@ -223,12 +222,12 @@ func Signin(c *core.Context) *core.Response { } // cache the token userAgent := c.GetUserAgent() - hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent) + hashedCacheKey := core.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) + hashedSessionKey := core.CreateAuthTokenHashedCacheKey(user.ID, sessionKey) _ = c.GetCache().Delete(hashedSessionKey) if err != nil { @@ -463,7 +462,7 @@ func Signout(c *core.Context) *core.Response { })) } userAgent := c.GetUserAgent() - hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent) + hashedCacheKey := core.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent) err = c.GetCache().Delete(hashedCacheKey) if err != nil { diff --git a/hooks/auth-check.go b/hooks/auth-check.go index 779cbda..1b3e1e7 100644 --- a/hooks/auth-check.go +++ b/hooks/auth-check.go @@ -9,7 +9,6 @@ import ( "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/cup/models" - "git.smarteching.com/goffee/cup/utils" "gorm.io/gorm" ) @@ -31,7 +30,7 @@ var CheckSessionCookie core.Hook = func(c *core.Context) { } else { userAgent := c.GetUserAgent() - hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent) + hashedCacheKey := core.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent) cachedToken, err := c.GetCache().Get(hashedCacheKey) if err != nil { @@ -102,7 +101,7 @@ var AuthCheck core.Hook = func(c *core.Context) { return } userAgent := c.GetUserAgent() - hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent) + hashedCacheKey := core.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent) cachedToken, err := c.GetCache().Get(hashedCacheKey) if err != nil { diff --git a/utils/helpers.go b/utils/helpers.go index 8ca7dd8..7a2e922 100644 --- a/utils/helpers.go +++ b/utils/helpers.go @@ -6,8 +6,6 @@ package utils import ( - "crypto/md5" - "fmt" "log" "time" @@ -71,13 +69,7 @@ func CreateSeedData() { } } -// generate a hashed string to be used as key for caching auth jwt token -func CreateAuthTokenHashedCacheKey(userID uint, userAgent string) string { - cacheKey := fmt.Sprintf("userid:_%v_useragent:_%v_jwt_token", userID, userAgent) - hashedCacheKey := fmt.Sprintf("%v", fmt.Sprintf("%x", md5.Sum([]byte(cacheKey)))) - return hashedCacheKey -} func FormatUnix(value int64) string { return time.Unix(value, 0).Format("2006-01-02 15:04:05") diff --git a/utils/session.go b/utils/session.go deleted file mode 100644 index 27e202d..0000000 --- a/utils/session.go +++ /dev/null @@ -1,170 +0,0 @@ -// 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 -} From fc57baff78fd8c933cfb4c381cd8dd62c784de05 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Mon, 18 May 2026 15:54:37 -0500 Subject: [PATCH 4/5] update to new core session system --- controllers/authentication.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/authentication.go b/controllers/authentication.go index 500533d..35ab468 100644 --- a/controllers/authentication.go +++ b/controllers/authentication.go @@ -1,5 +1,5 @@ // Copyright 2023 Harran Ali . All rights reserved. -// Copyright (c) 2024 Zeni Kim +// Copyright (c) 2026 Zeni Kim // Use of this source code is governed by MIT-style // license that can be found in the LICENSE file. From f4476d3e89207dae1581d36f31ac6d0f1efc90a9 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Mon, 18 May 2026 22:37:26 -0500 Subject: [PATCH 5/5] queue system settings expanded. Update debug to goffee log system --- config/queue.go | 10 +++++++ controllers/queuesample.go | 55 ++++++++++++++++++++++++++++++++++++++ register-queues.go | 3 ++- routes.go | 3 +++ workers/workers.go | 7 ++--- 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 controllers/queuesample.go diff --git a/config/queue.go b/config/queue.go index 8060572..0c01453 100644 --- a/config/queue.go +++ b/config/queue.go @@ -16,5 +16,15 @@ func GetQueueConfig() core.QueueConfig { // For enabling and disabling the queue system // set to true to enable it, set to false to disable EnableQueue: false, + + // Number of concurrent workers processing tasks + Concurrency: 10, + + // Queue names with priority weights (higher number = higher priority) + Queues: map[string]int{ + "critical": 6, + "default": 3, + "low": 1, + }, } } diff --git a/controllers/queuesample.go b/controllers/queuesample.go new file mode 100644 index 0000000..0745a17 --- /dev/null +++ b/controllers/queuesample.go @@ -0,0 +1,55 @@ +// Copyright (c) 2025 Zeni Kim +// Use of this source code is governed by MIT-style +// license that can be found in the LICENSE file. + +package controllers + +import ( + "encoding/json" + "fmt" + "time" + + "git.smarteching.com/goffee/core" + "git.smarteching.com/goffee/cup/workers" + "github.com/hibiken/asynq" +) + +// Make samples queues +func Queuesample(c *core.Context) *core.Response { + + // Get client queue asynq + client := c.GetQueueClient() + + // Create a task with typename and payload. + payload, err := json.Marshal(workers.EmailTaskPayload{UserID: 42}) + if err != nil { + c.GetLogger().Error(err.Error()) + return c.Response.SetStatusCode(500).Json(`{"message": "internal error"}`) + } + + t1 := asynq.NewTask(workers.TypeWelcomeEmail, payload) + + t2 := asynq.NewTask(workers.TypeReminderEmail, payload) + + // Process the task immediately. + info, err := client.Enqueue(t1) + if err != nil { + c.GetLogger().Error(err.Error()) + return c.Response.SetStatusCode(500).Json(`{"message": "internal error"}`) + } + c.GetLogger().Info(fmt.Sprintf(" [*] Successfully enqueued task: %+v", info)) + + // Process 2 task 1 min later. + for i := 1; i < 3; i++ { + info, err = client.Enqueue(t2, asynq.ProcessIn(1*time.Minute)) + if err != nil { + c.GetLogger().Error(err.Error()) + return c.Response.SetStatusCode(500).Json(`{"message": "internal error"}`) + } + c.GetLogger().Info(fmt.Sprintf(" [*] Successfully enqueued task: %+v", info)) + } + + message := "{\"message\": \"Task queued\"}" + return c.Response.Json(message) + +} diff --git a/register-queues.go b/register-queues.go index a1cf059..169ad84 100644 --- a/register-queues.go +++ b/register-queues.go @@ -6,6 +6,7 @@ package main import ( "git.smarteching.com/goffee/core" + "git.smarteching.com/goffee/cup/config" "git.smarteching.com/goffee/cup/workers" ) @@ -26,5 +27,5 @@ func registerQueues() { //######################################## // Start queue server, DO NOT TOUCH //######################################## - go queque.RunQueueserver() + go queque.RunQueueserver(config.GetQueueConfig()) } diff --git a/routes.go b/routes.go index faaf06b..b6b1f3b 100644 --- a/routes.go +++ b/routes.go @@ -28,6 +28,9 @@ func registerRoutes() { controller.Post("/reset-password", controllers.ResetPasswordRequest) controller.Post("/reset-password/code/:code", controllers.SetNewPassword) + // queue sample route + controller.Get("/queuesample", controllers.Queuesample) + // Uncomment the lines below to enable user administration controller.Get("/admin/users", controllers.AdminUsersList) controller.Post("/admin/users", controllers.AdminUsersList) diff --git a/workers/workers.go b/workers/workers.go index e7a9cad..6edde6f 100644 --- a/workers/workers.go +++ b/workers/workers.go @@ -3,8 +3,9 @@ package workers import ( "context" "encoding/json" - "log" + "fmt" + "git.smarteching.com/goffee/core/logger" "github.com/hibiken/asynq" ) @@ -25,7 +26,7 @@ func HandleWelcomeEmailTask(ctx context.Context, t *asynq.Task) error { if err := json.Unmarshal(t.Payload(), &p); err != nil { return err } - log.Printf(" [*] Send Welcome Email to User %d", p.UserID) + logger.ResolveLogger().Info(fmt.Sprintf(" [*] Send Welcome Email to User %d", p.UserID)) return nil } @@ -34,6 +35,6 @@ func HandleReminderEmailTask(ctx context.Context, t *asynq.Task) error { if err := json.Unmarshal(t.Payload(), &p); err != nil { return err } - log.Printf(" [*] Send Reminder Email to User %d", p.UserID) + logger.ResolveLogger().Info(fmt.Sprintf(" [*] Send Reminder Email to User %d", p.UserID)) return nil }