package handlers import ( "encoding/json" "gestion/db" "log" "net/http" "github.com/gin-gonic/gin" ) // RegisterPushToken enregistre le push token Expo d'un client // POST /api/v1/push-token func RegisterPushToken(c *gin.Context) { clientID := c.GetInt("client_id") if clientID == 0 { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } var req struct { PushToken string `json:"push_token" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "push_token requis"}) return } database := c.MustGet("database").(*db.Database) if err := database.SaveClientPushToken(clientID, req.PushToken); err != nil { log.Printf("❌ [PUSH_TOKEN] Erreur sauvegarde token pour client %d: %v", clientID, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur enregistrement push token"}) return } log.Printf("✅ [PUSH_TOKEN] Token enregistré pour client %d", clientID) c.JSON(http.StatusOK, gin.H{"success": true}) } // UnregisterPushToken supprime le push token d'un client (au logout) // DELETE /api/v1/push-token func UnregisterPushToken(c *gin.Context) { clientID := c.GetInt("client_id") if clientID == 0 { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } database := c.MustGet("database").(*db.Database) if err := database.DeleteClientPushToken(clientID); err != nil { log.Printf("❌ [PUSH_TOKEN] Erreur suppression token pour client %d: %v", clientID, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur suppression push token"}) return } log.Printf("✅ [PUSH_TOKEN] Token supprimé pour client %d", clientID) c.JSON(http.StatusOK, gin.H{"success": true}) } // GetClientNotifications retourne les notifications du client connecté // GET /api/v1/notifications func GetClientNotifications(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } notifKey := "notifications:" + username // Récupérer toutes les notifications (max 50) results, err := db.Redis.LRange(db.RedisCtx, notifKey, 0, 49).Result() if err != nil { log.Printf("❌ [GET_NOTIFICATIONS] Erreur Redis: %v", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur récupération notifications"}) return } type Notification struct { CommandID int `json:"command_id"` Type string `json:"type"` Message string `json:"message"` CreatedAt string `json:"created_at"` Read bool `json:"read"` } notifications := make([]Notification, 0, len(results)) unreadCount := 0 for _, raw := range results { var n Notification if err := json.Unmarshal([]byte(raw), &n); err != nil { continue } notifications = append(notifications, n) if !n.Read { unreadCount++ } } log.Printf("✅ [GET_NOTIFICATIONS] %d notifications pour %s (%d non lues)", len(notifications), username, unreadCount) c.JSON(http.StatusOK, gin.H{ "notifications": notifications, "unread_count": unreadCount, "total": len(notifications), }) } // RegisterLivreurPushToken enregistre le push token Expo d'un livreur // POST /api/v1/livreur/push-token func RegisterLivreurPushToken(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } var req struct { PushToken string `json:"push_token" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "push_token requis"}) return } database := c.MustGet("database").(*db.Database) if err := database.SaveUserPushToken(username, req.PushToken); err != nil { log.Printf("❌ [LIVREUR_PUSH_TOKEN] Erreur sauvegarde token pour %s: %v", username, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur enregistrement push token"}) return } log.Printf("✅ [LIVREUR_PUSH_TOKEN] Token enregistré pour livreur %s", username) c.JSON(http.StatusOK, gin.H{"success": true}) } // UnregisterLivreurPushToken supprime le push token d'un livreur (au logout) // DELETE /api/v1/livreur/push-token func UnregisterLivreurPushToken(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } database := c.MustGet("database").(*db.Database) if err := database.DeleteUserPushToken(username); err != nil { log.Printf("❌ [LIVREUR_PUSH_TOKEN] Erreur suppression token pour %s: %v", username, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur suppression push token"}) return } log.Printf("✅ [LIVREUR_PUSH_TOKEN] Token supprimé pour livreur %s", username) c.JSON(http.StatusOK, gin.H{"success": true}) } // GetLivreurNotifications retourne les notifications du livreur connecté // GET /api/v1/livreur/notifications func GetLivreurNotifications(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } notifKey := "notifications:" + username results, err := db.Redis.LRange(db.RedisCtx, notifKey, 0, 49).Result() if err != nil { log.Printf("❌ [LIVREUR_NOTIFICATIONS] Erreur Redis: %v", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur récupération notifications"}) return } type Notification struct { CommandID int `json:"command_id"` Type string `json:"type"` Message string `json:"message"` CreatedAt string `json:"created_at"` Read bool `json:"read"` } notifications := make([]Notification, 0, len(results)) unreadCount := 0 for _, raw := range results { var n Notification if err := json.Unmarshal([]byte(raw), &n); err != nil { continue } notifications = append(notifications, n) if !n.Read { unreadCount++ } } log.Printf("✅ [LIVREUR_NOTIFICATIONS] %d notifications pour %s (%d non lues)", len(notifications), username, unreadCount) c.JSON(http.StatusOK, gin.H{ "notifications": notifications, "unread_count": unreadCount, "total": len(notifications), }) } // MarkLivreurNotificationsRead marque toutes les notifications du livreur comme lues // POST /api/v1/livreur/notifications/read func MarkLivreurNotificationsRead(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } notifKey := "notifications:" + username results, err := db.Redis.LRange(db.RedisCtx, notifKey, 0, -1).Result() if err != nil { log.Printf("❌ [LIVREUR_MARK_READ] Erreur Redis LRange: %v", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lecture notifications"}) return } markedCount := 0 for i, raw := range results { var n map[string]interface{} if err := json.Unmarshal([]byte(raw), &n); err != nil { continue } if read, ok := n["read"].(bool); ok && read { continue } n["read"] = true updated, _ := json.Marshal(n) db.Redis.LSet(db.RedisCtx, notifKey, int64(i), string(updated)) markedCount++ } log.Printf("✅ [LIVREUR_MARK_READ] %d notifications marquées lues pour %s", markedCount, username) c.JSON(http.StatusOK, gin.H{ "success": true, "marked_count": markedCount, }) } // RegisterAdminPushToken enregistre le push token d'un admin // POST /api/v2/admin/protected/push-token func RegisterAdminPushToken(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } var req struct { PushToken string `json:"push_token" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "push_token requis"}) return } database := c.MustGet("database").(*db.Database) if err := database.SaveUserPushToken(username, req.PushToken); err != nil { log.Printf("❌ [ADMIN_PUSH_TOKEN] Erreur sauvegarde pour %s: %v", username, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur enregistrement push token"}) return } log.Printf("✅ [ADMIN_PUSH_TOKEN] Token enregistré pour admin %s", username) c.JSON(http.StatusOK, gin.H{"success": true}) } // UnregisterAdminPushToken supprime le push token d'un admin (au logout) // DELETE /api/v2/admin/protected/push-token func UnregisterAdminPushToken(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } database := c.MustGet("database").(*db.Database) if err := database.DeleteUserPushToken(username); err != nil { log.Printf("❌ [ADMIN_PUSH_TOKEN] Erreur suppression pour %s: %v", username, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur suppression push token"}) return } log.Printf("✅ [ADMIN_PUSH_TOKEN] Token supprimé pour admin %s", username) c.JSON(http.StatusOK, gin.H{"success": true}) } // RegisterCabinePushToken enregistre le push token d'un agent cabine // POST /api/v1/cabine/push-token func RegisterCabinePushToken(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } var req struct { PushToken string `json:"push_token" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "push_token requis"}) return } database := c.MustGet("database").(*db.Database) if err := database.SaveUserPushToken(username, req.PushToken); err != nil { log.Printf("❌ [CABINE_PUSH_TOKEN] Erreur sauvegarde pour %s: %v", username, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur enregistrement push token"}) return } log.Printf("✅ [CABINE_PUSH_TOKEN] Token enregistré pour cabine %s", username) c.JSON(http.StatusOK, gin.H{"success": true}) } // UnregisterCabinePushToken supprime le push token d'un agent cabine (au logout) // DELETE /api/v1/cabine/push-token func UnregisterCabinePushToken(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } database := c.MustGet("database").(*db.Database) if err := database.DeleteUserPushToken(username); err != nil { log.Printf("❌ [CABINE_PUSH_TOKEN] Erreur suppression pour %s: %v", username, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur suppression push token"}) return } log.Printf("✅ [CABINE_PUSH_TOKEN] Token supprimé pour cabine %s", username) c.JSON(http.StatusOK, gin.H{"success": true}) } // MarkNotificationsRead marque toutes les notifications comme lues // POST /api/v1/notifications/read func MarkNotificationsRead(c *gin.Context) { username := c.GetString("username") if username == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"}) return } notifKey := "notifications:" + username // Récupérer toutes les notifications results, err := db.Redis.LRange(db.RedisCtx, notifKey, 0, -1).Result() if err != nil { log.Printf("❌ [MARK_NOTIFICATIONS_READ] Erreur Redis LRange: %v", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lecture notifications"}) return } // Réécrire chaque notification avec read=true markedCount := 0 for i, raw := range results { var n map[string]interface{} if err := json.Unmarshal([]byte(raw), &n); err != nil { continue } if read, ok := n["read"].(bool); ok && read { continue } n["read"] = true updated, _ := json.Marshal(n) db.Redis.LSet(db.RedisCtx, notifKey, int64(i), string(updated)) markedCount++ } log.Printf("✅ [MARK_NOTIFICATIONS_READ] %d notifications marquées lues pour %s", markedCount, username) c.JSON(http.StatusOK, gin.H{ "success": true, "marked_count": markedCount, }) }