90 lines
2.7 KiB
PHP
90 lines
2.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Uploads\Services;
|
|
|
|
use App\Models\Artwork;
|
|
use App\Models\User;
|
|
use App\Uploads\Exceptions\DraftQuotaException;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
final class DraftQuotaService
|
|
{
|
|
/**
|
|
* @param array{files:array<int, UploadedFile>, main_hash:string} $incomingFiles
|
|
* @return array<int, string>
|
|
*/
|
|
public function assertCanCreateDraft(User $user, array $incomingFiles): array
|
|
{
|
|
$maxDrafts = max(1, (int) config('uploads.draft_quota.max_drafts_per_user', 10));
|
|
$maxStorageMb = max(1, (int) config('uploads.draft_quota.max_draft_storage_mb_per_user', 1024));
|
|
$maxStorageBytes = $maxStorageMb * 1024 * 1024;
|
|
$policy = (string) config('uploads.draft_quota.duplicate_hash_policy', 'block');
|
|
$warnings = [];
|
|
|
|
$draftCount = DB::table('uploads')
|
|
->where('user_id', (int) $user->id)
|
|
->where('status', 'draft')
|
|
->count();
|
|
|
|
if ($draftCount >= $maxDrafts) {
|
|
throw DraftQuotaException::draftLimit();
|
|
}
|
|
|
|
$currentDraftStorage = (int) DB::table('upload_files as uf')
|
|
->join('uploads as u', 'u.id', '=', 'uf.upload_id')
|
|
->where('u.user_id', (int) $user->id)
|
|
->where('u.status', 'draft')
|
|
->sum(DB::raw('COALESCE(uf.size, 0)'));
|
|
|
|
$incomingSize = $this->incomingSizeBytes((array) ($incomingFiles['files'] ?? []));
|
|
|
|
if (($currentDraftStorage + $incomingSize) > $maxStorageBytes) {
|
|
throw DraftQuotaException::storageLimit();
|
|
}
|
|
|
|
$mainHash = strtolower(trim((string) ($incomingFiles['main_hash'] ?? '')));
|
|
if ($mainHash !== '' && $this->publishedMainHashExists($mainHash)) {
|
|
if ($policy === 'warn') {
|
|
$warnings[] = 'duplicate_hash';
|
|
} else {
|
|
throw DraftQuotaException::duplicateUpload();
|
|
}
|
|
}
|
|
|
|
return $warnings;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, UploadedFile> $files
|
|
*/
|
|
private function incomingSizeBytes(array $files): int
|
|
{
|
|
$total = 0;
|
|
|
|
foreach ($files as $file) {
|
|
if (! $file instanceof UploadedFile) {
|
|
continue;
|
|
}
|
|
|
|
$size = $file->getSize();
|
|
if (is_numeric($size)) {
|
|
$total += (int) $size;
|
|
}
|
|
}
|
|
|
|
return max(0, $total);
|
|
}
|
|
|
|
private function publishedMainHashExists(string $hash): bool
|
|
{
|
|
return Artwork::query()
|
|
->where('hash', $hash)
|
|
->where('artwork_status', 'published')
|
|
->whereNotNull('published_at')
|
|
->exists();
|
|
}
|
|
}
|