presseportale/app/Models/ArchivePendingJob.php
Kevin Adametz 60650baeb8 archive: Auftragstabelle statt Marker, exklusiver Abschluss, Quarantäne, Restore über Payload-Identität und signierte Exporte, Schlüsselrotation (Umsetzungsaudit 29.09.: A-01 bis A-15, OPT-6, 5.1 d; 02.10.: O04, O05, O07, O08)
- A-01/A-02/A-04: archive_pending_jobs je Vorgang (ArchivePendingQueue),
  Depublikationsstand am Auftrag festgehalten, Nachholer ohne Schlüssel
  FAILURE; archive_pending_at nur noch Sammelmarker
- O07: Direktweg und Nachholer beanspruchen den Auftrag unter Zeilensperre
- O08: fehlende Snapshot-Datei → Verweis auf den image_removed-Eintrag oder
  offener Auftrag, nie ein still unvollständiger Nachweis
- A-03/A-06: Sweep verschiebt in die Quarantäne, Pause beim Restore,
  archive:reregister; Restore-Abgleich löscht Anwendungskopien erneut
- O04/O05: Restore ordnet über den Payload-Pfad zu; unsignierte Exporte nur
  mit --accept-unsigned, reregister bricht bei ungültigem/fehlendem Export ab
- A-05, A-07 bis A-15: Altbestand als möglicherweise veröffentlicht, Hold-
  Historie, Zweck per POST, Nachweise gelöschter Bilder nur mit
  compliance:manage, Anhänge auf ihrer Disk, Schlüssel-ID und
  ARCHIVE_PREVIOUS_KEYS, HMAC per HKDF, archive:verify-export
- 5.1 d: Depublikation im Meldeverfahren trägt legal_request_id

PressReleaseService.php enthält zusätzlich K-01 (Prüfung im Publish-Lock),
N19 (archive() gibt Kopien frei) und N09 (markImagesPublished nur für
auslieferbare Bilder), weil die Datei ungeteilt committet ist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:09:01 +00:00

74 lines
2.1 KiB
PHP

<?php
namespace App\Models;
use App\Enums\ArchiveReasonCategory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
/**
* Ausstehender Beweisarchiv-Eintrag (Umsetzungsaudit 29.09.2026, A-02):
* einer je Vorgang, angelegt in der fachlichen Transaktion, gelöscht mit
* dem geschriebenen Eintrag. `event` ist ein Wert aus ArchiveEvent oder
* eine der Sonderarten unten. Anlegen und Abschließen nur über
* App\Services\Archive\ArchivePendingQueue.
*
* @property int $id
* @property int $press_release_id
* @property string $event
* @property string|null $subject_type
* @property int|null $subject_id
* @property ArchiveReasonCategory|null $reason_category
* @property int|null $legal_request_id
* @property int|null $actor_id
* @property string|null $note
* @property array<string, mixed>|null $payload
* @property int $attempts
* @property string|null $last_error
* @property Carbon|null $last_attempted_at
* @property Carbon|null $created_at
*/
class ArchivePendingJob extends Model
{
/**
* Nie veröffentlichtes Bild, dessen Sofortlöschung gescheitert ist
* (A-10): kein Archiveintrag, sondern die Löschung wird nachgeholt.
*/
public const EVENT_IMAGE_PURGE = 'image_purge';
/**
* Marker `archive_pending_at` aus der Zeit vor den Aufträgen (Deploy
* vom 28.09.2026). Der Nachholer wendet darauf die bisherige Heuristik
* einmalig an.
*/
public const EVENT_LEGACY_MARKER = 'legacy_marker';
public const UPDATED_AT = null;
protected $fillable = [
'press_release_id',
'event',
'subject_type',
'subject_id',
'reason_category',
'legal_request_id',
'actor_id',
'note',
'payload',
'attempts',
'last_error',
'last_attempted_at',
'created_at',
];
protected function casts(): array
{
return [
'reason_category' => ArchiveReasonCategory::class,
'payload' => 'encrypted:array',
'attempts' => 'integer',
'last_attempted_at' => 'datetime',
'created_at' => 'datetime',
];
}
}