Fix 500 error on admin access: make navigation layout resilient and auto-run pending migrations on admin entry
This commit is contained in:
@@ -8,6 +8,7 @@ use Illuminate\Http\RedirectResponse;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
@@ -26,11 +27,14 @@ class AccountController extends Controller
|
|||||||
->latest()
|
->latest()
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$communications = Communication::where('user_id', $user->id)
|
$communications = collect();
|
||||||
->orWhere('email', strtolower($user->email))
|
if (Schema::hasTable('communications')) {
|
||||||
->with('replies')
|
$communications = Communication::where('user_id', $user->id)
|
||||||
->latest()
|
->orWhere('email', strtolower($user->email))
|
||||||
->get();
|
->with('replies')
|
||||||
|
->latest()
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
return view('account.index', compact('user', 'orders', 'communications'));
|
return view('account.index', compact('user', 'orders', 'communications'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ use App\Models\Communication;
|
|||||||
use App\Models\CommunicationReply;
|
use App\Models\CommunicationReply;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class CommunicationController extends Controller
|
class CommunicationController extends Controller
|
||||||
@@ -20,6 +22,10 @@ class CommunicationController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): View
|
public function index(Request $request): View
|
||||||
{
|
{
|
||||||
|
if (! Schema::hasTable('communications')) {
|
||||||
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
$query = Communication::with(['user', 'order', 'replies'])->latest();
|
$query = Communication::with(['user', 'order', 'replies'])->latest();
|
||||||
|
|
||||||
// Status filter
|
// Status filter
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use App\Models\Order;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class CustomerController extends Controller
|
class CustomerController extends Controller
|
||||||
@@ -17,6 +19,10 @@ class CustomerController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): View
|
public function index(Request $request): View
|
||||||
{
|
{
|
||||||
|
if (! Schema::hasTable('communications')) {
|
||||||
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
$search = trim($request->input('q', ''));
|
$search = trim($request->input('q', ''));
|
||||||
|
|
||||||
// Query registered non-admin users
|
// Query registered non-admin users
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ use App\Models\Order;
|
|||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class ContactController extends Controller
|
class ContactController extends Controller
|
||||||
@@ -60,6 +62,10 @@ class ContactController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communications')) {
|
||||||
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
$communication = Communication::create([
|
$communication = Communication::create([
|
||||||
'user_id' => Auth::id(),
|
'user_id' => Auth::id(),
|
||||||
'order_id' => $orderId,
|
'order_id' => $orderId,
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ namespace App\Http\Middleware;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class EnsureUserIsAdmin
|
class EnsureUserIsAdmin
|
||||||
@@ -23,6 +26,16 @@ class EnsureUserIsAdmin
|
|||||||
->with('error', 'Je hebt geen beheerderstoegang tot deze pagina.');
|
->with('error', 'Je hebt geen beheerderstoegang tot deze pagina.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-migrate check: Ensure that new database tables (like communications) are migrated
|
||||||
|
// automatically when an admin enters the admin panel, avoiding 500 errors on Coolify/production.
|
||||||
|
try {
|
||||||
|
if (! Schema::hasTable('communications')) {
|
||||||
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('Auto-migration in EnsureUserIsAdmin failed: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,14 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$draftBadgeCount = \App\Models\Product::where('status', 'draft')->count();
|
$draftBadgeCount = 0;
|
||||||
|
try {
|
||||||
|
if (\Illuminate\Support\Facades\Schema::hasTable('products')) {
|
||||||
|
$draftBadgeCount = \App\Models\Product::where('status', 'draft')->count();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$draftBadgeCount = 0;
|
||||||
|
}
|
||||||
@endphp
|
@endphp
|
||||||
<a href="{{ route('admin.makerworld.staging') }}" class="px-3 py-1.5 rounded-lg transition flex items-center space-x-1.5 {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-slate-900 text-white font-semibold' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
|
<a href="{{ route('admin.makerworld.staging') }}" class="px-3 py-1.5 rounded-lg transition flex items-center space-x-1.5 {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-slate-900 text-white font-semibold' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
|
||||||
<i data-lucide="layers" class="w-3.5 h-3.5"></i>
|
<i data-lucide="layers" class="w-3.5 h-3.5"></i>
|
||||||
@@ -74,7 +81,14 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$unreadCommCount = \App\Models\Communication::where('status', 'new')->count();
|
$unreadCommCount = 0;
|
||||||
|
try {
|
||||||
|
if (\Illuminate\Support\Facades\Schema::hasTable('communications')) {
|
||||||
|
$unreadCommCount = \App\Models\Communication::where('status', 'new')->count();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$unreadCommCount = 0;
|
||||||
|
}
|
||||||
@endphp
|
@endphp
|
||||||
<a href="{{ route('admin.communications.index') }}" class="px-3 py-1.5 rounded-lg transition flex items-center space-x-1.5 {{ request()->routeIs('admin.communications.*') ? 'bg-slate-900 text-white font-semibold' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
|
<a href="{{ route('admin.communications.index') }}" class="px-3 py-1.5 rounded-lg transition flex items-center space-x-1.5 {{ request()->routeIs('admin.communications.*') ? 'bg-slate-900 text-white font-semibold' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
|
||||||
<i data-lucide="message-square" class="w-3.5 h-3.5"></i>
|
<i data-lucide="message-square" class="w-3.5 h-3.5"></i>
|
||||||
@@ -101,7 +115,14 @@
|
|||||||
<!-- Right Actions -->
|
<!-- Right Actions -->
|
||||||
<div class="flex items-center space-x-2.5">
|
<div class="flex items-center space-x-2.5">
|
||||||
@php
|
@php
|
||||||
$isShopOffline = \App\Models\Setting::get('shop_offline', '0') === '1';
|
$isShopOffline = false;
|
||||||
|
try {
|
||||||
|
if (\Illuminate\Support\Facades\Schema::hasTable('settings')) {
|
||||||
|
$isShopOffline = \App\Models\Setting::get('shop_offline', '0') === '1';
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$isShopOffline = false;
|
||||||
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<!-- Webshop Offline / Online Status Switcher -->
|
<!-- Webshop Offline / Online Status Switcher -->
|
||||||
@@ -175,7 +196,7 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Offline Alert Banner -->
|
<!-- Offline Alert Banner -->
|
||||||
@if($isShopOffline ?? (\App\Models\Setting::get('shop_offline', '0') === '1'))
|
@if(!empty($isShopOffline))
|
||||||
<div class="bg-rose-500 text-white px-4 py-2.5 text-xs font-medium border-b border-rose-600 shadow-sm flex flex-col sm:flex-row items-center justify-between gap-3 sticky top-16 z-20">
|
<div class="bg-rose-500 text-white px-4 py-2.5 text-xs font-medium border-b border-rose-600 shadow-sm flex flex-col sm:flex-row items-center justify-between gap-3 sticky top-16 z-20">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<span class="w-2 h-2 rounded-full bg-white animate-pulse shrink-0"></span>
|
<span class="w-2 h-2 rounded-full bg-white animate-pulse shrink-0"></span>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use App\Models\Order;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
class CustomerCommunicationTest extends TestCase
|
class CustomerCommunicationTest extends TestCase
|
||||||
@@ -231,4 +232,14 @@ class CustomerCommunicationTest extends TestCase
|
|||||||
$this->actingAs($this->customer)->get(route('admin.communications.index'))->assertRedirect(route('home'));
|
$this->actingAs($this->customer)->get(route('admin.communications.index'))->assertRedirect(route('home'));
|
||||||
$this->actingAs($this->customer)->get(route('admin.customers.index'))->assertRedirect(route('home'));
|
$this->actingAs($this->customer)->get(route('admin.customers.index'))->assertRedirect(route('home'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_admin_layout_does_not_crash_even_if_communications_table_is_initially_missing(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('communication_replies');
|
||||||
|
Schema::dropIfExists('communications');
|
||||||
|
|
||||||
|
// Layout renders with HTTP 200 without 500 server error even when table was absent
|
||||||
|
$response = $this->actingAs($this->admin)->get(route('admin.content.index'));
|
||||||
|
$response->assertStatus(200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user