I am receiving this error when attempting to create a livestream channel on my website: Agora-SDK [ERROR]: [client-f42b7] join number: 1, Joining channel failed, rollback AgoraRTCException: AgoraRTCError CAN_NOT_GET_GATEWAY_SERVER: flag: 4096, message: AgoraRTCError CAN_NOT_GET_GATEWAY_SERVER: dynamic key expired. However as you can see in the code below I am generating a token and I even have a means of regenerating expired tokens.
`<?php
namespace App\Http\Controllers;
use App\Models\Channel;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
use App\Services\AgoraTokenService;
use App\Http\Controllers\TeammateController;
use App\Http\Controllers\ScoutController;
class ChannelController extends Controller
{
protected $agoraTokenService;
public function __construct(AgoraTokenService $agoraTokenService)
{
$this->agoraTokenService = $agoraTokenService;
}
public function generateAgoraToken(Request $request)
{
$channelName = $request->input('channelName');
if (!$channelName) {
return response()->json(['error' => 'Channel name is required.'], 400);
}
try {
$token = $this->agoraTokenService->generateToken($channelName);
return response()->json(['token' => $token]);
} catch (\Exception $e) {
return response()->json(['error' => 'Failed to generate token: ' . $e->getMessage()], 500);
}
}
public function index()
{
$userId = auth()->id();
$teammateIds = TeammateController::getTeammateIds($userId);
$scoutIds = ScoutController::getScoutIds($userId);
$allIds = array_merge([$userId], $teammateIds, $scoutIds);
$channels = Channel::whereIn('host_id', $allIds)->get();
return view('channels.index', compact('channels'));
}
public function create()
{
return view('channels.create');
}
public function store(Request $request)
{
$data = $request->validate([
'channelName' => 'nullable|string|max:255|unique:channels,channelName',
]);
$channel = new Channel;
$channel->host_id = auth()->id();
$defaultChannelName = "LockerRoom Live with " . auth()->user()->username . "! " . Carbon::now()->format('m/d/Y g:i:s A');
$originalChannelName = $data['channelName'] ?? $defaultChannelName;
$sanitizedChannelName = preg_replace('/[^a-zA-Z0-9_]/', '_', $originalChannelName);
$channel->channelName = $originalChannelName;
$channel->sanitizedChannelName = $sanitizedChannelName;
try {
$token = $this->agoraTokenService->generateToken($channel->sanitizedChannelName);
Log::info('Storing token for channel: ' . $channel->sanitizedChannelName);
$channel->token = $token;
$channel->token_expires_at = now()->addHours(24);
$channel->save();
return redirect()->route('channels.show', $channel->id)->with([
'message' => 'Channel created successfully!',
'token' => $token,
]);
} catch (\Exception $e) {
Log::error('Failed to create channel: ' . $e->getMessage());
return back()->with('error', 'Failed to create channel: ' . $e->getMessage());
}
}
public function show(Channel $channel)
{
if ($channel->token_expires_at <= now()) {
Log::warning('Token for channel ' . $channel->channelName . ' has expired. Regenerating.');
$token = $this->agoraTokenService->generateToken($channel->sanitizedChannelName);
$channel->token = $token;
$channel->token_expires_at = now()->addHours(24);
$channel->save();
}
else {
$token = $channel->token; // Get the token from the model if it's not expired
}
$isHost = auth()->id() === $channel->host_id;
return view('channels.show', [
'channel' => $channel,
'token' => $token,
'isHost' => $isHost,
'agora_app_id' => config('agora.app_id')
]);
}
public function edit(Channel $channel)
{
return view('channels.edit', ['channel' => $channel]);
}
public function update(Request $request, Channel $channel)
{
$data = $request->validate([
'channelName' => 'required|string|max:255|unique:channels,channelName,' . $channel->id,
]);
$channel->channelName = $data['channelName'];
// Update the sanitizedChannelName as well
$channel->sanitizedChannelName = preg_replace('/[^a-zA-Z0-9_]/', '_', $channel->channelName);
$channel->save();
return redirect()->route('channels.show', $channel->id)->with('message', 'Channel updated successfully!');
}
public function destroy(Channel $channel)
{
if ($channel->host_id !== auth()->id()) {
return redirect()->back()->with('error', 'Unauthorized action!');
}
$channel->delete();
return redirect()->route('channels.index')->with('message', 'Channel deleted successfully!');
}
}`
<?php
namespace App\Services;
use App\Services\Agora\AccessToken;
use App\Services\Agora\RtcTokenBuilder;
use Illuminate\Support\Facades\Log;
class AgoraTokenService
{
private $appId;
private $appCertificate;
public function __construct()
{
$this->appId = config('agora.app_id');
$this->appCertificate = config('agora.app_certificate');
}
public function generateToken($channelName)
{
try {
$token = RtcTokenBuilder::buildTokenWithUid(
$this->appId,
$this->appCertificate,
$channelName,
0,
RtcTokenBuilder::RolePublisher,
86400
);
Log::info('Token generated for channel: ' . $channelName . ' Token: ' . $token);
return $token;
} catch (\Exception $e) {
Log::error('Token generation failed: ' . $e->getMessage());
throw new \Exception('Agora token generation failed: ' . $e->getMessage());
}
}
}
<div class="container">
<h1>{{ $channel->channelName }}</h1>
<div>
<div id="agora-video-player"
data-agora-app-id="{{ $agora_app_id }}"
data-token="{{ $token }}"
data-is-host="{{ $isHost }}"
data-channel-name="{{ $channel->sanitizedChannelName }}"
style="width: 100%; height: 400px; background-color: #eee;">
Livestream Placeholder
</div>
</div>
</div>
<script src="{{ asset('build/assets/js/AgoraRTC_N-production.js') }}"></script>
<script src="{{ asset('build/assets/js/agoraSetup.js') }}"></script>