diff --git a/app/Livewire/Admin/Hosts.php b/app/Livewire/Admin/Hosts.php index bf67494..d8e602c 100644 --- a/app/Livewire/Admin/Hosts.php +++ b/app/Livewire/Admin/Hosts.php @@ -53,6 +53,10 @@ class Hosts extends Component 'datacenters' => Datacenter::query()->orderBy('name')->get(), 'statuses' => ['pending', 'onboarding', 'active', 'error', 'disabled'], 'total' => Host::query()->count(), + // Für die aufklappbare Anleitung: dieselbe Adresse, die später in + // der Befehlszeile steht, damit hier nichts anderes behauptet wird + // als dort getan wird. + 'archiveUrl' => \App\Support\HostTakeoverCommand::archiveUrl(), ]); } } diff --git a/lang/de/hosts.php b/lang/de/hosts.php index f1fdf27..51d1be5 100644 --- a/lang/de/hosts.php +++ b/lang/de/hosts.php @@ -135,28 +135,41 @@ return [ 'complete_host_onboarding' => 'Onboarding abschließen', ], 'takeover' => [ - 'before_title' => 'Vorher: Rettungssystem starten', - 'before_body' => 'Bestelle den Server beim Anbieter und starte sein Rettungssystem, bevor du hier anlegst. Direkt nach dem Anlegen zeigt diese Seite eine Befehlszeile, die im Rettungssystem eingefügt wird — sie wird genau einmal gezeigt.', + 'how_title' => 'So läuft eine Host-Übernahme ab', + 'how_sub' => 'Sechs Schritte, etwa 20 bis 40 Minuten. Die ersten beiden passieren beim Anbieter — bevor hier etwas angelegt wird.', + + 'badge_before' => 'vorher', + 'badge_now' => 'jetzt', + 'badge_later' => 'danach', + + 's1_title' => 'Server bestellen', + 's1_body' => 'Eine dedizierte Maschine. Keine Cloud-Instanz: ohne Hardware-Virtualisierung (/dev/kvm) startet später kein einziger Gast, und das Skript weist die Maschine gleich zu Beginn ab. Bei Hetzner CPX/CX ist das immer so, bei netcup je nach Produkt.', + + 's2_title' => 'Rettungssystem starten', + 's2_body' => 'Im Kundenbereich des Anbieters das Rettungssystem einschalten UND den Server neu starten. Einschalten allein genügt nicht — er muss wirklich darin hochgefahren sein, sonst weigert sich das Skript. Es überschreibt Platten und prüft deshalb zuerst, ob es das darf.', + + 's3_title' => 'Host hier anlegen', + 's3_body' => 'Name, Rechenzentrum, öffentliche IP und root-Kennwort eintragen und speichern. Erst danach entsteht der Einmal-Code, und ab dann läuft seine Frist von 24 Stunden.', + + 's4_title' => 'Die Befehlszeile kopieren', + 's4_body' => 'Sie erscheint direkt nach dem Speichern und wird genau einmal gezeigt.', + + 's5_title' => 'Im Rettungssystem einfügen', + 's5_body' => 'Per SSH als root auf den Server, Zeile einfügen, Eingabetaste. Es wird nichts abgetippt und nichts ausgefüllt: die Zeile trägt alles, was der Server vor dem Tunnel braucht.', + 's5_hint' => 'Die Zeile holt das Installationsskript von :url, packt es nach /opt/clupilot aus und startet es. Mehr lädt der Server nicht nach.', + + 's6_title' => 'Zusehen', + 's6_body' => 'Der Fortschritt läuft auf der Host-Seite mit, Abschnitt für Abschnitt. Bis der Tunnel steht, meldet der Server nichts — das ist so gewollt und dauert über den ersten Neustart hinweg. Danach kommt alles Vorherige auf einmal nach, mit den Zeiten von damals.', 'missing_title' => 'Der Tunnel ist noch nicht eingerichtet', 'missing_body' => 'Es fehlt: :settings. Ohne diese Werte entsteht eine Befehlszeile, die sauber aussieht, läuft — und in einem Tunnel endet, der nie einen Handshake hat. Das fällt erst auf der Maschine auf. Trage sie unter Einstellungen ein, bevor du einen Host anlegst.', 'title' => ':name ist angelegt', - 'subtitle' => 'Drei Schritte, danach macht der Server den Rest allein.', + 'subtitle' => 'Noch drei Schritte, dann macht der Server den Rest allein.', 'once_title' => 'Diese Zeile gibt es nur jetzt', 'once_body' => 'In der Datenbank steht nur der Hash des Codes, der WireGuard-Schlüssel steht dort gar nicht. Wer die Seite verlässt oder neu lädt, bekommt sie nicht wieder, sondern legt einen neuen Code an — der alte ist damit wertlos.', - 'step1_title' => 'Rettungssystem starten', - 'step1_body' => 'Im Kundenbereich des Anbieters das Rettungssystem einschalten und den Server neu starten. Einschalten allein genügt nicht: er muss wirklich darin hochgefahren sein, sonst weigert sich das Skript — es überschreibt Platten und prüft deshalb zuerst, ob es das darf.', - - 'step2_title' => 'Diese Zeile im Rettungssystem einfügen', - 'step2_body' => 'Per SSH als root auf den Server, Zeile einfügen, Eingabetaste. Es wird nichts abgetippt und nichts ausgefüllt: sie trägt alles, was der Server vor dem Tunnel braucht.', - 'step2_hint' => 'Die Zeile holt das Installationsskript von :url, packt es nach /opt/clupilot aus und startet es. Mehr lädt der Server nicht nach.', - - 'step3_title' => 'Zusehen', - 'step3_body' => 'Der Fortschritt läuft in der Konsole mit, Abschnitt für Abschnitt. Bis der Tunnel steht, meldet der Server nichts — das ist so gewollt und dauert über den ersten Neustart hinweg. Danach kommt alles Vorherige auf einmal nach, mit den Zeiten von damals.', - 'watch' => 'Fortschritt ansehen', 'copy' => 'Kopieren', 'copied' => 'Kopiert', diff --git a/lang/en/hosts.php b/lang/en/hosts.php index 0f4e0b6..099f196 100644 --- a/lang/en/hosts.php +++ b/lang/en/hosts.php @@ -135,28 +135,41 @@ return [ 'complete_host_onboarding' => 'Complete onboarding', ], 'takeover' => [ - 'before_title' => 'First: boot the rescue system', - 'before_body' => 'Order the server at the provider and boot its rescue system before you add it here. Right after you add it, this page shows a command line to paste into the rescue system — shown exactly once.', + 'how_title' => 'How a host takeover works', + 'how_sub' => 'Six steps, roughly 20 to 40 minutes. The first two happen at the provider — before anything is created here.', + + 'badge_before' => 'first', + 'badge_now' => 'now', + 'badge_later' => 'then', + + 's1_title' => 'Order the server', + 's1_body' => 'A dedicated machine. Not a cloud instance: without hardware virtualisation (/dev/kvm) no guest will ever start, and the script turns the machine away at the very beginning. Hetzner CPX/CX never has it; netcup depends on the product.', + + 's2_title' => 'Boot the rescue system', + 's2_body' => 'Enable the rescue system in the provider\'s panel AND restart the server. Enabling alone is not enough — it has to actually be running in it, or the script refuses. It overwrites disks, so it checks first whether it may.', + + 's3_title' => 'Add the host here', + 's3_body' => 'Name, datacenter, public IP and root password, then save. The one-time code is created at that point, and its 24-hour window starts running.', + + 's4_title' => 'Copy the command line', + 's4_body' => 'It appears right after saving and is shown exactly once.', + + 's5_title' => 'Paste it into the rescue system', + 's5_body' => 'SSH in as root, paste the line, press enter. Nothing is typed out and nothing is filled in: the line carries everything the server needs before the tunnel exists.', + 's5_hint' => 'The line fetches the installer from :url, unpacks it to /opt/clupilot and starts it. The server downloads nothing else.', + + 's6_title' => 'Watch', + 's6_body' => 'Progress appears on the host page, section by section. Until the tunnel is up the server reports nothing — that is intended, and it lasts across the first reboot. Everything before it then arrives at once, carrying the times it actually happened.', 'missing_title' => 'The tunnel is not set up yet', 'missing_body' => 'Missing: :settings. Without these, the command line looks clean, runs, and ends in a tunnel that never handshakes — which only shows up on the machine. Fill them in under Settings before adding a host.', 'title' => ':name is created', - 'subtitle' => 'Three steps, then the server does the rest on its own.', + 'subtitle' => 'Three steps left, then the server does the rest on its own.', 'once_title' => 'This line exists only now', 'once_body' => 'The database holds only the hash of the code, and not the WireGuard key at all. Leaving or reloading this page does not bring it back — it mints a new code, and the old one becomes worthless.', - 'step1_title' => 'Boot the rescue system', - 'step1_body' => 'Enable the rescue system in the provider\'s panel and restart the server. Enabling alone is not enough: it has to actually be running in it, or the script refuses — it overwrites disks, so it checks first whether it may.', - - 'step2_title' => 'Paste this line into the rescue system', - 'step2_body' => 'SSH in as root, paste the line, press enter. Nothing is typed out and nothing is filled in: it carries everything the server needs before the tunnel exists.', - 'step2_hint' => 'The line fetches the installer from :url, unpacks it to /opt/clupilot and starts it. The server downloads nothing else.', - - 'step3_title' => 'Watch', - 'step3_body' => 'Progress appears in the console, section by section. Until the tunnel is up the server reports nothing — that is intended, and it lasts across the first reboot. Everything before it then arrives at once, carrying the times it actually happened.', - 'watch' => 'Watch progress', 'copy' => 'Copy', 'copied' => 'Copied', diff --git a/resources/views/components/admin/takeover-guide.blade.php b/resources/views/components/admin/takeover-guide.blade.php new file mode 100644 index 0000000..331a165 --- /dev/null +++ b/resources/views/components/admin/takeover-guide.blade.php @@ -0,0 +1,109 @@ +@props([ + // Die fertige Befehlszeile, sobald der Host angelegt ist — sonst null. + 'command' => null, + 'archiveUrl' => '', + 'hostUuid' => null, +]) + +{{-- Die Anleitung für eine Host-Übernahme, an EINER Stelle geschrieben. + + Sie steht bewusst dort, wo jemand sie braucht, bevor er anfängt: die + Schritte 1 und 2 passieren beim Anbieter, und wer sie erst nach dem Anlegen + liest, hat den Einmal-Code schon in der Zwischenablage und dessen Frist + schon laufen. Die erste Fassung dieser Seite zeigte die Anleitung erst + hinterher — das war genau dieser Fehler. + + Deshalb sind IMMER alle sechs Schritte zu sehen, auch die erledigten und + die kommenden. Eine Anleitung, die nur den aktuellen Schritt zeigt, + beantwortet die Frage „wie lange dauert das noch" nicht. --}} + +@php + $done = $command !== null; + // Schritt 3 ist das Formular auf dieser Seite; ist die Zeile da, sind 1–3 + // vorbei und 4 ist dran. + $current = $done ? 4 : 3; + + $steps = [ + 1 => ['title' => __('hosts.takeover.s1_title'), 'body' => __('hosts.takeover.s1_body')], + 2 => ['title' => __('hosts.takeover.s2_title'), 'body' => __('hosts.takeover.s2_body')], + 3 => ['title' => __('hosts.takeover.s3_title'), 'body' => __('hosts.takeover.s3_body')], + 4 => ['title' => __('hosts.takeover.s4_title'), 'body' => __('hosts.takeover.s4_body')], + 5 => ['title' => __('hosts.takeover.s5_title'), 'body' => __('hosts.takeover.s5_body')], + 6 => ['title' => __('hosts.takeover.s6_title'), 'body' => __('hosts.takeover.s6_body')], + ]; +@endphp + +
{{ __('hosts.takeover.how_sub') }}
+{{ $step['body'] }}
+ + @if ($number === 4 && $done) +{{ $command }}
+
+ {{ __('hosts.takeover.s5_hint', ['url' => $archiveUrl]) }}
+ @endif + + @if ($number === 6 && $hostUuid) + + @endif +{{ __('hosts.takeover.footnote') }}
+{{ __('hosts.create_sub') }}
- {{-- Was der Betreiber VORHER wissen muss, steht vor dem Formular und - nicht danach: das Rettungssystem zu starten dauert beim Anbieter ein - paar Minuten. Wer das erst hinterher liest, hat den Code schon in - der Zwischenablage und wartet. --}} -{{ __('hosts.takeover.before_title') }}
-{{ __('hosts.takeover.before_body') }}
-{{ __('hosts.takeover.missing_title') }}
@@ -24,6 +15,12 @@{{ __('hosts.takeover.subtitle') }}
@@ -71,58 +67,6 @@{{ __('hosts.takeover.once_body') }}
-{{ __('hosts.takeover.step1_body') }}
-{{ __('hosts.takeover.step2_body') }}
- -{{ $command }}
-
- {{ __('hosts.takeover.step2_hint', ['url' => $archiveUrl]) }}
-{{ __('hosts.takeover.step3_body') }}
- -{{ __('hosts.takeover.footnote') }}
+