diff --git a/.env.example b/.env.example index 3bfebb2..c26fba1 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,7 @@ APP_ENV=production APP_KEY= APP_DEBUG=false APP_URL=https://jouwdriedee.nl +APP_TIMEZONE=Europe/Amsterdam APP_LOCALE=nl APP_FALLBACK_LOCALE=nl diff --git a/app/Http/Controllers/Admin/CommunicationController.php b/app/Http/Controllers/Admin/CommunicationController.php index fae10b9..335ca69 100644 --- a/app/Http/Controllers/Admin/CommunicationController.php +++ b/app/Http/Controllers/Admin/CommunicationController.php @@ -6,13 +6,12 @@ use App\Http\Controllers\Controller; use App\Mail\ContactInquiryReplyMail; use App\Models\Communication; use App\Models\CommunicationReply; +use App\Services\DatabaseSchemaHelper; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; -use Illuminate\Support\Facades\Schema; use Illuminate\View\View; class CommunicationController extends Controller @@ -22,9 +21,7 @@ class CommunicationController extends Controller */ public function index(Request $request): View { - if (! Schema::hasTable('communications')) { - Artisan::call('migrate', ['--force' => true]); - } + DatabaseSchemaHelper::ensureCommunicationsTables(); $query = Communication::with(['user', 'order', 'replies'])->latest(); diff --git a/app/Http/Controllers/Admin/CustomerController.php b/app/Http/Controllers/Admin/CustomerController.php index dd26f55..fb5ab99 100644 --- a/app/Http/Controllers/Admin/CustomerController.php +++ b/app/Http/Controllers/Admin/CustomerController.php @@ -6,10 +6,9 @@ use App\Http\Controllers\Controller; use App\Models\Communication; use App\Models\Order; use App\Models\User; +use App\Services\DatabaseSchemaHelper; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Facades\Artisan; -use Illuminate\Support\Facades\Schema; use Illuminate\View\View; class CustomerController extends Controller @@ -19,9 +18,7 @@ class CustomerController extends Controller */ public function index(Request $request): View { - if (! Schema::hasTable('communications')) { - Artisan::call('migrate', ['--force' => true]); - } + DatabaseSchemaHelper::ensureCommunicationsTables(); $search = trim($request->input('q', '')); diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php index 03cdf67..6092bc8 100644 --- a/app/Http/Controllers/ContactController.php +++ b/app/Http/Controllers/ContactController.php @@ -7,13 +7,12 @@ use App\Mail\ContactInquiryReceivedMail; use App\Models\Communication; use App\Models\Order; use App\Models\Setting; +use App\Services\DatabaseSchemaHelper; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; -use Illuminate\Support\Facades\Schema; use Illuminate\View\View; class ContactController extends Controller @@ -62,13 +61,7 @@ class ContactController extends Controller } } - if (! Schema::hasTable('communications')) { - Artisan::call('migrate', ['--force' => true]); - } - - if (! \Illuminate\Support\Facades\Schema::hasTable('communications')) { - \Illuminate\Support\Facades\Artisan::call('migrate', ['--force' => true]); - } + DatabaseSchemaHelper::ensureCommunicationsTables(); $communication = Communication::create([ 'user_id' => Auth::id(), diff --git a/app/Http/Middleware/EnsureUserIsAdmin.php b/app/Http/Middleware/EnsureUserIsAdmin.php index ac9fdb5..cdace9f 100644 --- a/app/Http/Middleware/EnsureUserIsAdmin.php +++ b/app/Http/Middleware/EnsureUserIsAdmin.php @@ -2,11 +2,9 @@ namespace App\Http\Middleware; +use App\Services\DatabaseSchemaHelper; use Closure; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Artisan; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Schema; use Symfony\Component\HttpFoundation\Response; class EnsureUserIsAdmin @@ -28,13 +26,7 @@ class EnsureUserIsAdmin // 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()); - } + DatabaseSchemaHelper::ensureCommunicationsTables(); return $next($request); } diff --git a/app/Services/DatabaseSchemaHelper.php b/app/Services/DatabaseSchemaHelper.php new file mode 100644 index 0000000..0761e01 --- /dev/null +++ b/app/Services/DatabaseSchemaHelper.php @@ -0,0 +1,65 @@ +where('migration', 'like', '%create_communications_table%')->delete(); + } + + Artisan::call('migrate', ['--force' => true]); + + // Direct DDL Fallback if artisan migrate didn't create them + if (! Schema::hasTable('communications')) { + Schema::create('communications', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete(); + $table->foreignId('order_id')->nullable()->constrained('orders')->nullOnDelete(); + $table->string('name'); + $table->string('email')->index(); + $table->string('phone')->nullable(); + $table->string('subject'); + $table->text('message'); + $table->string('status')->default('new')->index(); + $table->text('admin_notes')->nullable(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->timestamp('last_replied_at')->nullable(); + $table->timestamps(); + }); + } + + if (! Schema::hasTable('communication_replies')) { + Schema::create('communication_replies', function (Blueprint $table) { + $table->id(); + $table->foreignId('communication_id')->constrained('communications')->cascadeOnDelete(); + $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete(); + $table->boolean('is_admin_reply')->default(true); + $table->text('message'); + $table->timestamps(); + }); + } + } catch (\Throwable $e) { + Log::error('ensureCommunicationsTables error: '.$e->getMessage(), ['trace' => $e->getTraceAsString()]); + } + } +} diff --git a/config/app.php b/config/app.php index dfa47ba..d40e58e 100644 --- a/config/app.php +++ b/config/app.php @@ -65,7 +65,7 @@ return [ | */ - 'timezone' => 'UTC', + 'timezone' => env('APP_TIMEZONE', 'Europe/Amsterdam'), /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2026_11_02_084554_create_communications_table.php b/database/migrations/2026_11_02_084554_create_communications_table.php index 8a52de3..3839fd2 100644 --- a/database/migrations/2026_11_02_084554_create_communications_table.php +++ b/database/migrations/2026_11_02_084554_create_communications_table.php @@ -11,31 +11,35 @@ return new class extends Migration */ public function up(): void { - Schema::create('communications', function (Blueprint $table) { - $table->id(); - $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete(); - $table->foreignId('order_id')->nullable()->constrained('orders')->nullOnDelete(); - $table->string('name'); - $table->string('email')->index(); - $table->string('phone')->nullable(); - $table->string('subject'); - $table->text('message'); - $table->string('status')->default('new')->index(); // new, in_progress, resolved, closed - $table->text('admin_notes')->nullable(); - $table->string('ip_address', 45)->nullable(); - $table->text('user_agent')->nullable(); - $table->timestamp('last_replied_at')->nullable(); - $table->timestamps(); - }); + if (! Schema::hasTable('communications')) { + Schema::create('communications', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete(); + $table->foreignId('order_id')->nullable()->constrained('orders')->nullOnDelete(); + $table->string('name'); + $table->string('email')->index(); + $table->string('phone')->nullable(); + $table->string('subject'); + $table->text('message'); + $table->string('status')->default('new')->index(); // new, in_progress, resolved, closed + $table->text('admin_notes')->nullable(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->timestamp('last_replied_at')->nullable(); + $table->timestamps(); + }); + } - Schema::create('communication_replies', function (Blueprint $table) { - $table->id(); - $table->foreignId('communication_id')->constrained('communications')->cascadeOnDelete(); - $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete(); - $table->boolean('is_admin_reply')->default(true); - $table->text('message'); - $table->timestamps(); - }); + if (! Schema::hasTable('communication_replies')) { + Schema::create('communication_replies', function (Blueprint $table) { + $table->id(); + $table->foreignId('communication_id')->constrained('communications')->cascadeOnDelete(); + $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete(); + $table->boolean('is_admin_reply')->default(true); + $table->text('message'); + $table->timestamps(); + }); + } } /** diff --git a/resources/views/admin/makerworld/index.blade.php b/resources/views/admin/makerworld/index.blade.php index af870d1..955ccc6 100644 --- a/resources/views/admin/makerworld/index.blade.php +++ b/resources/views/admin/makerworld/index.blade.php @@ -295,9 +295,9 @@