package controllers import ( "context" "nearle/domain" "nearle/models" "nearle/utils" "net/http" "sync" firebase "firebase.google.com/go" "firebase.google.com/go/messaging" "github.com/gofiber/fiber/v2" "google.golang.org/api/option" ) var ( fcmApp *firebase.App fcmClient *messaging.Client fcmOnce sync.Once fcmInitErr error ) // getFCMClient handles the singleton initialization of Firebase func getFCMClient() (*messaging.Client, error) { fcmOnce.Do(func() { ctx := context.Background() opt := option.WithCredentialsFile("nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json") app, err := firebase.NewApp(ctx, nil, opt) if err != nil { fcmInitErr = err return } fcmApp = app client, err := app.Messaging(ctx) if err != nil { fcmInitErr = err return } fcmClient = client }) return fcmClient, fcmInitErr } func NotifyUser(c *fiber.Ctx) error { // Parse the request body var body struct { Token string `json:"token"` Notification models.FcmNotification `json:"notification"` Data map[string]string `json:"data"` } if err := c.BodyParser(&body); err != nil { utils.Error("NotifyUser parsing body error", "error", err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "code": http.StatusBadRequest, "message": err.Error(), "status": false, }) } // Use shared client instead of initializing on every request client, err := getFCMClient() if err != nil { utils.Error("NotifyUser Firebase initialization error", "error", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "code": http.StatusInternalServerError, "message": "FCM Initialization error: " + err.Error(), "status": false, }) } // Construct the message message := &messaging.Message{ Token: body.Token, Notification: &messaging.Notification{ Title: body.Notification.Title, Body: body.Notification.Body, }, Android: &messaging.AndroidConfig{ Priority: "high", Notification: &messaging.AndroidNotification{ Sound: "ring", }, }, Data: body.Data, } // Send the message _, err = client.Send(context.Background(), message) if err != nil { utils.Error("NotifyUser FCM send error", "error", err) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "code": http.StatusInternalServerError, "message": err.Error(), "status": false, }) } // Return structured response return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "FCM message sent successfully!", "status": true, }) } func NotifyUsers(c *fiber.Ctx) error { var data models.Notifications if err := c.BodyParser(&data); err != nil { utils.Error("NotifyUsers parsing body error", "error", err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "code": http.StatusBadRequest, "message": err.Error(), "status": false, }) } result := domain.SendNotifications(data) if !result { utils.Error("NotifyUsers bulk send failed") return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Failed to send notifications", "status": false, }) } return c.JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, }) }