Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 40 additions & 18 deletions app/Http/Controllers/BulkEventUploadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,38 @@ function ($attribute, $value, $fail) {
->withInput();
}

$tempDisk = $this->tempDisk();
$path = $file->storeAs(
'temp/bulk-events',
'bulk_events_'.time().'_'.Str::random(8).'.'.$extension,
['disk' => $tempDisk, 'visibility' => 'private']
);
$tempDisk = BulkEventUploadCache::tempDisk();
$path = 'temp/bulk-events/bulk_events_'.time().'_'.Str::random(8).'.'.$extension;

try {
$stored = Storage::disk($tempDisk)->put(
$path,
$file->get(),
['visibility' => 'private']
);
} catch (\Throwable $e) {
Log::error('Bulk event upload: failed to store temp file', [
'disk' => $tempDisk,
'path' => $path,
'error' => $e->getMessage(),
]);

return redirect()->route('admin.bulk-upload.index')
->withErrors(['file' => 'Could not store upload on '.$tempDisk.': '.$e->getMessage()])
->withInput();
}

if ($stored === false || ! Storage::disk($tempDisk)->exists($path)) {
Log::error('Bulk event upload: temp file missing immediately after store', [
'disk' => $tempDisk,
'path' => $path,
'stored' => $stored,
]);

return redirect()->route('admin.bulk-upload.index')
->withErrors(['file' => 'Could not store upload on shared disk ('.$tempDisk.'). Check AWS credentials / BULK_UPLOAD_TEMP_DISK.'])
->withInput();
}

$headerCheck = BulkEventUploadValidator::validateRequiredColumns($path, $tempDisk);
if (isset($headerCheck['error'])) {
Expand Down Expand Up @@ -188,16 +214,22 @@ public function import(Request $request): RedirectResponse
}

$path = (string) ($payload['path'] ?? '');
$disk = (string) ($payload['disk'] ?? $this->tempDisk());
$disk = BulkEventUploadCache::tempDisk($payload['disk'] ?? null);

if ($path === '' || ! Storage::disk($disk)->exists($path)) {
Log::warning('Bulk event import: temp file missing', [
'token' => $token,
'disk' => $disk,
'cached_disk' => $payload['disk'] ?? null,
'path' => $path,
]);

return redirect()->route('admin.bulk-upload.index')
->withErrors(['import' => 'Uploaded file no longer available. Please upload again.']);
->withErrors(['import' => 'Uploaded file no longer available (disk: '.$disk.'). Please upload again.']);
}

if (($payload['disk'] ?? null) !== $disk) {
BulkEventUploadCache::merge($token, ['disk' => $disk]);
}

BulkEventUploadCache::merge($token, [
Expand Down Expand Up @@ -237,14 +269,4 @@ public function report(Request $request, ?string $token = null): View|RedirectRe
return redirect()->route('admin.bulk-upload.index')
->with('info', 'Report no longer available. Run an import to see a new report.');
}

/**
* Shared temp disk (Amazon S3). `local` is not shared across web nodes.
*/
private function tempDisk(): string
{
$disk = (string) config('filesystems.bulk_upload_temp_disk', 's3');

return $disk !== '' ? $disk : 's3';
}
}
2 changes: 1 addition & 1 deletion app/Jobs/ProcessBulkEventImportJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function handle(): void
}

$path = (string) ($payload['path'] ?? '');
$disk = (string) ($payload['disk'] ?? 'local');
$disk = BulkEventUploadCache::tempDisk($payload['disk'] ?? null);
$defaultCreatorEmail = $payload['default_creator_email'] ?? null;

if ($path === '' || ! Storage::disk($disk)->exists($path)) {
Expand Down
2 changes: 1 addition & 1 deletion app/Jobs/ValidateBulkEventUploadJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function handle(): void
}

$path = (string) ($payload['path'] ?? '');
$disk = (string) ($payload['disk'] ?? 'local');
$disk = BulkEventUploadCache::tempDisk($payload['disk'] ?? null);
$defaultCreatorEmail = $payload['default_creator_email'] ?? null;

if ($path === '' || ! Storage::disk($disk)->exists($path)) {
Expand Down
20 changes: 20 additions & 0 deletions app/Services/BulkEventUploadCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ public static function key(string $token): string
return 'bulk_upload_'.$token;
}

/**
* Shared temp disk for validate → import. Never use `local` on deployed apps
* (Forge may still set BULK_UPLOAD_TEMP_DISK=local from the old example).
*/
public static function tempDisk(?string $cachedDisk = null): string
{
$configured = (string) ($cachedDisk ?: config('filesystems.bulk_upload_temp_disk', 's3'));
$cloud = (string) config('filesystems.cloud', 's3');

if (app()->environment('local', 'testing')) {
return $configured !== '' ? $configured : 'local';
}

if ($configured === '' || $configured === 'local') {
return $cloud !== '' ? $cloud : 's3';
}

return $configured;
}

/**
* @return array<string, mixed>|null
*/
Expand Down
14 changes: 14 additions & 0 deletions tests/Unit/BulkEventUploadCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,18 @@ public function test_preview_from_result_uses_failures_only_for_large_uploads():
$this->assertCount(1, $preview['row_statuses']);
$this->assertSame(42, $preview['row_statuses'][0]['row']);
}

public function test_temp_disk_rejects_local_outside_local_environment(): void
{
config([
'filesystems.bulk_upload_temp_disk' => 'local',
'filesystems.cloud' => 's3',
'app.env' => 'production',
]);
app()->detectEnvironment(fn () => 'production');

$this->assertSame('s3', BulkEventUploadCache::tempDisk());
$this->assertSame('s3', BulkEventUploadCache::tempDisk('local'));
$this->assertSame('s3', BulkEventUploadCache::tempDisk('s3'));
}
}
Loading