*/ public const ALLOWED_EXTENSIONS = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'txt']; /** @var list */ public const ALLOWED_MIMES = [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/zip', 'application/x-zip-compressed', 'text/plain', ]; /** * Store the uploaded attachment for the given press release. * * @return array{disk:string,path:string,original_name:string,mime:string,size:int} */ public function store(UploadedFile $upload, int $pressReleaseId): array { $this->assertValidUpload($upload); $extension = strtolower($upload->getClientOriginalExtension() ?: $upload->extension()); $originalName = $upload->getClientOriginalName(); $baseSlug = Str::slug(pathinfo($originalName, PATHINFO_FILENAME)) ?: 'attachment'; $baseSlug = Str::limit($baseSlug, 60, ''); $directory = sprintf('press-releases/%d/attachments', $pressReleaseId); $filename = Str::uuid()->toString().'-'.$baseSlug.'.'.$extension; $relativePath = $directory.'/'.$filename; $disk = $this->disk(); $stream = fopen($upload->getRealPath(), 'r'); if ($stream === false) { throw new RuntimeException('Could not open uploaded file stream.'); } try { $disk->put($relativePath, $stream, 'public'); } finally { if (is_resource($stream)) { fclose($stream); } } return [ 'disk' => 'public', 'path' => $relativePath, 'original_name' => $originalName, 'mime' => $upload->getMimeType() ?: 'application/octet-stream', 'size' => $upload->getSize() ?: 0, ]; } public function delete(string $disk, string $path): void { if (blank($path)) { return; } try { Storage::disk($disk)->delete($path); } catch (\Throwable) { // Swallow — deletion is best-effort; a missing file is acceptable. } } private function assertValidUpload(UploadedFile $upload): void { $extension = strtolower($upload->getClientOriginalExtension() ?: $upload->extension()); if (! in_array($extension, self::ALLOWED_EXTENSIONS, true)) { throw new RuntimeException('Unsupported attachment extension: '.$extension); } if ($upload->getSize() > self::MAX_BYTES) { throw new RuntimeException('Attachment exceeds maximum size.'); } } private function disk(): Filesystem { return Storage::disk('public'); } }