From 0b9d13fb2d15229cf951b8dffd7a8b8f94014adc Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Wed, 1 Oct 2025 16:04:51 +0530 Subject: [PATCH 1/4] add cloud sync data in cache --- backend/go-app/main.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index b10d441d..475357ee 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3818,9 +3818,10 @@ func handleCloudJob(job shuffle.CloudSyncJob) error { // Handles jobs from remote (cloud) func remoteOrgJobController(org shuffle.Org, body []byte) error { type retStruct struct { - Success bool `json:"success"` - Reason string `json:"reason"` - Jobs []shuffle.CloudSyncJob `json:"jobs"` + Success bool `json:"success"` + Reason string `json:"reason"` + Jobs []shuffle.CloudSyncJob `json:"jobs"` + SyncFeatures shuffle.SyncFeatures `json:"sync_features"` } responseData := retStruct{} @@ -3887,6 +3888,14 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error { log.Printf("Got job with reason %s and %d job(s)", responseData.Reason, len(responseData.Jobs)) } + cacheKey := fmt.Sprintf("org_sync_features_%s", org.Id) + featuresBytes, err := json.Marshal(responseData.SyncFeatures) + if err != nil { + log.Printf("[ERROR] Failed to marshal SyncFeatures for cache: %s", err) + } else { + shuffle.SetCache(ctx, cacheKey, featuresBytes, 30) + } + for _, job := range responseData.Jobs { err = handleCloudJob(job) if err != nil { From c13137ce8c8826b20712e203ac44d4d10d923660 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Thu, 2 Oct 2025 18:55:53 +0530 Subject: [PATCH 2/4] Add tenant restriction --- backend/go-app/main.go | 41 ++++++++++++++++++++++++++++++++++++++++- frontend/src/App.jsx | 7 +++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 475357ee..55bc89c2 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -935,6 +935,45 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { log.Printf("[DEBUG] Failed to get org during getinfo: %s", err) } + childOrgs := []shuffle.Org{} + if len(org.CreatorOrg) > 0 { + childOrgs, err = shuffle.GetAllChildOrgs(ctx, org.CreatorOrg) + if err != nil { + log.Printf("[ERROR] Failed to get child orgs during getinfo: %s", err) + childOrgs = []shuffle.Org{} + } + } + // Change this deadline date as release date while pushing to production + deadline := time.Date(2025, 10, 5, 0, 0, 0, 0, time.UTC).Unix() + if len(org.CreatorOrg) > 0 && len(childOrgs) > 3 && org.Created >= deadline { + parentOrg, err := shuffle.GetOrg(ctx, org.CreatorOrg) + if err != nil { + log.Printf("[ERROR] Failed to get parent org during getinfo: %s", err) + } else { + parent := shuffle.HandleCheckLicense(ctx, *parentOrg) + parentOrg := &parent + if !parentOrg.SyncFeatures.MultiTenant.Active { + userInfo.ActiveOrg = shuffle.OrgMini{ + Id: parentOrg.Id, + Name: parentOrg.Name, + Role: userInfo.Role, + Branding: parentOrg.Branding, + Image: parentOrg.Image, + } + log.Printf("[INFO] Parent org %s has more than 3 child orgs and is not licensed. Moving user %s to parent org %s", parentOrg.Name, userInfo.Username, parentOrg.Name) + + err = shuffle.SetUser(ctx, &userInfo, false) + if err != nil { + log.Printf("[WARNING] Failed setting user to parent org: %s", err) + } + + resp.WriteHeader(200) + resp.Write([]byte(`{"success": true, "reason": "Parent org has more than 3 child orgs and is not licensed. Moving to parent org. Contact support@shuffler.io for more information", "switch_parent": true}`)) + return + } + } + } + //if err == nil { if len(org.Id) > 0 { if userInfo.Role == "" { @@ -3893,7 +3932,7 @@ func remoteOrgJobController(org shuffle.Org, body []byte) error { if err != nil { log.Printf("[ERROR] Failed to marshal SyncFeatures for cache: %s", err) } else { - shuffle.SetCache(ctx, cacheKey, featuresBytes, 30) + shuffle.SetCache(ctx, cacheKey, featuresBytes, 1800) } for _, job := range responseData.Jobs { diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9425a1c6..75363d20 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -180,6 +180,13 @@ const App = (message, props) => { var userInfo = {}; if (responseJson.success === true) { //console.log("USER: ", responseJson); + if (responseJson?.switch_parent === true) { + toast.info(responseJson.reason) + setTimeout(() => { + window.location.reload(); + }, 3000); + return + } userInfo = responseJson; setIsLoggedIn(true); From c3ae26b429d7418036ebf5b369e73b2373d1a1b6 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Fri, 3 Oct 2025 16:52:25 +0530 Subject: [PATCH 3/4] Fix cloud sync issue when setup first time --- backend/go-app/main.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 55bc89c2..b00a1628 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4940,7 +4940,15 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { // 2. Add iterative sync schedule for interval seconds // 3. Add another environment for the org's users org.CloudSync = true - org.SyncFeatures = responseData.SyncFeatures + + // set cache here for 30 min + cacheKey := fmt.Sprintf("org_sync_features_%s", org.Id) + featuresBytes, err := json.Marshal(responseData.SyncFeatures) + if err != nil { + log.Printf("[ERROR] Failed to marshal SyncFeatures for cache: %s", err) + } else { + shuffle.SetCache(ctx, cacheKey, featuresBytes, 1800) + } org.SyncConfig = shuffle.SyncConfig{ Apikey: responseData.SessionKey, From 2d780192c380b317002145855ba01d72a639493e Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Fri, 3 Oct 2025 18:38:37 +0530 Subject: [PATCH 4/4] Fail org loading base on the timestamp --- backend/go-app/main.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index b00a1628..17817cd5 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -943,9 +943,22 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { childOrgs = []shuffle.Org{} } } - // Change this deadline date as release date while pushing to production - deadline := time.Date(2025, 10, 5, 0, 0, 0, 0, time.UTC).Unix() - if len(org.CreatorOrg) > 0 && len(childOrgs) > 3 && org.Created >= deadline { + + failToLoadOrgs := []string{} + sort.Slice(childOrgs, func(i, j int) bool { + return childOrgs[i].Created < childOrgs[j].Created + }) + + for index, org := range childOrgs { + + if index < 3 { + continue + } + + failToLoadOrgs = append(failToLoadOrgs, org.Id) + } + + if len(org.CreatorOrg) > 0 && len(childOrgs) > 3 && shuffle.ArrayContains(failToLoadOrgs, userInfo.ActiveOrg.Id) { parentOrg, err := shuffle.GetOrg(ctx, org.CreatorOrg) if err != nil { log.Printf("[ERROR] Failed to get parent org during getinfo: %s", err)