Deploy Ladill Meet / deploy (push) Successful in 50s
Route guests through silent SSO to the Meet product page, fix Afia context query, add conference green-room UX and copy, and extend service API for Care/CRM/Invoice calendar integration. Co-authored-by: Cursor <cursoragent@cursor.com>
158 lines
5.3 KiB
PHP
158 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Meet;
|
|
|
|
use App\Models\CalendarConnection;
|
|
use App\Models\Room;
|
|
use App\Models\User;
|
|
use App\Services\Meet\Calendar\GoogleCalendarProvider;
|
|
use App\Services\Meet\Calendar\MailCalendarProvider;
|
|
use App\Services\Meet\Calendar\MicrosoftCalendarProvider;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class CalendarService
|
|
{
|
|
public function __construct(
|
|
protected GoogleCalendarProvider $google,
|
|
protected MicrosoftCalendarProvider $microsoft,
|
|
protected MailCalendarProvider $mail,
|
|
) {}
|
|
|
|
public function syncRoomCreated(Room $room, User $host): void
|
|
{
|
|
if (! $room->scheduled_at) {
|
|
return;
|
|
}
|
|
|
|
if ($host->email) {
|
|
$eventId = $this->mail->createEventForMailbox($host->email, $room);
|
|
if ($eventId) {
|
|
$room->update(['calendar_event_id' => $eventId]);
|
|
}
|
|
}
|
|
|
|
$connection = $this->connectionFor($host);
|
|
if ($connection && in_array($connection->provider, ['google', 'microsoft'], true)) {
|
|
$this->provider($connection)->createEvent($connection, $room, $host);
|
|
}
|
|
}
|
|
|
|
public function syncRoomUpdated(Room $room, User $host): void
|
|
{
|
|
if (! $room->scheduled_at) {
|
|
return;
|
|
}
|
|
|
|
if ($host->email) {
|
|
if ($room->calendar_event_id) {
|
|
$this->mail->updateEventForMailbox($host->email, $room);
|
|
} else {
|
|
$this->syncRoomCreated($room, $host);
|
|
}
|
|
}
|
|
|
|
$connection = $this->connectionFor($host);
|
|
if ($connection && $room->calendar_event_id && in_array($connection->provider, ['google', 'microsoft'], true)) {
|
|
$this->provider($connection)->updateEvent($connection, $room, $host);
|
|
}
|
|
}
|
|
|
|
public function syncRoomCancelled(Room $room, User $host): void
|
|
{
|
|
if ($host->email && $room->calendar_event_id) {
|
|
$this->mail->deleteEventForMailbox($host->email, $room);
|
|
}
|
|
|
|
$connection = $this->connectionFor($host);
|
|
if ($connection && $room->calendar_event_id && in_array($connection->provider, ['google', 'microsoft'], true)) {
|
|
$this->provider($connection)->deleteEvent($connection, $room);
|
|
}
|
|
|
|
$room->update(['calendar_event_id' => null]);
|
|
}
|
|
|
|
public function connectionFor(User $user, ?string $provider = null): ?CalendarConnection
|
|
{
|
|
$query = CalendarConnection::where('user_ref', $user->ownerRef());
|
|
|
|
if ($provider) {
|
|
$query->where('provider', $provider);
|
|
}
|
|
|
|
return $query->orderByDesc('updated_at')->first();
|
|
}
|
|
|
|
protected function provider(CalendarConnection $connection): GoogleCalendarProvider|MicrosoftCalendarProvider|MailCalendarProvider
|
|
{
|
|
return match ($connection->provider) {
|
|
'microsoft' => $this->microsoft,
|
|
'mail' => $this->mail,
|
|
default => $this->google,
|
|
};
|
|
}
|
|
|
|
public function connectMail(User $user, int $organizationId, ?string $mailboxEmail = null): CalendarConnection
|
|
{
|
|
$email = strtolower(trim($mailboxEmail ?: (string) $user->email));
|
|
|
|
return CalendarConnection::updateOrCreate(
|
|
['user_ref' => $user->ownerRef(), 'provider' => 'mail'],
|
|
[
|
|
'owner_ref' => $user->ownerRef(),
|
|
'organization_id' => $organizationId,
|
|
'calendar_id' => $email,
|
|
'access_token' => null,
|
|
'refresh_token' => null,
|
|
'expires_at' => null,
|
|
'metadata' => ['source' => 'ladill_mail'],
|
|
],
|
|
);
|
|
}
|
|
|
|
public function storeTokens(User $user, int $organizationId, string $provider, array $tokens): CalendarConnection
|
|
{
|
|
return CalendarConnection::updateOrCreate(
|
|
['user_ref' => $user->ownerRef(), 'provider' => $provider],
|
|
[
|
|
'owner_ref' => $user->ownerRef(),
|
|
'organization_id' => $organizationId,
|
|
'access_token' => $tokens['access_token'] ?? null,
|
|
'refresh_token' => $tokens['refresh_token'] ?? null,
|
|
'expires_at' => isset($tokens['expires_in']) ? now()->addSeconds((int) $tokens['expires_in']) : null,
|
|
'calendar_id' => $tokens['calendar_id'] ?? 'primary',
|
|
'metadata' => $tokens['metadata'] ?? null,
|
|
],
|
|
);
|
|
}
|
|
|
|
public function oauthRedirectUrl(string $provider): ?string
|
|
{
|
|
return match ($provider) {
|
|
'google' => $this->google->authorizeUrl(),
|
|
'microsoft' => $this->microsoft->authorizeUrl(),
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
public function handleOAuthCallback(string $provider, string $code, User $user, int $organizationId): ?CalendarConnection
|
|
{
|
|
try {
|
|
$tokens = match ($provider) {
|
|
'google' => $this->google->exchangeCode($code),
|
|
'microsoft' => $this->microsoft->exchangeCode($code),
|
|
default => null,
|
|
};
|
|
|
|
if (! $tokens) {
|
|
return null;
|
|
}
|
|
|
|
return $this->storeTokens($user, $organizationId, $provider, $tokens);
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Calendar OAuth failed', ['provider' => $provider, 'error' => $e->getMessage()]);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|