136 lines
4.5 KiB
PHP
136 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Event;
|
|
use App\Models\EventJoinToken;
|
|
use App\Models\GuestPolicySetting;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class EventJoinTokenService
|
|
{
|
|
public function createToken(Event $event, array $attributes = []): EventJoinToken
|
|
{
|
|
return DB::transaction(function () use ($event, $attributes) {
|
|
$tokenValue = $this->generateUniqueToken();
|
|
|
|
$payload = [
|
|
'event_id' => $event->id,
|
|
'token' => $tokenValue,
|
|
'label' => Arr::get($attributes, 'label'),
|
|
'usage_limit' => Arr::get($attributes, 'usage_limit'),
|
|
'metadata' => Arr::get($attributes, 'metadata', []),
|
|
];
|
|
|
|
if ($expiresAt = Arr::get($attributes, 'expires_at')) {
|
|
$payload['expires_at'] = $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);
|
|
}
|
|
}
|
|
|
|
if ($createdBy = Arr::get($attributes, 'created_by')) {
|
|
$payload['created_by'] = $createdBy;
|
|
}
|
|
|
|
return tap(EventJoinToken::create($payload), function (EventJoinToken $model) use ($tokenValue) {
|
|
$model->setAttribute('plain_token', $tokenValue);
|
|
});
|
|
});
|
|
}
|
|
|
|
public function revoke(EventJoinToken $joinToken, ?string $reason = null): EventJoinToken
|
|
{
|
|
unset($joinToken->plain_token);
|
|
$joinToken->revoked_at = now();
|
|
|
|
if ($reason) {
|
|
$metadata = $joinToken->metadata ?? [];
|
|
$metadata['revoked_reason'] = $reason;
|
|
$joinToken->metadata = $metadata;
|
|
}
|
|
|
|
$joinToken->save();
|
|
|
|
return $joinToken;
|
|
}
|
|
|
|
public function incrementUsage(EventJoinToken $joinToken): void
|
|
{
|
|
$joinToken->increment('usage_count');
|
|
|
|
$event = $joinToken->event()
|
|
->with(['eventPackage.package', 'eventPackages.package', 'tenant'])
|
|
->first();
|
|
|
|
if ($event && $event->tenant) {
|
|
$usageTracker = app(\App\Services\Packages\PackageUsageTracker::class);
|
|
$limitEvaluator = app(\App\Services\Packages\PackageLimitEvaluator::class);
|
|
|
|
$eventPackage = $limitEvaluator->resolveEventPackageForPhotoUpload($event->tenant, $event->id, $event);
|
|
|
|
if ($eventPackage && $eventPackage->package?->max_guests !== null) {
|
|
$previous = (int) $eventPackage->used_guests;
|
|
$eventPackage->increment('used_guests');
|
|
$eventPackage->refresh();
|
|
|
|
$usageTracker->recordGuestUsage($eventPackage, $previous, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function findToken(string $token, bool $includeInactive = false): ?EventJoinToken
|
|
{
|
|
$hash = $this->hashToken($token);
|
|
|
|
return EventJoinToken::query()
|
|
->where(function ($query) use ($hash, $token) {
|
|
$query->where('token_hash', $hash)
|
|
->orWhere(function ($inner) use ($token) {
|
|
$inner->whereNull('token_hash')
|
|
->where('token', $token);
|
|
});
|
|
})
|
|
->when(! $includeInactive, function ($query) {
|
|
$query->whereNull('revoked_at')
|
|
->where(function ($query) {
|
|
$query->whereNull('expires_at')
|
|
->orWhere('expires_at', '>', now());
|
|
})
|
|
->where(function ($query) {
|
|
$query->whereNull('usage_limit')
|
|
->orWhereColumn('usage_limit', '>', 'usage_count');
|
|
});
|
|
})
|
|
->first();
|
|
}
|
|
|
|
public function findActiveToken(string $token): ?EventJoinToken
|
|
{
|
|
return $this->findToken($token);
|
|
}
|
|
|
|
protected function generateUniqueToken(int $length = 48): string
|
|
{
|
|
do {
|
|
$token = Str::random($length);
|
|
$hash = $this->hashToken($token);
|
|
} while (EventJoinToken::where('token_hash', $hash)->exists());
|
|
|
|
return $token;
|
|
}
|
|
|
|
protected function hashToken(string $token): string
|
|
{
|
|
return hash('sha256', $token);
|
|
}
|
|
}
|