package controllers import ( "bytes" "context" "encoding/json" "fmt" "io" "io/ioutil" "nearle/db" "nearle/domain" "nearle/models" "nearle/utils" "net/http" "net/url" "time" // "os" "strconv" "github.com/gofiber/fiber/v2" "github.com/redis/go-redis/v9" "golang.org/x/oauth2" "golang.org/x/oauth2/google" ) func GetApplocations(c *fiber.Ctx) error { aid, _ := strconv.Atoi(c.Query("applocationid")) result := domain.GetApplocations(aid) return c.JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Successful", "details": result, }) } func GetApplocationsv2(c *fiber.Ctx) error { userid, _ := strconv.Atoi(c.Query("userid")) result := domain.GetApplocationsv2(userid) return c.JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Successful", "details": result, }) } func GetApplocationConfig(c *fiber.Ctx) error { aid, _ := strconv.Atoi(c.Query("applocationid")) result := domain.GetApplocationConfig(aid) return c.JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Successful", "details": result, }) } func GetApplocationadmins(c *fiber.Ctx) error { aid, _ := strconv.Atoi(c.Query("applocationid")) result := domain.GetApplocationadmins(aid) return c.JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Successful", "details": result, }) } func GetAppPricing(c *fiber.Ctx) error { aid, _ := strconv.Atoi(c.Query("applocationid")) cid, _ := strconv.Atoi(c.Query("configid")) pid, _ := strconv.Atoi(c.Query("pricingtypeid")) result := domain.GetAppPricing(aid, cid, pid) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func GetAllAppPricing(c *fiber.Ctx) error { aid, _ := strconv.Atoi(c.Query("applocationid")) result := domain.GetAllAppPricing(aid) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func CreateAppPricing(c *fiber.Ctx) error { var data models.Apppricing if err := c.BodyParser(&data); err != nil { return err } result := domain.CreateAppProcing(data) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func GetAppConfig(c *fiber.Ctx) error { cid, _ := strconv.Atoi(c.Query("configid")) result := domain.GetAppconfig(cid) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func GetAllAppConfig(c *fiber.Ctx) error { result := domain.GetAllAppconfig() return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func GetApptypes(c *fiber.Ctx) error { tag := c.Query("tag") result := domain.GetApptypes(tag) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func GetSubcategories(c *fiber.Ctx) error { mid, _ := strconv.Atoi(c.Query("moduleid")) // 0 means all modules cid, _ := strconv.Atoi(c.Query("categoryid")) // 0 means all categories result := domain.GetSubcategories(mid, cid) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func GetCategories(c *fiber.Ctx) error { mid, _ := strconv.Atoi(c.Query("moduleid", "0")) // default to 0 result := domain.GetCategories(mid) // Ensure result is never null if result == nil { result = []models.AppCategory{} } return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } const ( fcmURL = "https://fcm.googleapis.com/v1/projects/nearle-gear/messages:send" scope = "https://www.googleapis.com/auth/firebase.messaging" serviceAcc = "nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json" // Path to your service account JSON ) var client *http.Client // Structure of FCM message // GetClientFromServiceAccount reads the service account JSON file and generates a new token func GetClientFromServiceAccount() (*http.Client, error) { data, err := ioutil.ReadFile(serviceAcc) if err != nil { return nil, fmt.Errorf("failed to read service account file: %v", err) } config, err := google.JWTConfigFromJSON(data, scope) if err != nil { return nil, fmt.Errorf("failed to parse service account JSON: %v", err) } // Use the token source to generate an HTTP client that automatically refreshes the token client := config.Client(oauth2.NoContext) return client, nil } func sendFCMMessage(token, title, body string) error { if client == nil { var err error client, err = GetClientFromServiceAccount() if err != nil { return fmt.Errorf("failed to get client: %v", err) } } notification := Notification{ Title: title, Body: body, } fcmRequest := FCMRequestBody{} fcmRequest.Message.Notification = notification fcmRequest.Message.Token = token requestBody, err := json.Marshal(fcmRequest) if err != nil { return err } req, err := http.NewRequest("POST", fcmURL, bytes.NewBuffer(requestBody)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { bodyBytes, _ := io.ReadAll(resp.Body) return fmt.Errorf("failed to send message: %s", string(bodyBytes)) } return nil } func NotifyAdmin(c *fiber.Ctx) error { body := new(struct { Token []string `json:"token"` Notification struct { Title string `json:"title"` Body string `json:"body"` Sound string `json:"sound"` Type string `json:"type"` } `json:"notification"` }) if err := c.BodyParser(body); err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") } if len(body.Token) == 0 { return fiber.NewError(fiber.StatusBadRequest, "No tokens provided") } // Only send FCM to provided tokens var successCount, failureCount int for _, token := range body.Token { err := sendFCMMessage(token, body.Notification.Title, body.Notification.Body) if err != nil { utils.Logger.Errorf("❌ Failed to send to %s: %v", token, err) failureCount++ } else { utils.Logger.Infof("✅ Notification sent to %s", token) successCount++ } } return c.Status(fiber.StatusOK).JSON(fiber.Map{ "message": "Admin notifications sent", "success": successCount, "failure": failureCount, }) } func NotifyTenant(c *fiber.Ctx) error { body := new(struct { Token []string `json:"token"` TenantID int `json:"tenantid"` ModuleID int `json:"moduleid"` LocationID int `json:"locationid"` CustomerID int `json:"customerid"` Notification struct { Title string `json:"title"` Body string `json:"body"` Sound string `json:"sound"` Type string `json:"type"` } `json:"notification"` }) if err := c.BodyParser(body); err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") } if len(body.Token) == 0 { return fiber.NewError(fiber.StatusBadRequest, "No tokens provided") } successCount := 0 failureCount := 0 // Loop through each token and send notification + insert DB log for _, token := range body.Token { err := sendFCMMessage(token, body.Notification.Title, body.Notification.Body) successCode := 0 if err != nil { utils.Logger.Errorf("❌ Failed to send to %s: %v", token, err) failureCount++ } else { utils.Logger.Infof("✅ Notification sent to %s", token) successCount++ successCode = 1 } // Insert into tenantnotifications table insertQuery := ` INSERT INTO tenantnotifications (title, message, tenantid, moduleid, locationid, customerid, notificationdate, successcode) VALUES (?, ?, ?, ?, ?, ?, NOW(), ?) ` if dbErr := db.DB.Exec( insertQuery, body.Notification.Title, body.Notification.Body, body.TenantID, body.ModuleID, body.LocationID, body.CustomerID, successCode, ).Error; dbErr != nil { utils.Logger.Errorf("❌ DB Insert Error: %v", dbErr) } } return c.Status(fiber.StatusOK).JSON(fiber.Map{ "message": "Tenant notifications processed", "success": successCount, "failure": failureCount, }) } func GetTenantNotifications(c *fiber.Ctx) error { tenantid, _ := strconv.Atoi(c.Query("tenantid")) locationid, _ := strconv.Atoi(c.Query("locationid")) customerid, _ := strconv.Atoi(c.Query("customerid")) moduleid, _ := strconv.Atoi(c.Query("moduleid")) pageno, _ := strconv.Atoi(c.Query("pageno")) pagesize, _ := strconv.Atoi(c.Query("pagesize")) if pageno == 0 { pageno = 1 } if pagesize == 0 { pagesize = 10 } result, _ := domain.GetTenantNotifications( tenantid, locationid, customerid, moduleid, pageno, pagesize, ) return c.JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Successful", "details": result, }) } type Notification struct { Title string `json:"title"` Body string `json:"body"` } type FCMRequestBody struct { Message struct { Notification Notification `json:"notification"` Token string `json:"token"` } `json:"message"` } type WebhookPayload struct { ID string `json:"id"` Event string `json:"event"` Timestamp string `json:"timestamp"` Vendor string `json:"vendor"` FailedAttempts int `json:"failedAttempts"` WebhookID string `json:"webhookId"` Payload interface{} `json:"payload"` Context interface{} `json:"context"` } func WebhookHandler(c *fiber.Ctx) error { utils.Info("➡️ Incoming webhook request") var webhook WebhookPayload if err := c.BodyParser(&webhook); err != nil { utils.Logger.Errorf("❌ Failed to parse webhook JSON: %v", err) utils.Logger.Errorf("Request body: %s", string(c.Body())) return c.Status(fiber.StatusBadRequest).SendString("Invalid JSON") } utils.Logger.Infof("✅ Received event: %s at %s", webhook.Event, webhook.Timestamp) utils.Logger.Infof("🧾 Payload: %+v", webhook.Payload) return c.SendStatus(fiber.StatusOK) } func RegisterWebhookHandler(c *fiber.Ctx) error { // Shopfront GraphQL API endpoint url := "https://testshopfront.onshopfront.com/api/v2/graphql" // Webhook registration mutation payload := map[string]string{ "query": ` mutation { registerWebhook(input: { name: "Sale Webhook", url: "https://jupiter.nearle.app/live/api/v1/utils/webhooks", events: [sale_created] }) { id name url events active } }`, } // Marshal payload body, _ := json.Marshal(payload) // Create POST request req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIzODUiLCJqdGkiOiI2OTA5MmM3ZmY0YzgzNmExNTBmZWIyOGU3N2RkOWYzMDFjNjM4ZGQ5ODViOGExZjMzYjdhODY1MzFhMzQxNjM0NTQ2ZjMxMTY0M2JjMGUzNSIsImlhdCI6MTc1MzExMTY5NC44OTAyNjksIm5iZiI6MTc1MzExMTY5NC44OTAyNzQsImV4cCI6MTc1NDMyMTI5NC44Nzg2OTYsInN1YiI6IiIsInNjb3BlcyI6WyJjcmVhdGVfd2ViaG9va3MiXX0.WtE2eYES3MWdcvQZIu0AubIzUAv839-c_UoSUdCx5ez2Px03u-YZbCX114LxAhcLIZbQBiyaFB24pW3ojQf_RIdIvDnWPoMo7RWDDVYiHT9xHbe6Crht4ORkaYF2IOijXCYgDRgXT3whBNVldFhFAyfhcj9qVehJdmpRh5QGmt0PT9LWoy7XeFR_ksmOPjufvv1JZLITYilT7SijrkpSvDsqUBlCXj86ifIFY9q3A2zexb7GlSvdE_RmRegPr0kLA23i4J4uwo9BBPDaJaIGYIP4C6x5j9fYBOmf0v3cSt1teoVGBFedjrizH4qvWazDZl44OlwlE6QQYCXpmjgFWXtLeiI54JQ-tygCbxi7xB_O4v8oqj_G4aP-aM-PykC5ge11ekJuCJXhsLcJajgztcjVVz8Bmnh0U5nnRG9Qx0szTiuy07p6y73IN4gWfLFjRd16pryhPjAHzI5bzbRGG_Pd0M5NcT7krZkQGEDkpuNgDKni6GgLQBy4My_mskJXjTw9lYP5i2Okn9CywJ5qaPNRMWW8eiEpb8qwzXxS2IRQQSKnpkT88P1LcSUMBhermYq60KFuh1RKYG5jNoWo2xwCsNJygCCk4iwtAvlhBoAZiOPwYJcgSM6jPdwqoUuRys_tqs938zIuf9hTdDosm5WM9z1sxXX-nqSfLqFuN_M") // Replace with valid token // ✅ Add debug logs utils.Logger.Infof("Request body: %s", string(body)) utils.Logger.Infof("Authorization header: %s", req.Header.Get("Authorization")) // Send the request resp, err := http.DefaultClient.Do(req) if err != nil { utils.Logger.Errorf("Failed to register webhook: %v", err) return c.Status(500).SendString("Webhook registration failed") } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) utils.Logger.Infof("Webhook registration response: %s", respBody) return c.Status(resp.StatusCode).SendString(string(respBody)) } type WebhookInput struct { Topic string `json:"topic"` DeliveryUrl string `json:"delivery_url"` } func Createwebhook(c *fiber.Ctx) error { code := c.Query("code") vendor := c.Query("vendor") // ✅ Handle OAuth callback if code != "" && vendor != "" { utils.Logger.Infof("✅ Received OAuth Code: %s", code) utils.Logger.Infof("✅ Vendor: %s", vendor) // 🔁 Exchange code for access token tokenResp, err := http.PostForm("https://"+vendor+".onshopfront.com/oauth/token", url.Values{ "client_id": []string{"kY3Dfvohj8BV4lEoUDKSEsyRGnlGpXi8"}, "client_secret": []string{"Qk2eUXHaveIFEMNTny0yvVB1aHWvIY5J7blKwdHx"}, "code": []string{code}, "grant_type": []string{"client_credentials"}, "redirect_uri": []string{"https://jupiter.nearle.app/live/api/v1/utils/createwebhook"}, }) if err != nil { utils.Logger.Errorf("❌ Token exchange error: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "status": false, "message": "Failed to exchange code for access token", }) } defer tokenResp.Body.Close() var tokenData map[string]interface{} json.NewDecoder(tokenResp.Body).Decode(&tokenData) utils.Logger.Infof("🔐 Token response: %v", tokenData) accessToken, ok := tokenData["access_token"].(string) if !ok || accessToken == "" { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Access token missing in response", }) } // ✅ Send accessToken back or store it (in DB, session, etc.) return c.JSON(fiber.Map{ "status": true, "message": "OAuth successful", "access_token": accessToken, }) } // ✅ Handle webhook registration var input WebhookInput if err := c.BodyParser(&input); err != nil { utils.Logger.Errorf("❌ BodyParser Error: %v", err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "status": false, "message": "Invalid request body", }) } if input.Topic == "" || input.DeliveryUrl == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "status": false, "message": "Topic and Delivery URL are required", }) } payload := map[string]string{ "topic": input.Topic, "delivery_url": input.DeliveryUrl, } jsonPayload, _ := json.Marshal(payload) // ❗ Replace this with actual token from DB/session accessToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIzODUiLCJqdGkiOiIwNmI3Y2M0YTA4YzY1NDhlMmYzNTU3Nzg0MDAyNDdmMjk0ZTliOTBkMzQ4MjNjODZkNjJhNTU4NjZhZWE5M2EwNTZlYjQ4ZjM2NTQ3MTE0YyIsImlhdCI6MTc1MzM0MjczNi42MDc2NTcsIm5iZiI6MTc1MzM0MjczNi42MDc2NjIsImV4cCI6MTc1NDU1MjMzNi41OTQ3MTksInN1YiI6IiIsInNjb3BlcyI6WyJjcmVhdGVfd2ViaG9va3MiXX0.jl4ln8Qv2apX4A_uxohS2Rqj6hShBQD1M2tGV-8fkUPrxg8anh-4aDEApFiaZuB8PkfereU5nPlMP_r5Hs5vWNTIgZyp6A-uwUp89c7NZyQng9aCfmRCN0yN0VeV4SWMKasp6uFacG-_MrzToQPtsYGGam7tG8j5apfcy3spXvJSC7CwwfRxL-Kpi1mJ-t272onWZblEhLrwiMbKc_6B5mVCV8TkCsY6YS8UDT5SC93X8_l2X3wjg2MP-fL9zUWz13qK4xHVDvqpx2mc0fG-ZI7oJpafC5j8GszVkobKqQTwMACuJSt5l6AgWcau4uPKglMIzUqapipSuu4jjKvQCQQAwLaL8Xc9Zy_vtMgGepdNEcuRn7uZqg1CMHCUKqvYWu-YU_4hWlzHVUu3A4SPcs5ZfxIj5D5smMffQb_2FQm4PThMlJKD4T697VLm0jKfDN0P8c3PINhiEpixJFh-njlCCNUXylO4SxG2Q8Yg_nBMLGuTevsSZaZLXd4_Zvsw3tZpoDOGWyZ0-fjBcbCvjmq_eY2S1emdwHYNhoFo8j0FJjKuEnwj0XylaQu6bt1PF8E_LQit9SK2umVMRAtb7ayocoz9iSi6NP3416aI7B97pUwqWKzy9or7LKtydAfzfuFCzlhuXf_iW3VhQR8QlFJtUuKIt7Nfu3gGAm6pk0" req, err := http.NewRequest("POST", "https://testshopfront.onshopfront.com/api/v1/webhooks", bytes.NewBuffer(jsonPayload)) if err != nil { utils.Logger.Errorf("❌ Failed to create request: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "status": false, "message": "Failed to create webhook request", }) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+accessToken) client := &http.Client{} resp, err := client.Do(req) if err != nil { utils.Logger.Errorf("❌ Error calling Shopfront API: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "status": false, "message": "Error sending request to Shopfront", }) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) utils.Logger.Infof("🔁 Shopfront Response: %s", string(body)) return c.Status(resp.StatusCode).Send(body) } func WebhookReceiver(c *fiber.Ctx) error { var webhookData struct { ID string `json:"id"` Event string `json:"event"` Timestamp string `json:"timestamp"` Vendor string `json:"vendor"` FailedAttempts int `json:"failedAttempts"` WebhookID string `json:"webhookId"` Payload map[string]interface{} `json:"payload"` Context interface{} `json:"context"` } if err := c.BodyParser(&webhookData); err != nil { utils.Logger.Errorf("❌ Error parsing webhook payload: %v", err) return c.SendStatus(fiber.StatusBadRequest) // 400 } // Log webhook info for debugging utils.Info("🔔 Webhook received:") utils.Logger.Infof("ID: %s", webhookData.ID) utils.Logger.Infof("Event: %s", webhookData.Event) utils.Logger.Infof("Timestamp: %s", webhookData.Timestamp) utils.Logger.Infof("Vendor: %s", webhookData.Vendor) utils.Logger.Infof("Payload: %v", webhookData.Payload) // Return 200 OK with no body return c.SendStatus(fiber.StatusOK) } func GetShopfrontOrders(c *fiber.Ctx) error { url := "https://testshopfront.onshopfront.com/graphql" accessToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIzODUiLCJqdGkiOiI5NzMzZjk3ZjlmNjAxMWM4NDc4NDE3Y2JlN2MzM2VjZjM0YzJiNWUyNDMzNjBmNTliNTg3NzgzZWZkOGYxOGQ0NjQ3MjZmMjMzMzAwOTE2YSIsImlhdCI6MTc1MzQyOTE2Ni4zMzkxNiwibmJmIjoxNzUzNDI5MTY2LjMzOTE2NSwiZXhwIjoxNzU0NjM4NzY2LjMyMjUzOSwic3ViIjoiIiwic2NvcGVzIjpbIioiXX0.1ne-a29-wS_HUn3vVnuUA1ikXeXfLtz6AMktFfbj1Zc4L1ih_PJAHvzODbgErv9pR-6AucDyxH2ZlJSb7MAL-V93yDMGK672n4lxXNDcVqgZEGjCrn7WlyopLhZDhjKJMzRFn1CN4FkW2P31Jgl55W0Ff_prX13usLy_Cad8rNlnp15TaTgJed-m40hsOeWEjQDP_LJWQCHSf2wXNPx-tpRe1FEbh6QQNV4nT3TRqUQEhWvd_nQXiqnZaqY0q6Xbgq_Vn-laNNySIvtyISaoS0jxfrSF2856FBhF-WKIzMfQs1oeQioZL-2IH1QGKrbCJfV9Z4gTaGfVLV8ZAWp1XB3CmpDFgEJih_H76u1NvIXs8S343jq6klV1GKaqvuYfz8RfwtNx7mfP6mOwve3SkRSuHZOedzcgKjdETL8AWx7AT-6gbSdGX49ktqD0NbixzfBu2MGkBn6j7vMVgtNqfrYHcfZAP65gRlo9UGsKAvAFbAZnGoO-FHC3R9iSOM2llmLNg65OoaPazavOfnKhLWzk06gS3t_8HJenqTM_7tp6YsW-W57k5y5MzJYcHvXSplvG8VYUFEkRo6PeGq7cib_78IopAp3vgxODSaDa9ETkn2RHGLS8IyMK6pAP7OLpZyc6lxNgDqnZE3S2kAqdrOgvrld3NzP4hWpwA3dka8" query := `{ "query": "query { orders(first: 10) { edges { node { id invoiceNumber orderDate status totalCost } } } }" }` req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(query))) if err != nil { utils.Logger.Errorf("❌ Request creation failed: %v", err) return c.Status(500).JSON(fiber.Map{"error": "Request creation failed"}) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+accessToken) client := &http.Client{} resp, err := client.Do(req) if err != nil { utils.Logger.Errorf("❌ Request failed: %v", err) return c.Status(500).JSON(fiber.Map{"error": "Request execution failed"}) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) return c.Status(resp.StatusCode).Send(body) } func RegisterInventoryWebhook(c *fiber.Ctx) error { // Log or process the incoming payload var payload map[string]interface{} if err := c.BodyParser(&payload); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Invalid payload", }) } utils.Logger.Infof("📦 Inventory Webhook Received: %v", payload) // Always return 2xx status with no body return c.SendStatus(fiber.StatusNoContent) // 204 } func GetAppModule(c *fiber.Ctx) error { result := domain.GetAppModule() return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": result, }) } func GetUserBonusSummary(c *fiber.Ctx) error { userid, err := strconv.Atoi(c.Query("userid")) if err != nil || userid == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": 400, "message": "Valid userid is required", }) } data, err := domain.GetUserBonusSummary(userid) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": 500, "message": err.Error(), }) } return c.Status(fiber.StatusOK).JSON(fiber.Map{ "status": true, "code": 200, "message": "Success", "details": data, }) } func CreateAppLocationConfig(c *fiber.Ctx) error { var data models.AppLocationConfigRequest if err := c.BodyParser(&data); err != nil { return err } result := domain.CreateAppLocationConfig(data) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": result, }) } func UpdateAppLocationConfig(c *fiber.Ctx) error { var data models.AppLocationConfigRequest if err := c.BodyParser(&data); err != nil { return err } result := domain.UpdateAppLocationConfig(data) return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": result, }) } func GetUserRoles(c *fiber.Ctx) error { roles, err := domain.GetUserRoles() if err != nil { return c.Status(500).JSON(fiber.Map{ "success": false, "message": "Failed to fetch roles", "error": err.Error(), }) } return c.JSON(fiber.Map{ "status": true, "code": 200, "message": "Success", "details": roles, }) } // ============================================================================ // REDIS USER STORAGE ENDPOINTS // ============================================================================ // StoreUserRedis - POST /api/v1/utils/users/redis // Store user data in Redis with ZSET index and HASH storage func CreateUserRedis(c *fiber.Ctx) error { ctx := context.Background() // Parse request body var userData struct { UserID int `json:"userid"` Username string `json:"username"` FirstName string `json:"firstname"` LastName string `json:"lastname"` ContactNo string `json:"contactno"` UserFCMToken string `json:"userfcmtoken"` AppVersion string `json:"app_version"` DeviceInfo string `json:"device_info"` } if err := c.BodyParser(&userData); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": http.StatusBadRequest, "message": "Invalid request body", }) } // Validate required fields if userData.UserID == 0 || userData.Username == "" || userData.FirstName == "" || userData.LastName == "" { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": http.StatusBadRequest, "message": "userid, username, firstname, and lastname are required", }) } // Check Redis connection pong, err := db.Rdb.Ping(ctx).Result() utils.Logger.Infof("[Redis] Ping: %s Error: %v", pong, err) if err != nil { utils.Logger.Errorf("❌ Redis connection error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Redis connection error: " + err.Error(), }) } // Marshal user data to JSON jsonData, err := json.Marshal(userData) if err != nil { utils.Logger.Errorf("❌ JSON marshal error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to serialize user data", }) } // Store in HASH: "user:{userid}" userKey := fmt.Sprintf("user:%d", userData.UserID) if err := db.Rdb.Set(ctx, userKey, jsonData, 0).Err(); err != nil { utils.Logger.Errorf("❌ Redis SET error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to store user in Redis", }) } utils.Logger.Infof("✅ User stored in HASH — key=%s", userKey) // Add to ZSET with timestamp as score: "user" → {score: timestamp, member: userid} now := time.Now().Unix() zadd := db.Rdb.ZAdd(ctx, "user", redis.Z{ Score: float64(now), Member: fmt.Sprintf("%d", userData.UserID), }) if zadd.Err() != nil { utils.Logger.Errorf("❌ Redis ZADD error: %v", zadd.Err()) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to index user in ZSET", }) } utils.Logger.Infof("✅ User indexed in ZSET — key=user, member=%d, score=%d", userData.UserID, now) return c.Status(http.StatusCreated).JSON(fiber.Map{ "status": true, "code": http.StatusCreated, "message": "User stored successfully in Redis", "data": userData, }) } // GetUserRedis - GET /api/v1/utils/users/redis/:userid // Retrieve user data from Redis by userid func GetUserRedis(c *fiber.Ctx) error { ctx := context.Background() userIDStr := c.Query("userid") pageStr := c.Query("page") pageSizeStr := c.Query("pagesize") // 🔹 CASE 1: Single user if userIDStr != "" { userID, err := strconv.Atoi(userIDStr) if err != nil || userID == 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": http.StatusBadRequest, "message": "Valid userid parameter is required", }) } userKey := fmt.Sprintf("user:%d", userID) userData, err := db.Rdb.Get(ctx, userKey).Result() if err != nil { return c.Status(http.StatusNotFound).JSON(fiber.Map{ "status": false, "code": http.StatusNotFound, "message": "User not found", }) } var user map[string]interface{} json.Unmarshal([]byte(userData), &user) return c.JSON(fiber.Map{ "status": true, "data": user, }) } // 🔹 CASE 2: ALL users with pagination var ( page int pageSize int err error ) if pageStr != "" { page, err = strconv.Atoi(pageStr) if err != nil || page <= 0 { return c.Status(400).JSON(fiber.Map{ "status": false, "message": "invalid page", }) } } if pageSizeStr != "" { pageSize, err = strconv.Atoi(pageSizeStr) if err != nil || pageSize <= 0 { return c.Status(400).JSON(fiber.Map{ "status": false, "message": "invalid pagesize", }) } } var start, end int64 if page > 0 && pageSize > 0 { offset := (page - 1) * pageSize start = int64(offset) end = int64(offset + pageSize - 1) } else { start = 0 end = -1 // fetch ALL } // ✅ Get user IDs from ZSET userIDs, err := db.Rdb.ZRevRange(ctx, "user", start, end).Result() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to fetch users", }) } // Convert to keys var keys []string for _, id := range userIDs { keys = append(keys, fmt.Sprintf("user:%s", id)) } // ✅ Use MGET (fast) values, err := db.Rdb.MGet(ctx, keys...).Result() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to fetch user data", }) } var users []map[string]interface{} for _, val := range values { if val == nil { continue } var user map[string]interface{} json.Unmarshal([]byte(val.(string)), &user) users = append(users, user) } // ✅ total count (optional) // total, _ := db.Rdb.ZCard(ctx, "user").Result() return c.JSON(fiber.Map{ "code": http.StatusOK, "data": users, "message": "Success", "status": true, }) } // UpdateUserRedis - PUT /api/v1/utils/users/redis/:userid // Update user data in Redis func UpdateUserRedis(c *fiber.Ctx) error { ctx := context.Background() userIDStr := c.Params("userid") userID, err := strconv.Atoi(userIDStr) if err != nil || userID == 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": http.StatusBadRequest, "message": "Valid userid parameter is required", }) } // Parse request body var userData map[string]interface{} if err := c.BodyParser(&userData); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": http.StatusBadRequest, "message": "Invalid request body", }) } // Check Redis connection _, err = db.Rdb.Ping(ctx).Result() if err != nil { utils.Logger.Errorf("❌ Redis connection error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Redis connection error", }) } // Check if user exists userKey := fmt.Sprintf("user:%d", userID) exists, err := db.Rdb.Exists(ctx, userKey).Result() if err != nil || exists == 0 { utils.Logger.Warnf("⚠️ User not found for update — key=%s", userKey) return c.Status(http.StatusNotFound).JSON(fiber.Map{ "status": false, "code": http.StatusNotFound, "message": "User not found", }) } // Add userid to update data userData["userid"] = userID // Marshal updated data jsonData, err := json.Marshal(userData) if err != nil { utils.Logger.Errorf("❌ JSON marshal error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to serialize user data", }) } // Update in HASH if err := db.Rdb.Set(ctx, userKey, jsonData, 0).Err(); err != nil { utils.Logger.Errorf("❌ Redis SET error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to update user in Redis", }) } utils.Logger.Infof("✅ User updated in HASH — key=%s", userKey) return c.Status(http.StatusOK).JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "User updated successfully", "data": userData, }) } // DeleteUserRedis - DELETE /api/v1/utils/users/redis/:userid // Delete user from both HASH and ZSET func DeleteUserRedis(c *fiber.Ctx) error { ctx := context.Background() userIDStr := c.Params("userid") userID, err := strconv.Atoi(userIDStr) if err != nil || userID == 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": http.StatusBadRequest, "message": "Valid userid parameter is required", }) } // Check Redis connection _, err = db.Rdb.Ping(ctx).Result() if err != nil { utils.Logger.Errorf("❌ Redis connection error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Redis connection error", }) } userKey := fmt.Sprintf("user:%d", userID) userIDStr = fmt.Sprintf("%d", userID) // Delete from HASH delResult, err := db.Rdb.Del(ctx, userKey).Result() if err != nil { utils.Logger.Errorf("❌ Redis DEL error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to delete user", }) } utils.Logger.Infof("✅ User deleted from HASH — key=%s, deleted=%d", userKey, delResult) // Remove from ZSET remResult, err := db.Rdb.ZRem(ctx, "user", userIDStr).Result() if err != nil { utils.Logger.Errorf("❌ Redis ZREM error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to remove user from index", }) } utils.Logger.Infof("✅ User removed from ZSET — key=user, member=%s, removed=%d", userIDStr, remResult) return c.Status(http.StatusOK).JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "User deleted successfully", }) } // GetAllUsersRedis - GET /api/v1/utils/users/redis // Retrieve all users from ZSET func GetAllUsersRedis(c *fiber.Ctx) error { ctx := context.Background() // Check Redis connection _, err := db.Rdb.Ping(ctx).Result() if err != nil { utils.Logger.Errorf("❌ Redis connection error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Redis connection error", }) } // Get all user IDs from ZSET userIDs, err := db.Rdb.ZRange(ctx, "user", 0, -1).Result() if err != nil { utils.Logger.Errorf("❌ Redis ZRANGE error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to fetch users", }) } utils.Logger.Infof("✅ Retrieved %d user IDs from ZSET", len(userIDs)) // Fetch each user's data var users []map[string]interface{} for _, userIDStr := range userIDs { userKey := fmt.Sprintf("user:%s", userIDStr) userData, err := db.Rdb.Get(ctx, userKey).Result() if err != nil { utils.Logger.Warnf("⚠️ Failed to get user data — key=%s", userKey) continue } var user map[string]interface{} if err := json.Unmarshal([]byte(userData), &user); err != nil { utils.Logger.Warnf("⚠️ Failed to unmarshal user data — key=%s", userKey) continue } users = append(users, user) } return c.Status(http.StatusOK).JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Success", "count": len(users), "data": users, }) } // UserExistsRedis - GET /api/v1/utils/users/redis/:userid/exists // Check if user exists in Redis func UserExistsRedis(c *fiber.Ctx) error { ctx := context.Background() userIDStr := c.Params("userid") userID, err := strconv.Atoi(userIDStr) if err != nil || userID == 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "status": false, "code": http.StatusBadRequest, "message": "Valid userid parameter is required", }) } // Check Redis connection _, err = db.Rdb.Ping(ctx).Result() if err != nil { utils.Logger.Errorf("❌ Redis connection error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Redis connection error", }) } userKey := fmt.Sprintf("user:%d", userID) exists, err := db.Rdb.Exists(ctx, userKey).Result() if err != nil { utils.Logger.Errorf("❌ Redis EXISTS error: %v", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to check user existence", }) } userExists := exists > 0 utils.Logger.Infof("✅ User existence check — userid=%d, exists=%v", userID, userExists) return c.Status(http.StatusOK).JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Success", "exists": userExists, }) } func CreateRiderPeriodicLog(c *fiber.Ctx) error { ctx := context.Background() var log models.RiderLog if err := c.BodyParser(&log); err != nil { return c.Status(400).JSON(fiber.Map{ "status": false, "message": "Invalid request body", }) } // Parse logdate t, err := time.Parse("2006-01-02 15:04:05", log.LogDate) if err != nil { return c.Status(400).JSON(fiber.Map{ "status": false, "message": "Invalid logdate format", }) } timestamp := t.Unix() // Key logKey := fmt.Sprintf("rider_periodic_log:%d:%d", log.UserID, timestamp) data, _ := json.Marshal(log) // Store STRING err = db.Rdb.Set(ctx, logKey, data, 0).Err() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to store log", }) } // ✅ Per-user ZSET userZsetKey := fmt.Sprintf("rider_periodic_logs:%d", log.UserID) err = db.Rdb.ZAdd(ctx, userZsetKey, redis.Z{ Score: float64(timestamp), Member: logKey, }).Err() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to index user log", }) } // 🔥 ADD THIS (GLOBAL INDEX) err = db.Rdb.ZAdd(ctx, "rider_periodic_logs_all", redis.Z{ Score: float64(timestamp), Member: logKey, }).Err() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to index global log", }) } return c.JSON(fiber.Map{ "status": true, "message": "Rider periodic log stored", }) } func GetRiderPeriodicLogs(c *fiber.Ctx) error { ctx := context.Background() userID := c.Query("userid") var keys []string var err error if userID != "" { // ✅ USER specific (latest only) zsetKey := fmt.Sprintf("rider_periodic_logs:%s", userID) keys, err = db.Rdb.ZRevRange(ctx, zsetKey, 0, 0).Result() } else { // ✅ GLOBAL (latest only) keys, err = db.Rdb.ZRevRange(ctx, "rider_periodic_logs_all", 0, 0).Result() } if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to fetch logs", }) } if len(keys) == 0 { return c.JSON(fiber.Map{ "code": 200, "data": []interface{}{}, "message": "No logs found", "status": true, }) } // 🔥 Get only one value val, err := db.Rdb.Get(ctx, keys[0]).Result() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to fetch log data", }) } var log map[string]interface{} json.Unmarshal([]byte(val), &log) return c.JSON(fiber.Map{ "code": 200, "data": log, "message": "Success", "status": true, }) } func CreateRiderStatus(c *fiber.Ctx) error { ctx := context.Background() var status models.RiderStatus if err := c.BodyParser(&status); err != nil { return c.Status(400).JSON(fiber.Map{ "status": false, "message": "Invalid request body", }) } if status.UserID == 0 || status.Status == "" { return c.Status(400).JSON(fiber.Map{ "status": false, "message": "userid and status required", }) } key := fmt.Sprintf("rider_status:%d", status.UserID) data, _ := json.Marshal(status) // ✅ Store latest status err := db.Rdb.Set(ctx, key, data, 0).Err() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to store status", }) } // 🔥 ADD THIS (MISSING PART) err = db.Rdb.ZAdd(ctx, "rider_status_all", redis.Z{ Score: float64(time.Now().Unix()), Member: key, }).Err() if err != nil { return c.Status(500).JSON(fiber.Map{ "status": false, "message": "Failed to index status", }) } return c.JSON(fiber.Map{ "status": true, "message": "Rider status updated", }) } func GetRiderStatus(c *fiber.Ctx) error { ctx := context.Background() userIDStr := c.Query("userid") pageStr := c.Query("page") pageSizeStr := c.Query("pagesize") // 🔹 CASE 1: Single user if userIDStr != "" { key := fmt.Sprintf("rider_status:%s", userIDStr) val, err := db.Rdb.Get(ctx, key).Result() if err != nil { return c.Status(http.StatusNotFound).JSON(fiber.Map{ "status": false, "code": http.StatusNotFound, "message": "Status not found", "data": []interface{}{}, }) } var data map[string]interface{} json.Unmarshal([]byte(val), &data) return c.Status(http.StatusOK).JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Success", "data": data, }) } // 🔹 CASE 2: ALL users (with optional pagination) page, _ := strconv.Atoi(pageStr) pageSize, _ := strconv.Atoi(pageSizeStr) var start, end int64 if page > 0 && pageSize > 0 { offset := (page - 1) * pageSize start = int64(offset) end = int64(offset + pageSize - 1) } else { start = 0 end = -1 // return ALL } // 🔥 Fetch all status keys from global ZSET keys, err := db.Rdb.ZRevRange(ctx, "rider_status_all", start, end).Result() if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to fetch statuses", "data": []interface{}{}, }) } // 🔥 Fast fetch using MGET values, err := db.Rdb.MGet(ctx, keys...).Result() if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "status": false, "code": http.StatusInternalServerError, "message": "Failed to fetch data", "data": []interface{}{}, }) } var result []map[string]interface{} for _, val := range values { if val == nil { continue } var item map[string]interface{} json.Unmarshal([]byte(val.(string)), &item) result = append(result, item) } return c.Status(http.StatusOK).JSON(fiber.Map{ "status": true, "code": http.StatusOK, "message": "Success", "data": result, }) }