fix: backfill datacenters on migrate; resolve billing customer by user link
- migration seeds fsn/hel + any host-referenced code so existing installs keep selectable datacenters after upgrade - Billing::customer() resolves via user_id, email only as legacy fallback Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/portal-design
parent
4e86e135a6
commit
b44e224bee
|
|
@ -65,8 +65,13 @@ class Billing extends Component
|
||||||
private function customer(): ?Customer
|
private function customer(): ?Customer
|
||||||
{
|
{
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
if (! $user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return $user ? Customer::query()->where('email', $user->email)->first() : null;
|
// Prefer the explicit link; fall back to email for legacy unlinked accounts.
|
||||||
|
return Customer::query()->where('user_id', $user->id)->first()
|
||||||
|
?? Customer::query()->where('email', $user->email)->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
|
|
@ -17,6 +19,33 @@ return new class extends Migration
|
||||||
$table->boolean('active')->default(true);
|
$table->boolean('active')->default(true);
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The host form now only offers codes from this table, so an existing
|
||||||
|
// install must not upgrade into an empty list. Backfill every code
|
||||||
|
// already referenced by a host, plus the previously hardcoded defaults.
|
||||||
|
$names = ['fsn' => 'Falkenstein', 'hel' => 'Helsinki'];
|
||||||
|
$locations = ['fsn' => 'DE', 'hel' => 'FI'];
|
||||||
|
|
||||||
|
$codes = ['fsn', 'hel'];
|
||||||
|
if (Schema::hasTable('hosts')) {
|
||||||
|
$codes = array_values(array_unique(array_merge(
|
||||||
|
$codes,
|
||||||
|
DB::table('hosts')->whereNotNull('datacenter')->distinct()->pluck('datacenter')->all(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$now = now();
|
||||||
|
foreach ($codes as $code) {
|
||||||
|
DB::table('datacenters')->insert([
|
||||||
|
'uuid' => (string) Str::uuid(),
|
||||||
|
'code' => $code,
|
||||||
|
'name' => $names[$code] ?? Str::upper($code),
|
||||||
|
'location' => $locations[$code] ?? null,
|
||||||
|
'active' => true,
|
||||||
|
'created_at' => $now,
|
||||||
|
'updated_at' => $now,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ it('creates a datacenter', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects a duplicate datacenter code', function () {
|
it('rejects a duplicate datacenter code', function () {
|
||||||
Datacenter::factory()->create(['code' => 'fsn']);
|
Datacenter::query()->firstOrCreate(['code' => 'fsn'], ['name' => 'Falkenstein']);
|
||||||
|
|
||||||
Livewire::actingAs(admin())
|
Livewire::actingAs(admin())
|
||||||
->test(Datacenters::class)
|
->test(Datacenters::class)
|
||||||
|
|
@ -45,7 +45,7 @@ it('toggles a datacenter active flag', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('offers only known datacenter codes when adding a host', function () {
|
it('offers only known datacenter codes when adding a host', function () {
|
||||||
Datacenter::factory()->create(['code' => 'fsn', 'name' => 'Falkenstein']);
|
Datacenter::query()->firstOrCreate(['code' => 'fsn'], ['name' => 'Falkenstein']);
|
||||||
|
|
||||||
// unknown code rejected
|
// unknown code rejected
|
||||||
Livewire::actingAs(admin())
|
Livewire::actingAs(admin())
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ it('lists real hosts for admins', function () {
|
||||||
|
|
||||||
it('creates a host and starts onboarding with an encrypted password', function () {
|
it('creates a host and starts onboarding with an encrypted password', function () {
|
||||||
Queue::fake();
|
Queue::fake();
|
||||||
\App\Models\Datacenter::factory()->create(['code' => 'fsn']);
|
\App\Models\Datacenter::query()->firstOrCreate(['code' => 'fsn'], ['name' => 'Falkenstein']);
|
||||||
|
|
||||||
Livewire::actingAs(admin())
|
Livewire::actingAs(admin())
|
||||||
->test(HostCreate::class)
|
->test(HostCreate::class)
|
||||||
|
|
@ -62,7 +62,7 @@ it('creates a host and starts onboarding with an encrypted password', function (
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects a duplicate public ip', function () {
|
it('rejects a duplicate public ip', function () {
|
||||||
\App\Models\Datacenter::factory()->create(['code' => 'fsn']);
|
\App\Models\Datacenter::query()->firstOrCreate(['code' => 'fsn'], ['name' => 'Falkenstein']);
|
||||||
Host::factory()->create(['public_ip' => '203.0.113.99']);
|
Host::factory()->create(['public_ip' => '203.0.113.99']);
|
||||||
|
|
||||||
Livewire::actingAs(admin())
|
Livewire::actingAs(admin())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue