package store import ( "context" "errors" "fmt" "time" "github.com/jackc/pgx/v5" ) // Asset ist eine zu einem Beitrag hochgeladene Datei (aktuell: das // Standbild/der Screenshot). Append-only wie extraction/finding/ // evidence_package — ein hochgeladenes Beweisstück wird nicht // nachträglich ausgetauscht, siehe Migration. type Asset struct { ID string SubmissionID string Kind string Path string SHA256 string CreatedAt time.Time } // CreateAsset speichert ein Asset. sha256Hex ist der Hex-kodierte // SHA-256-Digest der Datei (siehe evidence.HashBytes) — dieselbe Form, // in der evidence_package.SHA256 seinen Hash speichert. func (s *Store) CreateAsset(ctx context.Context, submissionID, kind, path, sha256Hex string) (Asset, error) { var a Asset err := s.Pool.QueryRow(ctx, ` INSERT INTO asset (submission_id, kind, path, sha256) VALUES ($1, $2, $3, $4) RETURNING id, submission_id, kind, path, sha256, created_at `, submissionID, kind, path, sha256Hex).Scan( &a.ID, &a.SubmissionID, &a.Kind, &a.Path, &a.SHA256, &a.CreatedAt, ) if err != nil { return Asset{}, fmt.Errorf("store: create asset: %w", err) } return a, nil } // GetLatestAssetForSubmission liefert das zuletzt hochgeladene Asset // eines Beitrags. Liefert ErrNotFound, wenn kein Asset hochgeladen wurde // — das ist der Normalfall (ein Standbild ist optional), kein Fehler, // den Aufrufer wie einen echten Datenbankfehler behandeln sollten. func (s *Store) GetLatestAssetForSubmission(ctx context.Context, submissionID string) (Asset, error) { var a Asset err := s.Pool.QueryRow(ctx, ` SELECT id, submission_id, kind, path, sha256, created_at FROM asset WHERE submission_id = $1 ORDER BY created_at DESC LIMIT 1 `, submissionID).Scan( &a.ID, &a.SubmissionID, &a.Kind, &a.Path, &a.SHA256, &a.CreatedAt, ) if errors.Is(err, pgx.ErrNoRows) { return Asset{}, ErrNotFound } if err != nil { return Asset{}, fmt.Errorf("store: get latest asset: %w", err) } return a, nil }