What is the best Socket.IO design for my application? - sockets

Realising this is gonna be a very general question but I am gonna try to be as specific as possible:
What is the best way to design/structure an Socket.IO app?
I have a NodeJS backend with React frontend, with authentication (user must log in). I have several REST endpoints, for example /foo, /bar, /baz.
I know you can use rooms and namespaces, and I know you can add authentication to the connect as middleware, but I have no idea what the best solution is to glue this all together. I will be using this socket for multiple purposes. For each purpose I am curious what the best way to go is (flow).
General CRUD messages: When someone posts a "foo" on the server side, it needs to also send this to that particular user. WHen someone deletes a "foo", it also needs to send something to this user. So this CRUD messaging should only be for one specific user (based on logged in user ID). How would structure those messages? Namespace for "foo"? Multiple event listeners: on "foo create", on "foo delete", on "foo update?" How to make sure you only send to this user?
I have multiple pages on the client side, for the respective CRUD endpoint. So when I am on the "foo" page, I need to get updates on the "foo" backend object. How can I accomplish this?
General server side messages: I will be running long-running scripts on the server side, started by a user (or by a time trigger). If I go to that page in react and if there are long running scripts active that belong to me, I need to see those logging. (but again, they are personal so those messages are only for me).
Thanks in advance if you need more clarification just ask me and I will add this to my question.
EDIT:
I think the CRUD part can better be created as having only an "listener for updates" (like the firebase onSnapshot). So on page foo, I will listen to updates in the foo database, but the updates or creations are dont through normal REST API. Is that indeed the better way?

You can authenticate socket.io connection in 'connection' event or using middleware - doc.
Also you can use some package from npm, for example this
After authentication store user data in socket object or as separate object in 'connection' event scope.
io.on('connection', (socket) => {
const handshake = socket.handshake;
const user = // fetch user obj according data in handshake, for example, from jwt token in header
});
So after you can use user object in other events for this connection.
Private messages according to your task I implemented in my project using rooms. Here abstract example:
// this is just a helper to get room name according to userId
getUserRoomName(userId) {
return `user_${userId}`;
}
// function to send data to user
sendToUser(userId, event, data) {
io.to(getUserRoomName(userId)).emit(event, data);
}
// in 'connection' event add join to user room
io.on('connection', (socket) => {
const handshake = socket.handshake;
const user = // fetch user obj according data in handshake, for example, from jwt token in header
// join to private room
socket.join(getUserRoomName(user.userId), () => {
// some logic
});
});
So, when the user connected to socket.io we join his connection to private user room. And every connection of same user will be joined to same room, so we can isolate data sending messages to this room.
Using sendToUser method you can send any type of data to all user connections from any part of your application:
sendToUser(userId, 'foo_create', data);
OR
sendToUser(userId, 'foo', {
action: 'create',
// some other data
});

Related

Is there a smart possibility to get API results without sending requests every second? [VueJS | Vuetify]

So I made a website to show which services on my server are running and which are offline.
The site is an Vuetify App running in a docker container. My services are monitored via UptimeRobot.
Currently I use:
created: function () {
this.interval = setInterval(() => this.getStatuses(), 1000);
},
To trigger my API request function every second to update the status of my services.
But is there some smarter possibility to only update on change and not request every second to see if something happened?
Like I send one request to get the status and then receive a message when something changed? I hope you can understand, whats my problem. It's hard to decribe.
Yes you can by firing an event. for example:
in your app.js
window.Fire = new Vue();
For example here you create a user then you want to update table after creating a new user, Follow these steps:
createUser(){
// FireUpdate is your fire name, you can give it any name you want!
// Call this after you post something to specific route.
Fire.$emit('FireUpadte');
}
Then you will load new users using this approach:
created(){
// Load new Users after created.
Fire.$on('FireUpadte', () => { this.createUser(); });
}
For more information check this video: https://www.youtube.com/watch?v=DHuTkJzH2jI&list=PLB4AdipoHpxaHDLIaMdtro1eXnQtl_UvE&index=20
What you're looking for are websockets. You establish a websocket connection and it stays open, allowing the server to notify the web app when something changes.
You can run your own socket.io server on a Node.js backend or use a service like Pusher.com (very cheap, free tier is pretty big).
I highly recommend going the Pusher.com route, they also have great tutorials ; )
https://pusher.com

Wrap findOne in sails.js User controller to make findMe

I'd like to create a new endpoint "/user/me" to call an action on my User controller called findMe(req, res). I'd like it to behave almost identically to the blueprint "/user/:id" which routes to findOne(req, res), except it will return the current user's data based on the logged in user's id (which is stored in req.session after login).
Is there an easy way to write findMe so it can pass the user ID to findOne and leave all the remaining request processing to the core sails findOne action?
I'm keen not to work with the findOne methods on User model directly but rather, use findOne on the User controller because it has added benefits, like honouring the ?polulate=... parameter, etc. That's said, tell me if this is a bad idea. I'm slightly concerned about the security implications - could s malicious user get to other users data via a forged /user/me request?
In other environments I've known this as server side redirects (different to client side redirects / 30x).
Thanks
Adam
You can totally access the blueprints from a controller. This is how you would do it if you store the user id inside the session as req.session.user.id:
// UserController.js
module.exports = {
me: function (req, res) {
req.params.id = req.session.user.id;
return sails.hooks.blueprints.middleware.findone(req, res);
}
};
However, this would not allow you to use other params such as populate.
I personally think you should implement your own action as it won't be really complicated and will be easier to secure the requests.

