Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook) and split all 44 files exceeding 500 LOC into domain-focused modules: - consent-service (Go): models, handlers, services, database splits - backend-core (Python): security_api, rbac_api, pdf_service, auth splits - admin-core (TypeScript): 5 page.tsx + sidebar extractions - pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits - voice-service (Python): enhanced_task_orchestrator split Result: 0 violations, 36 exempted (pipeline, tests, pure-data files). Go build verified clean. No behavior changes — pure structural splits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
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})
|
|
}
|