feat(website-type): implement BootstrapWebsiteTypeController and related request/resource for domain-based bootstrapping

This commit is contained in:
2026-07-31 13:39:31 -03:00
parent b478c23f3b
commit f603a82b5e
8 changed files with 160 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Domains\Tenant\Controllers\AdminApp;
use App\Domains\Tenant\Models\WebsiteType;
use App\Domains\Tenant\Requests\BootstrapAdminAppRequest;
use App\Domains\Tenant\Resources\AdminApp\BootstrapAdminAppResource;
use App\Http\Controllers\Controller;
class BootstrapAdminAppController extends Controller
{
public function __invoke(
BootstrapAdminAppRequest $request
): BootstrapAdminAppResource {
/** @var string $domain */
$domain = $request->validated('dominio');
return BootstrapAdminAppResource::make(
WebsiteType::query()
->with('siteLogo')
->where('dominio', $domain)
->firstOrFail()
);
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace App\Domains\Tenant\Requests;
class BootstrapAdminAppRequest extends BootstrapTenantRequest {}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Domains\Tenant\Resources\AdminApp;
use App\Domains\Tenant\Models\WebsiteType;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin WebsiteType
*/
class BootstrapAdminAppResource extends JsonResource
{
/**
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'codigo' => $this->codigo,
'website_type_code' => $this->codigo,
'nombre' => $this->nombre,
'dominio' => $this->dominio,
'primary_color' => $this->primary_color,
'secondary_color' => $this->secondary_color,
'danger_color' => $this->danger_color,
'success_color' => $this->success_color,
'warning_color' => $this->warning_color,
'body_color' => $this->body_color,
'darker_body_color' => $this->darker_body_color,
'surface_color' => $this->surface_color,
'background_color' => $this->background_color,
'border_color' => $this->border_color,
'login_header_footer_color' => $this->login_header_footer_color,
'site_logo' => $this->siteLogo?->getTemporaryUrl(1440),
];
}
}

View File

@@ -1,9 +1,15 @@
<?php
use App\Domains\Tenant\Controllers\AdminApp\BootstrapAdminAppController;
use App\Domains\Tenant\Controllers\AdminApp\BootstrapTenantController;
use App\Domains\Tenant\Controllers\AdminApp\WebsiteExtraController;
use Illuminate\Support\Facades\Route;
Route::get(
'v1/adminapp/bootstrap/{dominio}',
BootstrapAdminAppController::class
);
Route::prefix('v1/adminapp/tenant')
->middleware(['auth:sanctum', 'adminapp.tenant'])
->group(function (): void {

View File

@@ -30,6 +30,7 @@ class WebsiteTypeSeeder extends Seeder
['codigo' => 'shopit'],
[
'nombre' => 'ShopIt',
'dominio' => 'localhost',
...self::PRESENTATION,
'site_logo' => $this->onTicketLogo(),
],
@@ -60,6 +61,7 @@ class WebsiteTypeSeeder extends Seeder
['codigo' => 'onticket'],
[
'nombre' => 'OnTicket',
'dominio' => 'onticket.localhost',
...self::PRESENTATION,
'site_logo' => $this->onTicketLogo(),
],

View File

@@ -42,6 +42,7 @@ class WebsiteTypeSeederTest extends TestCase
->sole();
$this->assertSame('ShopIt', $shopIt->nombre);
$this->assertSame('localhost', $shopIt->dominio);
$this->assertSame($expectedPresentation, $shopIt->only(array_keys($expectedPresentation)));
$this->assertSame('onticket_logo.png', $shopIt->siteLogo->filename);
Storage::disk('s3')->assertExists($shopIt->siteLogo->path);
@@ -66,6 +67,7 @@ class WebsiteTypeSeederTest extends TestCase
->sole();
$this->assertSame('OnTicket', $onTicket->nombre);
$this->assertSame('onticket.localhost', $onTicket->dominio);
$this->assertSame($expectedPresentation, $onTicket->only(array_keys($expectedPresentation)));
$this->assertSame('onticket_logo.png', $onTicket->siteLogo->filename);
Storage::disk('s3')->assertExists($onTicket->siteLogo->path);

View File

@@ -0,0 +1,57 @@
<?php
namespace Tests\Feature\Tenant;
use App\Domains\Tenant\Models\WebsiteType;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class BootstrapAdminAppControllerTest extends TestCase
{
use RefreshDatabase;
public function test_it_publicly_bootstraps_the_admin_app_by_domain(): void
{
WebsiteType::query()->create([
'codigo' => 'shopit',
'nombre' => 'ShopIt',
'dominio' => 'admin.shopit.test',
'primary_color' => '#112233',
'secondary_color' => '#445566',
'danger_color' => '#aa0000',
'success_color' => '#00aa00',
'warning_color' => '#ffaa00',
'body_color' => '#666666',
'darker_body_color' => '#333333',
'surface_color' => '#ffffff',
'background_color' => '#f8f8f8',
'border_color' => '#eaeaea',
'login_header_footer_color' => '#313131',
]);
$this->getJson('/api/v1/adminapp/bootstrap/ADMIN.SHOPIT.TEST')
->assertOk()
->assertJsonPath('data.codigo', 'shopit')
->assertJsonPath('data.website_type_code', 'shopit')
->assertJsonPath('data.dominio', 'admin.shopit.test')
->assertJsonPath('data.primary_color', '#112233')
->assertJsonPath('data.warning_color', '#ffaa00')
->assertJsonPath('data.login_header_footer_color', '#313131')
->assertJsonPath('data.site_logo', null);
}
public function test_it_returns_not_found_for_an_unknown_domain(): void
{
$this->getJson('/api/v1/adminapp/bootstrap/unknown.test')
->assertNotFound();
}
public function test_it_rejects_an_invalid_domain(): void
{
$invalidDomain = str_repeat('a', 256);
$this->getJson("/api/v1/adminapp/bootstrap/{$invalidDomain}")
->assertUnprocessable()
->assertJsonValidationErrors(['dominio']);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Tests\Unit\Tenant;
use App\Domains\Tenant\Models\WebsiteType;
use App\Domains\Tenant\Resources\AdminApp\BootstrapAdminAppResource;
use Tests\TestCase;
class BootstrapAdminAppResourceTest extends TestCase
{
public function test_it_exposes_the_website_type_code(): void
{
$websiteType = new WebsiteType([
'codigo' => 'shopit',
'nombre' => 'ShopIt',
'dominio' => 'admin.shopit.test',
]);
$websiteType->setRelation('siteLogo', null);
$data = BootstrapAdminAppResource::make($websiteType)->resolve(request());
$this->assertSame('shopit', $data['codigo']);
$this->assertSame('shopit', $data['website_type_code']);
}
}