Files
backend_jupiter/controllers/utilsController.go
2026-05-25 11:45:56 +05:30

1510 lines
40 KiB
Go

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,
})
}