Files
ladill-meet/app/Services/Meet/SessionService.php
T
isaaccladandCursor a9d3a8bd64
Deploy Ladill Meet / deploy (push) Successful in 47s
Sync email team invites, meeting room fixes, Afia, and org settings.
Publish monorepo meet changes: identity API invites, join/room session fixes,
Afia assistant panel, settings nav, and SSO team provisioning.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 06:45:35 +00:00

229 lines
7.1 KiB
PHP

<?php
namespace App\Services\Meet;
use App\Models\Participant;
use App\Models\Room;
use App\Models\Session;
use App\Models\User;
use App\Services\Meet\Media\MediaProviderInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class SessionService
{
public function __construct(
protected MediaProviderInterface $media,
protected RoomService $rooms,
) {}
public function start(Room $room, User $host): Session
{
$existing = $room->activeSession();
if ($existing) {
return $existing;
}
app(LicenseService::class)->assertCanStartSession($room->organization, $room->branch_id);
$session = Session::create([
'owner_ref' => $room->owner_ref,
'room_id' => $room->id,
'media_room_name' => $this->rooms->mediaRoomName($room),
'status' => 'live',
'started_at' => now(),
]);
$room->update(['status' => 'live']);
$this->media->createRoom($session->media_room_name);
$this->joinParticipant($session, $host, 'host');
if ($room->setting('auto_record', false)) {
app(RecordingService::class)->start($session, $host);
}
app(MeetNotificationService::class)->meetingStarted($session, $host);
app(\App\Services\Integrations\WebhookDispatcher::class)->meetingStarted($session);
AuditLogger::record($room->owner_ref, 'session.started', $room->organization_id, $host->ownerRef(), Session::class, $session->id);
return $session;
}
public function end(Session $session, string $actorRef): Session
{
$session->update([
'status' => 'ended',
'ended_at' => now(),
]);
$session->room->update(['status' => 'ended']);
$session->participants()
->where('status', 'joined')
->update(['status' => 'left', 'left_at' => now()]);
foreach ($session->recordings()->where('status', 'recording')->get() as $recording) {
app(RecordingService::class)->stop($recording, $actorRef);
}
if ($session->room->isRecurring()) {
app(RecurringMeetingService::class)->spawnNextOccurrences($session->room, 1);
}
AuditLogger::record($session->owner_ref, 'session.ended', $session->room->organization_id, $actorRef, Session::class, $session->id);
app(\App\Services\Integrations\WebhookDispatcher::class)->meetingEnded($session);
app(UsageMeteringService::class)->recordSessionUsage($session);
return $session;
}
public function joinParticipant(
Session $session,
?User $user,
string $role,
?string $displayName = null,
?string $email = null,
): Participant {
$userRef = $user?->ownerRef();
$existing = $session->participants()
->when($userRef, fn ($q) => $q->where('user_ref', $userRef))
->when(! $userRef && $email, fn ($q) => $q->where('email', $email))
->whereIn('status', ['invited', 'waiting', 'joined'])
->first();
if ($existing) {
$existing->update([
'status' => 'joined',
'joined_at' => now(),
'left_at' => null,
'display_name' => $displayName ?? $existing->display_name,
'ip_address' => request()?->ip(),
]);
return $existing;
}
$participant = Participant::create([
'owner_ref' => $session->owner_ref,
'session_id' => $session->id,
'user_ref' => $userRef,
'display_name' => $displayName ?? $user?->name ?? 'Guest',
'email' => $email,
'role' => $role,
'status' => 'joined',
'joined_at' => now(),
'ip_address' => request()?->ip(),
]);
$this->updatePeakParticipants($session);
AuditLogger::record(
$session->owner_ref,
'participant.joined',
$session->room->organization_id,
$userRef,
Participant::class,
$participant->id,
);
return $participant;
}
public function rememberParticipantSession(Request $request, Participant $participant, ?Session $session = null): void
{
$session ??= $participant->session;
$request->session()->put("meet.participant.{$session->uuid}", $participant->uuid);
}
public function joinedParticipantForUser(Session $session, User $user): ?Participant
{
return $session->participants()
->where('user_ref', $user->ownerRef())
->whereIn('status', ['invited', 'waiting', 'joined'])
->first();
}
public function leaveParticipant(Participant $participant): Participant
{
$participant->update([
'status' => 'left',
'left_at' => now(),
'hand_raised' => false,
]);
AuditLogger::record(
$participant->owner_ref,
'participant.left',
$participant->session->room->organization_id,
$participant->user_ref,
Participant::class,
$participant->id,
);
return $participant;
}
public function generateMediaToken(Participant $participant): string
{
$session = $participant->session;
$room = $session->room;
$isHost = $participant->isHost();
$breakoutService = app(BreakoutService::class);
$webinarService = app(WebinarService::class);
$mediaRoom = $breakoutService->mediaRoomForParticipant($participant);
$canPublish = $webinarService->canPublish($room, $participant) && $participant->canPublishMedia();
return $this->media->generateToken(
$mediaRoom,
$participant->uuid,
$participant->display_name,
[
'can_publish' => $canPublish,
'can_subscribe' => true,
'can_publish_data' => true,
'room_admin' => $isHost,
],
);
}
public function getOrStartSession(Room $room, User $host): Session
{
if ($room->activeSession()) {
return $room->activeSession();
}
return $this->start($room, $host);
}
public function lock(Session $session, string $actorRef): Session
{
$session->update(['is_locked' => true, 'locked_at' => now()]);
AuditLogger::record($session->owner_ref, 'meeting.locked', $session->room->organization_id, $actorRef, Session::class, $session->id);
return $session;
}
public function unlock(Session $session, string $actorRef): Session
{
$session->update(['is_locked' => false, 'locked_at' => null]);
AuditLogger::record($session->owner_ref, 'meeting.unlocked', $session->room->organization_id, $actorRef, Session::class, $session->id);
return $session;
}
protected function updatePeakParticipants(Session $session): void
{
$count = $session->participants()->where('status', 'joined')->count();
if ($count > $session->peak_participants) {
$session->update(['peak_participants' => $count]);
}
}
}