Skip to content

Commit cee928e

Browse files
committed
make things easier
1 parent 558c924 commit cee928e

6 files changed

Lines changed: 295 additions & 7 deletions

File tree

app/Http/Controllers/BadgeController.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
use App\Models\Badge\State_Payment\Paid;
99
use App\Models\Badge\State_Payment\Unpaid;
1010
use App\Models\Event;
11+
use App\Models\EventUser;
1112
use App\Models\Species;
1213
use App\Models\User;
1314
use App\Notifications\BadgeCreatedNotification;
1415
use App\Services\BadgeCalculationService;
16+
use App\Services\TokenRefreshService;
1517
use Illuminate\Http\Request;
1618
use Illuminate\Support\Facades\DB;
1719
use Illuminate\Support\Facades\Gate;
20+
use Illuminate\Support\Facades\Http;
1821
use Illuminate\Support\Facades\Storage;
1922
use Inertia\Inertia;
2023

@@ -305,4 +308,108 @@ public function destroy(Request $request, Badge $badge)
305308

306309
return redirect()->route('badges.index');
307310
}
311+
312+
public function refreshPrepaidBadges(Request $request)
313+
{
314+
$user = $request->user();
315+
$activeEvent = Event::getActiveEvent();
316+
317+
if (!$activeEvent) {
318+
return response()->json(['error' => 'No active event found'], 400);
319+
}
320+
321+
try {
322+
// Get fresh token or use existing one
323+
$tokenService = new TokenRefreshService($user);
324+
$accessToken = $tokenService->getValidAccessToken();
325+
326+
if (!$accessToken) {
327+
return response()->json(['error' => 'Unable to get authentication token'], 401);
328+
}
329+
330+
// Get or create EventUser relationship
331+
$eventUser = EventUser::firstOrCreate([
332+
'user_id' => $user->id,
333+
'event_id' => $activeEvent->id,
334+
], [
335+
'attendee_id' => null,
336+
'valid_registration' => false,
337+
'prepaid_badges' => 0,
338+
]);
339+
340+
// Get attendee info
341+
$attendeeListResponse = Http::attsrv()
342+
->withToken($accessToken)
343+
->get('/attendees')
344+
->json();
345+
346+
$regId = $attendeeListResponse['ids'][0] ?? null;
347+
348+
if (!$regId) {
349+
return response()->json(['error' => 'No registration found'], 404);
350+
}
351+
352+
// Get registration status
353+
$statusResponse = Http::attsrv()
354+
->withToken($accessToken)
355+
->get('/attendees/' . $regId . '/status');
356+
357+
// Update EventUser with attendee info
358+
$eventUser->update([
359+
'attendee_id' => $regId,
360+
'valid_registration' => in_array($statusResponse->json()['status'], ['paid', 'checked in']),
361+
]);
362+
363+
// Check for fursuit packages
364+
$fursuit = Http::attsrv()
365+
->withToken($accessToken)
366+
->get('/attendees/' . $regId . '/packages/fursuit')
367+
->json();
368+
369+
$totalPrepaidBadges = 0;
370+
371+
if ($fursuit['present'] && $fursuit['count'] > 0) {
372+
// Get additional fursuit badges
373+
$fursuitAdditional = Http::attsrv()
374+
->withToken($accessToken)
375+
->get('/attendees/' . $regId . '/packages/fursuitadd')
376+
->json();
377+
378+
$additionalCopies = $fursuitAdditional['present'] ? $fursuitAdditional['count'] : 0;
379+
$totalPrepaidBadges = $fursuit['count'] + $additionalCopies;
380+
381+
// Update the prepaid badges count
382+
$eventUser->update([
383+
'prepaid_badges' => $totalPrepaidBadges,
384+
]);
385+
386+
// Mark as not created in reg system
387+
Http::attsrv()
388+
->withToken($accessToken)
389+
->post('/attendees/' . $regId . '/additional-info/fursuitbadge', [
390+
'created' => false,
391+
]);
392+
} else {
393+
// No fursuit packages found
394+
$eventUser->update([
395+
'prepaid_badges' => 0,
396+
]);
397+
}
398+
399+
return response()->json([
400+
'success' => true,
401+
'prepaid_badges' => $totalPrepaidBadges,
402+
'prepaid_badges_left' => $user->getPrepaidBadgesLeft($activeEvent->id),
403+
]);
404+
405+
} catch (\Exception $e) {
406+
\Log::error('Failed to refresh prepaid badges', [
407+
'user_id' => $user->id,
408+
'event_id' => $activeEvent->id,
409+
'error' => $e->getMessage(),
410+
]);
411+
412+
return response()->json(['error' => 'Failed to refresh prepaid badges'], 500);
413+
}
414+
}
308415
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
3+
namespace App\Http\Middleware;
4+
5+
use App\Models\Event;
6+
use App\Models\EventUser;
7+
use App\Services\TokenRefreshService;
8+
use Closure;
9+
use Illuminate\Http\Request;
10+
use Illuminate\Support\Facades\Auth;
11+
use Illuminate\Support\Facades\Http;
12+
use Illuminate\Support\Facades\Log;
13+
14+
class EnsureEventUserMiddleware
15+
{
16+
public function handle(Request $request, Closure $next)
17+
{
18+
$user = $request->user();
19+
20+
if (!$user) {
21+
return $next($request);
22+
}
23+
24+
$activeEvent = Event::getActiveEvent();
25+
26+
if (!$activeEvent) {
27+
return $next($request);
28+
}
29+
30+
// Check if user already has an EventUser entry for current event
31+
$eventUser = EventUser::where([
32+
'user_id' => $user->id,
33+
'event_id' => $activeEvent->id,
34+
])->first();
35+
36+
if ($eventUser && $eventUser->valid_registration) {
37+
return $next($request);
38+
}
39+
40+
try {
41+
// Get fresh token or use existing one
42+
$tokenService = new TokenRefreshService($user);
43+
$accessToken = $tokenService->getValidAccessToken();
44+
45+
if (!$accessToken) {
46+
Log::warning('User missing access token, logging out', ['user_id' => $user->id]);
47+
Auth::logout();
48+
return redirect()->route('welcome')->with('message', 'Your session has expired. Please log in again to access your registration.');
49+
}
50+
51+
// Get attendee info
52+
$attendeeListResponse = Http::attsrv()
53+
->withToken($accessToken)
54+
->get('/attendees');
55+
56+
if (!$attendeeListResponse->successful()) {
57+
Log::warning('Failed to get attendee list', ['user_id' => $user->id, 'status' => $attendeeListResponse->status()]);
58+
Auth::logout();
59+
return redirect()->route('welcome')->with('message', 'Unable to verify your registration. Please log in again.');
60+
}
61+
62+
$attendeeData = $attendeeListResponse->json();
63+
$regId = $attendeeData['ids'][0] ?? null;
64+
65+
if (!$regId) {
66+
Log::info('User has no active registration', ['user_id' => $user->id]);
67+
Auth::logout();
68+
return redirect()->route('welcome')->with('message', 'Please register for the convention first before trying to obtain a fursuit badge.');
69+
}
70+
71+
// Get registration status
72+
$statusResponse = Http::attsrv()
73+
->withToken($accessToken)
74+
->get('/attendees/' . $regId . '/status');
75+
76+
if (!$statusResponse->successful()) {
77+
Log::warning('Failed to get registration status', ['user_id' => $user->id, 'reg_id' => $regId]);
78+
Auth::logout();
79+
return redirect()->route('welcome')->with('message', 'Unable to verify your registration status. Please log in again.');
80+
}
81+
82+
$statusData = $statusResponse->json();
83+
$validRegistration = in_array($statusData['status'], ['paid', 'checked in']);
84+
85+
if (!$validRegistration) {
86+
Log::info('User has invalid registration', ['user_id' => $user->id, 'status' => $statusData['status']]);
87+
Auth::logout();
88+
return redirect()->route('welcome')->with('message', 'Please complete your convention registration payment before accessing fursuit badges.');
89+
}
90+
91+
// Create or update EventUser relationship
92+
$eventUser = EventUser::updateOrCreate([
93+
'user_id' => $user->id,
94+
'event_id' => $activeEvent->id,
95+
], [
96+
'attendee_id' => $regId,
97+
'valid_registration' => $validRegistration,
98+
]);
99+
100+
// Check for fursuit packages to set prepaid badges
101+
try {
102+
$fursuit = Http::attsrv()
103+
->withToken($accessToken)
104+
->get('/attendees/' . $regId . '/packages/fursuit')
105+
->json();
106+
107+
if ($fursuit['present'] && $fursuit['count'] > 0) {
108+
$fursuitAdditional = Http::attsrv()
109+
->withToken($accessToken)
110+
->get('/attendees/' . $regId . '/packages/fursuitadd')
111+
->json();
112+
113+
$additionalCopies = $fursuitAdditional['present'] ? $fursuitAdditional['count'] : 0;
114+
$totalPrepaidBadges = $fursuit['count'] + $additionalCopies;
115+
116+
$eventUser->update(['prepaid_badges' => $totalPrepaidBadges]);
117+
118+
// Mark as not created in reg system
119+
Http::attsrv()
120+
->withToken($accessToken)
121+
->post('/attendees/' . $regId . '/additional-info/fursuitbadge', [
122+
'created' => false,
123+
]);
124+
}
125+
} catch (\Exception $e) {
126+
Log::warning('Failed to check fursuit packages', ['user_id' => $user->id, 'error' => $e->getMessage()]);
127+
// Continue without setting prepaid badges if fursuit package check fails
128+
}
129+
130+
return $next($request);
131+
132+
} catch (\Exception $e) {
133+
Log::error('EventUser middleware error', [
134+
'user_id' => $user->id,
135+
'event_id' => $activeEvent->id,
136+
'error' => $e->getMessage(),
137+
]);
138+
139+
Auth::logout();
140+
return redirect()->route('welcome')->with('message', 'An error occurred while verifying your registration. Please log in again.');
141+
}
142+
}
143+
}

