Always name the host we are being served from, and stop promising a duration
── Why the domain list looked wrong ─────────────────────────────────────────
Reported three times, and it was not only the setting. The list came from
`admin_access.site_hosts` and the instance zone — and where SITE_HOST is not
configured, site_hosts is EMPTY. So an installation reached by a name nobody
wrote into the config listed ONLY the instance zone, and described it as the
customers' domain: the page a visitor opened to check our addresses omitted
the address they had just typed, and named the wrong thing as ours.
The host serving the request is now always in the list. It is the one address
that is true by construction and needs no configuration to be right.
(The zone itself is still one setting, and it is now clupilot.cloud on this
machine. On another installation it is Konsole → Integrationen → DNS-Zone.)
── The clock ────────────────────────────────────────────────────────────────
"Eine Zeit angeben geht nicht — wenn es fertig ist, ist es fertig." Correct,
and it was claimed three times over: an eyebrow saying "In 20 Minuten
betriebsbereit", a provisioning log with minute marks down the side, and a
closing line repeating it. A run waits on a hypervisor, on a certificate
authority and on DNS propagating. A customer who once waits forty minutes has
been told something untrue by their supplier, and the page cannot take it back.
What replaces it is what actually IS promised and kept: the order of the
steps, ticked off, numbered 1/5 to 5/5 — and then the sentence that answers
the real question behind "how long", which is not a number but "do I have to
sit here": you get an email with the address and the credentials when it is
ready.
The same claim is gone from the hero meta ("< 20 Min." → "automatisch"), from
the figure line ("20 Minuten bis zum Betrieb" → "0 Termine bis zum Start") and
from the FAQ, which now says outright that we do not name a duration and why.
Kept deliberately: the 45-minute training session and the three-to-seven
working days for setup. Those are appointments we schedule and commitments we
control, not predictions about a machine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feature/betriebsmodus
v1.3.20
parent
3a4324fb6f
commit
d5b2612e24
|
|
@ -31,6 +31,21 @@ class OfficialDomains
|
||||||
$entries[] = ['host' => (string) $host, 'what' => 'site'];
|
$entries[] = ['host' => (string) $host, 'what' => 'site'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The host this page is being served from, always.
|
||||||
|
//
|
||||||
|
// Without it an installation that has not configured SITE_HOST — a
|
||||||
|
// development box, or one reached by a name nobody wrote into the
|
||||||
|
// config — listed ONLY the instance zone, and described it as the
|
||||||
|
// customers' domain. So the page a visitor opened to check our
|
||||||
|
// addresses omitted the address they had just typed, and named the
|
||||||
|
// wrong thing as ours. Reported exactly that way.
|
||||||
|
//
|
||||||
|
// Deduplicated below by hostname, so a configured site host does not
|
||||||
|
// appear twice.
|
||||||
|
if (($current = request()?->getHost()) && ! in_array($current, array_column($entries, 'host'), true)) {
|
||||||
|
$entries[] = ['host' => $current, 'what' => 'site'];
|
||||||
|
}
|
||||||
|
|
||||||
if ($app = (string) config('admin_access.app_host')) {
|
if ($app = (string) config('admin_access.app_host')) {
|
||||||
$entries[] = ['host' => $app, 'what' => 'portal'];
|
$entries[] = ['host' => $app, 'what' => 'portal'];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@
|
||||||
['99,95', '99.95', 2, '% Verfügbarkeit'],
|
['99,95', '99.95', 2, '% Verfügbarkeit'],
|
||||||
['365', '365', 0, 'Sicherungen pro Jahr'],
|
['365', '365', 0, 'Sicherungen pro Jahr'],
|
||||||
['12', '12', 0, 'Wiederherstellungstests'],
|
['12', '12', 0, 'Wiederherstellungstests'],
|
||||||
['20', '20', 0, 'Minuten bis zum Betrieb'],
|
['0', '0', 0, 'Termine bis zum Start'],
|
||||||
['0', '0', 0, 'Datenverluste'],
|
['0', '0', 0, 'Datenverluste'],
|
||||||
] as [$shown, $target, $decimals, $label])
|
] as [$shown, $target, $decimals, $label])
|
||||||
<div class="flex items-baseline gap-2.5">
|
<div class="flex items-baseline gap-2.5">
|
||||||
|
|
@ -262,8 +262,19 @@
|
||||||
{{-- ════ The dark hinge — how it arrives ════════════════════════════════ --}}
|
{{-- ════ The dark hinge — how it arrives ════════════════════════════════ --}}
|
||||||
<section id="ablauf" class="bg-plate py-24 text-plate-text lg:py-32">
|
<section id="ablauf" class="bg-plate py-24 text-plate-text lg:py-32">
|
||||||
<div class="mx-auto max-w-[1120px] px-5 sm:px-6">
|
<div class="mx-auto max-w-[1120px] px-5 sm:px-6">
|
||||||
|
{{-- No clock anywhere in this section any more.
|
||||||
|
"In 20 Minuten betriebsbereit", a log with minute marks down the
|
||||||
|
side, "betriebsbereit in unter 20 Minuten" — three promises about
|
||||||
|
a duration nobody controls. A provisioning run waits on a
|
||||||
|
hypervisor, on a certificate authority and on DNS propagating;
|
||||||
|
when it is done it is done, and a customer who once waited
|
||||||
|
forty minutes has been told something untrue by their supplier.
|
||||||
|
|
||||||
|
What replaces it is the thing that IS promised and IS kept: the
|
||||||
|
order of the steps, and a message when it is ready. Nobody has to
|
||||||
|
sit and watch. --}}
|
||||||
<div class="rv max-w-[46ch]">
|
<div class="rv max-w-[46ch]">
|
||||||
<p class="lbl !text-accent">In 20 Minuten betriebsbereit</p>
|
<p class="lbl !text-accent">Ohne Termin, ohne Warten</p>
|
||||||
<h2 class="mt-3 text-[clamp(2rem,4vw,3.1rem)] font-bold leading-[1.06] tracking-[-0.035em] text-white">
|
<h2 class="mt-3 text-[clamp(2rem,4vw,3.1rem)] font-bold leading-[1.06] tracking-[-0.035em] text-white">
|
||||||
Nur der erste Schritt ist Ihrer.
|
Nur der erste Schritt ist Ihrer.
|
||||||
</h2>
|
</h2>
|
||||||
|
|
@ -272,7 +283,7 @@
|
||||||
<div class="mt-14 grid gap-12 lg:grid-cols-[.85fr_1.15fr] lg:gap-16">
|
<div class="mt-14 grid gap-12 lg:grid-cols-[.85fr_1.15fr] lg:gap-16">
|
||||||
<ol class="rv space-y-8">
|
<ol class="rv space-y-8">
|
||||||
@foreach ([
|
@foreach ([
|
||||||
['Sie', 'Paket wählen', 'Zwei Minuten online. Die Instanz wird unmittelbar danach automatisch erstellt.'],
|
['Sie', 'Paket wählen', 'Ein Formular online. Unmittelbar danach beginnt die Instanz von selbst zu entstehen — Sie müssen nichts weiter tun und auf nichts warten.'],
|
||||||
['Wir', 'Einrichtung', 'Benutzer und Rechte, Ordnerstruktur, Ihr Logo, Ihre Domain. Auf Wunsch übernehmen wir Bestandsdaten aus Dropbox, OneDrive oder vom alten Server — mit schriftlichem Übernahmeprotokoll.'],
|
['Wir', 'Einrichtung', 'Benutzer und Rechte, Ordnerstruktur, Ihr Logo, Ihre Domain. Auf Wunsch übernehmen wir Bestandsdaten aus Dropbox, OneDrive oder vom alten Server — mit schriftlichem Übernahmeprotokoll.'],
|
||||||
['Gemeinsam', 'Einschulung', '45 Minuten für Ihr Team per Videokonferenz, Kurzanleitung als PDF mit Ihren eigenen Screenshots.'],
|
['Gemeinsam', 'Einschulung', '45 Minuten für Ihr Team per Videokonferenz, Kurzanleitung als PDF mit Ihren eigenen Screenshots.'],
|
||||||
] as $i => [$who, $what, $body])
|
] as $i => [$who, $what, $body])
|
||||||
|
|
@ -296,23 +307,31 @@
|
||||||
<span class="lbl !text-plate-muted">Bereitstellungsprotokoll</span>
|
<span class="lbl !text-plate-muted">Bereitstellungsprotokoll</span>
|
||||||
<span class="rounded-sm bg-plate px-2 py-0.5 font-mono text-xs text-plate-muted">Beispiel</span>
|
<span class="rounded-sm bg-plate px-2 py-0.5 font-mono text-xs text-plate-muted">Beispiel</span>
|
||||||
</div>
|
</div>
|
||||||
|
{{-- The steps in order, ticked as they pass. The column that
|
||||||
|
held minute marks now holds a number: WHAT happens and in
|
||||||
|
which order is a promise that can be kept. --}}
|
||||||
@foreach ([
|
@foreach ([
|
||||||
['0:40', 'Instanz erstellt und gestartet'],
|
'Instanz erstellt und gestartet',
|
||||||
['1:10', 'Verschlüsselung und Firewall aktiv'],
|
'Verschlüsselung und Firewall aktiv',
|
||||||
['2:30', 'Domain verbunden, Zertifikat ausgestellt'],
|
'Domain verbunden, Zertifikat ausgestellt',
|
||||||
['4:15', 'Erste Sicherung angelegt und geprüft'],
|
'Erste Sicherung angelegt und geprüft',
|
||||||
['5:00', 'Logo und Farben hinterlegt'],
|
'Logo und Farben hinterlegt',
|
||||||
] as [$at, $what])
|
] as $i => $what)
|
||||||
<div class="logline flex items-center gap-4 border-b border-plate-rule px-5 py-3.5 font-mono text-sm">
|
<div class="logline flex items-center gap-4 border-b border-plate-rule px-5 py-3.5 text-sm">
|
||||||
<time class="w-10 shrink-0 tabular-nums text-plate-muted">{{ $at }}</time>
|
<span class="grid size-5 shrink-0 place-items-center rounded-pill bg-success-bright/15">
|
||||||
|
<x-ui.icon name="check" class="size-3 text-success-bright" />
|
||||||
|
</span>
|
||||||
<span class="min-w-0 flex-1 truncate text-plate-text">{{ $what }}</span>
|
<span class="min-w-0 flex-1 truncate text-plate-text">{{ $what }}</span>
|
||||||
<span class="shrink-0 text-xs font-medium text-success-bright">OK</span>
|
<span class="shrink-0 font-mono text-xs text-plate-muted">{{ $i + 1 }}/5</span>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
<p class="flex items-center gap-2.5 px-5 py-4 text-sm font-medium text-white">
|
<div class="flex items-start gap-2.5 px-5 py-4">
|
||||||
<x-ui.icon name="check" class="size-4 text-success-bright" />
|
<x-ui.icon name="mail" class="mt-0.5 size-4 shrink-0 text-accent" />
|
||||||
Instanz betriebsbereit in unter 20 Minuten.
|
<p class="text-sm leading-relaxed text-white">
|
||||||
</p>
|
Fertig. Sie bekommen eine E-Mail mit Adresse und Zugangsdaten,
|
||||||
|
<span class="text-plate-muted">sobald Ihre Cloud bereit ist — Sie müssen nicht danebensitzen.</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -669,7 +688,7 @@
|
||||||
@foreach ([
|
@foreach ([
|
||||||
['Was passiert mit unseren Daten, wenn wir kündigen?', 'Sie erhalten vor Vertragsende einen vollständigen Export in offenen Standardformaten. Ihre Cloud basiert auf Open-Source-Software — Sie sind nie eingesperrt. Nach der Übergabe löschen wir Instanz und Sicherungen und bestätigen das schriftlich.'],
|
['Was passiert mit unseren Daten, wenn wir kündigen?', 'Sie erhalten vor Vertragsende einen vollständigen Export in offenen Standardformaten. Ihre Cloud basiert auf Open-Source-Software — Sie sind nie eingesperrt. Nach der Übergabe löschen wir Instanz und Sicherungen und bestätigen das schriftlich.'],
|
||||||
['Ersetzt das Microsoft 365?', 'Es ersetzt den Dateiteil. Viele Kunden behalten Microsoft für E-Mail und nutzen die Cloud als zentrale, sichere Ablage statt OneDrive oder Dropbox — zum fixen Firmenpreis statt pro Benutzer.'],
|
['Ersetzt das Microsoft 365?', 'Es ersetzt den Dateiteil. Viele Kunden behalten Microsoft für E-Mail und nutzen die Cloud als zentrale, sichere Ablage statt OneDrive oder Dropbox — zum fixen Firmenpreis statt pro Benutzer.'],
|
||||||
['Wie lange dauert die Einrichtung?', 'Die Instanz läuft wenige Minuten nach der Bestellung. Die vollständige Einrichtung — Benutzer und Rechte, Ordnerstruktur nach Branchenvorlage, Ihr Logo, Ihre Domain und die Einschulung — ist in drei bis sieben Werktagen abgeschlossen. Die Übernahme Ihrer Bestandsdaten aus Dropbox, OneDrive oder vom alten Server bieten wir gesondert an, inklusive schriftlichem Übernahmeprotokoll.'],
|
['Wie lange dauert die Einrichtung?', 'Die Instanz entsteht unmittelbar nach der Bestellung von selbst; Sie bekommen eine E-Mail, sobald sie bereit ist. Eine Dauer nennen wir bewusst nicht — sie hängt an Dingen, die wir nicht allein in der Hand haben. Die vollständige Einrichtung — Benutzer und Rechte, Ordnerstruktur nach Branchenvorlage, Ihr Logo, Ihre Domain und die Einschulung — ist in drei bis sieben Werktagen abgeschlossen. Die Übernahme Ihrer Bestandsdaten aus Dropbox, OneDrive oder vom alten Server bieten wir gesondert an, inklusive schriftlichem Übernahmeprotokoll.'],
|
||||||
['Was, wenn jemand versehentlich Dateien löscht?', 'Papierkorb und Versionierung fangen den Alltag ab. Darüber hinaus gibt es tägliche Sicherungen mit 30 Tagen Aufbewahrung: Ein Anruf genügt, wir stellen den gewünschten Stand wieder her.'],
|
['Was, wenn jemand versehentlich Dateien löscht?', 'Papierkorb und Versionierung fangen den Alltag ab. Darüber hinaus gibt es tägliche Sicherungen mit 30 Tagen Aufbewahrung: Ein Anruf genügt, wir stellen den gewünschten Stand wieder her.'],
|
||||||
['Brauchen wir eigene IT-Kenntnisse?', 'Nein. Bedienen kann es jeder, der mit Ordnern und Dateien umgeht — dafür gibt es die Einschulung. Alles Technische übernehmen wir dauerhaft.'],
|
['Brauchen wir eigene IT-Kenntnisse?', 'Nein. Bedienen kann es jeder, der mit Ordnern und Dateien umgeht — dafür gibt es die Einschulung. Alles Technische übernehmen wir dauerhaft.'],
|
||||||
['Mit wem spreche ich bei Fragen?', 'Direkt mit uns, per E-Mail oder Telefon, auf Deutsch, mit Antwort am selben Werktag. Kein Callcenter, keine Warteschleife.'],
|
['Mit wem spreche ich bei Fragen?', 'Direkt mit uns, per E-Mail oder Telefon, auf Deutsch, mit Antwort am selben Werktag. Kein Callcenter, keine Warteschleife.'],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue