nodejs websocket ws call send in controller - node.js

I'm trying to send an event if there is a request from controller.
using websocket ws:
// openWSConnection
const wsServer = new WebSocketServer({
port: '8080'
})
const publish = (data) => {
return data
}
const testSend = (socket) => {
publish({}, (err,data) => {
socket.send(data)
})
}
wsServer.on('connection', function (socket) {
testSend(socket)
})
module.exports = {
publish
}
In controller
router.post("/create", async (req, res, next) => {
openWSConnection.publish(
{
toUser_id,
type,
status,
title,
subtitle,
description,
})
})
Every time the create will triggered, Websocket are not able to send the data to the client.
I'm trying to make it work like, If there are events that is being created it will send the data to websocket and pass it to the client.
Thanks for the help!

Related

Express JS Controllers with Socket IO Issue

I am trying to setup Socket IO to work well in different controllers in my Express application.
Here is how I have Socket IO initialized in my server.js
//Run When Connection Received
io.on('connection', (socket) => {
socket.emit('message', 'This is a test message from the server.')
})
Here is what one of my controllers looks like:
const getComments = asyncHandler(async (request, response) => {
const { liveID, token: accessToken } = request.body
var source = new EventSource(
`https://streaming-graph.facebook.com/${liveID}/live_comments?access_token=${accessToken}&comment_rate=one_per_two_seconds&fields=from{name,id},message`
)
source.onmessage = function (event) {
// Do something with event.message for example
console.log(event)
// THIS IS WHERE I NEED TO EMIT A SOCKET IO EVENT.
// THE DATA IN 'event' NEEDS TO BE SENT BACK TO THE CLIENT VIA THE SOCKET
}
source.onerror = function (error) {
console.log('Error!')
console.log(error)
}
source.onopen = function (event) {
console.log(event)
}
})
I have been trying to follow this documentation from Socket IO but for the life in me I can not seem to figure out how to make this work with my above example I have tried so many different things and I just seem to be missing something.
https://socket.io/docs/v4/server-application-structure/
Any help here would be greatly appreciated!
What you can do to have access to io in controller is to attach it to request object like so:
app.use((req, res, next) => {
req.io = io;
next();
});
When a socket connects add an user id to the socket
io.on('connection', (socket) => {
// assuming you send user id in a header for example
socket.userId = socket.client.request.headers.userId;
})
Then in a controller:
const getComments = asyncHandler(async (request, response) => {
var io = request.io;
// https://socket.io/docs/v4/server-api/#serverfetchsockets
// return all Socket instances
const sockets = await io.fetchSockets();
// assuming there's a loggedin user object attached to request
const socket = sockets.find(s => s.userId === request.user.id);
const { liveID, token: accessToken } = request.body
var source = new EventSource(
`https://streaming-graph.facebook.com/${liveID}/live_comments?access_token=${accessToken}&comment_rate=one_per_two_seconds&fields=from{name,id},message`
)
source.onmessage = function (event) {
// Do something with event.message for example
console.log(event)
socket.emit(....);
}
source.onerror = function (error) {
console.log('Error!')
console.log(error)
}
source.onopen = function (event) {
console.log(event)
}
})

How to transfer data from a controller to a socket.io in nodejs?

