Adjust join token expiry for event dates
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-04 14:35:52 +01:00
parent 5c78ac00dd
commit 7025418d9e
5 changed files with 179 additions and 11 deletions

View File

@@ -882,9 +882,16 @@ class EventController extends Controller
);
}
$minimumExpiry = $this->joinTokenService->minimumExpiryForEvent($event);
$expiresAtRules = ['nullable', 'date', 'after:now'];
if ($minimumExpiry) {
$expiresAtRules[] = 'after_or_equal:'.$minimumExpiry->toDateTimeString();
}
$validated = $request->validate([
'label' => ['nullable', 'string', 'max:255'],
'expires_at' => ['nullable', 'date', 'after:now'],
'expires_at' => $expiresAtRules,
'usage_limit' => ['nullable', 'integer', 'min:1'],
]);

View File

@@ -33,7 +33,7 @@ class EventJoinTokenController extends Controller
{
$this->authorizeEvent($request, $event, 'join-tokens:manage');
$validated = $this->validatePayload($request);
$validated = $this->validatePayload($request, $event);
$token = $this->joinTokenService->createToken($event, array_merge($validated, [
'created_by' => Auth::id(),
@@ -52,7 +52,7 @@ class EventJoinTokenController extends Controller
abort(404);
}
$validated = $this->validatePayload($request, true);
$validated = $this->validatePayload($request, $event, true);
$payload = [];
@@ -115,11 +115,18 @@ class EventJoinTokenController extends Controller
}
}
private function validatePayload(Request $request, bool $partial = false): array
private function validatePayload(Request $request, Event $event, bool $partial = false): array
{
$minimumExpiry = $this->joinTokenService->minimumExpiryForEvent($event);
$expiresAtRules = [$partial ? 'nullable' : 'sometimes', 'nullable', 'date', 'after:now'];
if ($minimumExpiry) {
$expiresAtRules[] = 'after_or_equal:'.$minimumExpiry->toDateTimeString();
}
$rules = [
'label' => [$partial ? 'nullable' : 'sometimes', 'string', 'max:255'],
'expires_at' => [$partial ? 'nullable' : 'sometimes', 'nullable', 'date', 'after:now'],
'expires_at' => $expiresAtRules,
'usage_limit' => [$partial ? 'nullable' : 'sometimes', 'nullable', 'integer', 'min:1'],
'metadata' => [$partial ? 'nullable' : 'sometimes', 'nullable', 'array'],
'metadata.layout_customization' => ['nullable', 'array'],

View File

@@ -40,6 +40,14 @@ class Event extends Model
],
]);
});
static::updated(function (self $event): void {
if (! $event->wasChanged('date')) {
return;
}
app(EventJoinTokenService::class)->extendExpiryForEvent($event);
});
}
public function storageAssignments(): HasMany

View File

@@ -27,14 +27,21 @@ class EventJoinTokenService
];
if ($expiresAt = Arr::get($attributes, 'expires_at')) {
$payload['expires_at'] = $expiresAt instanceof Carbon
$resolvedExpiry = $expiresAt instanceof Carbon
? $expiresAt
: Carbon::parse($expiresAt);
} else {
$ttlHours = (int) (GuestPolicySetting::current()->join_token_ttl_hours ?? 0);
if ($ttlHours > 0) {
$payload['expires_at'] = now()->addHours($ttlHours);
$minimumExpiry = $this->minimumExpiryForEvent($event);
if ($minimumExpiry && $resolvedExpiry->lessThan($minimumExpiry)) {
$resolvedExpiry = $minimumExpiry;
}
$payload['expires_at'] = $resolvedExpiry;
} else {
$defaultExpiry = $this->defaultExpiryForEvent($event);
if ($defaultExpiry) {
$payload['expires_at'] = $defaultExpiry;
}
}
@@ -48,6 +55,21 @@ class EventJoinTokenService
});
}
public function extendExpiryForEvent(Event $event): int
{
$minimumExpiry = $this->minimumExpiryForEvent($event);
if (! $minimumExpiry) {
return 0;
}
return $event->joinTokens()
->whereNull('revoked_at')
->whereNotNull('expires_at')
->where('expires_at', '<', $minimumExpiry)
->update(['expires_at' => $minimumExpiry]);
}
public function revoke(EventJoinToken $joinToken, ?string $reason = null): EventJoinToken
{
unset($joinToken->plain_token);
@@ -162,6 +184,61 @@ class EventJoinTokenService
return hash('sha256', $token);
}
public function minimumExpiryForEvent(Event $event): ?Carbon
{
$eventDate = $this->resolveEventDate($event);
if (! $eventDate) {
return null;
}
$ttlHours = $this->defaultTtlHours();
if ($ttlHours > 0) {
return $eventDate->copy()->addHours($ttlHours);
}
return $eventDate;
}
private function defaultExpiryForEvent(Event $event): ?Carbon
{
$ttlHours = $this->defaultTtlHours();
if ($ttlHours <= 0) {
return null;
}
$defaultExpiry = now()->addHours($ttlHours);
$minimumExpiry = $this->minimumExpiryForEvent($event);
if ($minimumExpiry && $minimumExpiry->greaterThan($defaultExpiry)) {
$defaultExpiry = $minimumExpiry;
}
return $defaultExpiry;
}
private function resolveEventDate(Event $event): ?Carbon
{
if (! ($event->date instanceof Carbon)) {
return null;
}
$eventDate = $event->date->copy();
if ($eventDate->format('H:i:s') === '00:00:00') {
$eventDate = $eventDate->endOfDay();
}
return $eventDate;
}
private function defaultTtlHours(): int
{
return (int) (GuestPolicySetting::current()->join_token_ttl_hours ?? 0);
}
private function normalizeDeviceId(?string $deviceId): ?string
{
if (! is_string($deviceId) || trim($deviceId) === '') {