Compare commits
3 Commits
0e55e36cea
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f0f20659d | |||
| 98dacef556 | |||
| da6d51b7b2 |
@@ -23,7 +23,7 @@ class EvCompanyResource extends JsonResource
|
||||
'mm_description' => $this->mm_description,
|
||||
'contact' => $this->contact,
|
||||
'address' => $this->address,
|
||||
'logo' => $this->logo,
|
||||
'logo' => $this->logo_url,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace Modules\Catalog\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Catalog\Database\Factories\EvCompanyFactory;
|
||||
use Spatie\Activitylog\Models\Concerns\LogsActivity;
|
||||
@@ -72,4 +74,26 @@ class EvCompany extends Model
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* `logo` is stored as the disk-relative path Filament's FileUpload
|
||||
* writes (e.g. "logos/xxx.png"), not a URL — API consumers need a full
|
||||
* absolute URL to render it directly. Guards against the disk itself
|
||||
* already returning an absolute URL (e.g. an s3 disk), so this stays
|
||||
* correct if the storage disk ever changes from local.
|
||||
*/
|
||||
public function logoUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function (): ?string {
|
||||
if (blank($this->logo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$url = Storage::disk(config('filesystems.default'))->url($this->logo);
|
||||
|
||||
return str($url)->startsWith(['http://', 'https://']) ? $url : url($url);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Catalog\Models\Destination;
|
||||
use Modules\Catalog\Models\EvCompany;
|
||||
|
||||
@@ -19,6 +20,24 @@ test('lists active ev companies', function () {
|
||||
->assertJsonFragment(['id' => $active->id]);
|
||||
});
|
||||
|
||||
test('returns the company logo as a full absolute url', function () {
|
||||
$company = EvCompany::factory()->create(['is_active' => true, 'logo' => 'logos/example.png']);
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->getJson('/api/v1/companies')
|
||||
->assertSuccessful()
|
||||
->assertJsonFragment(['logo' => url(Storage::disk(config('filesystems.default'))->url($company->logo))]);
|
||||
});
|
||||
|
||||
test('returns a null logo when the company has none', function () {
|
||||
EvCompany::factory()->create(['is_active' => true, 'logo' => null]);
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->getJson('/api/v1/companies')
|
||||
->assertSuccessful()
|
||||
->assertJsonFragment(['logo' => null]);
|
||||
});
|
||||
|
||||
test('lists active destinations', function () {
|
||||
$active = Destination::factory()->create(['is_active' => true]);
|
||||
Destination::factory()->create(['is_active' => false]);
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Providers;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
@@ -23,6 +24,16 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function boot(): void
|
||||
{
|
||||
$this->configureRateLimiting();
|
||||
|
||||
// Belt-and-suspenders alongside bootstrap/app.php's trustProxies():
|
||||
// that already makes url()/asset() respect the proxy's
|
||||
// X-Forwarded-Proto, but if that header is ever missing or a proxy
|
||||
// is misconfigured, this still forces https:// asset/route URLs on
|
||||
// any environment whose APP_URL is itself https — so a plain-http
|
||||
// request never causes a mixed-content-blocked asset again.
|
||||
if (str(config('app.url'))->startsWith('https://')) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Modules\Identity\Http\Middleware\AuthenticateSanctumOrFastApiJwt;
|
||||
use Modules\Identity\Http\Middleware\EnsureFastApiAgent;
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
@@ -24,6 +25,22 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
// Staging/production sit behind a reverse proxy/load balancer that
|
||||
// terminates SSL — without this, Laravel never sees the original
|
||||
// request as HTTPS, so it generates http:// asset URLs, which
|
||||
// browsers then block as mixed content on the https:// page (e.g.
|
||||
// Filament's file-upload.js failing to load, breaking that field's
|
||||
// JS-enhanced dropzone). Trusting '*' is the standard Laravel
|
||||
// pattern when the proxy's IP isn't fixed/known in advance.
|
||||
$middleware->trustProxies(
|
||||
at: '*',
|
||||
headers: SymfonyRequest::HEADER_X_FORWARDED_FOR
|
||||
| SymfonyRequest::HEADER_X_FORWARDED_HOST
|
||||
| SymfonyRequest::HEADER_X_FORWARDED_PORT
|
||||
| SymfonyRequest::HEADER_X_FORWARDED_PROTO
|
||||
| SymfonyRequest::HEADER_X_FORWARDED_AWS_ELB,
|
||||
);
|
||||
|
||||
$middleware->alias([
|
||||
'fastapi.agent' => EnsureFastApiAgent::class,
|
||||
'api.auth' => AuthenticateSanctumOrFastApiJwt::class,
|
||||
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Reference in New Issue
Block a user