@yield('code')
+@yield('heading')
+@yield('message')
+ +diff --git a/app/Livewire/Files/Index.php b/app/Livewire/Files/Index.php index 0dbd4d4..b299851 100644 --- a/app/Livewire/Files/Index.php +++ b/app/Livewire/Files/Index.php @@ -107,7 +107,7 @@ class Index extends Component try { $content = $fleet->getFile($active, rtrim($this->path, '/').'/'.basename($name)); } catch (Throwable $e) { - $this->dispatch('notify', message: $e->getMessage()); + $this->dispatch('notify', message: __('files.download_failed', ['error' => $e->getMessage()])); return null; } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index e7285b2..73e697c 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -16,6 +16,11 @@ x-image: &image restart: unless-stopped env_file: - .env + # Safety net: force debug OFF in prod so an uncaught error can never render a + # stack trace to the user (the custom resources/views/errors/* pages show + # instead). `environment` overrides any APP_DEBUG left in the prod .env. + environment: + APP_DEBUG: "false" networks: - clusev diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 3648424..c7d1597 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -47,6 +47,9 @@ http { location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } + # Correct MIME for the PWA manifest (the bundled mime.types maps .webmanifest + # to octet-stream). Per-file default_type avoids replacing the global types map. + location = /site.webmanifest { default_type application/manifest+json; access_log off; } error_page 404 /index.php; diff --git a/lang/de/errors.php b/lang/de/errors.php new file mode 100644 index 0000000..d378e87 --- /dev/null +++ b/lang/de/errors.php @@ -0,0 +1,26 @@ + 'Zurück zum Dashboard', + 'generic_title' => 'Fehler', + + '403_title' => 'Kein Zugriff', + '403_body' => 'Du hast keine Berechtigung, diese Seite zu sehen.', + + '404_title' => 'Seite nicht gefunden', + '404_body' => 'Diese Seite existiert nicht oder wurde verschoben.', + + '419_title' => 'Sitzung abgelaufen', + '419_body' => 'Deine Sitzung ist abgelaufen. Bitte lade die Seite neu und melde dich erneut an.', + + '429_title' => 'Zu viele Anfragen', + '429_body' => 'Zu viele Anfragen in kurzer Zeit. Bitte einen Moment warten und erneut versuchen.', + + '500_title' => 'Unerwarteter Fehler', + '500_body' => 'Auf dem Server ist ein unerwarteter Fehler aufgetreten. Bitte später erneut versuchen.', + + '503_title' => 'Wartungsmodus', + '503_body' => 'Clusev wird gerade gewartet. Bitte in Kürze erneut versuchen.', +]; diff --git a/lang/de/files.php b/lang/de/files.php index 8f31d8d..45deda3 100644 --- a/lang/de/files.php +++ b/lang/de/files.php @@ -24,6 +24,7 @@ return [ // Upload / notifications 'uploaded' => '„:name“ hochgeladen.', 'upload_failed' => 'Upload fehlgeschlagen: :error', + 'download_failed' => 'Download fehlgeschlagen: :error', // Delete (confirm modal) 'delete_heading' => 'Datei löschen', diff --git a/lang/en/errors.php b/lang/en/errors.php new file mode 100644 index 0000000..d16b2da --- /dev/null +++ b/lang/en/errors.php @@ -0,0 +1,26 @@ + 'Back to dashboard', + 'generic_title' => 'Error', + + '403_title' => 'Access denied', + '403_body' => 'You do not have permission to view this page.', + + '404_title' => 'Page not found', + '404_body' => 'This page does not exist or has moved.', + + '419_title' => 'Session expired', + '419_body' => 'Your session has expired. Please reload the page and sign in again.', + + '429_title' => 'Too many requests', + '429_body' => 'Too many requests in a short time. Please wait a moment and try again.', + + '500_title' => 'Unexpected error', + '500_body' => 'An unexpected error occurred on the server. Please try again later.', + + '503_title' => 'Down for maintenance', + '503_body' => 'Clusev is undergoing maintenance. Please check back shortly.', +]; diff --git a/lang/en/files.php b/lang/en/files.php index 5022668..9677c1e 100644 --- a/lang/en/files.php +++ b/lang/en/files.php @@ -24,6 +24,7 @@ return [ // Upload / notifications 'uploaded' => '“:name” uploaded.', 'upload_failed' => 'Upload failed: :error', + 'download_failed' => 'Download failed: :error', // Delete (confirm modal) 'delete_heading' => 'Delete file', diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..743a2c2 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon.ico b/public/favicon.ico index e69de29..d086ff4 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..e0ad4fd --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,9 @@ + diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000..53e6382 Binary files /dev/null and b/public/icon-192.png differ diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000..3ad580f Binary files /dev/null and b/public/icon-512.png differ diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..2c0cb2c --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,14 @@ +{ + "name": "Clusev", + "short_name": "Clusev", + "description": "Self-hosted control panel to administer a fleet of Linux servers from one dashboard.", + "lang": "de", + "icons": [ + { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, + { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" } + ], + "theme_color": "#06080A", + "background_color": "#06080A", + "display": "standalone", + "start_url": "/" +} diff --git a/resources/views/errors/403.blade.php b/resources/views/errors/403.blade.php new file mode 100644 index 0000000..c15db4c --- /dev/null +++ b/resources/views/errors/403.blade.php @@ -0,0 +1,4 @@ +@extends('errors.layout', ['title' => __('errors.403_title')]) +@section('code', '403') +@section('heading', __('errors.403_title')) +@section('message', __('errors.403_body')) diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php new file mode 100644 index 0000000..f583f82 --- /dev/null +++ b/resources/views/errors/404.blade.php @@ -0,0 +1,4 @@ +@extends('errors.layout', ['title' => __('errors.404_title')]) +@section('code', '404') +@section('heading', __('errors.404_title')) +@section('message', __('errors.404_body')) diff --git a/resources/views/errors/419.blade.php b/resources/views/errors/419.blade.php new file mode 100644 index 0000000..a66726b --- /dev/null +++ b/resources/views/errors/419.blade.php @@ -0,0 +1,4 @@ +@extends('errors.layout', ['title' => __('errors.419_title')]) +@section('code', '419') +@section('heading', __('errors.419_title')) +@section('message', __('errors.419_body')) diff --git a/resources/views/errors/429.blade.php b/resources/views/errors/429.blade.php new file mode 100644 index 0000000..ade8e84 --- /dev/null +++ b/resources/views/errors/429.blade.php @@ -0,0 +1,4 @@ +@extends('errors.layout', ['title' => __('errors.429_title')]) +@section('code', '429') +@section('heading', __('errors.429_title')) +@section('message', __('errors.429_body')) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php new file mode 100644 index 0000000..a5699b5 --- /dev/null +++ b/resources/views/errors/500.blade.php @@ -0,0 +1,4 @@ +@extends('errors.layout', ['title' => __('errors.500_title')]) +@section('code', '500') +@section('heading', __('errors.500_title')) +@section('message', __('errors.500_body')) diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php new file mode 100644 index 0000000..5d4a8e1 --- /dev/null +++ b/resources/views/errors/503.blade.php @@ -0,0 +1,4 @@ +@extends('errors.layout', ['title' => __('errors.503_title')]) +@section('code', '503') +@section('heading', __('errors.503_title')) +@section('message', __('errors.503_body')) diff --git a/resources/views/errors/layout.blade.php b/resources/views/errors/layout.blade.php new file mode 100644 index 0000000..c0c4032 --- /dev/null +++ b/resources/views/errors/layout.blade.php @@ -0,0 +1,32 @@ + + +
+ + +@yield('code')
+@yield('message')
+ +