Initial commit: breakpilot-core - Shared Infrastructure
Docker Compose with 24+ services: - PostgreSQL (PostGIS), Valkey, MinIO, Qdrant - Vault (PKI/TLS), Nginx (Reverse Proxy) - Backend Core API, Consent Service, Billing Service - RAG Service, Embedding Service - Gitea, Woodpecker CI/CD - Night Scheduler, Health Aggregator - Jitsi (Web/XMPP/JVB/Jicofo), Mailpit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
948
consent-service/internal/handlers/dsr_handlers.go
Normal file
948
consent-service/internal/handlers/dsr_handlers.go
Normal file
@@ -0,0 +1,948 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/breakpilot/consent-service/internal/middleware"
|
||||
"github.com/breakpilot/consent-service/internal/models"
|
||||
"github.com/breakpilot/consent-service/internal/services"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// DSRHandler handles Data Subject Request HTTP endpoints
|
||||
type DSRHandler struct {
|
||||
dsrService *services.DSRService
|
||||
}
|
||||
|
||||
// NewDSRHandler creates a new DSR handler
|
||||
func NewDSRHandler(dsrService *services.DSRService) *DSRHandler {
|
||||
return &DSRHandler{
|
||||
dsrService: dsrService,
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// USER ENDPOINTS
|
||||
// ========================================
|
||||
|
||||
// CreateDSR creates a new data subject request (user-facing)
|
||||
func (h *DSRHandler) CreateDSR(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 models.CreateDSRRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user email if not provided
|
||||
if req.RequesterEmail == "" {
|
||||
var email string
|
||||
ctx := context.Background()
|
||||
h.dsrService.GetPool().QueryRow(ctx, "SELECT email FROM users WHERE id = $1", userID).Scan(&email)
|
||||
req.RequesterEmail = email
|
||||
}
|
||||
|
||||
// Set source as API
|
||||
req.Source = "api"
|
||||
|
||||
dsr, err := h.dsrService.CreateRequest(c.Request.Context(), req, &userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"message": "Ihre Anfrage wurde erfolgreich eingereicht",
|
||||
"request_number": dsr.RequestNumber,
|
||||
"dsr": dsr,
|
||||
})
|
||||
}
|
||||
|
||||
// GetMyDSRs returns DSRs for the current user
|
||||
func (h *DSRHandler) GetMyDSRs(c *gin.Context) {
|
||||
userID, err := middleware.GetUserID(c)
|
||||
if err != nil || userID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrs, err := h.dsrService.ListByUser(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch requests"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"requests": dsrs})
|
||||
}
|
||||
|
||||
// GetMyDSR returns a specific DSR for the current user
|
||||
func (h *DSRHandler) GetMyDSR(c *gin.Context) {
|
||||
userID, err := middleware.GetUserID(c)
|
||||
if err != nil || userID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
dsr, err := h.dsrService.GetByID(c.Request.Context(), dsrID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Request not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Verify ownership
|
||||
if dsr.UserID == nil || *dsr.UserID != userID {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, dsr)
|
||||
}
|
||||
|
||||
// CancelMyDSR cancels a user's own DSR
|
||||
func (h *DSRHandler) CancelMyDSR(c *gin.Context) {
|
||||
userID, err := middleware.GetUserID(c)
|
||||
if err != nil || userID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.CancelRequest(c.Request.Context(), dsrID, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Anfrage wurde storniert"})
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// ADMIN ENDPOINTS
|
||||
// ========================================
|
||||
|
||||
// AdminListDSR returns all DSRs with filters (admin only)
|
||||
func (h *DSRHandler) AdminListDSR(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse pagination
|
||||
limit := 20
|
||||
offset := 0
|
||||
if l := c.Query("limit"); l != "" {
|
||||
if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 {
|
||||
limit = parsed
|
||||
}
|
||||
}
|
||||
if o := c.Query("offset"); o != "" {
|
||||
if parsed, err := strconv.Atoi(o); err == nil && parsed >= 0 {
|
||||
offset = parsed
|
||||
}
|
||||
}
|
||||
|
||||
// Parse filters
|
||||
filters := models.DSRListFilters{}
|
||||
if status := c.Query("status"); status != "" {
|
||||
filters.Status = &status
|
||||
}
|
||||
if reqType := c.Query("request_type"); reqType != "" {
|
||||
filters.RequestType = &reqType
|
||||
}
|
||||
if assignedTo := c.Query("assigned_to"); assignedTo != "" {
|
||||
filters.AssignedTo = &assignedTo
|
||||
}
|
||||
if priority := c.Query("priority"); priority != "" {
|
||||
filters.Priority = &priority
|
||||
}
|
||||
if c.Query("overdue_only") == "true" {
|
||||
filters.OverdueOnly = true
|
||||
}
|
||||
if search := c.Query("search"); search != "" {
|
||||
filters.Search = &search
|
||||
}
|
||||
if from := c.Query("from_date"); from != "" {
|
||||
if t, err := time.Parse("2006-01-02", from); err == nil {
|
||||
filters.FromDate = &t
|
||||
}
|
||||
}
|
||||
if to := c.Query("to_date"); to != "" {
|
||||
if t, err := time.Parse("2006-01-02", to); err == nil {
|
||||
filters.ToDate = &t
|
||||
}
|
||||
}
|
||||
|
||||
dsrs, total, err := h.dsrService.List(c.Request.Context(), filters, limit, offset)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch requests"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"requests": dsrs,
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
})
|
||||
}
|
||||
|
||||
// AdminGetDSR returns a specific DSR (admin only)
|
||||
func (h *DSRHandler) AdminGetDSR(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
dsr, err := h.dsrService.GetByID(c.Request.Context(), dsrID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Request not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, dsr)
|
||||
}
|
||||
|
||||
// AdminCreateDSR creates a DSR manually (admin only)
|
||||
func (h *DSRHandler) AdminCreateDSR(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req models.CreateDSRRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Set source as admin_panel
|
||||
if req.Source == "" {
|
||||
req.Source = "admin_panel"
|
||||
}
|
||||
|
||||
dsr, err := h.dsrService.CreateRequest(c.Request.Context(), req, &userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"message": "Anfrage wurde erstellt",
|
||||
"request_number": dsr.RequestNumber,
|
||||
"dsr": dsr,
|
||||
})
|
||||
}
|
||||
|
||||
// AdminUpdateDSR updates a DSR (admin only)
|
||||
func (h *DSRHandler) AdminUpdateDSR(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req models.UpdateDSRRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// Update status if provided
|
||||
if req.Status != nil {
|
||||
err = h.dsrService.UpdateStatus(ctx, dsrID, models.DSRStatus(*req.Status), "", &userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update processing notes
|
||||
if req.ProcessingNotes != nil {
|
||||
h.dsrService.GetPool().Exec(ctx, `
|
||||
UPDATE data_subject_requests SET processing_notes = $1, updated_at = NOW() WHERE id = $2
|
||||
`, *req.ProcessingNotes, dsrID)
|
||||
}
|
||||
|
||||
// Update priority
|
||||
if req.Priority != nil {
|
||||
h.dsrService.GetPool().Exec(ctx, `
|
||||
UPDATE data_subject_requests SET priority = $1, updated_at = NOW() WHERE id = $2
|
||||
`, *req.Priority, dsrID)
|
||||
}
|
||||
|
||||
// Get updated DSR
|
||||
dsr, _ := h.dsrService.GetByID(ctx, dsrID)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Anfrage wurde aktualisiert",
|
||||
"dsr": dsr,
|
||||
})
|
||||
}
|
||||
|
||||
// AdminGetDSRStats returns dashboard statistics
|
||||
func (h *DSRHandler) AdminGetDSRStats(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
stats, err := h.dsrService.GetDashboardStats(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch statistics"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
|
||||
// AdminVerifyIdentity verifies the identity of a requester
|
||||
func (h *DSRHandler) AdminVerifyIdentity(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req models.VerifyDSRIdentityRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.VerifyIdentity(c.Request.Context(), dsrID, req.Method, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Identität wurde verifiziert"})
|
||||
}
|
||||
|
||||
// AdminAssignDSR assigns a DSR to a user
|
||||
func (h *DSRHandler) AdminAssignDSR(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req struct {
|
||||
AssigneeID string `json:"assignee_id" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
assigneeID, err := uuid.Parse(req.AssigneeID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assignee ID"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.AssignRequest(c.Request.Context(), dsrID, assigneeID, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Anfrage wurde zugewiesen"})
|
||||
}
|
||||
|
||||
// AdminExtendDSRDeadline extends the deadline for a DSR
|
||||
func (h *DSRHandler) AdminExtendDSRDeadline(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req models.ExtendDSRDeadlineRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.ExtendDeadline(c.Request.Context(), dsrID, req.Reason, req.Days, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Frist wurde verlängert"})
|
||||
}
|
||||
|
||||
// AdminCompleteDSR marks a DSR as completed
|
||||
func (h *DSRHandler) AdminCompleteDSR(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req models.CompleteDSRRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.CompleteRequest(c.Request.Context(), dsrID, req.ResultSummary, req.ResultData, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Anfrage wurde abgeschlossen"})
|
||||
}
|
||||
|
||||
// AdminRejectDSR rejects a DSR
|
||||
func (h *DSRHandler) AdminRejectDSR(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req models.RejectDSRRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.RejectRequest(c.Request.Context(), dsrID, req.Reason, req.LegalBasis, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Anfrage wurde abgelehnt"})
|
||||
}
|
||||
|
||||
// AdminGetDSRHistory returns the status history for a DSR
|
||||
func (h *DSRHandler) AdminGetDSRHistory(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
history, err := h.dsrService.GetStatusHistory(c.Request.Context(), dsrID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch history"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"history": history})
|
||||
}
|
||||
|
||||
// AdminGetDSRCommunications returns communications for a DSR
|
||||
func (h *DSRHandler) AdminGetDSRCommunications(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
comms, err := h.dsrService.GetCommunications(c.Request.Context(), dsrID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch communications"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"communications": comms})
|
||||
}
|
||||
|
||||
// AdminSendDSRCommunication sends a communication for a DSR
|
||||
func (h *DSRHandler) AdminSendDSRCommunication(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req models.SendDSRCommunicationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.SendCommunication(c.Request.Context(), dsrID, req, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Kommunikation wurde gesendet"})
|
||||
}
|
||||
|
||||
// AdminUpdateDSRStatus updates the status of a DSR
|
||||
func (h *DSRHandler) AdminUpdateDSRStatus(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.UpdateStatus(c.Request.Context(), dsrID, models.DSRStatus(req.Status), req.Comment, &userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Status wurde aktualisiert"})
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// EXCEPTION CHECKS (Art. 17)
|
||||
// ========================================
|
||||
|
||||
// AdminGetExceptionChecks returns exception checks for an erasure DSR
|
||||
func (h *DSRHandler) AdminGetExceptionChecks(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
checks, err := h.dsrService.GetExceptionChecks(c.Request.Context(), dsrID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch exception checks"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"exception_checks": checks})
|
||||
}
|
||||
|
||||
// AdminInitExceptionChecks initializes exception checks for an erasure DSR
|
||||
func (h *DSRHandler) AdminInitExceptionChecks(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
dsrID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request ID"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.InitErasureExceptionChecks(c.Request.Context(), dsrID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to initialize exception checks"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Ausnahmeprüfungen wurden initialisiert"})
|
||||
}
|
||||
|
||||
// AdminUpdateExceptionCheck updates a single exception check
|
||||
func (h *DSRHandler) AdminUpdateExceptionCheck(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
checkID, err := uuid.Parse(c.Param("checkId"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid check ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req struct {
|
||||
Applies bool `json:"applies"`
|
||||
Notes *string `json:"notes"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.dsrService.UpdateExceptionCheck(c.Request.Context(), checkID, req.Applies, req.Notes, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update exception check"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Ausnahmeprüfung wurde aktualisiert"})
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// TEMPLATE ENDPOINTS
|
||||
// ========================================
|
||||
|
||||
// AdminGetDSRTemplates returns all DSR templates
|
||||
func (h *DSRHandler) AdminGetDSRTemplates(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
rows, err := h.dsrService.GetPool().Query(ctx, `
|
||||
SELECT id, template_type, name, description, request_types, is_active, sort_order, created_at, updated_at
|
||||
FROM dsr_templates ORDER BY sort_order, name
|
||||
`)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch templates"})
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var templates []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id uuid.UUID
|
||||
var templateType, name string
|
||||
var description *string
|
||||
var requestTypes []byte
|
||||
var isActive bool
|
||||
var sortOrder int
|
||||
var createdAt, updatedAt time.Time
|
||||
|
||||
err := rows.Scan(&id, &templateType, &name, &description, &requestTypes, &isActive, &sortOrder, &createdAt, &updatedAt)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
templates = append(templates, map[string]interface{}{
|
||||
"id": id,
|
||||
"template_type": templateType,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"request_types": string(requestTypes),
|
||||
"is_active": isActive,
|
||||
"sort_order": sortOrder,
|
||||
"created_at": createdAt,
|
||||
"updated_at": updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"templates": templates})
|
||||
}
|
||||
|
||||
// AdminGetDSRTemplateVersions returns versions for a template
|
||||
func (h *DSRHandler) AdminGetDSRTemplateVersions(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
templateID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid template ID"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
rows, err := h.dsrService.GetPool().Query(ctx, `
|
||||
SELECT id, template_id, version, language, subject, body_html, body_text,
|
||||
status, published_at, created_by, approved_by, approved_at, created_at, updated_at
|
||||
FROM dsr_template_versions WHERE template_id = $1 ORDER BY created_at DESC
|
||||
`, templateID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch versions"})
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var versions []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id, tempID uuid.UUID
|
||||
var version, language, subject, bodyHTML, bodyText, status string
|
||||
var publishedAt, approvedAt *time.Time
|
||||
var createdBy, approvedBy *uuid.UUID
|
||||
var createdAt, updatedAt time.Time
|
||||
|
||||
err := rows.Scan(&id, &tempID, &version, &language, &subject, &bodyHTML, &bodyText,
|
||||
&status, &publishedAt, &createdBy, &approvedBy, &approvedAt, &createdAt, &updatedAt)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
versions = append(versions, map[string]interface{}{
|
||||
"id": id,
|
||||
"template_id": tempID,
|
||||
"version": version,
|
||||
"language": language,
|
||||
"subject": subject,
|
||||
"body_html": bodyHTML,
|
||||
"body_text": bodyText,
|
||||
"status": status,
|
||||
"published_at": publishedAt,
|
||||
"created_by": createdBy,
|
||||
"approved_by": approvedBy,
|
||||
"approved_at": approvedAt,
|
||||
"created_at": createdAt,
|
||||
"updated_at": updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"versions": versions})
|
||||
}
|
||||
|
||||
// AdminCreateDSRTemplateVersion creates a new template version
|
||||
func (h *DSRHandler) AdminCreateDSRTemplateVersion(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
templateID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid template ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
var req struct {
|
||||
Version string `json:"version" binding:"required"`
|
||||
Language string `json:"language"`
|
||||
Subject string `json:"subject" binding:"required"`
|
||||
BodyHTML string `json:"body_html" binding:"required"`
|
||||
BodyText string `json:"body_text"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Language == "" {
|
||||
req.Language = "de"
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
var versionID uuid.UUID
|
||||
err = h.dsrService.GetPool().QueryRow(ctx, `
|
||||
INSERT INTO dsr_template_versions (template_id, version, language, subject, body_html, body_text, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id
|
||||
`, templateID, req.Version, req.Language, req.Subject, req.BodyHTML, req.BodyText, userID).Scan(&versionID)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create version"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"message": "Version wurde erstellt",
|
||||
"id": versionID,
|
||||
})
|
||||
}
|
||||
|
||||
// AdminPublishDSRTemplateVersion publishes a template version
|
||||
func (h *DSRHandler) AdminPublishDSRTemplateVersion(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
versionID, err := uuid.Parse(c.Param("versionId"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid version ID"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := middleware.GetUserID(c)
|
||||
|
||||
ctx := c.Request.Context()
|
||||
_, err = h.dsrService.GetPool().Exec(ctx, `
|
||||
UPDATE dsr_template_versions
|
||||
SET status = 'published', published_at = NOW(), approved_by = $1, approved_at = NOW(), updated_at = NOW()
|
||||
WHERE id = $2
|
||||
`, userID, versionID)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to publish version"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Version wurde veröffentlicht"})
|
||||
}
|
||||
|
||||
// AdminGetPublishedDSRTemplates returns all published templates for selection
|
||||
func (h *DSRHandler) AdminGetPublishedDSRTemplates(c *gin.Context) {
|
||||
if !middleware.IsAdmin(c) && !middleware.IsDSB(c) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Admin or DSB access required"})
|
||||
return
|
||||
}
|
||||
|
||||
requestType := c.Query("request_type")
|
||||
language := c.DefaultQuery("language", "de")
|
||||
|
||||
ctx := c.Request.Context()
|
||||
query := `
|
||||
SELECT t.id, t.template_type, t.name, t.description,
|
||||
v.id as version_id, v.version, v.subject, v.body_html, v.body_text
|
||||
FROM dsr_templates t
|
||||
JOIN dsr_template_versions v ON t.id = v.template_id
|
||||
WHERE t.is_active = TRUE AND v.status = 'published' AND v.language = $1
|
||||
`
|
||||
args := []interface{}{language}
|
||||
|
||||
if requestType != "" {
|
||||
query += ` AND t.request_types @> $2::jsonb`
|
||||
args = append(args, `["`+requestType+`"]`)
|
||||
}
|
||||
|
||||
query += " ORDER BY t.sort_order, t.name"
|
||||
|
||||
rows, err := h.dsrService.GetPool().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch templates"})
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var templates []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var templateID, versionID uuid.UUID
|
||||
var templateType, name, version, subject, bodyHTML, bodyText string
|
||||
var description *string
|
||||
|
||||
err := rows.Scan(&templateID, &templateType, &name, &description, &versionID, &version, &subject, &bodyHTML, &bodyText)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
templates = append(templates, map[string]interface{}{
|
||||
"template_id": templateID,
|
||||
"template_type": templateType,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"version_id": versionID,
|
||||
"version": version,
|
||||
"subject": subject,
|
||||
"body_html": bodyHTML,
|
||||
"body_text": bodyText,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"templates": templates})
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// DEADLINE PROCESSING
|
||||
// ========================================
|
||||
|
||||
// ProcessDeadlines triggers deadline checking (called by scheduler)
|
||||
func (h *DSRHandler) ProcessDeadlines(c *gin.Context) {
|
||||
err := h.dsrService.ProcessDeadlines(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process deadlines"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Deadline processing completed"})
|
||||
}
|
||||
Reference in New Issue
Block a user