tlaverdure / laravel-echo-server

Socket.io server for Laravel Echo
MIT License
2.65k stars 511 forks source link

Missing member data for presence channel #581

Open erick-innosonian opened 3 years ago

erick-innosonian commented 3 years ago

Describe the bug I can't get member data when using presence channel.

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?

TemKaa1337 commented 3 years ago

Try to change this:

Route::post('/custom/broadcasting/auth', function (\Illuminate\Http\Request $request) {
    Log::debug('custom auth');
    $user = User::where('id', 1)->first();
    return [$user->name];
});

To this:

Route::post('/custom/broadcasting/auth', function (\Illuminate\Http\Request $request) {
    Log::debug('custom auth');
    $user = User::where('id', 1)->first();
    return ['channel_data' => ['id' => $user->id, 'user_id' => $user->id]];
});