validate(['callee_id' => 'required|integer|exists:users,id']); $caller = Auth::user(); $callee = User::findOrFail($request->callee_id); if ($caller->id === $callee->id) { return response()->json(['error' => 'Kendinizi arayamazsınız.'], 422); } // End any previous active calls VoiceCall::where('caller_id', $caller->id) ->whereIn('status', ['ringing', 'active']) ->update(['status' => 'ended', 'ended_at' => now()]); $channelName = 'vc_' . Str::random(20); $call = VoiceCall::create([ 'caller_id' => $caller->id, 'callee_id' => $callee->id, 'channel_name' => $channelName, 'status' => 'ringing', ]); $callerToken = AgoraTokenService::generateToken($channelName, $caller->id); $calleeToken = AgoraTokenService::generateToken($channelName, $callee->id); return response()->json([ 'call_id' => $call->id, 'channel_name' => $channelName, 'token' => $callerToken, 'callee' => [ 'id' => $callee->id, 'name' => $callee->name, 'avatar' => $callee->avatar ? \App\Support\MediaUrl::fromStoragePath($callee->avatar) : null, ], 'agora_app_id' => env('AGORA_APP_ID', ''), ]); } public function answer(VoiceCall $call) { $user = Auth::user(); abort_unless($call->callee_id === $user->id, 403); abort_unless($call->status === 'ringing', 422, 'Call is no longer ringing.'); $call->update(['status' => 'active', 'answered_at' => now()]); $token = AgoraTokenService::generateToken($call->channel_name, $user->id); return response()->json([ 'channel_name' => $call->channel_name, 'token' => $token, 'agora_app_id' => env('AGORA_APP_ID', ''), 'caller' => [ 'id' => $call->caller->id, 'name' => $call->caller->name, 'avatar' => $call->caller->avatar ? \App\Support\MediaUrl::fromStoragePath($call->caller->avatar) : null, ], ]); } public function decline(VoiceCall $call) { $user = Auth::user(); abort_unless($call->callee_id === $user->id || $call->caller_id === $user->id, 403); abort_unless($call->status === 'ringing', 422); $call->update(['status' => 'declined', 'ended_at' => now()]); return response()->json(['ok' => true]); } public function end(VoiceCall $call) { $user = Auth::user(); abort_unless($call->callee_id === $user->id || $call->caller_id === $user->id, 403); $call->update(['status' => 'ended', 'ended_at' => now()]); return response()->json(['ok' => true]); } public function poll(Request $request) { $user = Auth::user(); // Check for incoming ringing call $incoming = VoiceCall::where('callee_id', $user->id) ->where('status', 'ringing') ->with('caller') ->latest() ->first(); if ($incoming) { return response()->json([ 'type' => 'incoming', 'call_id' => $incoming->id, 'caller' => [ 'id' => $incoming->caller->id, 'name' => $incoming->caller->name, 'avatar' => $incoming->caller->avatar ? \App\Support\MediaUrl::fromStoragePath($incoming->caller->avatar) : null, ], ]); } // Check if an active call we're in has been ended by the other side $call_id = $request->query('call_id'); if ($call_id) { $call = VoiceCall::find($call_id); if ($call && in_array($user->id, [$call->caller_id, $call->callee_id])) { return response()->json([ 'type' => 'status', 'status' => $call->status, ]); } } return response()->json(['type' => 'none']); } }