diff --git a/app/Livewire/Admin/Integrations.php b/app/Livewire/Admin/Integrations.php index 814a0da..115b558 100644 --- a/app/Livewire/Admin/Integrations.php +++ b/app/Livewire/Admin/Integrations.php @@ -106,6 +106,15 @@ class Integrations extends Component public ?array $check = null; + /** + * Zu welchem Eintrag `$check` gehört. + * + * Ohne das erschien das Ergebnis eines DNS-Tests unter der Stripe-Karte — + * beide Prüfungen teilen sich eine Eigenschaft, und die Anzeige braucht + * den Bezug. + */ + public ?string $checkedKey = null; + // Plain settings — App\Support\ProvisioningSettings' full list. public string $dnsZone = ''; @@ -297,6 +306,7 @@ class Integrations extends Component $this->entered[$field] = ''; $this->check = null; + $this->checkedKey = null; $this->dispatch('notify', message: __('secrets.saved')); } @@ -332,6 +342,7 @@ class Integrations extends Component app(SecretVault::class)->forget($key); $this->check = null; + $this->checkedKey = null; $this->dispatch('notify', message: __('secrets.removed')); } @@ -435,6 +446,7 @@ class Integrations extends Component $candidate = trim((string) ($this->entered[self::field($key)] ?? '')) ?: null; $this->check = app($checker)->run($candidate); + $this->checkedKey = $key; } /** The dotless form key for a registry key (a dot means nesting to Livewire). */ @@ -540,6 +552,7 @@ class Integrations extends Component $this->lockAgain(); $this->check = null; + $this->checkedKey = null; } private function guardInfra(): void @@ -668,6 +681,9 @@ class Integrations extends Component // Everything else here is issued by somebody else (Stripe, // Hetzner, the mail host) and can only be pasted in. 'generatable' => $key === 'ssh.private_key', + // Das Ergebnis GEHÖRT zu diesem Eintrag, sonst null. So + // steht die Antwort unter dem Knopf, der sie ausgelöst hat. + 'check' => $this->checkedKey === $key ? $this->check : null, ]) ->keyBy('key'), ]); diff --git a/app/Services/Secrets/SecretVault.php b/app/Services/Secrets/SecretVault.php index cd18471..d489d40 100644 --- a/app/Services/Secrets/SecretVault.php +++ b/app/Services/Secrets/SecretVault.php @@ -3,6 +3,7 @@ namespace App\Services\Secrets; use App\Models\Operator; +use App\Services\Dns\DnsTokenCheck; use App\Services\Stripe\StripeCheck; use App\Support\OperatingMode; use Illuminate\Contracts\Encryption\DecryptException; @@ -115,6 +116,12 @@ final class SecretVault 'dns.token' => [ 'config' => 'provisioning.dns.token', 'label' => 'secrets.item.dns_token', + // Die Prüfung gab es längst — sie lief nur auf der + // Bereitschaftsseite und nicht an der Karte, auf der der Token + // eingetippt wird. Ein Wert prüfen zu können, BEVOR er gespeichert + // ist, ist genau der Punkt dieses Knopfes: ein falscher Token + // fällt sonst erst auf, wenn eine Bereitstellung an ihm stirbt. + 'check' => DnsTokenCheck::class, 'env_key' => 'HETZNER_DNS_TOKEN', ], 'monitoring.token' => [ diff --git a/lang/de/secrets.php b/lang/de/secrets.php index 2b45a5e..44b500d 100644 --- a/lang/de/secrets.php +++ b/lang/de/secrets.php @@ -72,6 +72,12 @@ return [ 'saved' => 'Gespeichert. Der neue Wert gilt ab sofort.', 'removed' => 'Entfernt. Es gilt wieder der Wert aus der Serverdatei.', 'empty' => 'Bitte einen Wert eingeben.', + 'check_writable' => 'Der Token darf schreiben. Ein Probeeintrag wurde angelegt und wieder entfernt.', + 'check_read_only' => 'Der Token darf lesen, aber nicht schreiben. Damit scheitert der A-Eintrag jeder neuen Instanz — nach der Bezahlung.', + 'check_write_failed' => 'Der Schreibversuch wurde abgelehnt. Über die Berechtigung des Tokens sagt das nichts.', + 'check_zone_not_found' => 'Die eingetragene Zone liegt nicht in diesem Konto. Einzelheiten auf der Bereitschaftsseite.', + 'check_zone_list_failed' => 'Die Zonenliste kam nicht. Über die Zonen dieses Kontos sagt das nichts.', + 'check_zone_list_unreadable' => 'Die Antwort war keine Zonenliste. Da hat etwas anderes geantwortet.', 'check_title' => 'Ergebnis der Prüfung', 'check_missing' => 'Es ist kein Schlüssel hinterlegt und auch keiner in der Serverdatei.', diff --git a/lang/en/secrets.php b/lang/en/secrets.php index f54a8ae..8238def 100644 --- a/lang/en/secrets.php +++ b/lang/en/secrets.php @@ -70,6 +70,12 @@ return [ 'saved' => 'Saved. The new value applies immediately.', 'removed' => 'Removed. The server file applies again.', 'empty' => 'Enter a value.', + 'check_writable' => 'The token may write. A probe record was created and removed again.', + 'check_read_only' => 'The token may read but not write. Every new instance\'s A record then fails — after payment.', + 'check_write_failed' => 'The write was rejected. That says nothing about the token\'s permissions.', + 'check_zone_not_found' => 'The configured zone is not in this account. Details on the readiness page.', + 'check_zone_list_failed' => 'The zone list did not arrive. That says nothing about this account\'s zones.', + 'check_zone_list_unreadable' => 'The answer was not a zone list. Something else replied.', 'check_title' => 'Result', 'check_missing' => 'No key is stored here and none is in the server file.', diff --git a/resources/views/components/admin/check-result.blade.php b/resources/views/components/admin/check-result.blade.php new file mode 100644 index 0000000..2e3f5b0 --- /dev/null +++ b/resources/views/components/admin/check-result.blade.php @@ -0,0 +1,59 @@ +@props(['check', 'key']) +{{-- + Die Antwort einer Prüfung, direkt unter dem Knopf, der sie ausgelöst hat. + + Vorher stand dieser Block ganz unten auf der Seite — hinter allen Karten und + hinter dem Speichern-Knopf. Der Testknopf sitzt oben in der Stripe-Karte; + wer ihn drückte, sah die Antwort nie, weil sie ausserhalb des Bildschirms + erschien. Gemeldet wurde das als „der Knopf tut nichts", und genau so sah es + aus. + + Und der Block ging von einem STRIPE-Ergebnis aus: `account`, `live`, + `webhooks`. Ein DnsTokenCheck-Ergebnis hat davon nichts — und sein Erfolg + heisst `writable`, lief also in genau diesen Zweig. Deshalb wird hier nach + dem geprüften Eintrag verzweigt statt nach `ok`. +--}} +
{{ __('secrets.check_webhooks_unknown') }}
+ @elseif ($check['webhooks'] === []) +{{ __('secrets.check_webhooks_unknown') }}
- @elseif ($check['webhooks'] === []) -