package handlers import ( "context" "net/http" "time" "github.com/breakpilot/consent-service/internal/middleware" "github.com/breakpilot/consent-service/internal/models" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // ======================================== // PUBLIC ENDPOINTS - Cookie Consent // ======================================== // GetCookieCategories returns all active cookie categories func (h *Handler) GetCookieCategories(c *gin.Context) { language := c.DefaultQuery("language", "de") ctx := context.Background() rows, err := h.db.Pool.Query(ctx, ` SELECT id, name, display_name_de, display_name_en, description_de, description_en, is_mandatory, sort_order FROM cookie_categories WHERE is_active = true ORDER BY sort_order ASC `) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch categories"}) return } defer rows.Close() var categories []map[string]interface{} for rows.Next() { var cat models.CookieCategory if err := rows.Scan(&cat.ID, &cat.Name, &cat.DisplayNameDE, &cat.DisplayNameEN, &cat.DescriptionDE, &cat.DescriptionEN, &cat.IsMandatory, &cat.SortOrder); err != nil { continue } // Return localized data displayName := cat.DisplayNameDE description := cat.DescriptionDE if language == "en" && cat.DisplayNameEN != nil { displayName = *cat.DisplayNameEN if cat.DescriptionEN != nil { description = cat.DescriptionEN } } categories = append(categories, map[string]interface{}{ "id": cat.ID, "name": cat.Name, "display_name": displayName, "description": description, "is_mandatory": cat.IsMandatory, }) } c.JSON(http.StatusOK, gin.H{"categories": categories}) } // SetCookieConsent sets cookie preferences for a user func (h *Handler) SetCookieConsent(c *gin.Context) { var req models.CookieConsentRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) return } userID, err := middleware.GetUserID(c) if err != nil || userID == uuid.Nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"}) return } ctx := context.Background() ipAddress := middleware.GetClientIP(c) userAgent := middleware.GetUserAgent(c) // Process each category for _, cat := range req.Categories { categoryID, err := uuid.Parse(cat.CategoryID) if err != nil { continue } _, err = h.db.Pool.Exec(ctx, ` INSERT INTO cookie_consents (user_id, category_id, consented) VALUES ($1, $2, $3) ON CONFLICT (user_id, category_id) DO UPDATE SET consented = $3, updated_at = NOW() `, userID, categoryID, cat.Consented) if err != nil { continue } } // Log to audit trail h.logAudit(ctx, &userID, "cookie_consent_updated", "cookie", nil, nil, ipAddress, userAgent) c.JSON(http.StatusOK, gin.H{"message": "Cookie preferences saved"}) } // GetMyCookieConsent returns cookie preferences for the current user func (h *Handler) GetMyCookieConsent(c *gin.Context) { userID, err := middleware.GetUserID(c) if err != nil || userID == uuid.Nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"}) return } ctx := context.Background() rows, err := h.db.Pool.Query(ctx, ` SELECT cc.category_id, cc.consented, cc.updated_at, cat.name, cat.display_name_de, cat.is_mandatory FROM cookie_consents cc JOIN cookie_categories cat ON cc.category_id = cat.id WHERE cc.user_id = $1 `, userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch preferences"}) return } defer rows.Close() var consents []map[string]interface{} for rows.Next() { var ( categoryID uuid.UUID consented bool updatedAt time.Time name string displayName string isMandatory bool ) if err := rows.Scan(&categoryID, &consented, &updatedAt, &name, &displayName, &isMandatory); err != nil { continue } consents = append(consents, map[string]interface{}{ "category_id": categoryID, "name": name, "display_name": displayName, "consented": consented, "is_mandatory": isMandatory, "updated_at": updatedAt, }) } c.JSON(http.StatusOK, gin.H{"cookie_consents": consents}) }