app/Policies/BadgePolicy.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,7 @@ public function create(User $user): bool
3838
return false;
3939
}
4040

41-
// Allow badge creation only if event allows orders (for paid badges)
42-
if (! $event->allowsOrders()) {
43-
return false;
44-
}
45-
46-
// Check if user has prepaid badges left
41+
// Check if user has prepaid badges left FIRST - these bypass order window restrictions
4742
$eventUser = $user->eventUser($event->id);
4843
if ($eventUser) {
4944
$prepaidBadges = $eventUser->prepaid_badges;
@@ -60,6 +55,11 @@ public function create(User $user): bool
6055
}
6156
}
6257

58+
// For paid badges, check if event allows orders
59+
if (! $event->allowsOrders()) {
60+
return false;
61+
}
62+
6363
return true;
6464
}
6565

bootstrap/app.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
'pos-auth' => \App\Http\Middleware\PosAuthMiddleware::class,
4747
'catch-auth' => \App\Http\Middleware\CatchEmAllAuthMiddleware::class,
4848
'catch-introduction' => \App\Http\Middleware\CatchEmAllIntroductionMiddleware::class,
49+
'ensure-event-user' => \App\Http\Middleware\EnsureEventUserMiddleware::class,
4950
]);
5051
})
5152
->withExceptions(function (Exceptions $exceptions) {

resources/js/Pages/Badges/BadgesIndex.vue

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import DataTable from "primevue/datatable";
1111
import Column from "primevue/column";
1212
import Tag from "primevue/tag";
1313
import { formatEuroFromCents } from "@/helpers.js";
14+
import { ref } from "vue";
15+
import axios from "axios";
1416
1517
defineOptions({
1618
layout: Layout
@@ -25,10 +27,27 @@ const props = defineProps({
2527
event: Object
2628
});
2729
30+
const isRefreshing = ref(false);
31+
2832
function canEditBadge(badge) {
2933
return badge.canEdit;
3034
}
3135
36+
async function refreshPrepaidBadges() {
37+
isRefreshing.value = true;
38+
39+
try {
40+
await axios.post(route('badges.refresh-prepaid'));
41+
// Refresh the page to show updated data
42+
router.reload({ only: ['prepaidBadges', 'prepaidBadgesLeft'] });
43+
} catch (error) {
44+
console.error('Failed to refresh prepaid badges:', error);
45+
// Could add toast notification here
46+
} finally {
47+
isRefreshing.value = false;
48+
}
49+
}
50+
3251
function getBadgeStatusName(status) {
3352
switch (status) {
3453
case 'pending':
@@ -129,6 +148,7 @@ function getFursuitSeverity(status) {
129148
>
130149
You may order additional badges starting {{ new Date(event.orderStartsAt).toLocaleDateString('de-DE') }}. If you have ordered additional badges trough your ticket, you may need to logout and log back in to customize them.
131150
</Message>
151+
132152
</div>
133153

134154
<PaymentInfoWidget />
@@ -254,6 +274,20 @@ function getFursuitSeverity(status) {
254274
</template>
255275
</Card>
256276

277+
<!-- Refresh Prepaid Badges Section -->
278+
<div v-if="event && !prepaidBadgesLeft" class="text-center mt-6">
279+
<p class="text-gray-600 mb-2">Not seeing your preordered badges? Your login session might be using old registration data.</p>
280+
<button
281+
@click="refreshPrepaidBadges"
282+
:disabled="isRefreshing"
283+
class="text-blue-600 hover:text-blue-800 underline text-sm transition-colors duration-200"
284+
>
285+
<i v-if="isRefreshing" class="pi pi-spin pi-spinner mr-1"></i>
286+
<i v-else class="pi pi-refresh mr-1"></i>
287+
{{ isRefreshing ? 'Refreshing...' : 'Refresh Now' }}
288+
</button>
289+
</div>
290+
257291
<!-- Unpicked Badges from Previous Years -->
258292
<Card v-if="unpickedBadges && unpickedBadges.length > 0" class="mt-6">
259293
<template #title>

routes/web.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
Route::get('/frontchannel-logout', [\App\Http\Controllers\AuthController::class, 'logoutCallback'])->name('logout.callback');
2121
});
2222

23-
Route::middleware('auth')->group(function () {
23+
Route::middleware(['auth', 'ensure-event-user'])->group(function () {
2424
Route::resource('badges', \App\Http\Controllers\BadgeController::class);
25+
Route::post('/badges/refresh-prepaid', [\App\Http\Controllers\BadgeController::class, 'refreshPrepaidBadges'])
26+
->name('badges.refresh-prepaid')
27+
->middleware('throttle:3,1'); // 3 requests per minute per user
2528
Route::get('/statistics', [\App\Http\Controllers\StatisticsController::class, 'index'])->name('statistics');
2629
});
2730
});

0 commit comments

Comments
 (0)