feat(ai-edits): add output storage backfill flow and coverage
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-07 10:10:45 +01:00
parent fb45d1f6ab
commit 8cc0918881
18 changed files with 1610 additions and 18 deletions

View File

@@ -20,6 +20,8 @@ use App\Support\ApiError;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
@@ -492,6 +494,11 @@ class EventPublicAiEditController extends BaseController
'storage_disk' => $output->storage_disk,
'storage_path' => $output->storage_path,
'provider_url' => $output->provider_url,
'url' => $this->resolveOutputUrl(
$output->storage_disk,
$output->storage_path,
$output->provider_url
),
'mime_type' => $output->mime_type,
'width' => $output->width,
'height' => $output->height,
@@ -502,4 +509,41 @@ class EventPublicAiEditController extends BaseController
])->values(),
];
}
private function resolveOutputUrl(?string $storageDisk, ?string $storagePath, ?string $providerUrl): ?string
{
$resolvedStoragePath = $this->normalizeOptionalString($storagePath);
if ($resolvedStoragePath !== null) {
if (Str::startsWith($resolvedStoragePath, ['http://', 'https://'])) {
return $resolvedStoragePath;
}
$disk = $this->resolveStorageDisk($storageDisk);
try {
return Storage::disk($disk)->url($resolvedStoragePath);
} catch (\Throwable $exception) {
Log::debug('Falling back to raw AI output storage path', [
'disk' => $disk,
'path' => $resolvedStoragePath,
'error' => $exception->getMessage(),
]);
return '/'.ltrim($resolvedStoragePath, '/');
}
}
return $this->normalizeOptionalString($providerUrl);
}
private function resolveStorageDisk(?string $disk): string
{
$candidate = trim((string) ($disk ?: config('filesystems.default', 'public')));
if ($candidate === '' || ! config("filesystems.disks.{$candidate}")) {
return (string) config('filesystems.default', 'public');
}
return $candidate;
}
}

View File

@@ -24,6 +24,8 @@ use App\Support\TenantMemberPermissions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
@@ -561,6 +563,11 @@ class AiEditController extends Controller
'storage_disk' => $output->storage_disk,
'storage_path' => $output->storage_path,
'provider_url' => $output->provider_url,
'url' => $this->resolveOutputUrl(
$output->storage_disk,
$output->storage_path,
$output->provider_url
),
'mime_type' => $output->mime_type,
'width' => $output->width,
'height' => $output->height,
@@ -571,4 +578,41 @@ class AiEditController extends Controller
])->values(),
];
}
private function resolveOutputUrl(?string $storageDisk, ?string $storagePath, ?string $providerUrl): ?string
{
$resolvedStoragePath = $this->normalizeOptionalString($storagePath);
if ($resolvedStoragePath !== null) {
if (Str::startsWith($resolvedStoragePath, ['http://', 'https://'])) {
return $resolvedStoragePath;
}
$disk = $this->resolveStorageDisk($storageDisk);
try {
return Storage::disk($disk)->url($resolvedStoragePath);
} catch (\Throwable $exception) {
Log::debug('Falling back to raw AI output storage path', [
'disk' => $disk,
'path' => $resolvedStoragePath,
'error' => $exception->getMessage(),
]);
return '/'.ltrim($resolvedStoragePath, '/');
}
}
return $this->normalizeOptionalString($providerUrl);
}
private function resolveStorageDisk(?string $disk): string
{
$candidate = trim((string) ($disk ?: config('filesystems.default', 'public')));
if ($candidate === '' || ! config("filesystems.disks.{$candidate}")) {
return (string) config('filesystems.default', 'public');
}
return $candidate;
}
}