ultimatepos/Modules/Communication/Http/Controllers/MeetingController.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'));
}
}
}