I'm building an web app to receive data from an api using nodejs as backend, and show this data on the client side using React. But it's my first time using socket.io.
Sockets.ts
function socket( io ){
io.on("connection", socket => {
var socketId = socket.id;
var clientIp = socket.request.connection.remoteAddress;
console.log('New connection ' + socketId + ' from ' + clientIp);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
}
export default socket;
router.controller.ts
export const getData: RequestHandler = async (req, res) => {
const options= {
method: 'GET',
};
const response = await fetch(citybikeurl, options)
.then((res: any) => res.json())
.catch((e: any) => {
console.error({ error: e });
});
console.log("RESPONSE: ", response);
res.json(response);
}
routes.ts
router.get('/', dataController.getData)
At the moment, I don't know if I'm passing any data from controller.ts to Sockets.ts, and after of this be able to emit the results.
You should be using a socket on the server-side as well.
Please refer to this guide for how to set up a socket server:
https://medium.com/#raj_36650/integrate-socket-io-with-node-js-express-2292ca13d891

socket io on client side only runs after I save the file containing the connection

Hey super new to socket io and web sockets in general. I have a chatting app scenario with socket io where client emits to the server and the server emits back to the client.
In my case the emits from the client side are picked up on the server side so far no problems.
However on the client side there are some socket.on statements there to pick up stuff from the server. A generic console.log after connection fires, but the other socket.on statements do not.
My thought was that maybe the server side emits aren't firing. Where it gets weird though is that when I change something in the client side file with the socket.on statements, the client side statements will pick up the emits from the server until the next time I refresh the page.
It is also work noting that these client on statements work with the info that was gathered before the save: if something is like
Client-side:
socket.on("message", (data) => {
console.log(foo)
});
and I change it to
socket.on("message", (data) => {
console.log(bar)
});
and then trigger the chain of emits,
the socket.on("message... will fire properly but it will log "foo". My guess is that saving the client side socket file is creating a new connection or something, but I'm curious as to why only the old connection is picking up things from the server and not the new one?
My code for reference:
(client)
const token = localStorage.getItem("messenger-token");
const socket = io.connect('http://localhost:3001', {
query: {token}
});
//const socket = io('http://localhost:3001');
socket.on("connect", () => {
console.log("connected to server");
socket.on("new-message", (data) => {
console.log(data.message)
store.dispatch(setNewMessage(data.message, data.sender));
});
socket.on("mark-as-read", (data) => {
store.dispatch(markedAsRead(data.convoToUpdate,'socket'));
});
});
(server)
//www
const server = http.createServer(app);
app.io.attach(server,{'pingInterval': 2000, 'pingTimeout': 50000});
//app
app.io = require('socket.io')();
require('./sockets')(app)
app.io.use(function(socket, next){
const token = socket.handshake.query.token
if (token) {
jwt.verify(token, process.env.SESSION_SECRET, (err, decoded) => {
if (err) {
return next(new Error('Authentication error'));
}
User.findOne({
where: { id: decoded.id },
}).then((user) => {
return next();
});
});
} else {
return next();
}
})
//sockets module
module.exports = (app) => {
app.io.on("connection", (socket) => {
console.log("connected")
socket.on("new-message", (data) => {
socket.broadcast.emit("new-message", {
message: data.message,
sender: data.sender,
});
socket.on("mark-as-read", (data) => {
socket.broadcast.emit("mark-as-read", {
convoToUpdate:data.convoId
});
});
});
I don't know if this is appropriate but it turns out I didn't have a problem.
the server logic:
socket.broadcast.emit("mark-as-read", {
convoToUpdate:data.convoId
});
});
uses broadcast.emit instead of regular emit. This sends the new info to everyone except the sender as seen in the docs. oops

How to make a realtime crud using vuex and socketio?

I want to perform a realtime crud using vuex, node, express and socketio but using the following syntax I can't get the result.
Server
index.js
const server = app.listen('3000',() => {
console.log('<--- Web Server Starter --->')
const io = require('./sockets')(server)
io.on('connection', socket=>{
console.log('client connected');
})
})
socket.js
let io
module.exports = {
init:httpServer =>{
io = require('socket.io')(httpServer)
return io
},
getIo:()=>{
if(!io){
throw new Error('socket io not initialized')
}
return io
}
}
user.js
const io = require('../socket')
router.post('/newuser',(req, res) => {
res.json({ serverResponse: 'userCreated' })
io.getIo().emit('newuser',{serverResponse:'created',user:user})
})
Client
Module user.js (VUEX actions)
getusers({ commit }){
const sessionToken = localStorage.getItem('sessionToken')
axios.get('/newuser', {
headers: {
Authorization: `Bearer ${localStorage.getItem('sessionToken')}`
},
})
.then(response => {
const socket = openSocket('http://localhost:3000')
socket.on('newuser', data => {
if (data.serverResponse === 'created') {
this.users = data.user
commit('GET_USERS', users)
})
})
.catch(error => {
alert(error)
})
})
When I create the new user, the user list is not updated automatically, but I have to refresh the page to see the new user, why does this happen?
This is because in your backend you are handling the post url-endpoint '/newuser' to get the data. You are not actually posting the data using socketio, therefore if you don't update the page, your axios.get function isn't going to have any new data to get until the API you are posting to has refreshed. It would be better to put your socket client code outside of the axios promise so that the data is primarily updated on the client by socketio and not by the API.
EDIT:
This is the code for it in your client
getusers({
commit
}) {
const sessionToken = localStorage.getItem('sessionToken')
axios.get('/newuser', {
headers: {
Authorization: `Bearer ${localStorage.getItem('sessionToken')}`
},
})
.then(response => {
console.log('success');
})
.catch(error => {
alert(error)
})
const socket = openSocket('http://localhost:3000')
socket.on('newuser', data => {
if (data.serverResponse === 'created') {
this.users = data.user
commit('GET_USERS', users)
})
})
You had a problem with your .catch part of the promise as it was inside the .then part and I also moved your socket code outside of the promise.
So in your server when you post to /newuser. you send emit from the socketio
on your client you dont need to use axios to check if some socket events has been fired, try to remove the axios.get() and keep
const socket = openSocket('http://localhost:3000')
socket.on('newuser', data => {
//do some thing
})

Event listeners don't seem to be triggering

I am working with the Asterisk ARI Node.js client and would like to listen for certain events and then perform an action. From my understanding after connecting to the server you can setup several different types of event listeners for events being published via WebSockets to perform tasks. In my code below I don't receive any output even though I am triggering these specific events and can connect via WSCat and watch the events streaming.
The app I am building should just listen for the events to occur and update a database. I will never need to access the Node application through an HTTP request which is why I returning forbidden on every request to the server. My end goal is to just have this application sitting on a server reacting to events.
'use strict';
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const client = require('ari-client');
const util = require('util');
const server = http.createServer((req, res) => {
res.statusCode = 403;
res.end('FORBIDDEN');
});
server.listen(port, hostname, () => {
client.connect('http://127.0.0.1:8088', 'username', 'password')
.then(function(ari) {
ari.on('DeviceStateChanged', function(event) {
console.log(event);
})
ari.on('ChannelCreated', function(event) {
console.log(event);
})
ari.on('BridgeCreated', function(event) {
console.log(event);
})
ari.on('StasisStart', function(event) {
console.log(event);
})
ari.on('PeerStatusChange', function(event) {
console.log('blah', event);
})
ari.on('Dial', function(event) {
console.log('Dial', event);
})
})
.catch(function(err) {
console.log(err);
})
});
Why create a server then? You might test the following.
'use strict';
const client = require('ari-client');
const util = require('util');
client.connect('http://127.0.0.1:8088', 'username', 'password')
.then(function(ari) {
ari.on('DeviceStateChanged', function(event) {
console.log(event);
})
ari.on('ChannelCreated', function(event) {
console.log(event);
})
ari.on('BridgeCreated', function(event) {
console.log(event);
})
ari.on('StasisStart', function(event) {
console.log(event);
})
ari.on('PeerStatusChange', function(event) {
console.log('blah', event);
})
ari.on('Dial', function(event) {
console.log('Dial', event);
})
})
.catch(function(err) {
console.log(err);
});

Resources