// Package dossier erzeugt das Nachweis-Dossier (PDF) aus Submission, // Findings, Verantwortungsmatrix und Beweiskette. Die Aufbereitung des // Inhalts (BuildContent) ist von der eigentlichen PDF-Zeichnung // (Render) getrennt, damit die fachliche Logik — was steht wo im // Dossier, in welcher Form — ohne PDF-Parsing testbar ist. package dossier import ( "encoding/hex" "fmt" "time" "github.com/netcell-it/deklarix/internal/evidence" "github.com/netcell-it/deklarix/internal/rules" ) // Disclaimer steht auf jedem erzeugten Dossier. Siehe CLAUDE.md, // Leitplanken: Deklarix ist ein Werkzeug, keine Rechtsberatung. const Disclaimer = "Dieses Dossier dokumentiert die durchgeführte Prüfung. " + "Es ist keine Rechtsberatung und ersetzt keine anwaltliche Prüfung im Einzelfall." // Submission sind die Basisdaten des geprüften Beitrags. type Submission struct { Platform string PostType string Caption string CreatedAt time.Time } // Participant ist ein Beteiligter aus der Verantwortungsmatrix. type Participant struct { Role string Name string Vorgegeben bool Freigegeben bool } // Data ist die Eingabe für Generate/BuildContent — alles, was ein // Dossier für einen Beitrag braucht. type Data struct { Submission Submission Facts rules.Facts Findings []rules.Finding Participants []Participant AssetHash []byte MetadataHash []byte TimestampToken []byte GeneratedAt time.Time } // FindingRow ist die dossier-taugliche Aufbereitung eines rules.Finding. type FindingRow struct { RuleID string Version int Severity string Title string Fix string Sources []string } // ParticipantRow ist die dossier-taugliche Aufbereitung eines Participant. type ParticipantRow struct { Role string Name string Vorgegeben bool Freigegeben bool } // Content ist die fertig aufbereitete, PDF-unabhängige Darstellung // eines Dossiers. type Content struct { Title string Platform string PostType string Caption string SubmittedAt time.Time GeneratedAt time.Time DisclosurePresent bool DisclosureWording string DisclosureBeforeCut bool Findings []FindingRow Participants []ParticipantRow AssetHashHex string MetadataHashHex string TimestampedAt time.Time Disclaimer string } // BuildContent bereitet Data zu Content auf. Fehlt eine Pflichtangabe // (Plattform, Zeitstempel-Token), wird ein Fehler geliefert statt ein // Dossier mit stillschweigend leeren Beweisfeldern zu erzeugen. func BuildContent(data Data) (Content, error) { if data.Submission.Platform == "" { return Content{}, fmt.Errorf("dossier: submission.platform fehlt") } if len(data.TimestampToken) == 0 { return Content{}, fmt.Errorf("dossier: timestamp token fehlt") } // AssetHash ist optional: nicht jede Prüfung hat ein hochgeladenes // Bild/Video (aktuell reine Caption-Prüfungen). Ein erfundener // Platzhalter-Hash für ein nicht existierendes Asset wäre selbst ein // Integritätsproblem — also zeigt das Dossier stattdessen "kein // Asset hinterlegt" an, siehe Render. if len(data.MetadataHash) == 0 { return Content{}, fmt.Errorf("dossier: metadata hash fehlt") } timestampedAt, err := evidence.TimestampTime(data.TimestampToken) if err != nil { return Content{}, fmt.Errorf("dossier: timestamp token: %w", err) } findings := make([]FindingRow, len(data.Findings)) for i, f := range data.Findings { findings[i] = FindingRow{ RuleID: f.RuleID, Version: f.RuleVersion, Severity: string(f.Severity), Title: f.Title, Fix: f.Fix, Sources: f.Sources, } } participants := make([]ParticipantRow, len(data.Participants)) for i, p := range data.Participants { participants[i] = ParticipantRow{ Role: p.Role, Name: p.Name, Vorgegeben: p.Vorgegeben, Freigegeben: p.Freigegeben, } } return Content{ Title: "Nachweis-Dossier", Platform: data.Submission.Platform, PostType: data.Submission.PostType, Caption: data.Submission.Caption, SubmittedAt: data.Submission.CreatedAt, GeneratedAt: data.GeneratedAt, DisclosurePresent: data.Facts.DisclosurePresent, DisclosureWording: data.Facts.DisclosureWording, DisclosureBeforeCut: data.Facts.DisclosureBeforeCut, Findings: findings, Participants: participants, AssetHashHex: hex.EncodeToString(data.AssetHash), MetadataHashHex: hex.EncodeToString(data.MetadataHash), TimestampedAt: timestampedAt, Disclaimer: Disclaimer, }, nil }