feat: Toastra-Plugin eingebunden + DKIM-Regenerierung fix

- Toastra als Toast-System integriert (ersetzt GlassToastra)
- CSS auf var(--mw-*) Dark-Theme angepasst, keine Klassen entfernt
- Livewire-Adapter mappt done/warn/error/info auf Toastra-Typen
- DnsDkim: doppelter sudo-install-dkim entfernt (DkimService macht es bereits)
- dkimReady() prüft Storage-Pfad statt sudo-Test (kein Sudoers-Eintrag nötig)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main v1.1.376
boban 2026-04-27 00:29:29 +02:00
parent f375ea0215
commit 486166c05a
5 changed files with 63 additions and 51 deletions

View File

@ -4,7 +4,6 @@ namespace App\Livewire\Ui\Nx\Domain;
use App\Models\Domain;
use App\Services\DkimService;
use Illuminate\Support\Facades\Process;
use Livewire\Attributes\Layout;
use Livewire\Attributes\On;
use Livewire\Attributes\Title;
@ -44,22 +43,7 @@ class DnsDkim extends Component
try {
/** @var DkimService $svc */
$svc = app(DkimService::class);
$res = $svc->generateForDomain($domain, 2048, $selector);
$priv = $res['priv_path'] ?? storage_path("app/private/dkim/{$domain->domain}/{$selector}.private");
$txt = storage_path("app/private/dkim/{$domain->domain}/{$selector}.txt");
if (!is_readable($txt) && !empty($res['dns_txt'])) {
file_put_contents($txt, $res['dns_txt']);
}
$proc = Process::run(['sudo', '-n', '/usr/local/sbin/mailwolt-install-dkim',
$domain->domain, $selector, $priv, $txt]);
if (!$proc->successful()) {
throw new \RuntimeException($proc->errorOutput());
}
Process::run(['sudo', '-n', '/usr/bin/systemctl', 'reload', 'opendkim']);
$svc->generateForDomain($domain, 2048, $selector);
$this->dispatch('toast', type: 'done', badge: 'DKIM',
title: 'DKIM erneuert', text: "Schlüssel für <b>{$domain->domain}</b> wurde neu generiert.", duration: 5000);
@ -71,8 +55,8 @@ class DnsDkim extends Component
private function dkimReady(string $domain, string $selector): bool
{
return Process::run(['sudo', '-n', '/usr/bin/test', '-s',
"/etc/opendkim/keys/{$domain}/{$selector}.private"])->successful();
$path = storage_path("app/private/dkim/{$domain}/{$selector}.private");
return is_file($path) && filesize($path) > 0;
}
public function render()

View File

@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '../js/plugins/GlassToastra/style.css';
@import '../js/plugins/Toastra/src/message.css';
@import "../fonts/BaiJamjuree/font.css";
@import "../fonts/Space/font.css";

View File

@ -9,7 +9,7 @@ import "@phosphor-icons/web/regular";
import "@phosphor-icons/web/bold";
import './components/sidebar.js';
import './plugins/GlassToastra/toastra.glass.js'
import './plugins/GlassToastra/livewire-adapter';
import './plugins/Toastra/index.js';
import './plugins/Toastra/livewire-adapter';
import './utils/events.js';

View File

@ -0,0 +1,35 @@
// Maps Livewire `toast` events → window.toastra.notify()
const TYPE_MAP = {
done: 'success',
warn: 'warning',
warning: 'warning',
error: 'error',
info: 'info',
default: 'default',
update: 'update',
};
function dispatchToast(detail) {
if (!window.toastra) return;
const { type = 'info', badge, title, text, duration } = detail || {};
window.toastra.notify({
type: TYPE_MAP[type] ?? 'info',
badge: badge ?? '',
title: title ?? '',
text: text ?? '',
duration: duration !== undefined ? duration : 4000,
close: true,
progressbar: true,
icon: true,
});
}
document.addEventListener('livewire:init', () => {
window.addEventListener('toast', (e) => dispatchToast(e?.detail || {}));
window.addEventListener('toast.clear', () => { /* Toastra hat kein clear-API */ });
window.addEventListener('toast.reload',(e) => setTimeout(() => window.location.reload(), e?.detail?.delay || 0));
});
window.showToast = dispatchToast;

View File

@ -603,13 +603,13 @@
/* background: linear-gradient(180deg, var(--accent), rgba(var(--toast-red), .6));*/
/* border-radius: 3px 0 0 3px;*/
/*}*/
/* ===== Toast · Glass v2 (Badge) ===== */
/* ===== Toast · Glass v2 mw dark theme ===== */
.notification.glass-mode .notification-container {
backdrop-filter: blur(14px);
background: rgba(17, 24, 39, .52); /* dunkleres Glas */
border: 1px solid rgba(255,255,255,.09);
background: color-mix(in srgb, var(--mw-bg3, #111420) 88%, transparent);
border: 1px solid var(--mw-b1, #1c2035);
border-radius: 16px;
box-shadow: 0 12px 40px rgba(0,0,0,.28);
box-shadow: 0 12px 40px rgba(0,0,0,.40);
}
.notification.glass-mode .notification-block {
@ -634,10 +634,10 @@
display: flex;
align-items: center;
gap: 10px;
padding-right: 36px; /* Platz für Close */
padding-right: 36px;
font-weight: 700;
letter-spacing: .2px;
color: #e5e7eb; /* text-gray-200 */
color: var(--mw-t1, #edf0fc);
}
/* Badge aus data-badge rendern */
@ -648,17 +648,17 @@
letter-spacing: .6px;
padding: 4px 8px;
border-radius: 9px;
border: 1px solid rgba(255,255,255,.10);
background: rgba(31,41,55,.55); /* dunkles Glas */
color: #cbd5e1; /* text-gray-300 */
box-shadow: inset 0 1px 0 rgba(255,255,255,.08);
border: 1px solid var(--mw-b2, #242840);
background: var(--mw-bg4, #161a28);
color: var(--mw-t3, #737b96);
box-shadow: inset 0 1px 0 rgba(255,255,255,.06);
}
/* Subtext */
.notification.glass-mode .notification-message {
margin-top: 6px;
line-height: 1.35;
color: #cbd5e1; /* text-gray-300 */
color: var(--mw-t2, #9aa3be);
}
/* Close-Button dezenter */
@ -666,12 +666,12 @@
top: 10px; right: 10px;
width: 30px; height: 30px;
display: grid; place-items: center;
background: rgba(255,255,255,.06);
border: 1px solid rgba(255,255,255,.10);
box-shadow: 0 6px 20px rgba(0,0,0,.25);
background: var(--mw-bg4, #161a28);
border: 1px solid var(--mw-b2, #242840);
box-shadow: 0 4px 12px rgba(0,0,0,.30);
}
.notification.glass-mode .notification-dismiss-btn:hover {
background: rgba(255,255,255,.12);
background: var(--mw-b1, #1c2035);
}
/* Progressbar dünn & soft */
@ -747,15 +747,15 @@
letter-spacing: .6px;
padding: 4px 8px;
border-radius: 9px;
border: 1px solid rgba(255,255,255,.10);
background: rgba(31,41,55,.55);
color: #cbd5e1; /* gray-300 */
box-shadow: inset 0 1px 0 rgba(255,255,255,.08);
border: 1px solid var(--mw-b2, #242840);
background: var(--mw-bg4, #161a28);
color: var(--mw-t3, #737b96);
box-shadow: inset 0 1px 0 rgba(255,255,255,.06);
}
/* keep your bold style on the title text itself */
.notification-title-text b {
color: #e5e7eb; /* gray-200 */
color: var(--mw-t1, #edf0fc);
font-weight: 800;
letter-spacing: .3px;
}
@ -764,14 +764,7 @@
.notification-subtitle {
margin-top: .25rem;
font-weight: 700;
color: #e5e7eb; /* gray-200 */
color: var(--mw-t1, #edf0fc);
}
/* Glass container (you already had this, listing again for completeness) */
.notification.glass-mode .notification-container {
backdrop-filter: blur(10px);
background: rgba(31,41,55,.45);
border: 1px solid rgba(255,255,255,.08);
border-radius: 14px;
box-shadow: 0 8px 30px rgba(0,0,0,.25);
}
/* Glass container duplicate removed see above */