Files
Investidor-V2/app/Services/PartnershipRoomUploads.php
T

70 lines
4.4 KiB
PHP

<?php
namespace App\Services;
use App\Jobs\ScanDocumentForMalware;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class PartnershipRoomUploads
{
/** @return array{uploaded:bool,document_id:string,replayed:bool} */
public function save(User $actor, int $roomId, UploadedFile $file, bool $shared, ?string $clientKey): array
{
$path = $file->getRealPath();
abort_unless(is_string($path), 503);
$hash = hash_file('sha256', $path);
abort_unless(is_string($hash), 503);
$commandHash = hash('sha256', json_encode(['sha256' => $hash, 'file_name' => basename($file->getClientOriginalName()), 'shared' => $shared], JSON_THROW_ON_ERROR));
$stored = null;
try {
$result = DB::transaction(function () use ($actor, $roomId, $file, $shared, $clientKey, $hash, $commandHash, &$stored) {
$exchange = app(PartnershipExchange::class);
$context = $exchange->room($actor, $roomId);
abort_unless($exchange->owns($actor, $context['partnership']) || $exchange->owns($actor, $context['interest']), 403);
$room = DB::table('partnership_rooms')->where('id', $roomId)->lockForUpdate()->first();
abort_unless($room !== null, 404);
if ($clientKey !== null) {
$previous = DB::table('partnership_events')->where('partnership_id', $context['partnership']->id)->where('actor_id', $actor->id)->where('event_type', 'partnership.document_added.v1')
->whereRaw("payload->'payload'->>'room_id' = ?", [(string) $roomId])->whereRaw("payload->'payload'->>'client_key' = ?", [$clientKey])->first(['payload']);
if ($previous) {
$payload = json_decode($previous->payload, true, 512, JSON_THROW_ON_ERROR)['payload'];
abort_unless($payload['upload_hash'] === $commandHash, 409, 'Chave de envio já utilizada com outro documento ou autorização.');
abort_unless(DB::table('partnership_documents')->where('room_id', $roomId)->where('document_id', $payload['document_id'])->where('owner_id', $actor->id)->exists(), 409, 'O envio original não está disponível para reconciliar.');
return ['uploaded' => true, 'document_id' => $payload['document_id'], 'replayed' => true];
}
}
abort_if(in_array($room->status, ['closed', 'cancelled'], true), 409, 'Sala encerrada.');
$documentId = (string) Str::uuid();
$stored = $file->storeAs('partnerships/'.$roomId, $documentId, 'local');
abort_unless(is_string($stored) && $stored !== '', 503);
abort_unless(hash_file('sha256', Storage::disk('local')->path($stored)) === $hash, 503, 'Os bytes armazenados não correspondem ao envio.');
DB::table('documents')->insert(['id' => $documentId, 'source_system' => 'partnership-exchange', 'data_classification' => 'restricted', 'file_name' => basename($file->getClientOriginalName()),
'media_type' => $file->getMimeType(), 'storage_key' => $stored, 'sha256' => $hash, 'status' => 'quarantined', 'size_bytes' => $file->getSize(), 'uploaded_by' => $actor->id]);
DB::table('partnership_documents')->insert(['room_id' => $roomId, 'document_id' => $documentId, 'owner_id' => $actor->id, 'shared' => $shared]);
$metadata = ['room_id' => $roomId, 'document_id' => $documentId, 'shared' => $shared];
if ($clientKey !== null) {
$metadata += ['client_key' => $clientKey, 'upload_hash' => $commandHash];
}
$exchange->event($context['partnership'], $actor, 'partnership.document_added.v1', $metadata, 'Documento privado anexado');
return ['uploaded' => true, 'document_id' => $documentId, 'replayed' => false];
});
} catch (\Throwable $error) {
if (is_string($stored) && str_starts_with($stored, 'partnerships/'.$roomId.'/')) {
Storage::disk('local')->delete($stored);
}
throw $error;
}
if (! $result['replayed']) {
ScanDocumentForMalware::dispatch($result['document_id'])->afterCommit();
}
return $result;
}
}