Fix: Installer + Wizard Step 5 robuster gegen IPv6/SSL-Fehler
- installer.sh: mailwolt-apply-domains mit 3-Phasen certbot (HTTP → LE → SSL), IPv6-Check vor certbot, Zertifikat-Ablauf-Check (10 Tage), Version-Datei schreiben - WizardDomains: noipv6-Status aus Helper-Output erkennen - Wizard: retryDomains()-Methode für Wiederholung ohne neuen Wizard-Durchlauf - Step 5 Blade: Hints pro Fehlerstatus, Retry-Button, "Trotzdem zum Login" - UpdatePage: Version aus Datei, Fallback auf git describe (kein "dev" mehr) - UpdatePage: refreshLowLevelState behandelt fehlende State-Datei als idle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
3c8eaa16df
commit
01e7db589a
|
|
@ -67,12 +67,18 @@ class WizardDomains extends Command
|
|||
$ssl ? 1 : 0,
|
||||
));
|
||||
|
||||
$helperOk = $out !== null && !str_contains((string) $out, '[x]');
|
||||
$helperOk = $out !== null && !str_contains((string) $out, '[x]');
|
||||
$outStr = (string) $out;
|
||||
|
||||
foreach (['ui', 'mail', 'webmail'] as $key) {
|
||||
$status = file_get_contents(self::STATE_DIR . "/{$key}");
|
||||
if ($status === 'running' || $status === 'pending') {
|
||||
file_put_contents(self::STATE_DIR . "/{$key}", $helperOk ? 'done' : 'error');
|
||||
$domain = $domains[$key] ?? '';
|
||||
if ($domain && str_contains($outStr, "[!] {$domain}:")) {
|
||||
file_put_contents(self::STATE_DIR . "/{$key}", 'noipv6');
|
||||
} else {
|
||||
file_put_contents(self::STATE_DIR . "/{$key}", $helperOk ? 'done' : 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,29 @@ class Wizard extends Component
|
|||
}
|
||||
}
|
||||
|
||||
public function retryDomains(): void
|
||||
{
|
||||
@unlink(self::STATE_DIR . '/done');
|
||||
foreach (['ui', 'mail', 'webmail'] as $k) {
|
||||
file_put_contents(self::STATE_DIR . "/{$k}", 'pending');
|
||||
}
|
||||
|
||||
$this->domainStatus = ['ui' => 'pending', 'mail' => 'pending', 'webmail' => 'pending'];
|
||||
$this->setupDone = false;
|
||||
|
||||
$ssl = (!$this->skipSsl && app()->isProduction()) ? 1 : 0;
|
||||
$artisan = base_path('artisan');
|
||||
$cmd = sprintf(
|
||||
'nohup php %s mailwolt:wizard-domains --ui=%s --mail=%s --webmail=%s --ssl=%d > /dev/null 2>&1 &',
|
||||
escapeshellarg($artisan),
|
||||
escapeshellarg($this->ui_domain),
|
||||
escapeshellarg($this->mail_domain),
|
||||
escapeshellarg($this->webmail_domain),
|
||||
$ssl,
|
||||
);
|
||||
@shell_exec($cmd);
|
||||
}
|
||||
|
||||
public function goToLogin(): mixed
|
||||
{
|
||||
return redirect()->route('login')->with('setup_done', true);
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ class UpdatePage extends Component
|
|||
$rcRaw = @trim(@file_get_contents(self::STATE_DIR . '/rc') ?: '');
|
||||
|
||||
$this->lowState = $state !== '' ? $state : null;
|
||||
$this->running = ($this->lowState !== 'done');
|
||||
$this->running = ($this->lowState === 'running');
|
||||
$this->rc = ($this->lowState === 'done' && is_numeric($rcRaw)) ? (int) $rcRaw : null;
|
||||
}
|
||||
|
||||
|
|
@ -255,16 +255,14 @@ class UpdatePage extends Component
|
|||
|
||||
protected function readCurrentVersion(): ?string
|
||||
{
|
||||
// Lokal: direkt aus git describe lesen damit Entwicklungsumgebung immer aktuell ist
|
||||
if (app()->isLocal()) {
|
||||
$tag = @trim((string) shell_exec('git -C ' . escapeshellarg(base_path()) . ' describe --tags --abbrev=0 2>/dev/null'));
|
||||
$v = $this->normalizeVersion($tag);
|
||||
if ($v) return $v;
|
||||
}
|
||||
|
||||
$v = @trim(@file_get_contents(self::VERSION_FILE) ?: '');
|
||||
if ($v !== '') return $v;
|
||||
|
||||
// Fallback: git tag (lokal immer, production wenn Datei fehlt)
|
||||
$tag = @trim((string) shell_exec('git -C ' . escapeshellarg(base_path()) . ' describe --tags --abbrev=0 2>/dev/null'));
|
||||
$v = $this->normalizeVersion($tag);
|
||||
if ($v) return $v;
|
||||
|
||||
$raw = @trim(@file_get_contents(self::VERSION_FILE_RAW) ?: '');
|
||||
if ($raw !== '') return $this->normalizeVersion($raw);
|
||||
|
||||
|
|
|
|||
193
installer.sh
193
installer.sh
|
|
@ -583,20 +583,11 @@ PHP_FPM_SOCK="/run/php/php${PHPV}-fpm.sock"
|
|||
|
||||
APP_DIR="/var/www/mailwolt"
|
||||
NGINX_SITE="/etc/nginx/sites-available/mailwolt.conf"
|
||||
ACME_ROOT="/var/www/letsencrypt"
|
||||
mkdir -p "${ACME_ROOT}/.well-known/acme-challenge"
|
||||
|
||||
# Alle Server-Namen sammeln
|
||||
ALL_NAMES="${UI_HOST} ${WEBMAIL_HOST}"
|
||||
|
||||
# Zertifikat-Pfade ermitteln (certbot oder self-signed)
|
||||
if [ "$SSL_AUTO" = "1" ] && [ -f "/etc/letsencrypt/live/${UI_HOST}/fullchain.pem" ]; then
|
||||
CERT="/etc/letsencrypt/live/${UI_HOST}/fullchain.pem"
|
||||
KEY="/etc/letsencrypt/live/${UI_HOST}/privkey.pem"
|
||||
else
|
||||
CERT="/etc/mailwolt/ssl/cert.pem"
|
||||
KEY="/etc/mailwolt/ssl/key.pem"
|
||||
fi
|
||||
|
||||
cat > "$NGINX_SITE" <<CONF
|
||||
# --- Phase 1: HTTP-only Vhosts mit ACME-Challenge ---
|
||||
cat > "${NGINX_SITE}" <<CONF
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
|
@ -605,8 +596,10 @@ server {
|
|||
root ${APP_DIR}/public;
|
||||
index index.php index.html;
|
||||
|
||||
location /.well-known/acme-challenge/ { root /var/www/html; }
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root ${ACME_ROOT};
|
||||
try_files \$uri =404;
|
||||
}
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.php?\$query_string;
|
||||
}
|
||||
|
|
@ -617,52 +610,158 @@ server {
|
|||
location ^~ /livewire/ {
|
||||
try_files \$uri /index.php?\$query_string;
|
||||
}
|
||||
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ {
|
||||
expires 30d; access_log off;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name ${UI_HOST} ${WEBMAIL_HOST};
|
||||
|
||||
ssl_certificate ${CERT};
|
||||
ssl_certificate_key ${KEY};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
root ${APP_DIR}/public;
|
||||
index index.php index.html;
|
||||
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.php?\$query_string;
|
||||
}
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:${PHP_FPM_SOCK};
|
||||
}
|
||||
location ^~ /livewire/ {
|
||||
try_files \$uri /index.php?\$query_string;
|
||||
}
|
||||
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ {
|
||||
expires 30d; access_log off;
|
||||
}
|
||||
}
|
||||
CONF
|
||||
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
# --- Phase 2: Let's Encrypt Zertifikate holen ---
|
||||
# Prüfen ob Server globales IPv6 hat (nötig wenn AAAA-Records existieren)
|
||||
has_global_ipv6() {
|
||||
ip -6 addr show scope global 2>/dev/null | grep -q 'inet6'
|
||||
}
|
||||
|
||||
cert_needs_action() {
|
||||
local domain="$1"
|
||||
local cert="/etc/letsencrypt/live/${domain}/fullchain.pem"
|
||||
[ ! -f "${cert}" ] && return 0
|
||||
# 0 = gültig für >10 Tage → überspringen; 1 = läuft ab → erneuern
|
||||
openssl x509 -checkend 864000 -noout -in "${cert}" 2>/dev/null && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
certbot_safe() {
|
||||
local domain="$1"
|
||||
local has_aaaa
|
||||
has_aaaa=$(dig +short AAAA "${domain}" 2>/dev/null | head -1)
|
||||
if [ -n "${has_aaaa}" ] && ! has_global_ipv6; then
|
||||
echo "[!] ${domain}: AAAA-Record vorhanden aber kein IPv6 auf diesem Server — Let's Encrypt würde fehlschlagen. Self-signed wird verwendet." >&2
|
||||
return 1
|
||||
fi
|
||||
certbot certonly --webroot \
|
||||
-w "${ACME_ROOT}" \
|
||||
-d "${domain}" \
|
||||
--non-interactive --agree-tos \
|
||||
--email "webmaster@${domain}" \
|
||||
--no-eff-email
|
||||
}
|
||||
|
||||
if [ "${SSL_AUTO}" = "1" ]; then
|
||||
for DOMAIN in "${UI_HOST}" "${WEBMAIL_HOST}"; do
|
||||
[ -z "${DOMAIN}" ] && continue
|
||||
if cert_needs_action "${DOMAIN}"; then
|
||||
certbot_safe "${DOMAIN}" || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# --- Phase 3: Finale Vhosts (LE-Cert oder self-signed Fallback) ---
|
||||
(
|
||||
cat <<CONF
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name ${UI_HOST} ${WEBMAIL_HOST};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root ${ACME_ROOT};
|
||||
try_files \$uri =404;
|
||||
}
|
||||
location / { return 301 https://\$host\$request_uri; }
|
||||
}
|
||||
CONF
|
||||
|
||||
if [ -n "${UI_HOST}" ]; then
|
||||
if [ "${SSL_AUTO}" = "1" ] && [ -f "/etc/letsencrypt/live/${UI_HOST}/fullchain.pem" ]; then
|
||||
CERT_UI="/etc/letsencrypt/live/${UI_HOST}/fullchain.pem"
|
||||
KEY_UI="/etc/letsencrypt/live/${UI_HOST}/privkey.pem"
|
||||
else
|
||||
CERT_UI="/etc/mailwolt/ssl/cert.pem"
|
||||
KEY_UI="/etc/mailwolt/ssl/key.pem"
|
||||
fi
|
||||
cat <<CONF
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name ${UI_HOST};
|
||||
|
||||
ssl_certificate ${CERT_UI};
|
||||
ssl_certificate_key ${KEY_UI};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
root ${APP_DIR}/public;
|
||||
index index.php index.html;
|
||||
|
||||
location / { try_files \$uri \$uri/ /index.php?\$query_string; }
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:${PHP_FPM_SOCK};
|
||||
}
|
||||
location ^~ /livewire/ { try_files \$uri /index.php?\$query_string; }
|
||||
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ { expires 30d; access_log off; }
|
||||
}
|
||||
CONF
|
||||
fi
|
||||
|
||||
if [ -n "${WEBMAIL_HOST}" ]; then
|
||||
if [ "${SSL_AUTO}" = "1" ] && [ -f "/etc/letsencrypt/live/${WEBMAIL_HOST}/fullchain.pem" ]; then
|
||||
CERT_WM="/etc/letsencrypt/live/${WEBMAIL_HOST}/fullchain.pem"
|
||||
KEY_WM="/etc/letsencrypt/live/${WEBMAIL_HOST}/privkey.pem"
|
||||
else
|
||||
CERT_WM="/etc/mailwolt/ssl/cert.pem"
|
||||
KEY_WM="/etc/mailwolt/ssl/key.pem"
|
||||
fi
|
||||
cat <<CONF
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name ${WEBMAIL_HOST};
|
||||
|
||||
ssl_certificate ${CERT_WM};
|
||||
ssl_certificate_key ${KEY_WM};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
root ${APP_DIR}/public;
|
||||
index index.php index.html;
|
||||
|
||||
location / { try_files \$uri \$uri/ /index.php?\$query_string; }
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:${PHP_FPM_SOCK};
|
||||
}
|
||||
location ^~ /livewire/ { try_files \$uri /index.php?\$query_string; }
|
||||
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ { expires 30d; access_log off; }
|
||||
}
|
||||
CONF
|
||||
fi
|
||||
) > "${NGINX_SITE}"
|
||||
|
||||
nginx -t && systemctl reload nginx
|
||||
HELPER
|
||||
chmod 755 /usr/local/sbin/mailwolt-apply-domains
|
||||
|
||||
# ===== Sudoers für www-data (certbot + helper) =====
|
||||
# ===== Sudoers für www-data (helper) =====
|
||||
cat > /etc/sudoers.d/mailwolt-certbot <<'SUDOERS'
|
||||
www-data ALL=(root) NOPASSWD: /usr/bin/certbot
|
||||
www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-apply-domains
|
||||
SUDOERS
|
||||
chmod 440 /etc/sudoers.d/mailwolt-certbot
|
||||
|
||||
# git safe.directory damit spätere pulls als root möglich sind
|
||||
git config --global --add safe.directory "${APP_DIR}" || true
|
||||
|
||||
# ===== Version-Datei schreiben =====
|
||||
mkdir -p /var/lib/mailwolt
|
||||
GIT_TAG="$(sudo -u "$APP_USER" -H bash -lc "git -C ${APP_DIR} describe --tags --abbrev=0 2>/dev/null || echo ''")"
|
||||
if [ -n "$GIT_TAG" ]; then
|
||||
echo "${GIT_TAG#v}" > /var/lib/mailwolt/version
|
||||
echo "$GIT_TAG" > /var/lib/mailwolt/version_raw
|
||||
log "Version: ${GIT_TAG}"
|
||||
else
|
||||
warn "Kein Git-Tag gefunden — Version-Datei wird nicht geschrieben"
|
||||
fi
|
||||
|
||||
# ===== App-User/Gruppen & Rechte (am ENDE ausführen) =====
|
||||
# User anlegen (nur falls noch nicht vorhanden) + Passwort setzen + Gruppe
|
||||
if ! id -u "$APP_USER" >/dev/null 2>&1; then
|
||||
|
|
|
|||
|
|
@ -167,6 +167,11 @@
|
|||
<div wire:poll.2s="pollSetup"></div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
$anyFailed = collect($domainStatus)->contains(fn($s) => in_array($s, ['error','nodns','noipv6']));
|
||||
$allDone = $setupDone;
|
||||
@endphp
|
||||
|
||||
<div style="margin-bottom:20px">
|
||||
<div style="font-size:15px;font-weight:600;color:var(--mw-t1)">Domains werden eingerichtet</div>
|
||||
<div style="font-size:12px;color:var(--mw-t4);margin-top:3px">SSL-Zertifikate werden beantragt und Nginx wird konfiguriert</div>
|
||||
|
|
@ -175,33 +180,90 @@
|
|||
@php
|
||||
$labels = ['ui' => $ui_domain, 'mail' => $mail_domain, 'webmail' => $webmail_domain];
|
||||
$statusConfig = [
|
||||
'pending' => ['icon' => '…', 'color' => 'var(--mw-t5)', 'bg' => 'var(--mw-bg3)', 'label' => 'Warte …'],
|
||||
'running' => ['icon' => '↻', 'color' => '#7dd3fc', 'bg' => 'rgba(14,165,233,.08)', 'label' => 'Wird registriert …', 'spin' => true],
|
||||
'done' => ['icon' => '✓', 'color' => 'rgba(34,197,94,.9)', 'bg' => 'rgba(34,197,94,.07)', 'label' => 'Abgeschlossen'],
|
||||
'nodns' => ['icon' => '!', 'color' => '#fbbf24', 'bg' => 'rgba(251,191,36,.07)', 'label' => 'Kein DNS-Eintrag gefunden'],
|
||||
'error' => ['icon' => '✗', 'color' => '#f87171', 'bg' => 'rgba(239,68,68,.07)', 'label' => 'Fehler bei Registrierung'],
|
||||
'skip' => ['icon' => '–', 'color' => 'var(--mw-t4)', 'bg' => 'var(--mw-bg3)', 'label' => 'SSL übersprungen'],
|
||||
'pending' => [
|
||||
'icon' => '…', 'color' => 'var(--mw-t5)', 'bg' => 'var(--mw-bg3)',
|
||||
'label' => 'Warte …', 'hint' => null,
|
||||
],
|
||||
'running' => [
|
||||
'icon' => '↻', 'color' => '#7dd3fc', 'bg' => 'rgba(14,165,233,.08)',
|
||||
'label' => 'Wird registriert …', 'spin' => true, 'hint' => null,
|
||||
],
|
||||
'done' => [
|
||||
'icon' => '✓', 'color' => 'rgba(34,197,94,.9)', 'bg' => 'rgba(34,197,94,.07)',
|
||||
'label' => 'SSL-Zertifikat ausgestellt', 'hint' => null,
|
||||
],
|
||||
'nodns' => [
|
||||
'icon' => '!', 'color' => '#fbbf24', 'bg' => 'rgba(251,191,36,.07)',
|
||||
'label' => 'Kein DNS-Eintrag gefunden',
|
||||
'hint' => 'Der A-Record dieser Domain zeigt nicht auf diesen Server oder ist noch nicht propagiert. DNS-Einstellungen prüfen und danach Retry klicken.',
|
||||
'hints_extra' => ['DNS A-Record auf Server-IP setzen', 'DNS-Propagierung abwarten (bis zu 24h)', 'Mit dig +short A domain.com prüfen'],
|
||||
],
|
||||
'noipv6' => [
|
||||
'icon' => '!', 'color' => '#fbbf24', 'bg' => 'rgba(251,191,36,.07)',
|
||||
'label' => 'IPv6 nicht konfiguriert',
|
||||
'hint' => 'Die Domain hat einen AAAA-Record, aber dieser Server hat kein aktives IPv6. Let\'s Encrypt prüft alle DNS-Records.',
|
||||
'hints_extra' => ['IPv6 am Server aktivieren', 'ODER: AAAA-Record aus dem DNS entfernen'],
|
||||
],
|
||||
'error' => [
|
||||
'icon' => '✗', 'color' => '#f87171', 'bg' => 'rgba(239,68,68,.07)',
|
||||
'label' => 'SSL-Zertifikat fehlgeschlagen',
|
||||
'hint' => 'Let\'s Encrypt konnte die Domain nicht verifizieren. Self-signed Zertifikat wird verwendet.',
|
||||
'hints_extra' => ['Port 80 muss von außen erreichbar sein', 'Firewall-Regeln prüfen (ufw allow 80)', 'AAAA-Record ohne IPv6 am Server entfernen', 'http://domain/.well-known/acme-challenge/ im Browser testen'],
|
||||
],
|
||||
'skip' => [
|
||||
'icon' => '–', 'color' => 'var(--mw-t4)', 'bg' => 'var(--mw-bg3)',
|
||||
'label' => 'SSL übersprungen', 'hint' => null,
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:10px">
|
||||
<div style="display:flex;flex-direction:column;gap:8px">
|
||||
@foreach(['ui' => 'UI Domain', 'mail' => 'Mail Domain', 'webmail' => 'Webmail Domain'] as $key => $typeLabel)
|
||||
@php
|
||||
$st = $domainStatus[$key] ?? 'pending';
|
||||
$cfg = $statusConfig[$st] ?? $statusConfig['pending'];
|
||||
@endphp
|
||||
<div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:{{ $cfg['bg'] }};border-radius:8px;border:1px solid var(--mw-b2)">
|
||||
<div style="width:28px;height:28px;border-radius:50%;background:{{ $cfg['bg'] }};border:1px solid {{ $cfg['color'] }};display:flex;align-items:center;justify-content:center;font-size:13px;color:{{ $cfg['color'] }};flex-shrink:0;{{ isset($cfg['spin']) ? 'animation:spin .9s linear infinite' : '' }}">
|
||||
{{ $cfg['icon'] }}
|
||||
<div style="border-radius:8px;border:1px solid var(--mw-b2);overflow:hidden">
|
||||
<div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:{{ $cfg['bg'] }}">
|
||||
<div style="width:28px;height:28px;border-radius:50%;background:{{ $cfg['bg'] }};border:1px solid {{ $cfg['color'] }};display:flex;align-items:center;justify-content:center;font-size:13px;color:{{ $cfg['color'] }};flex-shrink:0;{{ isset($cfg['spin']) ? 'animation:spin .9s linear infinite' : '' }}">
|
||||
{{ $cfg['icon'] }}
|
||||
</div>
|
||||
<div style="flex:1;min-width:0">
|
||||
<div style="font-size:11px;color:var(--mw-t4)">{{ $typeLabel }}</div>
|
||||
<div style="font-size:12.5px;color:var(--mw-t1);font-family:monospace;margin-top:1px">{{ $labels[$key] }}</div>
|
||||
</div>
|
||||
<span style="font-size:11.5px;color:{{ $cfg['color'] }};white-space:nowrap">{{ $cfg['label'] }}</span>
|
||||
</div>
|
||||
<div style="flex:1;min-width:0">
|
||||
<div style="font-size:11px;color:var(--mw-t4)">{{ $typeLabel }}</div>
|
||||
<div style="font-size:12.5px;color:var(--mw-t1);font-family:monospace;margin-top:1px">{{ $labels[$key] }}</div>
|
||||
@if(!empty($cfg['hint']))
|
||||
<div style="padding:10px 14px;background:rgba(0,0,0,.15);border-top:1px solid var(--mw-b2)">
|
||||
<div style="font-size:11.5px;color:var(--mw-t3);line-height:1.5;margin-bottom:{{ !empty($cfg['hints_extra']) ? '8px' : '0' }}">
|
||||
{{ $cfg['hint'] }}
|
||||
</div>
|
||||
@if(!empty($cfg['hints_extra']))
|
||||
<ul style="margin:0;padding-left:14px;display:flex;flex-direction:column;gap:3px">
|
||||
@foreach($cfg['hints_extra'] as $hint)
|
||||
<li style="font-size:11px;color:var(--mw-t4);line-height:1.4">{{ $hint }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
<span style="font-size:11.5px;color:{{ $cfg['color'] }}">{{ $cfg['label'] }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if($allDone && $anyFailed)
|
||||
<div style="margin-top:16px;padding:12px 14px;background:rgba(239,68,68,.05);border:1px solid rgba(239,68,68,.2);border-radius:8px">
|
||||
<div style="font-size:12px;color:var(--mw-t3);margin-bottom:10px">
|
||||
Einige Domains konnten nicht vollständig eingerichtet werden. Du kannst es erneut versuchen oder mit Self-signed Zertifikat fortfahren.
|
||||
</div>
|
||||
<button wire:click="retryDomains" wire:loading.attr="disabled" style="display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border-radius:7px;font-size:12px;font-weight:500;cursor:pointer;border:1px solid rgba(99,102,241,.4);background:rgba(99,102,241,.12);color:#a5b4fc">
|
||||
<svg wire:loading.remove wire:target="retryDomains" width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M10 6A4 4 0 1 1 8.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><path d="M8.5 1v2h2" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
<svg wire:loading wire:target="retryDomains" width="12" height="12" viewBox="0 0 12 12" fill="none" style="animation:spin .7s linear infinite"><path d="M10 6A4 4 0 1 1 6 2" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
|
@ -234,7 +296,7 @@
|
|||
<div style="display:flex;justify-content:flex-end;margin-top:20px">
|
||||
<button wire:click="goToLogin" class="mbx-btn-primary" style="font-size:12.5px;width:fit-content">
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2 6.5l2.5 2.5 5.5-5.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
Zum Login
|
||||
{{ collect($domainStatus)->contains(fn($s) => in_array($s, ['error','nodns','noipv6'])) ? 'Trotzdem zum Login' : 'Zum Login' }}
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue