parent
2fecb09984
commit
7b62798d6c
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Models\MailUser;
|
use App\Models\MailUser;
|
||||||
|
use App\Models\Setting;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use RecursiveDirectoryIterator;
|
use RecursiveDirectoryIterator;
|
||||||
use RecursiveIteratorIterator;
|
use RecursiveIteratorIterator;
|
||||||
|
|
@ -58,12 +59,11 @@ class UpdateMailboxStats extends Command
|
||||||
$messageCount = $this->messageCountViaFilesystem($maildir);
|
$messageCount = $this->messageCountViaFilesystem($maildir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update DB
|
Setting::set("mailbox.{$email}", [
|
||||||
$u->forceFill([
|
'used_bytes' => $usedBytes,
|
||||||
'used_bytes' => $usedBytes,
|
'message_count' => $messageCount,
|
||||||
'message_count' => (int) $messageCount,
|
'updated_at' => now()->toDateTimeString(),
|
||||||
'stats_refreshed_at' => now(),
|
]);
|
||||||
])->save();
|
|
||||||
|
|
||||||
$this->line(sprintf(
|
$this->line(sprintf(
|
||||||
"%-35s %7.1f MiB %5d msgs",
|
"%-35s %7.1f MiB %5d msgs",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
namespace App\Livewire\Ui\Mail;
|
namespace App\Livewire\Ui\Mail;
|
||||||
|
|
||||||
use App\Models\Domain;
|
use App\Models\Domain;
|
||||||
|
use App\Models\Setting;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Attributes\On;
|
use Livewire\Attributes\On;
|
||||||
|
|
@ -72,6 +73,19 @@ class MailboxList extends Component
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateMailboxStatsOne(string $email)
|
||||||
|
{
|
||||||
|
Artisan::call('mail:update-stats', ['--user' => $email]);
|
||||||
|
$this->dispatch('$refresh');
|
||||||
|
$this->dispatch('toast',
|
||||||
|
type: 'done',
|
||||||
|
badge: 'Mailbox',
|
||||||
|
title: 'Mailbox aktualisiert',
|
||||||
|
text: 'Die Mailbox-Statistiken wurden aktualisiert.',
|
||||||
|
duration: 6000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$system = Domain::query()->where('is_system', true)->first();
|
$system = Domain::query()->where('is_system', true)->first();
|
||||||
|
|
@ -82,17 +96,17 @@ class MailboxList extends Component
|
||||||
$domains = Domain::query()
|
$domains = Domain::query()
|
||||||
->when($system, fn ($q) => $q->whereKeyNot($system->id))
|
->when($system, fn ($q) => $q->whereKeyNot($system->id))
|
||||||
|
|
||||||
// Domain selbst ODER MailUser/ Aliasse müssen matchen
|
// Domain selbst ODER MailUser müssen matchen
|
||||||
->when($hasTerm, function ($q) use ($needle) {
|
->when($hasTerm, function ($q) use ($needle) {
|
||||||
$q->where(function ($w) use ($needle) {
|
$q->where(function ($w) use ($needle) {
|
||||||
$w->where('domain', 'like', $needle)
|
$w->where('domain', 'like', $needle)
|
||||||
->orWhereHas('mailUsers', fn($u) => $u->where('localpart', 'like', $needle));
|
->orWhereHas('mailUsers', fn($u) => $u->where('localpart', 'like', $needle));
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
->withCount(['mailUsers'])
|
->withCount(['mailUsers'])
|
||||||
|
|
||||||
// Beziehungen zunächst gefiltert laden (damit "test" nur passende Mailboxen zeigt)
|
// Relationen zunächst ggf. gefiltert laden
|
||||||
->with([
|
->with([
|
||||||
'mailUsers' => function ($q) use ($hasTerm, $needle) {
|
'mailUsers' => function ($q) use ($hasTerm, $needle) {
|
||||||
if ($hasTerm) $q->where('localpart', 'like', $needle);
|
if ($hasTerm) $q->where('localpart', 'like', $needle);
|
||||||
|
|
@ -103,32 +117,39 @@ class MailboxList extends Component
|
||||||
->orderBy('domain')
|
->orderBy('domain')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
// Domains, deren NAME den Suchbegriff trifft → ALLE Mailboxen/Aliasse zeigen
|
// Wenn der Domainname selbst matched → alle Mailboxen/Aliasse vollständig nachladen
|
||||||
if ($hasTerm) {
|
if ($hasTerm) {
|
||||||
$lower = Str::lower($term);
|
$lower = Str::lower($term);
|
||||||
foreach ($domains as $d) {
|
foreach ($domains as $d) {
|
||||||
if (Str::contains(Str::lower($d->domain), $lower)) {
|
if (Str::contains(Str::lower($d->domain), $lower)) {
|
||||||
// volle Relationen nachladen (überschreibt die gefilterten)
|
|
||||||
$d->setRelation('mailUsers', $d->mailUsers()->orderBy('localpart')->get());
|
$d->setRelation('mailUsers', $d->mailUsers()->orderBy('localpart')->get());
|
||||||
$d->setRelation('mailAliases', $d->mailAliases()->orderBy('local')->get());
|
$d->setRelation('mailAliases', $d->mailAliases()->orderBy('local')->get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vorbereitung für Blade (unverändert, arbeitet auf den ggf. gefilterten Relationen)
|
// Vorbereitung für Blade
|
||||||
foreach ($domains as $d) {
|
foreach ($domains as $d) {
|
||||||
$prepared = [];
|
$prepared = [];
|
||||||
$domainActive = (bool)($d->is_active ?? true);
|
$domainActive = (bool)($d->is_active ?? true);
|
||||||
|
|
||||||
foreach ($d->mailUsers as $u) {
|
foreach ($d->mailUsers as $u) {
|
||||||
$usedMB = (int) round(($u->used_bytes ?? 0) / 1024 / 1024);
|
// E-Mail sicher bestimmen (Fallback aus localpart + Domain)
|
||||||
|
$email = $u->email ?? ($u->localpart ? ($u->localpart.'@'.$d->domain) : null);
|
||||||
|
|
||||||
|
// Stats aus Redis/DB holen (kann array, json-string oder null sein)
|
||||||
|
$statsRaw = $email ? Setting::get("mailbox.$email") : null;
|
||||||
|
$stats = is_array($statsRaw)
|
||||||
|
? $statsRaw
|
||||||
|
: (is_string($statsRaw) ? (json_decode($statsRaw, true) ?: null) : null);
|
||||||
|
|
||||||
|
// Werte priorisieren: Setting → DB-Felder → 0
|
||||||
|
$usedBytes = (int)($stats['used_bytes'] ?? ($u->used_bytes ?? 0));
|
||||||
|
$messageCount = (int)($stats['message_count'] ?? ($u->message_count ?? 0));
|
||||||
|
|
||||||
|
$usedMB = (int) round($usedBytes / 1024 / 1024);
|
||||||
$quota = (int)($u->quota_mb ?? 0);
|
$quota = (int)($u->quota_mb ?? 0);
|
||||||
$usage = $quota > 0 ? min(100, (int) round($usedMB / max(1,$quota) * 100)) : 0;
|
$usage = $quota > 0 ? min(100, (int) round($usedMB / max(1, $quota) * 100)) : 0;
|
||||||
|
|
||||||
|
|
||||||
// $quota = (int)($u->quota_mb ?? 0);
|
|
||||||
// $used = (int)($u->used_mb ?? 0);
|
|
||||||
// $usage = $quota > 0 ? min(100, (int)round($used / max(1, $quota) * 100)) : 0;
|
|
||||||
|
|
||||||
$mailboxActive = (bool)($u->is_active ?? true);
|
$mailboxActive = (bool)($u->is_active ?? true);
|
||||||
$effective = $domainActive && $mailboxActive;
|
$effective = $domainActive && $mailboxActive;
|
||||||
|
|
@ -144,17 +165,14 @@ class MailboxList extends Component
|
||||||
'localpart' => (string)$u->localpart,
|
'localpart' => (string)$u->localpart,
|
||||||
'quota_mb' => $quota,
|
'quota_mb' => $quota,
|
||||||
'usage_percent' => $usage,
|
'usage_percent' => $usage,
|
||||||
'used_mb' => $usedMB,
|
'used_mb' => $usedMB, // MiB fürs UI
|
||||||
'message_count' => (int) ($u->message_count ?? 0),
|
'message_count' => $messageCount,
|
||||||
// 'used_mb' => $used,
|
|
||||||
// 'message_count' => (int)($u->message_count ?? $u->mails_count ?? 0),
|
|
||||||
'is_active' => $mailboxActive,
|
'is_active' => $mailboxActive,
|
||||||
'is_effective_active' => $effective,
|
'is_effective_active' => $effective,
|
||||||
'inactive_reason' => $reason,
|
'inactive_reason' => $reason,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// für Blade
|
|
||||||
$d->prepared_mailboxes = $prepared;
|
$d->prepared_mailboxes = $prepared;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,6 +181,98 @@ class MailboxList extends Component
|
||||||
'system' => $this->showSystemCard ? $system : null,
|
'system' => $this->showSystemCard ? $system : null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
// public function render()
|
||||||
|
// {
|
||||||
|
// $system = Domain::query()->where('is_system', true)->first();
|
||||||
|
// $term = trim($this->search);
|
||||||
|
// $hasTerm = $term !== '';
|
||||||
|
// $needle = '%'.str_replace(['%','_'], ['\%','\_'], $term).'%'; // LIKE-sicher
|
||||||
|
//
|
||||||
|
// $domains = Domain::query()
|
||||||
|
// ->when($system, fn ($q) => $q->whereKeyNot($system->id))
|
||||||
|
//
|
||||||
|
// // Domain selbst ODER MailUser/ Aliasse müssen matchen
|
||||||
|
// ->when($hasTerm, function ($q) use ($needle) {
|
||||||
|
// $q->where(function ($w) use ($needle) {
|
||||||
|
// $w->where('domain', 'like', $needle)
|
||||||
|
// ->orWhereHas('mailUsers', fn($u) => $u->where('localpart', 'like', $needle));
|
||||||
|
// });
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// ->withCount(['mailUsers'])
|
||||||
|
//
|
||||||
|
// // Beziehungen zunächst gefiltert laden (damit "test" nur passende Mailboxen zeigt)
|
||||||
|
// ->with([
|
||||||
|
// 'mailUsers' => function ($q) use ($hasTerm, $needle) {
|
||||||
|
// if ($hasTerm) $q->where('localpart', 'like', $needle);
|
||||||
|
// $q->orderBy('localpart');
|
||||||
|
// },
|
||||||
|
// ])
|
||||||
|
//
|
||||||
|
// ->orderBy('domain')
|
||||||
|
// ->get();
|
||||||
|
//
|
||||||
|
// // Domains, deren NAME den Suchbegriff trifft → ALLE Mailboxen/Aliasse zeigen
|
||||||
|
// if ($hasTerm) {
|
||||||
|
// $lower = Str::lower($term);
|
||||||
|
// foreach ($domains as $d) {
|
||||||
|
// if (Str::contains(Str::lower($d->domain), $lower)) {
|
||||||
|
// // volle Relationen nachladen (überschreibt die gefilterten)
|
||||||
|
// $d->setRelation('mailUsers', $d->mailUsers()->orderBy('localpart')->get());
|
||||||
|
// $d->setRelation('mailAliases', $d->mailAliases()->orderBy('local')->get());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Vorbereitung für Blade (unverändert, arbeitet auf den ggf. gefilterten Relationen)
|
||||||
|
// foreach ($domains as $d) {
|
||||||
|
// $prepared = [];
|
||||||
|
// $domainActive = (bool)($d->is_active ?? true);
|
||||||
|
//
|
||||||
|
// foreach ($d->mailUsers as $u) {
|
||||||
|
// $stats = Setting::get("mailbox.{$u->email}");
|
||||||
|
// $usedBytes = $stats['used_bytes'] ?? ($u->used_bytes ?? 0);
|
||||||
|
// $messageCount = $stats['message_count'] ?? ($u->message_count ?? 0);
|
||||||
|
// $usedMB = (int) round(($usedBytes) / 1024 / 1024);
|
||||||
|
// $quota = (int)($u->quota_mb ?? 0);
|
||||||
|
// $usage = $quota > 0 ? min(100, (int) round($usedMB / max(1,$quota) * 100)) : 0;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//// $quota = (int)($u->quota_mb ?? 0);
|
||||||
|
//// $used = (int)($u->used_mb ?? 0);
|
||||||
|
//// $usage = $quota > 0 ? min(100, (int)round($used / max(1, $quota) * 100)) : 0;
|
||||||
|
//
|
||||||
|
// $mailboxActive = (bool)($u->is_active ?? true);
|
||||||
|
// $effective = $domainActive && $mailboxActive;
|
||||||
|
//
|
||||||
|
// $reason = null;
|
||||||
|
// if (!$effective) {
|
||||||
|
// $reason = !$domainActive ? 'Domain inaktiv'
|
||||||
|
// : (!$mailboxActive ? 'Postfach inaktiv' : null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $prepared[] = [
|
||||||
|
// 'id' => $u->id,
|
||||||
|
// 'localpart' => (string)$u->localpart,
|
||||||
|
// 'quota_mb' => $quota,
|
||||||
|
// 'usage_percent' => $usage,
|
||||||
|
// 'used_mb' => $usedMB,
|
||||||
|
// 'message_count' => $messageCount,
|
||||||
|
// 'is_active' => $mailboxActive,
|
||||||
|
// 'is_effective_active' => $effective,
|
||||||
|
// 'inactive_reason' => $reason,
|
||||||
|
// ];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // für Blade
|
||||||
|
// $d->prepared_mailboxes = $prepared;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return view('livewire.ui.mail.mailbox-list', [
|
||||||
|
// 'domains' => $domains,
|
||||||
|
// 'system' => $this->showSystemCard ? $system : null,
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
|
||||||
// public function render()
|
// public function render()
|
||||||
// {
|
// {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Ui\System;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class UpdateManager extends Component
|
||||||
|
{
|
||||||
|
public bool $running = false;
|
||||||
|
public string $log = '';
|
||||||
|
public ?int $rc = null;
|
||||||
|
public ?string $latest = null; // optional: von Cache o.ä.
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->refreshState();
|
||||||
|
// Optional: latest Tag/Version aus Cache anzeigen
|
||||||
|
$this->latest = cache('mailwolt.update_available');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refreshState(): void
|
||||||
|
{
|
||||||
|
$state = @trim(@file_get_contents('/var/lib/mailwolt/update/state') ?: '');
|
||||||
|
$this->running = ($state === 'running');
|
||||||
|
|
||||||
|
$rcRaw = @trim(@file_get_contents('/var/lib/mailwolt/update/rc') ?: '');
|
||||||
|
$this->rc = is_numeric($rcRaw) ? (int)$rcRaw : null;
|
||||||
|
|
||||||
|
// letzte 200 Zeilen Log
|
||||||
|
$this->log = @shell_exec('tail -n 200 /var/log/mailwolt-update.log 2>/dev/null') ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runUpdate(): void
|
||||||
|
{
|
||||||
|
// Hinweis „Update verfügbar“ ausblenden
|
||||||
|
cache()->forget('mailwolt.update_available');
|
||||||
|
|
||||||
|
// Update im Hintergrund starten
|
||||||
|
@shell_exec('nohup sudo -n /usr/local/sbin/mw-update >/dev/null 2>&1 &');
|
||||||
|
|
||||||
|
$this->running = true;
|
||||||
|
$this->dispatch('toast',
|
||||||
|
type: 'info',
|
||||||
|
badge: 'Update',
|
||||||
|
title: 'Update gestartet',
|
||||||
|
text: 'Das System wird aktualisiert …',
|
||||||
|
duration: 6000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function poll(): void
|
||||||
|
{
|
||||||
|
$before = $this->running;
|
||||||
|
$this->refreshState();
|
||||||
|
|
||||||
|
if ($before && !$this->running && $this->rc !== null) {
|
||||||
|
$ok = ($this->rc === 0);
|
||||||
|
$this->dispatch('toast',
|
||||||
|
type: $ok ? 'done' : 'warn',
|
||||||
|
badge: 'Update',
|
||||||
|
title: $ok ? 'Update abgeschlossen' : 'Update fehlgeschlagen',
|
||||||
|
text: $ok ? 'Die neue Version ist aktiv.' : "Fehlercode: {$this->rc}. Log prüfen.",
|
||||||
|
duration: 8000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.ui.system.update-manager');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,20 +9,19 @@
|
||||||
<button class="sidebar-toggle translate-0 right-5 block sm:hidden text-white/60 hover:text-white text-2xl">
|
<button class="sidebar-toggle translate-0 right-5 block sm:hidden text-white/60 hover:text-white text-2xl">
|
||||||
<i class="ph ph-list"></i>
|
<i class="ph ph-list"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{{-- @if ($latest = cache('mailwolt.update_available'))--}}
|
||||||
@if ($latest = cache('mailwolt.update_available'))
|
{{-- <div class="bg-blue-900/40 text-blue-100 p-4 rounded-xl border border-blue-800">--}}
|
||||||
<div class="bg-blue-900/40 text-blue-100 p-4 rounded-xl border border-blue-800">
|
{{-- <div class="flex justify-between items-center">--}}
|
||||||
<div class="flex justify-between items-center">
|
{{-- <div>--}}
|
||||||
<div>
|
{{-- <strong>Neue Version verfügbar:</strong> {{ $latest }}--}}
|
||||||
<strong>Neue Version verfügbar:</strong> {{ $latest }}
|
{{-- </div>--}}
|
||||||
</div>
|
{{-- <button wire:click="runUpdate"--}}
|
||||||
<button wire:click="runUpdate"
|
{{-- class="bg-blue-600 hover:bg-blue-500 text-white px-3 py-1 rounded">--}}
|
||||||
class="bg-blue-600 hover:bg-blue-500 text-white px-3 py-1 rounded">
|
{{-- Jetzt aktualisieren--}}
|
||||||
Jetzt aktualisieren
|
{{-- </button>--}}
|
||||||
</button>
|
{{-- </div>--}}
|
||||||
</div>
|
{{-- </div>--}}
|
||||||
</div>
|
{{-- @endif--}}
|
||||||
@endif
|
|
||||||
{{-- <button id="sidebar-toggle-btn"--}}
|
{{-- <button id="sidebar-toggle-btn"--}}
|
||||||
{{-- class="action-button sidebar-toggle flex items-center justify-center p-1.5 shadow-2xl rounded">--}}
|
{{-- class="action-button sidebar-toggle flex items-center justify-center p-1.5 shadow-2xl rounded">--}}
|
||||||
{{-- <svg class="size-5 group-[.expanded]/side:rotate-180"--}}
|
{{-- <svg class="size-5 group-[.expanded]/side:rotate-180"--}}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,15 @@
|
||||||
<x-button.copy-btn :text="$r['value']" />
|
<x-button.copy-btn :text="$r['value']" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="px-4 pb-3">
|
||||||
|
<pre class="text-[12px] w-full rounded-lg bg-white/5 border border-white/10 text-white px-3 py-2 text-sm opacity-70 whitespace-pre-wrap break-all">{{ $r['value'] }}</pre>
|
||||||
|
@if(!empty($r['helpUrl']))
|
||||||
|
<a href="{{ $r['helpUrl'] }}" target="_blank" rel="noopener"
|
||||||
|
class="mt-2 inline-flex items-center gap-1 text-[12px] text-sky-300 hover:text-sky-200">
|
||||||
|
<i class="ph ph-arrow-square-out"></i>{{ $r['helpLabel'] }}
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,8 @@
|
||||||
|
|
||||||
<td class="px-3 py-2 rounded-r-xl">
|
<td class="px-3 py-2 rounded-r-xl">
|
||||||
<div class="flex items-center gap-2 justify-end">
|
<div class="flex items-center gap-2 justify-end">
|
||||||
|
<button wire:click="updateMailboxStatsOne('{{ $u['localpart'].'@'.$domain->domain }}')">Jetzt aktualisieren</button>
|
||||||
|
|
||||||
<button wire:click="updateMailboxStats"
|
<button wire:click="updateMailboxStats"
|
||||||
class="px-3 py-1 text-sm rounded-md bg-blue-600 text-white hover:bg-blue-700 transition">
|
class="px-3 py-1 text-sm rounded-md bg-blue-600 text-white hover:bg-blue-700 transition">
|
||||||
Jetzt aktualisieren
|
Jetzt aktualisieren
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<div class="bg-white/5 border border-white/10 rounded-xl p-4"
|
||||||
|
@if($running) wire:poll.3s="poll" @endif>
|
||||||
|
<div class="flex items-center justify-between gap-3">
|
||||||
|
<div class="text-white/80">
|
||||||
|
<strong>MailWolt Update</strong>
|
||||||
|
@if($latest)
|
||||||
|
<span class="ml-2 px-2 py-0.5 rounded-full text-xs border border-blue-400/40 bg-blue-500/10 text-blue-200">
|
||||||
|
verfügbar: {{ $latest }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
@if($running)
|
||||||
|
<span class="ml-2 px-2 py-0.5 rounded-full text-xs border border-white/20 bg-white/10">läuft …</span>
|
||||||
|
@elseif(!is_null($rc))
|
||||||
|
<span class="ml-2 px-2 py-0.5 rounded-full text-xs border {{ $rc===0 ? 'border-emerald-400/40 bg-emerald-500/10 text-emerald-200' : 'border-rose-400/40 bg-rose-500/10 text-rose-200' }}">
|
||||||
|
{{ $rc===0 ? 'fertig' : 'fehler' }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<button wire:click="runUpdate"
|
||||||
|
@disabled($running)
|
||||||
|
class="bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white px-3 py-1 rounded">
|
||||||
|
Jetzt aktualisieren
|
||||||
|
</button>
|
||||||
|
<button wire:click="refreshState"
|
||||||
|
class="border border-white/10 bg-white/5 text-white/80 px-3 py-1 rounded hover:border-white/20">
|
||||||
|
Neu laden
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($running || !empty($log))
|
||||||
|
<pre class="mt-3 max-h-64 overflow-auto text-xs bg-black/40 rounded p-3 border border-white/10 text-white/80">{{ $log }}</pre>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
@ -6,6 +6,10 @@
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="max-w-7xl mx-auto space-y-6 px-2 md:px-4">
|
<div class="max-w-7xl mx-auto space-y-6 px-2 md:px-4">
|
||||||
|
<div class="max-w-fit col-span-4 #lg:col-span-6">
|
||||||
|
<livewire:ui.system.update-manager />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-span-12 lg:col-span-6">
|
<div class="col-span-12 lg:col-span-6">
|
||||||
<livewire:ui.dashboard.top-bar />
|
<livewire:ui.dashboard.top-bar />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue