feat(api): registration auth, account creation, check-email & email notifications

- Add POST /public/check-email endpoint with rate limiting (10/min)
- Create user accounts during volunteer registration (new or returning)
- Returning volunteers authenticate with existing password
- Add password validation to VolunteerRegistrationRequest
- Normalize emails to lowercase throughout registration flow
- Handle race condition on duplicate accounts gracefully
- Create RegistrationConfirmationMail, RegistrationApprovedMail, RegistrationRejectedMail
- Wire approval/rejection emails into PersonController
- Add POST persons/{person}/reject endpoint
- Trigger TagSyncService on registration and approval
- Add CheckEmailTest, PersonApprovalEmailTest, extend VolunteerRegistrationTest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 00:37:04 +02:00
parent 4df82d8358
commit 8435e74fd3
17 changed files with 802 additions and 38 deletions

View File

@@ -195,8 +195,10 @@ class RegistrationSettingsTest extends TestCase
->assertJsonPath('data.0.section_count', 1);
}
public function test_section_preferences_stored_as_section_name(): void
public function test_section_preferences_stored_in_table(): void
{
\Illuminate\Support\Facades\Mail::fake();
// This is a regression check for the VolunteerRegistration flow
$event = Event::factory()->create([
'organisation_id' => $this->organisation->id,
@@ -207,7 +209,7 @@ class RegistrationSettingsTest extends TestCase
'organisation_id' => $this->organisation->id,
]);
FestivalSection::factory()->create([
$section = FestivalSection::factory()->create([
'event_id' => $event->id,
'name' => 'Backstage',
'show_in_registration' => true,
@@ -217,18 +219,20 @@ class RegistrationSettingsTest extends TestCase
'first_name' => 'Test',
'last_name' => 'Vrijwilliger',
'email' => 'test-section-pref@example.nl',
'password' => 'wachtwoord123',
'section_preferences' => [
['section_name' => 'Backstage', 'priority' => 1],
['festival_section_id' => $section->id, 'priority' => 1],
],
]);
$response->assertStatus(201);
$person = \App\Models\Person::where('email', 'test-section-pref@example.nl')->first();
$prefs = $person->custom_fields['section_preferences'];
$this->assertCount(1, $prefs);
$this->assertEquals('Backstage', $prefs[0]['section_name']);
$this->assertEquals(1, $prefs[0]['priority']);
$this->assertDatabaseHas('person_section_preferences', [
'person_id' => $person->id,
'festival_section_id' => $section->id,
'priority' => 1,
]);
}
}