Rechtebechebung für User mit Sudorechte
parent
28129cb989
commit
f8934b7a9a
|
|
@ -3,16 +3,33 @@
|
|||
namespace App\Livewire\Auth;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Livewire\Component;
|
||||
|
||||
class LoginForm extends Component
|
||||
{
|
||||
public ?array $banner = [];
|
||||
public bool $showBanner = false;
|
||||
|
||||
public string $name = '';
|
||||
public string $password = '';
|
||||
public ?string $error = null;
|
||||
public bool $show = false;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
// Flash nur EINMAL ziehen
|
||||
$flash = session()->pull('login_banner');
|
||||
if ($flash) {
|
||||
$this->banner = $flash;
|
||||
$this->showBanner = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function dismissBanner(): void
|
||||
{
|
||||
$this->showBanner = false;
|
||||
}
|
||||
|
||||
|
||||
public function login()
|
||||
{
|
||||
|
|
@ -28,7 +45,7 @@ class LoginForm extends Component
|
|||
|
||||
if (Auth::attempt([$field => $this->name, 'password' => $this->password], true)) {
|
||||
request()->session()->regenerate();
|
||||
return redirect()->intended(route('setup.wizard')) ;
|
||||
return redirect()->intended(route('ui.dashboard'));
|
||||
}
|
||||
|
||||
$this->error = 'Ungültige Zugangsdaten.';
|
||||
|
|
|
|||
|
|
@ -58,6 +58,13 @@ class SignupForm extends Component
|
|||
// nach erstem User: Signup deaktivieren
|
||||
if ($isFirstUser) {
|
||||
Setting::set('system.signup_enabled', 0); // Redis + DB
|
||||
return redirect()
|
||||
->route('login')
|
||||
->with('login_banner', [
|
||||
'type' => 'success',
|
||||
'title' => 'Registrierung abgeschlossen',
|
||||
'message' => 'Dein Konto wurde erfolgreich erstellt. Du kannst dich jetzt anmelden.',
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ class DomainDnsModal extends ModalComponent
|
|||
public array $static = [];
|
||||
/** @var array<int,array<string,string|int|null>> */
|
||||
public array $dynamic = [];
|
||||
/** @var array<int,array<string,string|int|null>> */
|
||||
public array $optional = [];
|
||||
|
||||
public static function modalMaxWidth(): string
|
||||
{
|
||||
|
|
@ -37,6 +39,7 @@ class DomainDnsModal extends ModalComponent
|
|||
'TXT' => 'bg-violet-500/20 text-violet-300',
|
||||
'SRV' => 'bg-rose-500/20 text-rose-300',
|
||||
'TLSA' => 'bg-red-500/20 text-red-300',
|
||||
'OPTIONAL' => 'bg-gray-500/20 text-gray-300',
|
||||
];
|
||||
|
||||
$d = Domain::findOrFail($domainId);
|
||||
|
|
@ -59,7 +62,6 @@ class DomainDnsModal extends ModalComponent
|
|||
|
||||
// --- Statische Infrastruktur (für alle Domains gleich) ---
|
||||
$this->static = [
|
||||
['type' => 'MX', 'name' => $base, 'value' => "10 {$mailServerFqdn}."],
|
||||
['type' => 'A', 'name' => $mailServerFqdn, 'value' => $ipv4],
|
||||
['type' => 'PTR', 'name' => $this->ptrFromIPv4($ipv4), 'value' => $tlsa->host],
|
||||
];
|
||||
|
|
@ -78,7 +80,7 @@ class DomainDnsModal extends ModalComponent
|
|||
}
|
||||
|
||||
// --- Domain-spezifisch ---
|
||||
$spf = "v=spf1 a mx ip4:{$ipv4} ip6:{$ipv6} ~all";
|
||||
$spf = "v=spf1 ip4:{$ipv4} ip6:{$ipv6} mx -all";
|
||||
$dmarc = "v=DMARC1; p=none; rua=mailto:dmarc@{$this->domainName}; pct=100";
|
||||
|
||||
$dkim = DB::table('dkim_keys')
|
||||
|
|
@ -90,19 +92,24 @@ class DomainDnsModal extends ModalComponent
|
|||
: ($dkim->public_key_txt ?? 'v=DKIM1; k=rsa; p=');
|
||||
|
||||
$this->dynamic = [
|
||||
['type' => 'MX', 'name' => $this->domainName, 'value' => "10 {$mailServerFqdn}."],
|
||||
|
||||
['type' => 'CNAME', 'name' => "autoconfig.$this->domainName", 'value' => "$mailServerFqdn."],
|
||||
['type' => 'CNAME', 'name' => "autodiscover.$this->domainName", 'value' => "$mailServerFqdn."],
|
||||
|
||||
// SRV Records für Autodiscover und Maildienste
|
||||
['type' => 'SRV', 'name' => "_autodiscover._tcp.$this->domainName", 'value' => "0 0 443 {$mailServerFqdn}."],
|
||||
['type' => 'SRV', 'name' => "_imaps._tcp.$this->domainName", 'value' => "0 0 993 {$mailServerFqdn}."],
|
||||
['type' => 'SRV', 'name' => "_pop3s._tcp.$this->domainName", 'value' => "0 0 995 {$mailServerFqdn}."],
|
||||
['type' => 'SRV', 'name' => "_submission._tcp.$this->domainName", 'value' => "0 0 587 {$mailServerFqdn}."],
|
||||
|
||||
// TXT Records
|
||||
['type' => 'TXT', 'name' => $this->domainName, 'value' => $spf, 'helpLabel' => 'SPF Record Syntax', 'helpUrl' => 'http://www.open-spf.org/SPF_Record_Syntax/'],
|
||||
['type' => 'TXT', 'name' => "_dmarc.{$this->domainName}", 'value' => $dmarc, 'helpLabel' => 'DMARC Assistant', 'helpUrl' => 'https://www.kitterman.com/dmarc/assistant.html'],
|
||||
['type' => 'TXT', 'name' => $dkimHost, 'value' => $dkimTxt, 'helpLabel' => 'DKIM Inspector', 'helpUrl' => 'https://dkimvalidator.com/'],
|
||||
|
||||
];
|
||||
|
||||
$this->optional = [
|
||||
// SRV Records für Autodiscover und Maildienste
|
||||
['type' => 'SRV', 'name' => "_autodiscover._tcp.$this->domainName", 'value' => "0 0 443 {$mailServerFqdn}."],
|
||||
['type' => 'SRV', 'name' => "_imaps._tcp.$this->domainName", 'value' => "0 0 993 {$mailServerFqdn}."],
|
||||
['type' => 'SRV', 'name' => "_pop3s._tcp.$this->domainName", 'value' => "0 0 995 {$mailServerFqdn}."],
|
||||
['type' => 'SRV', 'name' => "_submission._tcp.$this->domainName", 'value' => "0 0 587 {$mailServerFqdn}."],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,8 @@
|
|||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Jobs\InstallDkimKey;
|
||||
use App\Jobs\RemoveDkimKey;
|
||||
use App\Models\DkimKey;
|
||||
use App\Models\Domain;
|
||||
use App\Services\DkimService;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DomainObserver
|
||||
{
|
||||
|
|
@ -16,76 +13,54 @@ class DomainObserver
|
|||
*/
|
||||
public function created(Domain $domain): void
|
||||
{
|
||||
if ($domain->is_server) {
|
||||
return;
|
||||
}
|
||||
if ($domain->is_server) return;
|
||||
|
||||
$selector = (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
||||
$bits = (int) config('mailpool.defaults.dkim_bits', 2048);
|
||||
|
||||
$res = app(\App\Services\DkimService::class)
|
||||
->generateForDomain($domain, $bits, $selector);
|
||||
// Service erledigt: Key generieren, DB (upsert) pflegen, Helper ausführen, OpenDKIM reloaden
|
||||
app(\App\Services\DkimService::class)->generateForDomain($domain, $bits, $selector);
|
||||
|
||||
$dk = \App\Models\DkimKey::create([
|
||||
'domain_id' => $domain->id,
|
||||
'selector' => $res['selector'],
|
||||
'private_key_pem' => $res['private_pem'],
|
||||
'public_key_txt' => preg_replace('/^v=DKIM1; k=rsa; p=/', '', $res['dns_txt']),
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
// Helper aufrufen (Pfad aus $res['priv_path']!)
|
||||
dispatch(new \App\Jobs\InstallDkimKey(
|
||||
domainId: $domain->id,
|
||||
dkimKeyId: $dk->id,
|
||||
privPath: $res['priv_path'],
|
||||
dnsTxtContent: $res['dns_txt'],
|
||||
));
|
||||
|
||||
// DNS-Records gleich anlegen/aktualisieren
|
||||
app(\App\Services\DnsRecordService::class)->provision(
|
||||
$domain,
|
||||
$dk->selector,
|
||||
"v=DKIM1; k=rsa; p={$dk->public_key_txt}",
|
||||
[
|
||||
'spf_tail' => \App\Models\Setting::get('mailpool.spf_tail', '~all'),
|
||||
'spf_extra' => \App\Models\Setting::get('mailpool.spf_extra', []),
|
||||
'dmarc_policy' => \App\Models\Setting::get('mailpool.dmarc_policy', 'none'),
|
||||
'rua' => \App\Models\Setting::get('mailpool.rua', null),
|
||||
]
|
||||
);
|
||||
// DNS-Records: aktiven Key aus DB lesen und provisionieren
|
||||
$active = $domain->dkimKeys()->where('is_active', true)->latest()->first();
|
||||
if ($active) {
|
||||
app(\App\Services\DnsRecordService::class)->provision(
|
||||
$domain,
|
||||
$active->selector,
|
||||
"v=DKIM1; k=rsa; p={$active->public_key_txt}",
|
||||
[
|
||||
'spf_tail' => \App\Models\Setting::get('mailpool.spf_tail', '~all'),
|
||||
'spf_extra' => \App\Models\Setting::get('mailpool.spf_extra', []),
|
||||
'dmarc_policy' => \App\Models\Setting::get('mailpool.dmarc_policy', 'none'),
|
||||
'rua' => \App\Models\Setting::get('mailpool.rua', null),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public function created(Domain $domain): void
|
||||
// {
|
||||
// // Standardwerte aus Config oder .env
|
||||
// $selector = config('mailwolt.dkim.selector', 'mwl1');
|
||||
// $bits = (int) config('mailwolt.dkim.bits', 2048);
|
||||
// if ($domain->is_server) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Keypair erzeugen
|
||||
// $res = app(DkimService::class)->generateForDomain(
|
||||
// domainId: $domain,
|
||||
// bits: $bits,
|
||||
// selector: $selector
|
||||
// $selector = (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
||||
// $bits = (int) config('mailpool.defaults.dkim_bits', 2048);
|
||||
//
|
||||
// $res = app(\App\Services\DkimService::class)
|
||||
// ->generateForDomain($domain, $bits, $selector);
|
||||
//
|
||||
// // DNS-Records gleich anlegen/aktualisieren
|
||||
// app(\App\Services\DnsRecordService::class)->provision(
|
||||
// $domain,
|
||||
// $dk->selector,
|
||||
// "v=DKIM1; k=rsa; p={$dk->public_key_txt}",
|
||||
// [
|
||||
// 'spf_tail' => \App\Models\Setting::get('mailpool.spf_tail', '~all'),
|
||||
// 'spf_extra' => \App\Models\Setting::get('mailpool.spf_extra', []),
|
||||
// 'dmarc_policy' => \App\Models\Setting::get('mailpool.dmarc_policy', 'none'),
|
||||
// 'rua' => \App\Models\Setting::get('mailpool.rua', null),
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// // In dkim_keys speichern
|
||||
// $dk = DkimKey::create([
|
||||
// 'domain_id' => $domain->id,
|
||||
// 'selector' => $res['selector'],
|
||||
// 'private_key_pem' => $res['private_pem'],
|
||||
// 'public_key_txt' => preg_replace('/^v=DKIM1; k=rsa; p=/', '', $res['dns_txt']),
|
||||
// 'is_active' => true,
|
||||
// ]);
|
||||
//
|
||||
// // Helper-Job zum Installieren starten
|
||||
// InstallDkimKey::dispatch(
|
||||
// domainId: $domain->id,
|
||||
// dkimKeyId: $dk->id,
|
||||
// privPath: $res['priv_path'],
|
||||
// dnsTxtContent: $res['dns_txt']
|
||||
// )->afterCommit();
|
||||
// }
|
||||
|
||||
/**
|
||||
|
|
@ -93,12 +68,28 @@ class DomainObserver
|
|||
*/
|
||||
public function deleted(Domain $domain): void
|
||||
{
|
||||
// Falls SoftDeletes im Spiel, willst du evtl. forceDeleted spiegeln (s.u.)
|
||||
foreach ($domain->dkimKeys()->get() as $dk) {
|
||||
RemoveDkimKey::dispatch(
|
||||
domainId: $domain->id,
|
||||
selector: $dk->selector
|
||||
)->afterCommit();
|
||||
try {
|
||||
/** @var \App\Services\DkimService $svc */
|
||||
$svc = app(\App\Services\DkimService::class);
|
||||
|
||||
// Entferne DKIM aus OpenDKIM Config
|
||||
$svc->removeForDomain($domain);
|
||||
|
||||
// Optionale lokale Dateien löschen
|
||||
$path = storage_path("app/private/dkim/{$domain->domain}");
|
||||
if (is_dir($path)) {
|
||||
\Illuminate\Support\Facades\File::deleteDirectory($path);
|
||||
}
|
||||
|
||||
// Reload OpenDKIM
|
||||
\Illuminate\Support\Facades\Process::run(['sudo','-n','/usr/bin/systemctl','reload','opendkim']);
|
||||
|
||||
Log::info("Domain deleted + DKIM cleaned", ['domain' => $domain->domain]);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error("Domain delete cleanup failed", [
|
||||
'domain' => $domain->domain,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class DkimService
|
|||
|
||||
$dirKey = $this->dirKeyFor($domain);
|
||||
$selKey = preg_replace('/[^A-Za-z0-9._-]/', '_', substr($selector, 0, 32)); // schlicht & stabil
|
||||
|
||||
|
||||
// Disk "local" zeigt bei dir auf storage/app/private (siehe Kommentar in deinem Code)
|
||||
$disk = Storage::disk('local');
|
||||
$baseRel = "dkim/{$dirKey}";
|
||||
|
|
@ -159,36 +159,58 @@ class DkimService
|
|||
return $san;
|
||||
}
|
||||
|
||||
protected function safeKey($value, int $max = 64): string
|
||||
public function removeForDomain(Domain|string $domain): void
|
||||
{
|
||||
if (is_object($value)) {
|
||||
if (isset($value->id)) $value = $value->id;
|
||||
elseif (method_exists($value, 'getKey')) $value = $value->getKey();
|
||||
else $value = json_encode($value);
|
||||
$domainName = $domain instanceof Domain ? $domain->domain : $domain;
|
||||
$keyTable = '/etc/opendkim/KeyTable';
|
||||
$signTable = '/etc/opendkim/SigningTable';
|
||||
$keyDir = "/etc/opendkim/keys/{$domainName}";
|
||||
|
||||
// Tabellen bereinigen
|
||||
foreach ([$keyTable, $signTable] as $file) {
|
||||
if (is_file($file)) {
|
||||
$lines = file($file, FILE_IGNORE_NEW_LINES);
|
||||
$filtered = array_filter($lines, fn($l) => !str_contains($l, $domainName));
|
||||
file_put_contents($file, implode(PHP_EOL, $filtered) . PHP_EOL);
|
||||
}
|
||||
}
|
||||
$raw = (string) $value;
|
||||
$san = preg_replace('/[^A-Za-z0-9._-]/', '_', $raw);
|
||||
if ($san === '' ) $san = 'unknown';
|
||||
if (strlen($san) > $max) {
|
||||
$san = substr($san, 0, $max - 13) . '_' . substr(sha1($raw), 0, 12);
|
||||
|
||||
// Key-Verzeichnis löschen
|
||||
if (is_dir($keyDir)) {
|
||||
\Illuminate\Support\Facades\File::deleteDirectory($keyDir);
|
||||
}
|
||||
return $san;
|
||||
}
|
||||
|
||||
protected static function extractPublicKeyBase64(string $pem): string
|
||||
{
|
||||
// Hole den Body zwischen den Headern (multiline, dotall)
|
||||
if (!preg_match('/^-+BEGIN PUBLIC KEY-+\r?\n(.+?)\r?\n-+END PUBLIC KEY-+\s*$/ms', $pem, $m)) {
|
||||
throw new \RuntimeException('DKIM: Ungültiges Public-Key-PEM (Header/Footers nicht gefunden).');
|
||||
}
|
||||
|
||||
// Whitespace entfernen → reines Base64
|
||||
$b64 = preg_replace('/\s+/', '', $m[1]);
|
||||
|
||||
if ($b64 === '' || base64_decode($b64, true) === false) {
|
||||
throw new \RuntimeException('DKIM: Public Key Base64 ist leer/ungültig.');
|
||||
}
|
||||
|
||||
return $b64;
|
||||
}
|
||||
// protected function safeKey($value, int $max = 64): string
|
||||
// {
|
||||
// if (is_object($value)) {
|
||||
// if (isset($value->id)) $value = $value->id;
|
||||
// elseif (method_exists($value, 'getKey')) $value = $value->getKey();
|
||||
// else $value = json_encode($value);
|
||||
// }
|
||||
// $raw = (string) $value;
|
||||
// $san = preg_replace('/[^A-Za-z0-9._-]/', '_', $raw);
|
||||
// if ($san === '' ) $san = 'unknown';
|
||||
// if (strlen($san) > $max) {
|
||||
// $san = substr($san, 0, $max - 13) . '_' . substr(sha1($raw), 0, 12);
|
||||
// }
|
||||
// return $san;
|
||||
// }
|
||||
//
|
||||
// protected static function extractPublicKeyBase64(string $pem): string
|
||||
// {
|
||||
// // Hole den Body zwischen den Headern (multiline, dotall)
|
||||
// if (!preg_match('/^-+BEGIN PUBLIC KEY-+\r?\n(.+?)\r?\n-+END PUBLIC KEY-+\s*$/ms', $pem, $m)) {
|
||||
// throw new \RuntimeException('DKIM: Ungültiges Public-Key-PEM (Header/Footers nicht gefunden).');
|
||||
// }
|
||||
//
|
||||
// // Whitespace entfernen → reines Base64
|
||||
// $b64 = preg_replace('/\s+/', '', $m[1]);
|
||||
//
|
||||
// if ($b64 === '' || base64_decode($b64, true) === false) {
|
||||
// throw new \RuntimeException('DKIM: Public Key Base64 ist leer/ungültig.');
|
||||
// }
|
||||
//
|
||||
// return $b64;
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,32 @@
|
|||
<div class="w-full #min-h-[86vh] grid place-items-center px-4
|
||||
<div class="w-full #min-h-[86vh] grid place-items-center
|
||||
bg-[radial-gradient(1200px_600px_at_10%_-10%,rgba(59,130,246,.08),transparent),
|
||||
radial-gradient(900px_500px_at_90%_0%,rgba(99,102,241,.06),transparent)]">
|
||||
|
||||
{{-- Banner (dismissbar) --}}
|
||||
@if($showBanner && $banner)
|
||||
<div class="max-w-[520px] mb-5 rounded-2xl border border-emerald-400/30 text-emerald-300 bg-emerald-500/10 p-4 #md:p-5 shadow backdrop-blur"
|
||||
role="status" aria-live="polite">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="shrink-0 mt-0.5">
|
||||
<i class="ph ph-check-circle text-emerald-100 text-xl"></i>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-semibold text-emerald-100">
|
||||
{{ $banner['title'] ?? 'Erfolgreich registriert' }}
|
||||
</div>
|
||||
<div class="mt-0.5 text-sm text-emerald-200">
|
||||
{{ $banner['message'] ?? 'Dein Konto ist bereit. Melde dich jetzt an.' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="nx-card w-full max-w-[520px]">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<span class="nx-chip">Erster Login</span>
|
||||
<i class="ph ph-lock-simple text-white/60"></i>
|
||||
</div>
|
||||
|
||||
<p class="nx-subtle mb-7">
|
||||
Melde dich mit dem einmaligen Bootstrap-Konto an, um den Setup-Wizard zu starten.
|
||||
</p>
|
||||
|
||||
{{-- globale Fehlermeldung --}}
|
||||
@if($error)
|
||||
<div class="nx-alert mb-6">
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
@if($systemDomain)
|
||||
<div class="rounded-xl border border-white/10 bg-white/[0.04] px-3 py-3">
|
||||
<div class="grid grid-cols-1 #md:grid-cols-4 items-center gap-3">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 items-center gap-3">
|
||||
<div class=" flex items-center gap-1 text-white/90 font-medium truncate">
|
||||
<span class="relative flex size-2.5 mx-1">
|
||||
@if($systemDomain->is_active)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
<div class="rounded-xl border border-white/10 bg-white/[0.04] #rounded-xl #border #border-slate-700/50 #bg-slate-900/60">
|
||||
<div class="flex items-center justify-between px-4 py-2 text-[12px]">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-slate-800/80 text-slate-200 {{ $recordColors[$r['type']] ?? 'bg-slate-700/50 text-slate-300' }}">{{ $r['type'] }}</span>
|
||||
<span class="px-2 py-0.5 rounded {{ $recordColors[$r['type']] ?? 'bg-slate-700/50 text-slate-300' }}">{{ $r['type'] }}</span>
|
||||
<span class="text-slate-200">{{ $r['name'] }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-slate-300/70">
|
||||
|
|
@ -52,6 +52,30 @@
|
|||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@foreach ($optional as $r)
|
||||
<div class="rounded-xl border border-white/10 bg-white/[0.04] #rounded-xl #border #border-slate-700/50 #bg-slate-900/60">
|
||||
<div class="flex items-center justify-between px-4 py-2 text-[12px]">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded {{ $recordColors[$r['type']] ?? 'bg-slate-700/50 text-slate-300' }}">{{ $r['type'] }}</span>
|
||||
<span class="text-slate-200">{{ $r['name'] }}</span>
|
||||
</div>
|
||||
{{ $recordColors[$r['type']] }}
|
||||
<div class="flex items-center gap-2 text-slate-300/70">
|
||||
<x-button.copy-btn :text="$r['value']" />
|
||||
</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>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -80,10 +80,10 @@ Route::get('/login', [LoginController::class, 'show'])->name('login');
|
|||
Route::get('/signup', [\App\Http\Controllers\Auth\SignUpController::class, 'show' ])->middleware('signup.open')->name('signup');
|
||||
Route::post('/logout', [\App\Http\Controllers\Auth\LoginController::class, 'logout'])->name('logout');
|
||||
|
||||
Route::middleware(['auth', 'ensure.setup'])->group(function () {
|
||||
// Route::get('/dashboard', Dashboard::class)->name('dashboard');
|
||||
Route::get('/setup', [SetupWizard::class, 'show'])->name('setup.wizard');
|
||||
});
|
||||
//Route::middleware(['auth', 'ensure.setup'])->group(function () {
|
||||
//// Route::get('/dashboard', Dashboard::class)->name('dashboard');
|
||||
// Route::get('/setup', [SetupWizard::class, 'show'])->name('setup.wizard');
|
||||
//});
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue