Fix: Fehlerseiten als individuelle Dateien (4xx-Catch-all Laravel-Bug umgangen)

main v1.1.451
boban 2026-04-29 19:05:49 +02:00
parent 04e28903ea
commit 16fa6d3f4e
13 changed files with 114 additions and 177 deletions

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 400,
'type' => 'client',
'title' => 'Ungültige Anfrage',
'desc' => 'Die Anfrage enthält fehlerhafte oder unvollständige Daten.',
'icon' => '<path d="M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 401,
'type' => 'client',
'title' => 'Nicht angemeldet',
'desc' => 'Bitte melde dich an, um auf diese Seite zuzugreifen.',
'icon' => '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 403,
'type' => 'client',
'title' => 'Kein Zugriff',
'desc' => 'Du hast keine Berechtigung, diese Seite aufzurufen.',
'icon' => '<circle cx="12" cy="12" r="9"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 404,
'type' => 'client',
'title' => 'Seite nicht gefunden',
'desc' => 'Die aufgerufene Seite existiert nicht oder wurde verschoben.',
'icon' => '<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 405,
'type' => 'client',
'title' => 'Methode nicht erlaubt',
'desc' => 'Diese HTTP-Methode ist für die aufgerufene URL nicht zulässig.',
'icon' => '<circle cx="12" cy="12" r="9"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 419,
'type' => 'client',
'title' => 'Sitzung abgelaufen',
'desc' => 'Deine Sitzung ist abgelaufen. Lade die Seite neu und versuche es erneut.',
'icon' => '<circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 422,
'type' => 'client',
'title' => 'Ungültige Eingabe',
'desc' => 'Die übermittelten Daten konnten nicht verarbeitet werden.',
'icon' => '<path d="M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 429,
'type' => 'client',
'title' => 'Zu viele Anfragen',
'desc' => 'Du hast das Anfrage-Limit erreicht. Bitte warte einen Moment.',
'icon' => '<path d="M13 10V3L4 14h7v7l9-11h-7z"/>',
])

View File

