From cc3758c8d3384e98d2fff585de753097773f5166 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 27 May 2023 22:36:16 +0200 Subject: [PATCH] task locking --- grouptasks/grouptasks.go | 25 +++++++++++++++ main.go | 2 ++ modules/cache/categorygroup.go | 12 ++++---- modules/cache/lockedgrouptasksteps.go | 44 +++++++++++++++++++++++++++ modules/structs/grouptasks.go | 30 ++++++++++++------ modules/utils/globals.go | 3 ++ socketserver/hub.go | 17 +++++++++-- 7 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 modules/cache/lockedgrouptasksteps.go diff --git a/grouptasks/grouptasks.go b/grouptasks/grouptasks.go index 5ce44c8..b10e35f 100644 --- a/grouptasks/grouptasks.go +++ b/grouptasks/grouptasks.go @@ -278,3 +278,28 @@ func GetCategoryGroupTaskByCategoryAndGroupId(category string, groupId string) s return structs.Group{} } + +func StartUnlockLockedGroupTaskStepsTicker() { + ticker := time.NewTicker(1 * time.Second) + + for _ = range ticker.C { + for index, taskStep := range cache.GetLockedGroupTaskSteps() { + if time.Since(taskStep.LockedAt).Seconds() > 3 { + log.Debug().Msgf("Unlocked task step", index) + cache.RemoveLockedGroupTaskStep(index) + + socketclients.BroadcastMessageExceptUserSessionId(taskStep.LockedByUserSession, + structs.SendSocketMessage{ + Cmd: utils.SentCmdTaskUnlocked, + Body: struct { + GroupTaskId string + Step uint8 + }{ + GroupTaskId: taskStep.GroupTaskId, + Step: taskStep.Step, + }, + }) + } + } + } +} diff --git a/main.go b/main.go index 6e4615e..7c4a0aa 100644 --- a/main.go +++ b/main.go @@ -79,6 +79,8 @@ func main() { go socketserver.RunHub() socketserver.WebSocketServer(app) + go grouptasks.StartUnlockLockedGroupTaskStepsTicker() + app.Listen(config.Cfg.Host + ":" + config.Cfg.Port) } diff --git a/modules/cache/categorygroup.go b/modules/cache/categorygroup.go index 2dc6bd1..d855874 100644 --- a/modules/cache/categorygroup.go +++ b/modules/cache/categorygroup.go @@ -6,16 +6,16 @@ import ( "sync" ) -var CategoryGroups []structs.CategoryGroup +var categoryGroups []structs.CategoryGroup var cgMu sync.RWMutex func AddCategoryGroup(group structs.Group) { cgMu.Lock() defer cgMu.Unlock() - for index, categoryGroup := range CategoryGroups { + for index, categoryGroup := range categoryGroups { if categoryGroup.Category == group.Category { - CategoryGroups[index].Groups = append(CategoryGroups[index].Groups, group) + categoryGroups[index].Groups = append(categoryGroups[index].Groups, group) return } } @@ -26,7 +26,7 @@ func AddCategoryGroup(group structs.Group) { categoryGroup.Groups = append(categoryGroup.Groups, group) - CategoryGroups = append(CategoryGroups, categoryGroup) + categoryGroups = append(categoryGroups, categoryGroup) } func RemoveAllCategoryGroupsByCategory(category string) { @@ -35,7 +35,7 @@ func RemoveAllCategoryGroupsByCategory(category string) { cgMu.Lock() defer cgMu.Unlock() - CategoryGroups = append(CategoryGroups[:index], CategoryGroups[index+1:]...) + categoryGroups = append(categoryGroups[:index], categoryGroups[index+1:]...) } } } @@ -55,5 +55,5 @@ func GetCategoryGroups() []structs.CategoryGroup { cgMu.RLock() defer cgMu.RUnlock() - return CategoryGroups + return categoryGroups } diff --git a/modules/cache/lockedgrouptasksteps.go b/modules/cache/lockedgrouptasksteps.go new file mode 100644 index 0000000..6a0cc65 --- /dev/null +++ b/modules/cache/lockedgrouptasksteps.go @@ -0,0 +1,44 @@ +package cache + +import ( + "janex/admin-dashboard-backend/modules/structs" + "sync" + "time" +) + +var lockedGroupTaskSteps []*structs.LockedGroupTaskSteps +var lgtMu sync.RWMutex + +func AddLockedGroupTaskStep(lockedGroupTaskStep structs.LockedGroupTaskSteps) (IsAlreadyInList bool) { + lgtMu.Lock() + + var isAlreadyInList = false + + for _, step := range lockedGroupTaskSteps { + if step.GroupTaskId == lockedGroupTaskStep.GroupTaskId && + step.LockedByUserId == lockedGroupTaskStep.LockedByUserId { + isAlreadyInList = true + step.LockedAt = time.Now() + break + } + } + + if !isAlreadyInList { + lockedGroupTaskSteps = append(lockedGroupTaskSteps, &lockedGroupTaskStep) + } + + lgtMu.Unlock() + + return isAlreadyInList +} + +func RemoveLockedGroupTaskStep(i int) { + lockedGroupTaskSteps = append(lockedGroupTaskSteps[:i], lockedGroupTaskSteps[i+1:]...) +} + +func GetLockedGroupTaskSteps() []*structs.LockedGroupTaskSteps { + lgtMu.RLock() + defer lgtMu.RUnlock() + + return lockedGroupTaskSteps +} diff --git a/modules/structs/grouptasks.go b/modules/structs/grouptasks.go index d765ced..0573416 100644 --- a/modules/structs/grouptasks.go +++ b/modules/structs/grouptasks.go @@ -31,16 +31,16 @@ type GroupTasks struct { } type GroupTaskSteps struct { - Id string - GroupTasksId string - CreatorUserId string - Step uint8 - Status uint8 - Log string `gorm:"type:text"` - Inputs string `gorm:"type:json"` - StartedAt time.Time - EndedAt time.Time - Locked bool `gorm:"-"` // used by the web client to ensure that only one user can edit the input value + Id string + GroupTasksId string + CreatorUserId string + Step uint8 + Status uint8 + Log string `gorm:"type:text"` + Inputs string `gorm:"type:json"` + StartedAt time.Time + EndedAt time.Time + LockedByUserId string `gorm:"-"` // used by the web client to ensure that only one user can edit the input value } // read from file structure @@ -79,3 +79,13 @@ type TaskParameter struct { DisplayName string `json:"displayName"` Global bool `json:"global"` } + +// used for ui when a user is writing into input field to lock the task step for other users + +type LockedGroupTaskSteps struct { + LockedByUserId string + LockedByUserSession string // user session is needed to prevent sending the unlocking message to the user who are writing + GroupTaskId string + Step uint8 + LockedAt time.Time +} diff --git a/modules/utils/globals.go b/modules/utils/globals.go index 11d2ab0..1a9fd74 100644 --- a/modules/utils/globals.go +++ b/modules/utils/globals.go @@ -17,6 +17,8 @@ const ( HeaderXAuthorization = "X-Authorization" MaxAvatarSize = 5 * 1024 * 1024 // 5 MB + + GroupTaskLockedTime = 3 // seconds - need to be equal with web ) // commands sent to web clients @@ -37,6 +39,7 @@ const ( SentCmdScanResult = 14 SentCmdUpdateScannerLastUsed = 15 SentCmdTaskLocked = 16 + SentCmdTaskUnlocked = 17 ) // commands received from web clients diff --git a/socketserver/hub.go b/socketserver/hub.go index 8f0b1cc..ef27035 100644 --- a/socketserver/hub.go +++ b/socketserver/hub.go @@ -157,10 +157,21 @@ func RunHub() { case utils.ReceivedCmdTaskLocking: log.Info().Msgf("task locking %s", receivedMessage.Body) - socketclients.BroadcastMessageExceptUserSessionId(data.Conn.Locals("sessionId").(string), structs.SendSocketMessage{ - Cmd: utils.SentCmdTaskLocked, - Body: receivedMessage.Body, + isAlreadyInList := cache.AddLockedGroupTaskStep(structs.LockedGroupTaskSteps{ + LockedByUserId: receivedMessage.Body["lockedByUserId"].(string), + LockedByUserSession: data.Conn.Locals("sessionId").(string), + GroupTaskId: receivedMessage.Body["groupTaskId"].(string), + Step: uint8(receivedMessage.Body["step"].(float64)), + LockedAt: time.Now(), }) + + if !isAlreadyInList { + socketclients.BroadcastMessageExceptUserSessionId(data.Conn.Locals("sessionId").(string), structs.SendSocketMessage{ + Cmd: utils.SentCmdTaskLocked, + Body: receivedMessage.Body, + }) + } + break default: