227 lines
8.7 KiB
PHP
227 lines
8.7 KiB
PHP
<?php
|
|
|
|
namespace Modules\Communication\Http\Controllers;
|
|
|
|
use App\User;
|
|
use App\Utils\Util;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Illuminate\Support\Str;
|
|
use Modules\Communication\Entities\Meeting;
|
|
use Modules\Communication\Entities\MeetingParticipant;
|
|
use Modules\Communication\Events\MeetingInvite;
|
|
use Modules\Communication\Services\ChatRoomService;
|
|
use Modules\Communication\Services\LiveKitTokenService;
|
|
|
|
class MeetingController extends Controller
|
|
{
|
|
public function __construct(
|
|
private LiveKitTokenService $liveKitTokenService,
|
|
private ChatRoomService $chatRoomService,
|
|
private Util $util,
|
|
) {}
|
|
|
|
private function authorizeModule(): int
|
|
{
|
|
$business_id = request()->session()->get('user.business_id');
|
|
if (! auth()->user()->can('communication.access')) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
|
|
return (int) $business_id;
|
|
}
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$businessId = $this->authorizeModule();
|
|
$this->ensureMeetingTablesExist();
|
|
$userId = auth()->id();
|
|
|
|
$query = Meeting::where('business_id', $businessId)
|
|
->where(function ($q) use ($userId) {
|
|
$q->where('created_by', $userId)->orWhereHas('participants', fn ($p) => $p->where('user_id', $userId));
|
|
});
|
|
|
|
if ($request->filled('status') && in_array($request->status, ['scheduled', 'active', 'ended'], true)) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
|
|
$meetings = $query->with(['creator:id,first_name,last_name', 'participants.user:id,first_name,last_name'])
|
|
->orderByDesc('created_at')
|
|
->paginate(20);
|
|
|
|
$counts = [
|
|
'all' => Meeting::where('business_id', $businessId)
|
|
->where(function ($q) use ($userId) {
|
|
$q->where('created_by', $userId)->orWhereHas('participants', fn ($p) => $p->where('user_id', $userId));
|
|
})->count(),
|
|
'scheduled' => Meeting::where('business_id', $businessId)
|
|
->where('status', 'scheduled')
|
|
->where(function ($q) use ($userId) {
|
|
$q->where('created_by', $userId)->orWhereHas('participants', fn ($p) => $p->where('user_id', $userId));
|
|
})->count(),
|
|
'active' => Meeting::where('business_id', $businessId)
|
|
->where('status', 'active')
|
|
->where(function ($q) use ($userId) {
|
|
$q->where('created_by', $userId)->orWhereHas('participants', fn ($p) => $p->where('user_id', $userId));
|
|
})->count(),
|
|
'ended' => Meeting::where('business_id', $businessId)
|
|
->where('status', 'ended')
|
|
->where(function ($q) use ($userId) {
|
|
$q->where('created_by', $userId)->orWhereHas('participants', fn ($p) => $p->where('user_id', $userId));
|
|
})->count(),
|
|
];
|
|
|
|
if ($request->ajax()) {
|
|
return response()->json([
|
|
'meetings' => $meetings,
|
|
'counts' => $counts,
|
|
]);
|
|
}
|
|
|
|
return view('communication::spa');
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$businessId = $this->authorizeModule();
|
|
$this->ensureMeetingTablesExist();
|
|
|
|
$validated = $request->validate([
|
|
'title' => 'required|string|max:255',
|
|
'scheduled_at' => 'nullable|date',
|
|
'participant_ids' => 'sometimes|array',
|
|
'participant_ids.*' => 'integer|exists:users,id',
|
|
'start_now' => 'sometimes|boolean',
|
|
]);
|
|
|
|
$participantIds = array_unique(array_map('intval', $validated['participant_ids'] ?? []));
|
|
foreach ($participantIds as $participantId) {
|
|
$this->chatRoomService->assertColleague($businessId, $participantId);
|
|
}
|
|
|
|
$startNow = filter_var($validated['start_now'] ?? true, FILTER_VALIDATE_BOOLEAN);
|
|
$scheduledAt = ! empty($validated['scheduled_at'])
|
|
? $this->util->uf_date($validated['scheduled_at'], true)
|
|
: null;
|
|
$meeting = Meeting::create([
|
|
'business_id' => $businessId,
|
|
'title' => $validated['title'],
|
|
'livekit_room_name' => 'biz-'.$businessId.'-meet-'.Str::uuid(),
|
|
'status' => $startNow ? 'active' : 'scheduled',
|
|
'created_by' => auth()->id(),
|
|
'scheduled_at' => $scheduledAt,
|
|
'started_at' => $startNow ? now() : null,
|
|
]);
|
|
|
|
MeetingParticipant::create([
|
|
'meeting_id' => $meeting->id,
|
|
'user_id' => auth()->id(),
|
|
'role' => 'host',
|
|
'joined_at' => $startNow ? now() : null,
|
|
]);
|
|
|
|
foreach ($participantIds as $participantId) {
|
|
if ($participantId == auth()->id()) {
|
|
continue;
|
|
}
|
|
MeetingParticipant::create([
|
|
'meeting_id' => $meeting->id,
|
|
'user_id' => $participantId,
|
|
'role' => 'participant',
|
|
]);
|
|
event(new MeetingInvite($meeting, $participantId, $businessId, auth()->user()->user_full_name));
|
|
}
|
|
|
|
return response()->json($meeting->load(['creator', 'participants.user']));
|
|
}
|
|
|
|
public function show(int $id)
|
|
{
|
|
$businessId = $this->authorizeModule();
|
|
$this->ensureMeetingTablesExist();
|
|
|
|
return response()->json($this->findAccessibleMeeting($businessId, $id, auth()->id())->load(['creator', 'participants.user']));
|
|
}
|
|
|
|
public function room(int $id)
|
|
{
|
|
$businessId = $this->authorizeModule();
|
|
$this->ensureMeetingTablesExist();
|
|
$meeting = $this->findAccessibleMeeting($businessId, $id, auth()->id());
|
|
|
|
return view('communication::meetings.room', compact('meeting'));
|
|
}
|
|
|
|
public function token(Request $request, int $id)
|
|
{
|
|
$businessId = $this->authorizeModule();
|
|
$this->ensureMeetingTablesExist();
|
|
$meeting = $this->findAccessibleMeeting($businessId, $id, auth()->id());
|
|
|
|
if ($meeting->status === 'ended') {
|
|
return response()->json(['success' => false, 'msg' => __('communication::lang.meeting_ended')], 400);
|
|
}
|
|
if ($meeting->status === 'scheduled' && $meeting->created_by !== auth()->id()) {
|
|
return response()->json(['success' => false, 'msg' => __('communication::lang.meeting_not_started')], 400);
|
|
}
|
|
if ($meeting->status === 'scheduled' && $meeting->created_by === auth()->id()) {
|
|
$meeting->update(['status' => 'active', 'started_at' => now()]);
|
|
}
|
|
|
|
$participant = MeetingParticipant::where('meeting_id', $meeting->id)->where('user_id', auth()->id())->first();
|
|
if ($participant && ! $participant->joined_at) {
|
|
$participant->update(['joined_at' => now()]);
|
|
}
|
|
$isHost = ($participant?->role === 'host') || $meeting->created_by === auth()->id();
|
|
|
|
$translationEnabled = config('communication.translation_enabled', true)
|
|
&& filter_var($request->input('translation_enabled', true), FILTER_VALIDATE_BOOLEAN);
|
|
$language = $this->liveKitTokenService->normalizeLanguage($request->input('language'));
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'token' => $this->liveKitTokenService->createToken(
|
|
auth()->user(),
|
|
$meeting->livekit_room_name,
|
|
$isHost,
|
|
$language,
|
|
$translationEnabled,
|
|
),
|
|
'url' => $this->liveKitTokenService->getUrl(),
|
|
'room_name' => $meeting->livekit_room_name,
|
|
'language' => $language,
|
|
'translation_enabled' => $translationEnabled,
|
|
]);
|
|
}
|
|
|
|
public function end(int $id)
|
|
{
|
|
$businessId = $this->authorizeModule();
|
|
$meeting = Meeting::where('business_id', $businessId)->findOrFail($id);
|
|
if ($meeting->created_by !== auth()->id()) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
$meeting->update(['status' => 'ended', 'ended_at' => now()]);
|
|
|
|
return response()->json(['success' => true]);
|
|
}
|
|
|
|
private function findAccessibleMeeting(int $businessId, int $meetingId, int $userId): Meeting
|
|
{
|
|
return Meeting::where('business_id', $businessId)
|
|
->where(function ($q) use ($userId) {
|
|
$q->where('created_by', $userId)->orWhereHas('participants', fn ($p) => $p->where('user_id', $userId));
|
|
})
|
|
->findOrFail($meetingId);
|
|
}
|
|
|
|
private function ensureMeetingTablesExist(): void
|
|
{
|
|
if (! Schema::hasTable('meetings') || ! Schema::hasTable('meeting_participants')) {
|
|
abort(503, __('communication::lang.meeting_tables_missing'));
|
|
}
|
|
}
|
|
}
|