118 lines
4.5 KiB
PHP
118 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services\File;
|
|
|
|
use App\Models\StoredFile;
|
|
use App\Models\User;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class StoredFileService
|
|
{
|
|
public function list(array $filters, User $user, ?int $tenantId): LengthAwarePaginator
|
|
{
|
|
return StoredFile::query()
|
|
->when(! $user->isMasterAdmin(), function ($query) use ($user, $tenantId) {
|
|
$query->where(function ($scope) use ($user, $tenantId) {
|
|
$scope->where('uploaded_by', $user->id)
|
|
->when($tenantId, fn ($tenantScope) => $tenantScope->orWhere('tenant_id', $tenantId));
|
|
});
|
|
})
|
|
->when($filters['search'] ?? null, function ($query, $search) {
|
|
$query->where('original_name', 'like', "%{$search}%");
|
|
})
|
|
->when($filters['category'] ?? null, fn ($query, $category) => $query->where('category', $category))
|
|
->when($filters['mime_type'] ?? null, fn ($query, $mimeType) => $query->where('mime_type', $mimeType))
|
|
->latest('id')
|
|
->paginate($filters['per_page'] ?? 10);
|
|
}
|
|
|
|
public function store(UploadedFile $upload, User $user, ?int $tenantId, string $category): StoredFile
|
|
{
|
|
$uuid = (string) Str::uuid();
|
|
$extension = strtolower($upload->getClientOriginalExtension() ?: $upload->extension() ?: 'bin');
|
|
$scope = $tenantId ? "tenants/{$tenantId}" : 'global';
|
|
$objectKey = "{$scope}/{$category}/".now()->format('Y/m')."/{$uuid}.{$extension}";
|
|
$disk = Storage::disk('minio');
|
|
|
|
$disk->putFileAs(dirname($objectKey), $upload, basename($objectKey), [
|
|
'visibility' => 'private',
|
|
'ContentType' => $upload->getMimeType() ?: 'application/octet-stream',
|
|
]);
|
|
|
|
try {
|
|
return DB::transaction(fn () => StoredFile::create([
|
|
'uuid' => $uuid,
|
|
'tenant_id' => $tenantId,
|
|
'uploaded_by' => $user->id,
|
|
'disk' => 'minio',
|
|
'bucket' => (string) config('filesystems.disks.minio.bucket'),
|
|
'object_key' => $objectKey,
|
|
'original_name' => $upload->getClientOriginalName(),
|
|
'extension' => $extension,
|
|
'mime_type' => $upload->getMimeType(),
|
|
'size' => $upload->getSize(),
|
|
'category' => $category,
|
|
'visibility' => 'private',
|
|
]));
|
|
} catch (\Throwable $exception) {
|
|
$disk->delete($objectKey);
|
|
throw $exception;
|
|
}
|
|
}
|
|
|
|
public function temporaryUrl(StoredFile $file, User $user): array
|
|
{
|
|
$this->authorizeAccess($file, $user);
|
|
$expiresAt = now()->addMinutes(10);
|
|
|
|
return [
|
|
'url' => Storage::disk($file->disk)->temporaryUrl(
|
|
$file->object_key,
|
|
$expiresAt,
|
|
['ResponseContentDisposition' => 'inline; filename="'.addslashes($file->original_name).'"']
|
|
),
|
|
'expires_at' => $expiresAt->toIso8601String(),
|
|
];
|
|
}
|
|
|
|
public function delete(StoredFile $file, User $user): void
|
|
{
|
|
if (! $user->isMasterAdmin() && $file->uploaded_by !== $user->id) {
|
|
abort(403, 'File hanya dapat dihapus oleh pengunggah atau Master Admin.');
|
|
}
|
|
|
|
Storage::disk($file->disk)->delete($file->object_key);
|
|
$file->delete();
|
|
}
|
|
|
|
public function authorizeAccess(StoredFile $file, User $user): void
|
|
{
|
|
if ($user->isMasterAdmin() || $file->uploaded_by === $user->id) {
|
|
return;
|
|
}
|
|
|
|
$canAccessTenant = $file->tenant_id && $user->userTenants()
|
|
->where('tenant_id', $file->tenant_id)
|
|
->exists();
|
|
|
|
abort_unless($canAccessTenant, 403, 'Anda tidak memiliki akses ke file ini.');
|
|
}
|
|
|
|
public function validateProfilePhoto(StoredFile $file, User $user): void
|
|
{
|
|
if ($file->uploaded_by !== $user->id
|
|
|| $file->category !== 'profile'
|
|
|| ! str_starts_with((string) $file->mime_type, 'image/')
|
|
|| $file->size > 5 * 1024 * 1024) {
|
|
throw ValidationException::withMessages([
|
|
'profile_photo_file_id' => 'Foto profil harus berupa file gambar kategori profile yang Anda unggah sendiri.',
|
|
]);
|
|
}
|
|
}
|
|
}
|