Fix MakerWorld 2FA duplicate email loop and add email SMTP diagnostics

This commit is contained in:
2026-11-01 15:46:00 +01:00
parent 2d78d80934
commit 09285d0a46
12 changed files with 425 additions and 8 deletions
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
use Throwable;
class TestMailCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:test-mail {email : Het e-mailadres van de ontvanger (bijv. je eigen Gmail)}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Verstuur een test e-mail en controleer de mailer/SMTP verbinding';
/**
* Execute the console command.
*/
public function handle(): int
{
$recipient = $this->argument('email');
$defaultMailer = config('mail.default');
$smtpConfig = config("mail.mailers.{$defaultMailer}", []);
$fromAddress = config('mail.from.address');
$fromName = config('mail.from.name');
$this->info('=== Jouwdriedee.nl E-mail & SMTP Diagnostiek ===');
$this->table(['Instelling', 'Waarde'], [
['Actieve Mailer (MAIL_MAILER)', $defaultMailer],
['Verzender Adres (MAIL_FROM_ADDRESS)', $fromAddress],
['Verzender Naam (MAIL_FROM_NAME)', $fromName],
['SMTP Host', $smtpConfig['host'] ?? 'N.v.t. (geen smtp)'],
['SMTP Poort', $smtpConfig['port'] ?? 'N.v.t.'],
['SMTP Gebruiker', ! empty($smtpConfig['username']) ? $smtpConfig['username'] : '(geen / leeg)'],
['SMTP Wachtwoord ingesteld?', ! empty($smtpConfig['password']) ? 'Ja' : 'Nee'],
['Ontvanger', $recipient],
]);
if ($defaultMailer === 'log') {
$this->warn("⚠️ WAARSCHUWING: MAIL_MAILER staat momenteel op 'log'!");
$this->warn('Dit betekent dat er GEEN echte mail via internet wordt verzonden naar Gmail.');
$this->warn('De mail wordt weggeschreven naar storage/logs/laravel.log.');
$this->warn('Stel MAIL_MAILER=smtp in met geldige SMTP-gegevens op Coolify om echte mails te ontvangen.');
}
$this->line("\nVersturen van testmail naar {$recipient}...");
try {
Mail::raw("Dit is een test e-mail vanaf Jouwdriedee.nl om te controleren of de mailserver en bezorging naar {$recipient} correct functioneert.\n\nVerzonden op: ".now()->toIso8601String(), function ($message) use ($recipient) {
$message->to($recipient)
->subject('Test E-mail vanaf Jouwdriedee.nl');
});
$this->info("✓ Test e-mail succesvol verzonden via driver '{$defaultMailer}'!");
if ($defaultMailer === 'log') {
$this->comment('Controleer storage/logs/laravel.log om de gelogde e-mail te zien.');
} else {
$this->info("Controleer nu je inbox (en eventueel spambox) op {$recipient}.");
}
return self::SUCCESS;
} catch (Throwable $e) {
$this->error('❌ FOUT BIJ HET VERZENDEN:');
$this->error($e->getMessage());
$this->line("\nStack trace:");
$this->line($e->getTraceAsString());
return self::FAILURE;
}
}
}
@@ -6,6 +6,8 @@ use App\Http\Controllers\Controller;
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\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\View\View; use Illuminate\View\View;
class ContentController extends Controller class ContentController extends Controller
@@ -132,4 +134,38 @@ class ContentController extends Controller
return redirect()->route('admin.content.index') return redirect()->route('admin.content.index')
->with('success', 'Alle teksten en lay-out instellingen zijn hersteld naar de standaarden.'); ->with('success', 'Alle teksten en lay-out instellingen zijn hersteld naar de standaarden.');
} }
/**
* Send a test email to verify mail delivery (e.g. to Gmail).
*/
public function sendTestMail(Request $request): RedirectResponse
{
$validated = $request->validate([
'test_email' => 'required|email',
]);
$recipient = $validated['test_email'];
$defaultMailer = config('mail.default');
$fromAddress = config('mail.from.address');
try {
Mail::raw("Dit is een test e-mail vanaf Jouwdriedee.nl om de e-mail bezorging naar {$recipient} te controleren.\n\nActieve mailer: {$defaultMailer}\nAfzender: {$fromAddress}\nDatum: ".now()->format('d-m-Y H:i:s'), function ($message) use ($recipient) {
$message->to($recipient)
->subject('Test E-mail vanaf Jouwdriedee.nl');
});
if ($defaultMailer === 'log') {
return redirect()->back()
->with('warning', "De testmail is weggeschreven naar de serverlogs (MAIL_MAILER staat op 'log'). Er is GEEN echte e-mail naar {$recipient} verzonden. Stel op Coolify geldige SMTP-gegevens in (bijv. Brevo, Resend of je hosting SMTP) om echte e-mails te bezorgen.");
}
return redirect()->back()
->with('success', "Test e-mail succesvol verzonden via driver '{$defaultMailer}' naar {$recipient}! Controleer je inbox en eventueel je spambox.");
} catch (\Throwable $e) {
Log::error('Test mail error: '.$e->getMessage(), ['trace' => $e->getTraceAsString()]);
return redirect()->back()
->with('error', "Fout bij verzenden van e-mail via {$defaultMailer}: ".$e->getMessage());
}
}
} }
@@ -337,13 +337,13 @@ class MakerWorldController extends Controller
if ($result['status'] === '2fa_required') { if ($result['status'] === '2fa_required') {
return redirect() return redirect()
->route('admin.makerworld.index') ->route('admin.makerworld.index', ['tab' => 'auth'])
->with('warning', $result['message']) ->with('warning', $result['message'])
->with('makerworld_2fa_account', $result['account']); ->with('makerworld_2fa_account', $result['account']);
} }
return redirect() return redirect()
->route('admin.makerworld.index') ->route('admin.makerworld.index', ['tab' => 'auth'])
->with('error', $result['message']); ->with('error', $result['message']);
} }
@@ -369,11 +369,28 @@ class MakerWorldController extends Controller
} }
return redirect() return redirect()
->route('admin.makerworld.index') ->route('admin.makerworld.index', ['tab' => 'auth'])
->with('error', $result['message']) ->with('error', $result['message'])
->with('makerworld_2fa_account', $validated['account']); ->with('makerworld_2fa_account', $validated['account']);
} }
/**
* Resend verification code for 2FA manually on request.
*/
public function resendCode(Request $request): RedirectResponse
{
$validated = $request->validate([
'account' => 'required|string',
]);
$sent = $this->makerWorld->sendEmailVerificationCode($validated['account']);
return redirect()
->route('admin.makerworld.index', ['tab' => 'auth'])
->with($sent ? 'success' : 'error', $sent ? 'Er is een nieuwe verificatiecode verstuurd naar je e-mail.' : 'Kon verificatiecode niet opnieuw verzenden.')
->with('makerworld_2fa_account', $validated['account']);
}
/** /**
* Save MakerWorld session / bearer token directly. * Save MakerWorld session / bearer token directly.
*/ */
@@ -11,6 +11,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@@ -61,6 +62,7 @@ class ForgotPasswordController extends Controller
try { try {
Mail::to($user->email)->send(new PasswordResetMail($user, $resetUrl)); Mail::to($user->email)->send(new PasswordResetMail($user, $resetUrl));
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error("Fout bij verzenden van wachtwoordherstel e-mail naar {$user->email}: ".$e->getMessage());
report($e); report($e);
} }
@@ -9,6 +9,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\Log;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\View\View; use Illuminate\View\View;
use Throwable; use Throwable;
@@ -56,6 +57,7 @@ class RegisterController extends Controller
try { try {
Mail::to($user->email)->send(new WelcomeMail($user)); Mail::to($user->email)->send(new WelcomeMail($user));
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error("Fout bij verzenden van welkomstmail naar {$user->email}: ".$e->getMessage());
report($e); report($e);
} }
+2 -4
View File
@@ -111,14 +111,12 @@ class MakerWorldService
]; ];
} }
// Check if 2FA code is needed // Check if 2FA code is needed (Bambu Lab sends the code automatically when verifyCode is returned)
if (isset($data['loginType']) && $data['loginType'] === 'verifyCode') { if (isset($data['loginType']) && $data['loginType'] === 'verifyCode') {
$this->sendEmailVerificationCode($account);
return [ return [
'status' => '2fa_required', 'status' => '2fa_required',
'account' => $account, 'account' => $account,
'message' => 'Verificatiecode vereist. We hebben een 6-cijferige code naar je e-mail gestuurd.', 'message' => 'Verificatiecode vereist. Bambu Lab heeft een 6-cijferige verificatiecode naar je e-mail gestuurd.',
]; ];
} }
@@ -97,6 +97,16 @@
<i data-lucide="settings" class="w-3.5 h-3.5"></i> <i data-lucide="settings" class="w-3.5 h-3.5"></i>
<span>6. Algemeen & Thema</span> <span>6. Algemeen & Thema</span>
</button> </button>
<button
type="button"
@click="activeTab = 'email'"
:class="activeTab === 'email' ? 'bg-amber-400 text-slate-950 shadow-sm' : 'bg-slate-800 text-slate-300 hover:bg-slate-700'"
class="px-4 py-2.5 rounded-xl transition flex items-center space-x-1.5 whitespace-nowrap cursor-pointer"
>
<i data-lucide="mail" class="w-3.5 h-3.5"></i>
<span>7. E-mail & SMTP Testen</span>
</button>
</div> </div>
<!-- ==================================================== --> <!-- ==================================================== -->
@@ -508,6 +518,119 @@
</div> </div>
</div> </div>
<!-- ==================================================== -->
<!-- TAB 7: E-MAIL & SMTP DIAGNOSTIEK -->
<!-- ==================================================== -->
<div x-show="activeTab === 'email'" class="space-y-6">
<div class="p-6 rounded-3xl bg-slate-900 border border-slate-800 space-y-6">
<div class="border-b border-slate-800 pb-3 flex items-center justify-between">
<div>
<h3 class="font-heading font-black text-lg text-white flex items-center space-x-2">
<i data-lucide="mail-check" class="w-5 h-5 text-amber-400"></i>
<span>E-mail & SMTP Status</span>
</h3>
<p class="text-xs text-slate-400">Controleer waarom wachtwoordherstel- en registratiemails wel of niet aankomen op Gmail.</p>
</div>
@php
$activeMailer = config('mail.default');
@endphp
<span class="px-3 py-1 rounded-full text-xs font-black uppercase tracking-wider {{ $activeMailer === 'log' ? 'bg-amber-400/20 text-amber-400 border border-amber-400/40' : 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/40' }}">
Driver: {{ strtoupper($activeMailer) }}
</span>
</div>
@if($activeMailer === 'log')
<div class="p-4 rounded-2xl bg-amber-950/60 border border-amber-400/50 text-amber-200 text-xs space-y-2">
<div class="font-black text-sm flex items-center space-x-2 text-amber-300">
<i data-lucide="alert-triangle" class="w-5 h-5 shrink-0"></i>
<span>Belangrijke Oorzaak: MAIL_MAILER staat op 'log'</span>
</div>
<p>
Momenteel worden uitgaande e-mails (zoals wachtwoord vergeten en welkomstmails) weggeschreven naar de serverlogbestanden en <strong>niet via internet naar Gmail verzonden</strong>.
</p>
<p>
Om echte e-mails te versturen, moet op de server (Coolify Environment Variables) <code class="bg-slate-950 px-1.5 py-0.5 rounded text-amber-400 font-mono font-bold">MAIL_MAILER=smtp</code> ingesteld worden met de bijbehorende SMTP-host, poort en wachtwoord.
</p>
</div>
@endif
<!-- Server SMTP Overview Table -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs">
<div class="p-4 rounded-2xl bg-slate-950 border border-slate-800 space-y-2">
<div class="font-bold text-slate-400 uppercase tracking-wider text-[10px]">Huidige Server Mail Instellingen</div>
<div class="flex justify-between py-1 border-b border-slate-800">
<span class="text-slate-400">MAIL_MAILER:</span>
<span class="font-mono text-white font-bold">{{ config('mail.default') }}</span>
</div>
<div class="flex justify-between py-1 border-b border-slate-800">
<span class="text-slate-400">MAIL_HOST:</span>
<span class="font-mono text-white font-bold">{{ config('mail.mailers.smtp.host') ?: '(geen)' }}</span>
</div>
<div class="flex justify-between py-1 border-b border-slate-800">
<span class="text-slate-400">MAIL_PORT:</span>
<span class="font-mono text-white font-bold">{{ config('mail.mailers.smtp.port') ?: '(geen)' }}</span>
</div>
<div class="flex justify-between py-1 border-b border-slate-800">
<span class="text-slate-400">MAIL_USERNAME:</span>
<span class="font-mono text-white font-bold">{{ config('mail.mailers.smtp.username') ?: '(geen)' }}</span>
</div>
<div class="flex justify-between py-1 border-b border-slate-800">
<span class="text-slate-400">MAIL_FROM_ADDRESS:</span>
<span class="font-mono text-amber-400 font-bold">{{ config('mail.from.address') }}</span>
</div>
</div>
<div class="p-4 rounded-2xl bg-slate-950 border border-slate-800 space-y-2">
<div class="font-bold text-slate-400 uppercase tracking-wider text-[10px]">Gmail Bezorging Checklist</div>
<ul class="space-y-1.5 text-slate-300">
<li class="flex items-start space-x-2">
<span class="text-amber-400 font-bold shrink-0">1.</span>
<span><strong>SMTP Provider:</strong> Gebruik een werkende SMTP server (bijv. Brevo, Resend, Mailgun of Versio mail).</span>
</li>
<li class="flex items-start space-x-2">
<span class="text-amber-400 font-bold shrink-0">2.</span>
<span><strong>SPF DNS Record:</strong> Google vereist een SPF TXT-record op <code class="font-mono text-amber-400">jouwdriedee.nl</code>.</span>
</li>
<li class="flex items-start space-x-2">
<span class="text-amber-400 font-bold shrink-0">3.</span>
<span><strong>DKIM & DMARC:</strong> Zonder DKIM plaatst Gmail emails van nieuwe domeinen direct in de Spammap.</span>
</li>
</ul>
</div>
</div>
<!-- Test Email Form -->
<div class="p-5 rounded-2xl bg-slate-950 border border-slate-800 space-y-3">
<div class="font-bold text-white text-xs flex items-center space-x-2">
<i data-lucide="send" class="w-4 h-4 text-amber-400"></i>
<span>Direct een Test E-mail Versturen</span>
</div>
<p class="text-xs text-slate-400">
Vul hier een e-mailadres in (bijv. je eigen Gmail) om direct te testen of e-mails aankomen en of de verbinding met de mailserver slaagt.
</p>
<div class="flex flex-col sm:flex-row gap-3 pt-1">
<input
type="email"
name="test_email"
value="{{ auth()->user()->email ?? 'info@jouwdriedee.nl' }}"
placeholder="jouw-email@gmail.com"
class="flex-1 bg-slate-900 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white font-mono placeholder-slate-600 focus:outline-none focus:ring-2 focus:ring-amber-400"
>
<button
type="submit"
formaction="{{ route('admin.content.test-mail') }}"
formmethod="POST"
class="px-5 py-2.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs uppercase tracking-wider transition flex items-center justify-center space-x-2 shrink-0 cursor-pointer shadow-lg shadow-amber-400/20"
>
<i data-lucide="send" class="w-4 h-4"></i>
<span>Verstuur Testmail</span>
</button>
</div>
</div>
</div>
</div>
<!-- Sticky Save Action Bar --> <!-- Sticky Save Action Bar -->
<div class="sticky bottom-4 z-20 p-4 rounded-2xl bg-slate-900/95 backdrop-blur-md border border-amber-400/30 shadow-2xl flex items-center justify-between"> <div class="sticky bottom-4 z-20 p-4 rounded-2xl bg-slate-900/95 backdrop-blur-md border border-amber-400/30 shadow-2xl flex items-center justify-between">
<div class="flex items-center space-x-2 text-xs text-slate-300 font-bold"> <div class="flex items-center space-x-2 text-xs text-slate-300 font-bold">
@@ -62,7 +62,65 @@
</div> </div>
@endif @endif
<!-- Tab Bar --> <!-- 2FA Verificatiecode Invoerscherm (Bambu Lab) -->
@if(session('makerworld_2fa_account'))
<div class="bg-gradient-to-r from-slate-900 to-amber-950/80 p-6 sm:p-8 rounded-3xl border-2 border-amber-400 shadow-2xl">
<div class="max-w-xl mx-auto text-center space-y-4">
<div class="w-16 h-16 rounded-2xl bg-amber-400/20 text-amber-400 flex items-center justify-center mx-auto shadow-inner">
<i data-lucide="shield-check" class="w-8 h-8"></i>
</div>
<div>
<span class="px-3 py-1 rounded-full text-xs font-black uppercase tracking-wider bg-amber-400 text-slate-950">
Stap 2: Twee-staps Verificatie (2FA)
</span>
<h2 class="text-2xl font-black text-white mt-2">Bambu Lab Verificatiecode</h2>
<p class="text-xs text-slate-300 mt-1">
Vul de 6-cijferige verificatiecode in die Bambu Lab zojuist heeft verzonden naar <strong class="text-amber-300 font-mono">{{ session('makerworld_2fa_account') }}</strong>.
</p>
</div>
<form action="{{ route('admin.makerworld.verify-code') }}" method="POST" class="mt-4 space-y-4">
@csrf
<input type="hidden" name="account" value="{{ session('makerworld_2fa_account') }}">
<div class="flex justify-center">
<input
type="text"
name="code"
maxlength="6"
placeholder="123456"
autofocus
required
class="w-56 text-center text-3xl font-mono font-black tracking-widest px-4 py-3 bg-slate-950 border-2 border-amber-400 rounded-2xl text-amber-300 focus:outline-none focus:ring-4 focus:ring-amber-400/30 placeholder-slate-700"
>
</div>
<div class="flex items-center justify-center space-x-3 pt-2">
<button type="submit" class="px-8 py-3 bg-amber-400 hover:bg-amber-300 text-slate-950 font-black rounded-2xl text-xs uppercase tracking-wider shadow-lg shadow-amber-400/20 transition flex items-center space-x-2">
<i data-lucide="check" class="w-4 h-4"></i>
<span>Verifieer Code & Inloggen</span>
</button>
</div>
</form>
<div class="pt-3 border-t border-slate-800/80 flex items-center justify-center space-x-6 text-xs text-slate-400">
<form action="{{ route('admin.makerworld.resend-code') }}" method="POST">
@csrf
<input type="hidden" name="account" value="{{ session('makerworld_2fa_account') }}">
<button type="submit" class="hover:text-amber-400 underline font-bold transition">
Geen code ontvangen? Verstuur opnieuw
</button>
</form>
<span>&bull;</span>
<a href="{{ route('admin.makerworld.index', ['tab' => 'auth']) }}" class="hover:text-slate-300 underline font-bold transition">
Ander account proberen
</a>
</div>
</div>
</div>
@endif
<!-- TAB Bar -->
<div class="flex items-center space-x-2 border-b border-slate-800 pb-2 overflow-x-auto text-sm font-bold"> <div class="flex items-center space-x-2 border-b border-slate-800 pb-2 overflow-x-auto text-sm font-bold">
<a href="{{ route('admin.makerworld.index', ['tab' => 'collections']) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-2 {{ $currentTab === 'collections' ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.makerworld.index', ['tab' => 'collections']) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-2 {{ $currentTab === 'collections' ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}">
<i data-lucide="folder-sync" class="w-4 h-4"></i> <i data-lucide="folder-sync" class="w-4 h-4"></i>
@@ -353,3 +353,4 @@ function artikelBeheerApp() {
} }
</script> </script>
@endsection @endsection
+2
View File
@@ -115,6 +115,7 @@ Route::prefix('admin')->name('admin.')->middleware(['auth', 'admin'])->group(fun
Route::get('/content', [ContentController::class, 'index'])->name('content.index'); Route::get('/content', [ContentController::class, 'index'])->name('content.index');
Route::post('/content', [ContentController::class, 'update'])->name('content.update'); Route::post('/content', [ContentController::class, 'update'])->name('content.update');
Route::post('/content/reset', [ContentController::class, 'reset'])->name('content.reset'); Route::post('/content/reset', [ContentController::class, 'reset'])->name('content.reset');
Route::post('/content/test-mail', [ContentController::class, 'sendTestMail'])->name('content.test-mail');
// Producten Beheer // Producten Beheer
Route::resource('products', AdminProductController::class)->except(['show']); Route::resource('products', AdminProductController::class)->except(['show']);
@@ -131,6 +132,7 @@ Route::prefix('admin')->name('admin.')->middleware(['auth', 'admin'])->group(fun
Route::get('/makerworld', [MakerWorldController::class, 'index'])->name('makerworld.index'); Route::get('/makerworld', [MakerWorldController::class, 'index'])->name('makerworld.index');
Route::post('/makerworld/login', [MakerWorldController::class, 'login'])->name('makerworld.login'); Route::post('/makerworld/login', [MakerWorldController::class, 'login'])->name('makerworld.login');
Route::post('/makerworld/verify-code', [MakerWorldController::class, 'verifyCode'])->name('makerworld.verify-code'); Route::post('/makerworld/verify-code', [MakerWorldController::class, 'verifyCode'])->name('makerworld.verify-code');
Route::post('/makerworld/resend-code', [MakerWorldController::class, 'resendCode'])->name('makerworld.resend-code');
Route::post('/makerworld/token', [MakerWorldController::class, 'saveToken'])->name('makerworld.token'); Route::post('/makerworld/token', [MakerWorldController::class, 'saveToken'])->name('makerworld.token');
Route::post('/makerworld/disconnect', [MakerWorldController::class, 'disconnect'])->name('makerworld.disconnect'); Route::post('/makerworld/disconnect', [MakerWorldController::class, 'disconnect'])->name('makerworld.disconnect');
Route::get('/makerworld/preview', [MakerWorldController::class, 'preview'])->name('makerworld.preview'); Route::get('/makerworld/preview', [MakerWorldController::class, 'preview'])->name('makerworld.preview');
+30
View File
@@ -10,6 +10,7 @@ use App\Models\Material;
use App\Models\Product; use App\Models\Product;
use App\Models\User; use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
@@ -215,4 +216,33 @@ class AuthAndEmailTest extends TestCase
&& str_contains($mail->envelope()->subject, 'Bestelbevestiging'); && str_contains($mail->envelope()->subject, 'Bestelbevestiging');
}); });
} }
public function test_admin_can_trigger_test_email_via_web_and_cli(): void
{
Mail::fake();
$admin = User::create([
'name' => 'Admin Jouwdriedee',
'email' => 'info@jouwdriedee.nl',
'password' => bcrypt('Jw3D!x9#QmK7$vR2'),
'is_admin' => true,
]);
$this->actingAs($admin);
// Web endpoint
$response = $this->post(route('admin.content.test-mail'), [
'test_email' => 'testgmail@gmail.com',
]);
$response->assertRedirect();
$response->assertSessionHas('success');
// CLI command
$exitCode = Artisan::call('app:test-mail', [
'email' => 'testcli@gmail.com',
]);
$this->assertEquals(0, $exitCode);
}
} }
+68
View File
@@ -215,4 +215,72 @@ class MakerWorldImporterTest extends TestCase
$disconnectResponse->assertRedirect(); $disconnectResponse->assertRedirect();
$this->assertNull(Setting::get('makerworld_token')); $this->assertNull(Setting::get('makerworld_token'));
} }
public function test_makerworld_login_with_2fa_redirects_to_auth_tab_without_duplicate_email(): void
{
Http::fake([
'https://api.bambulab.com/v1/user-service/user/login' => Http::response([
'loginType' => 'verifyCode',
'message' => 'Verification code sent',
], 200),
'https://api.bambulab.com/v1/user-service/user/sendemail/code' => Http::response([
'success' => true,
], 200),
]);
$response = $this->post(route('admin.makerworld.login'), [
'account' => 'test2fa@jouwdriedee.nl',
'password' => 'secret_pass',
]);
$response->assertRedirect(route('admin.makerworld.index', ['tab' => 'auth']));
$response->assertSessionHas('makerworld_2fa_account', 'test2fa@jouwdriedee.nl');
// Verify that sendemail/code was NOT called automatically (Bambu Lab already sent it on login)
Http::assertNotSent(function ($request) {
return str_contains($request->url(), 'sendemail/code');
});
}
public function test_makerworld_can_verify_2fa_code(): void
{
Http::fake([
'https://api.bambulab.com/v1/user-service/user/login' => Http::response([
'accessToken' => 'token_after_2fa_code_9999',
], 200),
'https://api.bambulab.com/v1/design-user-service/my/preference' => Http::response([
'uid' => 12345,
'name' => 'Verified User',
], 200),
]);
$response = $this->post(route('admin.makerworld.verify-code'), [
'account' => 'test2fa@jouwdriedee.nl',
'code' => '123456',
]);
$response->assertRedirect(route('admin.makerworld.index', ['tab' => 'collections']));
$this->assertSame('token_after_2fa_code_9999', Setting::get('makerworld_token'));
}
public function test_makerworld_can_manually_resend_2fa_code(): void
{
Http::fake([
'https://api.bambulab.com/v1/user-service/user/sendemail/code' => Http::response([
'success' => true,
], 200),
]);
$response = $this->post(route('admin.makerworld.resend-code'), [
'account' => 'test2fa@jouwdriedee.nl',
]);
$response->assertRedirect(route('admin.makerworld.index', ['tab' => 'auth']));
$response->assertSessionHas('success');
Http::assertSent(function ($request) {
return str_contains($request->url(), 'sendemail/code') &&
$request['email'] === 'test2fa@jouwdriedee.nl';
});
}
} }