Certificate manager built on Laravel 13, Livewire 4, and Flux. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
74 lines
2.2 KiB
PHP
74 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Certificate;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use RuntimeException;
|
|
use ZipArchive;
|
|
|
|
class CertificateZipper
|
|
{
|
|
/**
|
|
* Build a password-protected (AES-256) ZIP archive containing the given
|
|
* certificates' files. Returns the absolute path to the created archive;
|
|
* the caller is responsible for deleting it once sent.
|
|
*
|
|
* @param Collection<int, Certificate> $certificates
|
|
*/
|
|
public function create(Collection $certificates, string $password): string
|
|
{
|
|
$archivePath = tempnam(sys_get_temp_dir(), 'certs_').'.zip';
|
|
|
|
$zip = new ZipArchive;
|
|
|
|
if ($zip->open($archivePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
|
|
throw new RuntimeException('Unable to create ZIP archive.');
|
|
}
|
|
|
|
$zip->setPassword($password);
|
|
$usedNames = [];
|
|
|
|
foreach ($certificates as $certificate) {
|
|
if (blank($certificate->file_path) || ! Storage::disk('local')->exists($certificate->file_path)) {
|
|
continue;
|
|
}
|
|
|
|
$entryName = $this->uniqueEntryName($certificate, $usedNames);
|
|
$usedNames[] = $entryName;
|
|
|
|
$zip->addFromString($entryName, Storage::disk('local')->get($certificate->file_path));
|
|
$zip->setEncryptionName($entryName, ZipArchive::EM_AES_256);
|
|
}
|
|
|
|
if ($zip->count() === 0) {
|
|
$zip->close();
|
|
@unlink($archivePath);
|
|
|
|
throw new RuntimeException('No files were available to add to the archive.');
|
|
}
|
|
|
|
$zip->close();
|
|
|
|
return $archivePath;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, string> $usedNames
|
|
*/
|
|
private function uniqueEntryName(Certificate $certificate, array $usedNames): string
|
|
{
|
|
$extension = pathinfo($certificate->file_path, PATHINFO_EXTENSION);
|
|
$base = Str::slug($certificate->title) ?: 'certificate';
|
|
$name = $base.'.'.$extension;
|
|
$counter = 1;
|
|
|
|
while (in_array($name, $usedNames, true)) {
|
|
$name = $base.'-'.(++$counter).'.'.$extension;
|
|
}
|
|
|
|
return $name;
|
|
}
|
|
}
|