@ -1,134 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php
$code = $exception->getStatusCode();
$map = [
400 => ['Ungültige Anfrage', 'Die Anfrage enthält fehlerhafte oder unvollständige Daten.'],
401 => ['Nicht angemeldet', 'Bitte melde dich an, um auf diese Seite zuzugreifen.'],
403 => ['Kein Zugriff', 'Du hast keine Berechtigung, diese Seite aufzurufen.'],
404 => ['Seite nicht gefunden', 'Die aufgerufene Seite existiert nicht oder wurde verschoben.'],
405 => ['Methode nicht erlaubt', 'Diese HTTP-Methode ist für diese URL nicht zulässig.'],
419 => ['Sitzung abgelaufen', 'Deine Sitzung ist abgelaufen. Lade die Seite neu und versuche es erneut.'],
422 => ['Ungültige Eingabe', 'Die übermittelten Daten konnten nicht verarbeitet werden.'],
429 => ['Zu viele Anfragen', 'Du hast das Anfrage-Limit erreicht. Bitte warte einen Moment.'],
];
[$title, $desc] = $map[$code] ?? ['Fehler ' . $code, 'Ein unbekannter Fehler ist aufgetreten.'];
$backUrl = $_SERVER['HTTP_REFERER'] ?? '/';
$iconPath = [
400 => 'M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z',
401 => 'M12 15v2m0 0v2m0-2h2m-2 0H10m2-9V6a4 4 0 0 0-8 0v4h1a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h1M8 10h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2z',
403 => 'M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636',
404 => 'M9.172 16.172a4 4 0 0 1 5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z',
405 => 'M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636',
419 => 'M12 8v4l3 3m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0z',
422 => 'M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z',
429 => 'M13 10V3L4 14h7v7l9-11h-7z',
];
$icon = $iconPath[$code] ?? $iconPath[400];
?>
<title>{{ $code }} · CluBird</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
width: 100%; min-height: 100vh;
background: #0f1117; color: #e2e4ea;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
display: flex; align-items: center; justify-content: center; padding: 16px;
}
.card {
width: 100%; max-width: 480px; padding: 44px 40px;
background: #181c24; border: 1px solid #252938;
border-radius: 18px; text-align: center;
animation: fadein .35s ease;
}
@keyframes fadein { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.logo {
display: inline-flex; align-items: center; gap: 14px; margin-bottom: 36px;
}
.logo-icon {
position: relative; width: 42px; height: 42px;
background: linear-gradient(135deg, #6366f1, #4338ca);
border-radius: 12px; display: flex; align-items: center; justify-content: center;
box-shadow: 0 0 22px rgba(99,102,241,.45);
}
.logo-icon::after {
content: ''; position: absolute; inset: -4px; border-radius: 16px;
border: 1px solid rgba(99,102,241,.3);
animation: glow 2s ease-in-out infinite;
}
@keyframes glow { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
.logo-text { font-size: 24px; font-weight: 700; letter-spacing: -.3px; }
.logo-text span { color: #6366f1; }
.err-icon {
width: 56px; height: 56px; margin: 0 auto 20px;
background: rgba(99,102,241,.12); border-radius: 50%;
display: flex; align-items: center; justify-content: center;
}
.err-icon svg { width: 26px; height: 26px; stroke: #818cf8; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.err-code { font-size: 64px; font-weight: 800; color: #6366f1; line-height: 1; margin-bottom: 12px; letter-spacing: -2px; }
h1 { font-size: 17px; font-weight: 600; color: #e2e4ea; margin-bottom: 8px; letter-spacing: -.2px; }
p { font-size: 13px; color: #4b5563; line-height: 1.65; margin-bottom: 28px; }
.divider { border: none; border-top: 1px solid #252938; margin: 0 0 28px; }
.btn-back {
display: inline-flex; align-items: center; gap: 6px;
padding: 9px 20px; border-radius: 9px;
background: #1f2433; border: 1px solid #2d3348;
color: #a5b4fc; font-size: 13px; font-weight: 500;
text-decoration: none; cursor: pointer;
transition: background .15s, border-color .15s;
}
.btn-back:hover { background: #252b3d; border-color: #3d4565; }
.btn-back svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.btn-home {
display: inline-flex; align-items: center; gap: 6px;
padding: 9px 20px; border-radius: 9px; margin-left: 8px;
background: linear-gradient(135deg, #6366f1, #4338ca);
border: none; color: #fff; font-size: 13px; font-weight: 500;
text-decoration: none; cursor: pointer;
box-shadow: 0 4px 14px rgba(99,102,241,.35);
transition: opacity .15s;
}
.btn-home:hover { opacity: .88; }
</style>
</head>
<body>
<div class="card">
<div class="logo">
<div class="logo-icon">
<svg width="22" height="22" viewBox="0 0 28 28" fill="none">
<path d="M5 19 C5 19 8 10 15 9 C19 8 23 11 24 15" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
<path d="M5 19 C6.5 22 9 23.5 12 23.5 C15 23.5 18 22 19.5 19" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="19" cy="8" r="2" fill="white"/>
</svg>
</div>
<div class="logo-text">Clu<span>Bird</span></div>
</div>
<div class="err-icon">
<svg viewBox="0 0 24 24"><path d="{{ $icon }}"/></svg>
</div>
<div class="err-code">{{ $code }}</div>
<h1>{{ $title }}</h1>
<p>{{ $desc }}</p>
<hr class="divider">
<a class="btn-back" href="javascript:history.back()">
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
Zurück
</a>
<a class="btn-home" href="/">
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Startseite
</a>
</div>
</body>
</html>

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 500,
'type' => 'server',
'title' => 'Serverfehler',
'desc' => 'Ein interner Fehler ist aufgetreten. Wir wurden benachrichtigt.',
'icon' => '<path d="M13 10V3L4 14h7v7l9-11h-7z"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 502,
'type' => 'server',
'title' => 'Bad Gateway',
'desc' => 'Der Server hat eine ungültige Antwort erhalten. Bitte versuche es gleich erneut.',
'icon' => '<path d="M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>',
])

View File

@ -0,0 +1,7 @@
@include('errors._base', [
'code' => 504,
'type' => 'server',
'title' => 'Gateway-Timeout',
'desc' => 'Der Server antwortet nicht rechtzeitig. Bitte versuche es in Kürze erneut.',
'icon' => '<circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 15"/>',
])

View File

@ -3,15 +3,6 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php
$code = $exception->getStatusCode();
$map = [
500 => ['Serverfehler', 'Ein interner Fehler ist aufgetreten. Wir wurden benachrichtigt.'],
502 => ['Bad Gateway', 'Der Server hat eine ungültige Antwort erhalten. Bitte versuche es gleich erneut.'],
504 => ['Gateway-Timeout', 'Der Server antwortet nicht rechtzeitig. Bitte versuche es in Kürze erneut.'],
];
[$title, $desc] = $map[$code] ?? ['Serverfehler ' . $code, 'Ein unerwarteter Fehler ist aufgetreten.'];
?>
<title>{{ $code }} · CluBird</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
@ -28,9 +19,7 @@ $map = [
animation: fadein .35s ease;
}
@keyframes fadein { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.logo {
display: inline-flex; align-items: center; gap: 14px; margin-bottom: 36px;
}
.logo { display: inline-flex; align-items: center; gap: 14px; margin-bottom: 36px; }
.logo-icon {
position: relative; width: 42px; height: 42px;
background: linear-gradient(135deg, #6366f1, #4338ca);
@ -45,41 +34,43 @@ $map = [
@keyframes glow { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
.logo-text { font-size: 24px; font-weight: 700; letter-spacing: -.3px; }
.logo-text span { color: #6366f1; }
.err-icon {
width: 56px; height: 56px; margin: 0 auto 20px;
background: rgba(239,68,68,.1); border-radius: 50%;
display: flex; align-items: center; justify-content: center;
border-radius: 50%; display: flex; align-items: center; justify-content: center;
background: {{ $type === 'server' ? 'rgba(239,68,68,.1)' : 'rgba(99,102,241,.12)' }};
}
.err-icon svg {
width: 26px; height: 26px; fill: none; stroke-width: 1.8;
stroke-linecap: round; stroke-linejoin: round;
stroke: {{ $type === 'server' ? '#f87171' : '#818cf8' }};
}
.err-code {
font-size: 64px; font-weight: 800; line-height: 1;
margin-bottom: 12px; letter-spacing: -2px;
color: {{ $type === 'server' ? '#f87171' : '#6366f1' }};
}
.err-icon svg { width: 26px; height: 26px; stroke: #f87171; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.err-code { font-size: 64px; font-weight: 800; color: #f87171; line-height: 1; margin-bottom: 12px; letter-spacing: -2px; }
h1 { font-size: 17px; font-weight: 600; color: #e2e4ea; margin-bottom: 8px; letter-spacing: -.2px; }
p { font-size: 13px; color: #4b5563; line-height: 1.65; margin-bottom: 28px; }
.divider { border: none; border-top: 1px solid #252938; margin: 0 0 28px; }
.btn-reload {
.btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 9px 20px; border-radius: 9px;
background: #1f2433; border: 1px solid #2d3348;
color: #fca5a5; font-size: 13px; font-weight: 500;
text-decoration: none; cursor: pointer;
font-size: 13px; font-weight: 500; text-decoration: none; cursor: pointer;
transition: background .15s, border-color .15s;
color: {{ $type === 'server' ? '#fca5a5' : '#a5b4fc' }};
}
.btn-reload:hover { background: #252b3d; border-color: #3d4565; }
.btn-reload svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.btn-home {
.btn:hover { background: #252b3d; border-color: #3d4565; }
.btn svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.btn-primary {
display: inline-flex; align-items: center; gap: 6px;
padding: 9px 20px; border-radius: 9px; margin-left: 8px;
background: linear-gradient(135deg, #6366f1, #4338ca);
border: none; color: #fff; font-size: 13px; font-weight: 500;
text-decoration: none; cursor: pointer;
box-shadow: 0 4px 14px rgba(99,102,241,.35);
transition: opacity .15s;
background: linear-gradient(135deg, #6366f1, #4338ca); border: none;
color: #fff; font-size: 13px; font-weight: 500; text-decoration: none;
box-shadow: 0 4px 14px rgba(99,102,241,.35); transition: opacity .15s;
}
.btn-home:hover { opacity: .88; }
.btn-primary:hover { opacity: .88; }
.btn-primary svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
</style>
</head>
<body>
@ -96,11 +87,7 @@ $map = [
</div>
<div class="err-icon">
@if($code === 502 || $code === 504)
<svg viewBox="0 0 24 24"><path d="M12 8v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg>
@else
<svg viewBox="0 0 24 24"><path d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
@endif
<svg viewBox="0 0 24 24">{!! $icon !!}</svg>
</div>
<div class="err-code">{{ $code }}</div>
@ -108,11 +95,18 @@ $map = [
<p>{{ $desc }}</p>
<hr class="divider">
<a class="btn-reload" href="javascript:location.reload()">
@if($type === 'server')
<a class="btn" href="javascript:location.reload()">
<svg viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-.1-6.87"/></svg>
Neu laden
</a>
<a class="btn-home" href="/">
@else
<a class="btn" href="javascript:history.back()">
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
Zurück
</a>
@endif
<a class="btn-primary" href="/">
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Startseite
</a>