Search code examples
phplaraveleventsvue.jsbroadcast

How to implement laravel-echo client into Vue project


I'm developing an application with Vuetify (Vue.js) as front-end that communicates with api to laravel backend server.

I'm trying to make a system of notifications with laravel-echo-server working with socket.io. And using laravel-echo into client too.

The code that I uses into a component of client to test if connection works is:

// Needed by laravel-echo
window.io = require('socket.io-client')

let token = this.$store.getters.token

let echo = new Echo({
  broadcaster: 'socket.io',
  host: 'http://localhost:6001',
  auth: {
    headers: {
      authorization: 'Bearer ' + token,
      'X-CSRF-TOKEN': 'too_long_csrf_token_hardcoded'
    }
  }
})

echo.private('Prova').listen('Prova', () => {
  console.log('IT WORKS!')
})

This is the code of laravel-echo-server.json

{
    "authHost": "http://gac-backend.test",
    "authEndpoint": "/broadcasting/auth",
    "clients": [],
    "database": "redis",
    "databaseConfig": {
        "redis": {},
        "sqlite": {
            "databasePath": "/database/laravel-echo-server.sqlite"
        }
    },
    "devMode": true,
    "host": null,
    "port": "6001",
    "protocol": "http",
    "socketio": {},
    "sslCertPath": "",
    "sslKeyPath": "",
    "sslCertChainPath": "",
    "sslPassphrase": "",
    "apiOriginAllow": {
        "allowCors": false,
        "allowOrigin": "",
        "allowMethods": "",
        "allowHeaders": ""
    }
}

I tried to modify apiOriginsAllow without success. The event is dispatched, I can see it into laravel-echo-server logs:

Channel: Prova
Event: App\Events\Prova

But after that, when I access into a component of the client that contains the connection code I can see in laravel-echo-server logs long error trace and the next error:

The client cannot be authenticated, got HTTP status 419

As you can see, I specified csrf token and authorization token into the headers of laravel echo client. But it doesn't work.

This is the code of routes/channels.php:

Broadcast::channel('Prova', function ($user) {
    return true;
});

I only want to listen an event, it doesn't important if it is private or public because when it works, I want to put it into service worker. Then, I suppose that is better if it is public.

  • How can I use laravel echo client out of laravel project?
  • Will be a problem if I make private event and try to listen it into a service worker?

Solution

  • To start client listeners i used Vuex. Then, when my application starts I dispatch the action INIT_CHANNEL_LISTENERS to start listeners.

    index.js of channels MODULE vuex

    import actions from './actions'
    import Echo from 'laravel-echo'
    import getters from './getters'
    import mutations from './mutations'
    
    window.io = require('socket.io-client')
    
    export default {
      state: {
        channel_listening: false,
        echo: new Echo({
          broadcaster: 'socket.io',
          // host: 'http://localhost:6001'
          host: process.env.CHANNEL_URL
        }),
        notifiable_public_channels: [
          {
            channel: 'Notificacio',
            event: 'Notificacio'
          },
          {
            channel: 'EstatRepetidor',
            event: 'BroadcastEstatRepetidor'
          }
        ]
      },
      actions,
      getters,
      mutations
    }
    

    actions.js of channels MODULE vuex

    import * as actions from '../action-types'
    import { notifyMe } from '../../helpers'
    // import { notifyMe } from '../../helpers'
    
    export default {
      /*
      * S'entent com a notifiable un event que té "títol" i "message" (Per introduir-los a la notificació)
      * */
      /**
       * Inicialitza tots els listeners per als canals. Creat de forma que es pugui ampliar.
       * En cas de voler afegir més canals "Notifiables" s'ha de afegir un registre al state del index.js d'aquest modul.
       * @param context
       */
      [ actions.INIT_CHANNEL_LISTENERS ] (context) {
        console.log('Initializing channel listeners...')
        context.commit('SET_CHANNEL_LISTENING', true)
    
        context.getters.notifiable_public_channels.forEach(listener => {
          context.dispatch(actions.INIT_PUBLIC_NOTIFIABLE_CHANNEL_LISTENER, listener)
        })
        // }
      },
    
      /**
       * Inicialitza un event notificable a través d'un canal.
       * Per anar bé hauria de tenir un titol i un missatge.
       * @param context
       * @param listener
       */
      [ actions.INIT_PUBLIC_NOTIFIABLE_CHANNEL_LISTENER ] (context, listener) {
        context.getters.echo.channel(listener.channel).listen(listener.event, payload => {
          notifyMe(payload.message, payload.title)
        })
      }
    }
    

    notifyMe function in helpers That function dispatches notification on the browser

    export function notifyMe (message, titol = 'TITLE', icon = icon) {
      if (!('Notification' in window)) {
        console.error('This browser does not support desktop notification')
      } else if (Notification.permission === 'granted') {
        let notification = new Notification(titol, {
          icon: icon,
          body: message,
          vibrate: [100, 50, 100],
          data: {
            dateOfArrival: Date.now(),
            primaryKey: 1
          }
        })
      }
    

    Then the backend uses laravel-echo-server like the question. Using redis to queue events and supervisor to start laravel-echo-server at startup of the server.