How to replace auto generated easyrtc id with your applications username in easyrtc application

I am developing one application using easyrtc tool with wavemaker tool.For a new user easy rtc provides automatically created easyrtc id.
In the chat window the random id are shown..i want to replace these ids with applications username..
I have find one solution where we have to set easyrtc.setUsername("") in client js file before calling easyrtc.connect function..
But this not solves the problem...
any help would be appriciated
Now, you can do it easyer, use this function:
easyrtc.idToName(easyrtcid)
Their is no easy way to solve this. However, it is possible using a mixture of server-side and client-side events to pass/receive user metadata when connected/disconnected. Here is a simple way to achieve this:
When a client connects to the server send user metadata via sendServerMessage on the connected event listener via client-side library. The server then receives the message from the client and stores the metadata about the user with that particular easyrtcid in a central location (ex. redis). The message sent to the server can be a json object with user metadata in a structured format. See details on connecting and sending a message to the server here: easyRTC Client-Side Documentation
When a client disconnects from the server remove their information from the data store using the onDisconnect event on the server side. This event provides a connectionObj which includes the easyrtcid of the user who disconnected. Use this identifier to remove the user from the datastore. You could also call generateRoomList() on the connectionObj to remove the user by easyrtcid and room from your datastore. You can read about the connection object here: connectionObj easyRTC documentation
Here is some example code of how to do this:
// Client-Side Javascript Code (Step 1)
easyrtc.connect('easyrtc.appname', function(easyrtcid){
// When we are connected we tell the server who we are by sending a message
// with our user metadata. This way we can store it so other users can
// access it.
easyrtc.sendServerMessage('newConnection', {name: 'John Smith'},
function(type, data){
// Message Was Successfully Sent to Server and a response was received
// with a the data available in the (data) variable.
}, function(code, message) {
// Something went wrong with sending the message... To be safe you
// could disconnect the client so you don't end up with an orphaned
// user with no metadata.
}
}, function(code, message) {
// Unable to connect! Notify the user something went wrong...
}
Here is how things would work on the server-side (node.js)
// Server-Side Javascript Code (Step 2)
easyrtc.events.on('disconnect', function(connectionObj, next){
connectionObj.generateRoomList(function(err, rooms){
for (room in rooms) {
// Remove the client from any data storage by room if needed
// Use "room" for room identifier and connectionObj.getEasyrtcid() to
// get the easyrtcid for the disconnected user.
}
});
// Send all other message types to the default handler. DO NOT SKIP THIS!
// If this is not in place then no other handlers will be called for the
// event. The client-side occupancy changed event depends on this.
easyrtc.events.emitDefault("disconnect", connectionObj, next);
});
Redis is a great way to keep track of the users connected if using rooms. You can use an hash style object with the first key being the room and each sub key/value being the users easyrtcid with a JSON hash of the metadata stored as it's value. It would have to be serialized to a string FYI and de-serialized on the lookup but this is simple using Javascript using the JSON.stringify and JSON.parse methods.
To detect occupancy changes in your application you could add a event listener to the easyrtc.setRoomOccupantListener method on the client-side and then when this event is fired send another message to the server to get all the users connected to it from the datastore.You would have to listen for a separate message on the server-side and return the users in the store deserialized back to the client. However, depending on your application this may or may not be needed.

grails+spring-security-facebook listen to login success event

im a newbie both in spring security and spring-security-facebook and in an app that we are building we have to couple them.Everything is working well i will need to know the way to listen to the facebook login success event. is there some one that already did a stuff that impose him to catch the facebook event success?? I need that because in the begining of the app (before adding the spring -security-Facebook plugin ); we have a special behaviour attached to the "grails.plugins.springsecurity.onInteractiveAuthenticationSuccessEvent" event (configured in the config.groovy file) and we have to execute the same special behaviour when the user connect with facebook account. Is there an event (extending springsecurity kinds of events) that we have to listen to?
any idea ??
Ps: when searching for solution we found a way to catch the FB js Events and work around to reach what we want as result but we would as possible to want to not go that way ....
The problem with it that current Spring Security Facebook authenticates user on each request (by listening to FB cookie, transparently), so you'll get this event on each request. Probably it's not what you want, right? It the same as having a filter on each request.
Btw, you can handle situation when user login into your app first time, but implementing onCreate(user, token) or afterCreate(user, token) in service FacebookAuthService (you have to create such service at this case)
Example:
File grails-app/services/FacebookAuthService.groovy:
class FacebookAuthService {
void afterCreate(def user, def token) {
log.info("New user!") //or any code there
}
}

openfire get online users

I'm using OpenFire server for instant messaging and JSJaC JavaScript library on the client. I'm new in XMPP technology.
What I want is on load I want to send a list of users and receive status for each. Something like
$(function(){
var UserList = ["Isis", "Jackob", "Oybek"];
con.send(UserList, OnComplete);
});
function OnComplete(myList){
for (el in myList)
if (el.IsOnline) {
// Do DOM Stuff
}
}
Is it possible?
I've been looking for the documentation, examples and other similar responses but didn't find anyting.
You can't query for presence. You can subscribe to presence. If you send your own presence in, the server will send you the current presence of everyone you have subscribed to, as well as every change they make to their presence from there on in. There's no way to tell when you're "done" getting presence, because you're never done. Just set up a callback to do something interesting whenever you get a presence change from the person you are subscribed to, and you'll be in good shape:
con.registerHandler('presence_in', function(p) {
var from = p.getFromJID()
// do something interesting with p, from, etc.
});