I'm trying to broadcasting to presence channel. But auth seems to not send a member data.
Versions
composer.json
"laravel/framework": "6.20.22",
"laravel/passport": "9.0",
"predis/predis": "1.1.7",
package.json
laravel-echo-server: 1.6.2
"socket.io-client": "2.4.0",
"laravel-echo": "1.10.0",
Server side
.env
BROADCAST_DRIVER=redis
QUEUE_CONNECTION=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_PREFIX=
laravel-echo-server.json
{
"authHost": "http://localhost:8080",
"authEndpoint": "/custom/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {},
"sqlite": {
"databasePath": "/database/laravel-echo-server.sqlite"
}
},
"devMode": true,
"host": null,
"port": "6001",
"protocol": "http",
"socketio": {},
"secureOptions": 67108864,
"sslCertPath": "",
"sslKeyPath": "",
"sslCertChainPath": "",
"sslPassphrase": "",
"subscribers": {
"http": true,
"redis": true
}
}
web.php
Route::post('/custom/broadcasting/auth', function (\Illuminate\Http\Request $request) {
Log::debug('custom auth');
$user = User::where('id', 1)->first();
return [$user->name];
});
Route::post('/messages', function (\Illuminate\Http\Request $request) {
broadcast(new \App\Events\MyEvent($request->clientId, $request->message));
return [
'message' => $request->message
];
});
MyEvent.php
class MyEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
public $clientID;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($clientID, $message)
{
$this->message = $message;
$this->clientID = $clientID;
}
public function broadcastOn()
{
return new PresenceChannel('chat.' . $this->clientID);
}
}
config/database.php
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
client side
bootstrap.js
import Echo from "laravel-echo"
window.io = require('socket.io-client');
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':8080',
authEndpoint: '/custom/broadcasting/auth',
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/custom/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
});
client.vue
export default {
props: ['clientId'],
data: function () {
return {
messages: [],
newMessage: '',
statusMessage: 'Joined a room',
isStarted: false,
members: [],
checkedMember: '',
}
},
created() {
},
computed: {
channel() {
var joining = window.Echo.join(`chat.${this.clientId}`)
console.log(joining);
return window.Echo.join(`chat.${this.clientId}`)
}
},
mounted() {
console.log(`Joining to chat.${this.clientId}`)
console.log('this channel', this.channel);
this.channel.here((users) => {
console.log(`Joined chat.${this.clientId}`)
this.members = users;
})
.joining((user) => {
console.log('Joining ', JSON.stringify(user));
this.members.put(user)
})
.leaving((user) => {
this.members = this.members.filter(member => user.id !== member.id)
console.log(`leaved ${user.name}`);
})
.error((error) => {
console.error(error);
})
.listen('MyEvent', (e) => {
console.log(e);
this.messages.push({
message: e.message.message,
user: e.user
});
});
},
methods: {
addMessage(clientId, message) {
axios.post('/messages', {
clientId,
message
}).then(response => {
this.messages.push({
message: response.data.message.message,
// user: response.data.user
});
});
},
sendMessage() {
this.addMessage(this.clientId, this.newMessage);
this.newMessage = '';
},
sendStartMessage() {
this.statusMessage = 'Started';
this.isStarted = true;
this.addMessage(this.clientId, 'start');
},
sendStopMessage() {
this.statusMessage = 'Ready';
this.isStarted = false;
this.addMessage(this.clientId, 'stop');
}
}
}
</script>
console debug
SocketIoPresenceChannel {events: {…}, listeners: {…}, name: "presence-chat.my-client-id", socket: Socket, options: {…}, …}
ㄴ eventFormatter: EventFormatter {namespace: "App.Events"}
ㄴ events: {presence:subscribed: ƒ, presence:joining: ƒ, presence:leaving: ƒ, App\Events\MyEvent: ƒ}
ㄴ listeners: {presence:subscribed: Array(1), presence:joining: Array(1), presence:leaving: Array(1), App\Events\MyEvent: Array(1)}
name: "presence-chat.my-client-id"
ㄴ options: {auth: {…}, authEndpoint: "/custom/broadcasting/auth", broadcaster: "socket.io", csrfToken: null, host: "localhost:8080", …}
ㄴ socket: Socket {io: Manager, nsp: "/", json: Socket, ids: 0, acks: {…}, …}
__proto__: SocketIoPrivateChannel
If I try with private and public channel, It works.
But If I try with Presence channel, always not working.
laravel echo server said
Event: [object Object]
(node:75981) UnhandledPromiseRejectionWarning: TypeError: Cannot convert undefined or null to object
at /Users/myname/.nvm/versions/node/v14.5.0/lib/node_modules/laravel-echo-server/dist/channels/presence-channel.js:78:21
at processTicksAndRejections (internal/process/task_queues.js:93:5)
(node:75981) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
Unable to join channel. Member data for presence channel missing
And wired thing is that I can listen an event.
Could you let me know the problem?
Related
Note: I have been on this issue for a week now and I have tried different solutions online including StackOverflow but none works
I have an app that I built in React Native Expo and I am adding a chat feature to the app, I built the app's API with Laravel.
I am now setting up websocket for the chat, I installed Laravel Echo and socket.io on the React Native app (client side) and on the backend (Laravel end) I am using laravel-echo-server and laravel broadcasting
I have set up the websocket and redis, because I am using redis as the broadcasting driver
everything works well on the backend, if an event was dispatched I can see on the laravel queue:work terminal and the laravel-echo-server also prints message Event: message.created
Channel: chats1
these are my backend code
Redis configuration in .env file
BROADCAST_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=port_number
REDIS_CLIENT=predis
the event class
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\Message;
class UserChatEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* The order instance.
*
* #var \App\Message
*/
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Message $message)
{
$this->message = $message;
}
public function broadcastAs()
{
return 'message.created';
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel("chats1");
}
}
laravel-echo-server.json
{
"authHost": "https://my_domain.com",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {
},
"sqlite": {
"databasePath": "/database/laravel-echo-server.sqlite"
}
},
"devMode": true,
"host": null,
"port": "6001",
"protocol": "http",
"socketio": {},
"secureOptions": secret_number,
"sslCertPath": "",
"sslKeyPath": "",
"sslCertChainPath": "",
"sslPassphrase": "",
"subscribers": {
"http": true,
"redis": true
},
"apiOriginAllow": {
"allowCors": true,
"allowOrigin": "*",
"allowMethods": "*",
"allowHeaders": "*"
}
}
database.php redis configuration
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 'port'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
React Native End
import Echo from 'laravel-echo';
import socketio from 'socket.io-client';
...
const echo = new Echo({
host: 'ws://my_domain.com:6001',
broadcaster: 'socket.io',
client: socketio,
});
echo
.channel('chats1')
.listen('.message.created', (ev: any) => {
Alert.alert('Socket Msg', 'Got data success')
console.log('socket msg:', JSON.stringify(ev))
}).error((er: any) => {
Alert.alert('Socket Err', 'An error occured')
console.log('socket error msg:', JSON.stringify(er))
});
Nothing is being logged in the console as response from laravel-echo-server, what I'm I doing wrong? or what didn't I do correctly, please I have been on this issue for a week now
I have finally found a solution to it, the issue was caused by socket.io new releases, I have to downgrade the socket.io to 2.4.0 from 4.x.x
"socket.io-client": "^2.4.0",
I found the solution here
Laravel Echo Server, Redis, Socket.IO: Can't seem to make them work
You have a typo. This part of the code should be:
.listen('message.created', (ev: any) => {
Alert.alert('Socket Msg', 'Got data success')
console.log('socket msg:', JSON.stringify(ev))
})
Remove the dot before message.created and try again.
I have some issues about to connect to broadcasting from React
I sure don't have any error on my code, this is my code
on my broadcast.php on config folder
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'encrypted' => true,
'host' => '192.168.0.114',
'port' => 6001,
'scheme' => 'http'
],
],
and on my api.php on route folder i am adding
Broadcast::routes(['middleware' => ['api', 'auth:sanctum']]);
on .env file
PUSHER_APP_ID=osamah
PUSHER_APP_KEY=osamah
PUSHER_APP_SECRET=osamah
PUSHER_APP_CLUSTER=mt1
on channel.php
Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
on my react listen
const echo = new Echo({
broadcaster: 'pusher',
key: 'osamah',
cluster: 'mt1',
wsHost: '192.168.0.114',
wsPort: 6001,
forceTLS: false,
disableStats: true,
authEndpoint: "http://192.168.0.114/api/broadcasting/auth",
auth: {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer 1542|3hVrlJ3A968IeZ23kejqTp8CJ9p4RCkcj66gRK4z'
},
}
});
echo.connector.pusher.connection.bind('connected', () => {
console.log('connected');
axios.get(`http://192.168.0.114/api/count`, {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer 1542|3hVrlJ3A968IeZ23kejqTp8CJ9p4RCkcj66gRK4z'
},
})
.then(res => {
console.log(res);
})
});
echo.private('App.Models.User.393').notification((e)=>console.log(e.user));
When I select a file it is uploaded to the public/upload-images folder. In the laravel Controller the validation fails because the params are not included in the request. When I dump and die. This is what I get. As you can see... the params are missing. Below that is the Dropzone setup.
echo '<pre>';
print_r(request()->all());
echo '</pre>';
die();
$data = request()->validate([
'image' => '',
'width' => '',
'height' => '',
'location' => '',
]);
$image = $data['image']->store('user-images', 'public');
$userImage = auth()->user()->images()->create([
'path' => $image,
'width' => $data['width'],
'height' => $data['height'],
'location' => $data['location'],
]);
return new UserImageResource($userImage);
(
[image] => Illuminate\Http\UploadedFile Object
(
[test:Symfony\Component\HttpFoundation\File\UploadedFile:private] =>
[originalName:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 20220406_125155.jpg
[mimeType:Symfony\Component\HttpFoundation\File\UploadedFile:private] => image/jpeg
[error:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 0
[hashName:protected] =>
[pathName:SplFileInfo:private] => /tmp/phpdAwZvd
[fileName:SplFileInfo:private] => phpdAwZvd
)
)
props: [
'imageWidth',
'imageHeight',
'location',
],
data: () => {
return {
dropzone: null,
}
},
mounted() {
this.dropzone = new Dropzone(this.$refs.userImage, this.settings);
},
computed: {
settings() {
return {
paramName: 'image',
url: '/api/user-images',
acceptedFiles: 'image/*',
params: {
'width': this.imageWidth,
'height': this.imageHeight,
'location': this.location,
},
headers: {
'X-CSRF-TOKEN': document.head.querySelector('meta[name=csrf-token]').content,
},
success: (e, res) => {
alert('uploaded!');
},
error: (e, error) => {
console.log(error);
}
};
}
}
I'm trying to send events to private channel, but I can't receive them.
Otherwise it works fine for public channels.
Here's my code :
Plugin : Echo.js
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_PUBLIC_KEY,
wsHost: process.env.VUE_APP_WEBSOCKETS_SERVER , //window.location.hostname,//
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
wsPort: 6001,
forceTLS: false,
/* key: process.env.MIX_ABLY_PUBLIC_KEY,
wsHost: 'realtime-pusher.ably.io',
wsPort: 443, */
disableStats: true,
encrypted: true,
auth: {
headers: {
'X-CSRF-TOKEN': Cookies.get('XSRF-TOKEN'),
},
},
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post(process.env.VUE_APP_WEBSOCKETS_SERVER+'/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
})
Laravel Broadcasting.php :
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => false,
'encrypted' => true,
'host' => '127.0.0.1',
'port' => 6001,
'scheme' => 'http',
],
Api Routes :
Broadcast::routes(['middleware' => ['auth:sanctum']]);
Client Side :
created() {
Pusher.logToConsole = true;
Echo.logToConsole = true;
window.Echo.private('shop')
.listen('MessageSent', (e) => {
console.log('Hi' + e)
})
},
I notice that pusher subscribed to the channel with no errors, but the result in console is like this :
Pusher : : ["Event sent",{"event":"pusher:subscribe","data":{"channel":"private-shop"}}]
Hi maybe this could help you. Please make sure that the response in the authorize function is used correctly. I realize after a while that the response token was send as response not response.data. Try this.
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_PUBLIC_KEY,
wsHost: process.env.VUE_APP_WEBSOCKETS_SERVER , //window.location.hostname,//
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
wsPort: 6001,
forceTLS: false,
/* key: process.env.MIX_ABLY_PUBLIC_KEY,
wsHost: 'realtime-pusher.ably.io',
wsPort: 443, */
disableStats: true,
encrypted: true,
auth: {
headers: {
'X-CSRF-TOKEN': Cookies.get('XSRF-TOKEN'),
},
},
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post(process.env.VUE_APP_WEBSOCKETS_SERVER+'/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response);
})
.catch(error => {
callback(true, error);
});
}
};
},
})
I encountered CORS policy error in my laravel 5.8 / jquery 3.4.1 app when I need send post request to create google calendar event with api like:
public function calendar_add_event(Request $request)
{
session_start();
$startDateTime = $request->start_date;
$endDateTime = $request->end_date;
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$this->client->setAccessToken($_SESSION['access_token']);
$service = new Google_Service_Calendar($this->client);
$calendarId = 'primary';
$event = new Google_Service_Calendar_Event([
'summary' => $request->title,
'description' => $request->description,
'start' => ['dateTime' => $startDateTime],
'end' => ['dateTime' => $endDateTime],
'reminders' => ['useDefault' => true],
]);
$results = $service->events->insert($calendarId, $event);
if (!$results) {
return response()->json(['status' => 'error', 'message' => 'Something went wrong']);
}
return response()->json(['status' => 'success', 'message' => 'Event Created']);
} else {
return redirect()->route('oauthCallback');
}
}
In js code I send post request :
backendCalendar.prototype.saveCalendarAddEvent = function (user_id) {
var href = this_backend_home_url + "/admin/calendar_add_event";
$.ajax( {
type: "POST",
dataType: "json",
url: href,
data: { "title": $('#new_event_title').val(), "description": $('#new_event_description').val(), "start_date": $('#new_event_start_date').val(), "end_date": $('#new_event_end_date').val(), "_token": this_csrf_token},
success: function( response )
{
popupAlert("New event was added successfully !", 'success')
},
error: function( error )
{
popupErrorMessage(error.responseJSON.message)
}
});
} // backendCalendar.prototype.saveCalendarAddEvent
I installed https://github.com/barryvdh/laravel-cors package and left file config/cors.php without changes :
<?php
return [
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedOriginsPatterns' => [],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'],
'exposedHeaders' => [],
'maxAge' => 0,
];
in routes/web.php I added HandleCors middleware :
Route::group(['middleware' => ['auth', 'isVerified', 'CheckUserStatus'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
...
Route::post('calendar_add_event', 'gCalendarController#calendar_add_event')->middleware(\Barryvdh\Cors\HandleCors::class);
and I hoped that would fix my problems, but I still have this error : https://imgur.com/a/uyTy30Y
How to fix it ?
I found a decision here : Access-Control-Allow-Origin error sending a jQuery Post to Google API's
by additing additive parameters crossDomain and modification of dataType :
$.ajax({
url: 'https://www.googleapis.com/moderator/v1/series?key='+key,
data: myData,
type: 'GET',
crossDomain: true,
dataType: 'jsonp',
success: function() { alert("Success"); },
error: function() { alert('Failed!'); },
beforeSend: setHeader
});