change file limitation logic
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled

This commit is contained in:
2026-05-01 11:46:47 +03:30
parent 8475ccf873
commit edc7f1955e
7 changed files with 96 additions and 6 deletions

View File

@@ -37,6 +37,10 @@ BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CHAT_ATTACHMENT_MAX_FILES=5
CHAT_ATTACHMENT_MAX_FILE_SIZE_KB=2097152
CHAT_LIVEWIRE_MAX_UPLOAD_TIME=60
CACHE_STORE=database
# CACHE_PREFIX=

View File

@@ -9,6 +9,7 @@ use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Number;
use Livewire\Attributes\Locked;
use Livewire\Component;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
@@ -41,16 +42,19 @@ class MessageComposer extends Component
{
$this->body = trim($this->body);
$hasAttachments = $this->hasAttachments();
$maxAttachmentFiles = $this->maxAttachmentFiles();
$maxAttachmentFileSizeKilobytes = $this->maxAttachmentFileSizeKilobytes();
$maxAttachmentFileSizeLabel = $this->maxAttachmentFileSizeLabel();
$validated = $this->validate([
'body' => [$hasAttachments ? 'nullable' : 'required', 'string', 'max:4000'],
'attachments' => ['array', 'max:5'],
'attachments.*' => ['file', 'max:10240'],
'attachments' => ['array', 'max:'.$maxAttachmentFiles],
'attachments.*' => ['file', 'max:'.$maxAttachmentFileSizeKilobytes],
], [
'body.required' => __('Write a message or attach a file before sending.'),
'attachments.max' => __('Attach up to :max files at a time.'),
'attachments.max' => __('Attach up to :max files at a time.', ['max' => $maxAttachmentFiles]),
'attachments.*.file' => __('Each attachment must be a valid file.'),
'attachments.*.max' => __('Each attachment must be 10 MB or smaller.'),
'attachments.*.max' => __('Each attachment must be :size or smaller.', ['size' => $maxAttachmentFileSizeLabel]),
]);
$conversation = $this->conversation();
@@ -172,6 +176,14 @@ class MessageComposer extends Component
];
}
public function attachmentLimitSummary(): string
{
return __('Up to :count files, :size each', [
'count' => $this->maxAttachmentFiles(),
'size' => $this->maxAttachmentFileSizeLabel(),
]);
}
private function conversation(): Conversation
{
$conversation = Conversation::query()
@@ -190,6 +202,21 @@ class MessageComposer extends Component
->isNotEmpty();
}
private function maxAttachmentFiles(): int
{
return (int) config('chat.attachments.max_files', 5);
}
private function maxAttachmentFileSizeKilobytes(): int
{
return (int) config('chat.attachments.max_file_size_kilobytes', 2 * 1024 * 1024);
}
private function maxAttachmentFileSizeLabel(): string
{
return Number::fileSize($this->maxAttachmentFileSizeKilobytes() * 1024);
}
private function emojiForCode(string $code): ?string
{
$code = strtoupper($code);

View File

@@ -15,7 +15,7 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
//
$this->configureFileUploads();
}
/**
@@ -47,4 +47,14 @@ class AppServiceProvider extends ServiceProvider
: null,
);
}
protected function configureFileUploads(): void
{
$maxFileSizeKilobytes = (int) config('chat.attachments.max_file_size_kilobytes');
config([
'livewire.temporary_file_upload.rules' => ['required', 'file', 'max:'.$maxFileSizeKilobytes],
'livewire.temporary_file_upload.max_upload_time' => (int) config('chat.attachments.livewire_max_upload_time'),
]);
}
}

9
config/chat.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
return [
'attachments' => [
'max_files' => (int) env('CHAT_ATTACHMENT_MAX_FILES', 5),
'max_file_size_kilobytes' => (int) env('CHAT_ATTACHMENT_MAX_FILE_SIZE_KB', 2 * 1024 * 1024),
'livewire_max_upload_time' => (int) env('CHAT_LIVEWIRE_MAX_UPLOAD_TIME', 60),
],
];

4
public/.user.ini Normal file
View File

@@ -0,0 +1,4 @@
upload_max_filesize=2048M
post_max_size=2050M
max_input_time=3600
max_execution_time=3600

View File

@@ -21,7 +21,7 @@
<div class="flex items-start justify-between gap-3">
<div>
<div class="text-sm font-medium text-zinc-900 dark:text-zinc-100">{{ __('Attachments') }}</div>
<div class="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400">{{ __('Up to 5 files, 10 MB each') }}</div>
<div class="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400">{{ $this->attachmentLimitSummary() }}</div>
</div>
<flux:button

View File

@@ -180,6 +180,42 @@ test('participants can send file messages', function () {
Storage::disk('local')->assertExists($message->attachmentPath());
});
test('chat file uploads are configured for two gigabyte attachments', function () {
expect(config('chat.attachments.max_file_size_kilobytes'))->toBe(2 * 1024 * 1024)
->and(config('livewire.temporary_file_upload.rules'))->toContain('max:2097152')
->and(config('livewire.temporary_file_upload.max_upload_time'))->toBe(60);
});
test('participants can send files larger than the previous ten megabyte limit', function () {
Storage::fake('local');
$user = User::factory()->create();
$conversation = Conversation::factory()
->direct()
->for($user, 'creator')
->create();
ConversationParticipant::factory()->for($conversation)->for($user)->create();
$this->actingAs($user);
Livewire::test(MessageComposer::class, ['conversationId' => $conversation->id])
->set('attachments', [
UploadedFile::fake()->create('large-export.zip', 13 * 1024, 'application/zip'),
])
->call('sendMessage')
->assertHasNoErrors();
$message = Message::query()
->where('conversation_id', $conversation->id)
->where('user_id', $user->id)
->where('type', Message::TypeFile)
->first();
expect($message)->not->toBeNull();
expect($message->attachmentName())->toBe('large-export.zip');
});
test('file metadata is captured before temporary uploads are moved', function () {
Storage::fake('local');