Files
breakpilot-core/consent-service/internal/handlers/gdpr.go
Benjamin Admin 92c86ec6ba [split-required] [guardrail-change] Enforce 500 LOC budget across all services
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>
2026-04-27 00:09:30 +02:00

169 lines
4.7 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"
)
// ========================================
// GDPR / DATA SUBJECT RIGHTS
// ========================================
// GetMyData returns all data we have about the user
func (h *Handler) GetMyData(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()
ipAddress := middleware.GetClientIP(c)
userAgent := middleware.GetUserAgent(c)
// Get user info
var user models.User
err = h.db.Pool.QueryRow(ctx, `
SELECT id, external_id, email, role, created_at, updated_at
FROM users WHERE id = $1
`, userID).Scan(&user.ID, &user.ExternalID, &user.Email, &user.Role, &user.CreatedAt, &user.UpdatedAt)
// Get consents
consentRows, _ := h.db.Pool.Query(ctx, `
SELECT uc.consented, uc.consented_at, ld.type, ld.name, dv.version
FROM user_consents uc
JOIN document_versions dv ON uc.document_version_id = dv.id
JOIN legal_documents ld ON dv.document_id = ld.id
WHERE uc.user_id = $1
`, userID)
defer consentRows.Close()
var consents []map[string]interface{}
for consentRows.Next() {
var consented bool
var consentedAt time.Time
var docType, docName, version string
consentRows.Scan(&consented, &consentedAt, &docType, &docName, &version)
consents = append(consents, map[string]interface{}{
"document_type": docType,
"document_name": docName,
"version": version,
"consented": consented,
"consented_at": consentedAt,
})
}
// Get cookie consents
cookieRows, _ := h.db.Pool.Query(ctx, `
SELECT cat.name, cc.consented, cc.updated_at
FROM cookie_consents cc
JOIN cookie_categories cat ON cc.category_id = cat.id
WHERE cc.user_id = $1
`, userID)
defer cookieRows.Close()
var cookieConsents []map[string]interface{}
for cookieRows.Next() {
var name string
var consented bool
var updatedAt time.Time
cookieRows.Scan(&name, &consented, &updatedAt)
cookieConsents = append(cookieConsents, map[string]interface{}{
"category": name,
"consented": consented,
"updated_at": updatedAt,
})
}
// Log data access
h.logAudit(ctx, &userID, "data_access", "user", &userID, nil, ipAddress, userAgent)
c.JSON(http.StatusOK, gin.H{
"user": map[string]interface{}{
"id": user.ID,
"email": user.Email,
"created_at": user.CreatedAt,
},
"consents": consents,
"cookie_consents": cookieConsents,
"exported_at": time.Now(),
})
}
// RequestDataExport creates a data export request
func (h *Handler) RequestDataExport(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()
ipAddress := middleware.GetClientIP(c)
userAgent := middleware.GetUserAgent(c)
var requestID uuid.UUID
err = h.db.Pool.QueryRow(ctx, `
INSERT INTO data_export_requests (user_id, status)
VALUES ($1, 'pending')
RETURNING id
`, userID).Scan(&requestID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create export request"})
return
}
// Log to audit trail
h.logAudit(ctx, &userID, "data_export_requested", "export_request", &requestID, nil, ipAddress, userAgent)
c.JSON(http.StatusAccepted, gin.H{
"message": "Export request created. You will be notified when ready.",
"request_id": requestID,
})
}
// RequestDataDeletion creates a data deletion request
func (h *Handler) RequestDataDeletion(c *gin.Context) {
userID, err := middleware.GetUserID(c)
if err != nil || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"})
return
}
var req struct {
Reason string `json:"reason"`
}
c.ShouldBindJSON(&req)
ctx := context.Background()
ipAddress := middleware.GetClientIP(c)
userAgent := middleware.GetUserAgent(c)
var requestID uuid.UUID
err = h.db.Pool.QueryRow(ctx, `
INSERT INTO data_deletion_requests (user_id, status, reason)
VALUES ($1, 'pending', $2)
RETURNING id
`, userID, req.Reason).Scan(&requestID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create deletion request"})
return
}
// Log to audit trail
h.logAudit(ctx, &userID, "data_deletion_requested", "deletion_request", &requestID, nil, ipAddress, userAgent)
c.JSON(http.StatusAccepted, gin.H{
"message": "Deletion request created. We will process your request within 30 days.",
"request_id": requestID,
})
}