SocketIO syncUpdate model for different user - angularjs

I have been writing an web app, where individual user will post their task and will be displayed on their news feed. I have used socket io with angular fullstack framework by yeoman.
what I am trying to do is, when user adds new data to goal model, on socketSyncUpadtes, $rootScope.getUserGoals get updated and user see new entry. but I want that to happen for each user, thats why I included userid, to make sure, it only pulls user specific data.
But in reality what is happening is, all connected user getting this socket stream and getting their model updated too, but as page refreshed it is always only what that query pulls, only user specific data.
this is how I get user specific data
$rootScope.getUserGoals = function (userid){
//get current todo task views
$http.get('/api/goals/name/'+userid).success(function(goals) {
$rootScope.userGoalArr = goals;
socket.syncUpdates('goal', $rootScope.userGoalArr);
});
};
then it displays in view with this
<ul class = "holdTaskul" ng-repeat="view in filtered = userGoalArr | orderBy:'-created'">
I looked up on google and search a lot to figure out how to separate each user socket stream and attached them to their login credential. I have come across this socket-jwt and session token. I am not expert on socket. I would much appreciate if someone could point me to right direction about this, what I am intending to do..
my socket configuration is as follows for socket.syncUpdate function
syncUpdates: function (modelName, array, cb) {
cb = cb || angular.noop;
/**
* Syncs item creation/updates on 'model:save'
*/
socket.on(modelName + ':save', function (item) {
var oldItem = _.find(array, {_id: item._id});
var index = array.indexOf(oldItem);
var event = 'created';
// replace oldItem if it exists
// otherwise just add item to the collection
if (oldItem) {
array.splice(index, 1, item);
event = 'updated';
} else {
array.push(item);
}
cb(event, item, array);
});
/**
* Syncs removed items on 'model:remove'
*/
socket.on(modelName + ':remove', function (item) {
var event = 'deleted';
_.remove(array, {_id: item._id});
cb(event, item, array);
});
},
I have also configured socket for sending auth token by this
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
query: 'token=' + Auth.getToken(),
path: '/socket.io-client'
});
and used socketio-jwt to send the secret session to socket service by that
module.exports = function (socketio) {
socketio.use(require('socketio-jwt').authorize({
secret: config.secrets.session,
handshake: true
}));

Related

Differentiate Initiator & Participants in Opentok

I am using Node + Angular to create Multiple Video Chat. Creating sessions and tokens on server side and use them to connect with others in client side. I have defined the userType while generating Token. Now, what I want is that only 'organizers' will be able to initiate the video and 'users' can only subscribe to the video. I am able to differentiate it on the level of subscribers, but not in publishers.
Here : streamCreated() is working well but sessionConnected() is not working as I am not able to put it into subContainer
var publisher = OT.initPublisher('publisher',{name:'MyGroup'});
// Attach event handlers
session.on({
// This function runs when session.connect() asynchronously completes
sessionConnected: function(event) {
// Publish the publisher we initialzed earlier (this will trigger 'streamCreated' on other
// clients)
if(JSON.parse(session.connection.data).userType === 'organizer'){
var subContainer = document.createElement('div');
document.getElementById('initiator-container').appendChild(subContainer);
}else{
var subContainer = document.createElement('div');document.getElementById('publisher').appendChild(subContainer);
}
session.publish(publisher,subContainer);
},
// This function runs when another client publishes a stream (eg. session.publish())
streamCreated: function(event) {
// Create a container for a new Subscriber, assign it an id using the streamId, put it inside
if(JSON.parse(event.stream.connection.data).userType == 'organizer'){
// set it as initiator
// initiator-container
var subContainer = document.createElement('div');
subContainer.id = 'stream-' + event.stream.streamId;
document.getElementById('initiator-container').appendChild(subContainer);
var subscriberProperties = {height: 486,width:'100%'};
// Subscribe to the stream that caused this event, put it inside the container we just made
session.subscribe(event.stream, subContainer,subscriberProperties);
}else{
// participants
var subContainer = document.createElement('div');
subContainer.id = 'stream-' + event.stream.streamId;
document.getElementById('subscribers').appendChild(subContainer);
// Subscribe to the stream that caused this event, put it inside the container we just made
session.subscribe(event.stream, subContainer);
}
},
streamDestroyed: function(event){
console.log("Stream " + event.stream.name + " ended. " + event.reason);
},
// stream property changed
streamPropertyChanged: function(event){
console.log(event)
}
});
// Connect to the Session using the 'apiKey' of the application and a 'token' for permission
session.connect(token);
Anyone with possible solution?
I'm not sure exactly what you're trying to do but it seems like you want to put the Publishers and Subscribers in different containers depending on what the userType is in the connection data.
The issue is doing this in the session.publish call but it's actually the initPublisher call that is inserting the publisher. I think you want to wait and create your Publisher after you are connected so that you know what type of user you are. So move the initPublisher call into sessionConnected like this:
session.on('streamCreated', function(event) {
var elementId = JSON.parse(session.connection.data).userType === 'organizer' ? 'publisher' : 'initiator-container';
var publisher = OT.initPublisher(elementId, {insertMode: 'append'});
session.publish(publisher);
});
Also insertMode is your friend. It will make inserting these elements a bit easier.

Plaid: how to create public token in the front end (Ionic/angular) and send to server

I am follow this tutorial in order to get a transactions/accounts detail from the plaid API. Plaid'd quickstart guide user ejs in order to send to the server with the onSuccess function. How do I create this token using ionic?
Plaid quick guide also suggests that we use code blow
var linkHandler = Plaid.create({
env: 'sandbox',
clientName: 'Client Name',
key: '<PUBLIC_KEY>',
product: ['auth', 'transactions'],
token: '<GENERATED_PUBLIC_TOKEN>',
onSuccess: function(public_token, metadata) {
// You do not need to repeat the /item/public_token/exchange
// process when a user uses Link in update mode.
// The Item's access_token has not changed.
},
and also suggest to use this code
// Create a public_token for use with Plaid Link's update mode
client.createPublicToken(ACCESS_TOKEN, (err, result) => {
// Handle err
// Use the generated public_token to initialize Plaid Link in update
// mode for a user's Item so that they can provide updated credentials
// or MFA information
const publicToken = result.public_token;
});
in order to create a public token and get the access token. I can't use this function because I'm getting an error 'Plaid and/or client is not defined
How do I create this public token using Ionic front end and node back end?
What's the workflow here?
Thanks in advance
On the server side, you'll need to initialize the Plaid node client library first. You will also want to make an exchange token call, to exchange the public_token from Link for an API access_token. You'll then save the access_token and use it to retrieve transaction and account data:
// Initialize the Plaid API client with your API keys (https://dashboard.plaid.com/account/keys)
// Use plaid.environments.production, plaid.environments.development, or plaid.environments.sandbox
const plaid = require('plaid');
const client = new plaid.Client(client_id, secret, public_key, plaid.environments.sandbox);
client.exchangePublicToken(PUBLIC_TOKEN, function(error, tokenResponse) {
if (error != null) {
var msg = 'Could not exchange public_token!';
console.log(msg + '\n' + error);
}
const ACCESS_TOKEN = tokenResponse.access_token;
const ITEM_ID = tokenResponse.item_id;
console.log('Access Token: ' + ACCESS_TOKEN);
console.log('Item ID: ' + ITEM_ID);
// Now retrieve transactions or account information with the access_token
});
For the client-side in your Ionic app, you'll need to include the link-initialize.js script before calling Plaid.create. Specifically:
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js">
</script>
Here's a full client-side HTML example:
<button id="link-button">Link Account</button>
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script type="text/javascript">
var handler = Plaid.create({
clientName: 'Plaid Walkthrough Demo',
env: 'sandbox',
key: '[PUBLIC_KEY]', // public_key is at https://dashboard.plaid.com/account/keys
product: ['auth'],
onLoad: function() {
// Optional, called when Link loads
},
onSuccess: function(public_token, metadata) {
// Send the public_token to your app server.
// The metadata object contains info about the institution the
// user selected and the account ID, if `selectAccount` is enabled.
$.post('/get_access_token', {
public_token: public_token,
});
},
onExit: function(err, metadata) {
// The user exited the Link flow.
}
});
</script>
An Angular library was recently created: click here and here
I had some issues implementing Plaid successfully at first; these tips should help:
Implement the library as shown in the second link above. Make sure for step two you import in your app.module.ts file. Don't use the jQuery provided in the documentation.
Using this library, a button will be automatically created within the Angular element <mr-ngx-plaid-link-button>. This button will act as a submit button unless you use DOM or other methods to change it to type button on ngAfterViewInit()
All five events in step three are listeners for various events. The onSuccess() callback will return the public token - only after you have (a) successfully submitted bank credentials (user_good / pass_good on Sandbox) and (b) clicked the Continue button to exit the Plaid Link

Send Notification properly using Socket.io Redis server

OBJECTIVE: When post is added to perticular channel send notification to all connected subscribed users for that channel.
Relational Database tables over view: Post, Channels, Users, Channels_Users
Post: id, title, content, channel_id(Indicate post is related to what channel)
Channels: id, name (List of Channels)
Users: id, username (User Table)
Channels_Users: id, channel_id, user_id (This table indicate which channels user is subscribed to.)
Goal: When post is created, send notification to perticular user group.
I have tried serveral ways and it is working as aspected. But I am looking for the correct way of doing this.
Server Side: socket.js
Naming conventions:
Channel name:
channel-1, channel-2, channel-3, ....
Namespace name('namespace-'+[channelName]) :
namespace-channel-1, namespace-channel-2, ...
var app = require('express')();
var request = require('request');
var http = require('http').Server(app);
var io = require('socket.io').listen(http);
var Redis = require('ioredis');
var redis = new Redis(####, 'localhost');
// Get all channels from Database and loop through it
// subscribe to redis channel
// initialize namespace inorder to send message to it
for(var i=1; i=<50; i++) {
redis.subscribe('channel-'+i);
io.of('/namespace-channel-'+i).on('connection', function(socket){
console.log('user connected');
});
}
// We have been subscribed to redis channel so
// this block will hear if there is any new message from redis server
redis.on('message', function(channel, message) {
message = JSON.parse(message);
// send message to specific namespace, with event = newPost
io.of('/namespace-'+channel).emit('newPost', message.data);
});
//Listen
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
Client side: Angularjs Socket factory code: (Mentioning main socket code)
// userChannels: user subscribed channels array
// Loop through all channel and hear for new Post and
// display notification if there is newPost event
userChannels.forEach(function (c) {
let socket = {};
console.info('Attempting to connect to namespace'+c);
socket = io.connect(SOCKET_URL + ':3000/namespace-'+c, {query: "Authorization=token"});
socket.on('connect',function(){
if(socket.connected){
console.info("namespace-" + c + " Connected!");
// On New post event for this name space
socket.on('newPost', function(data) {
/* DISPLAY DESKTOP NOTIFICATION */
});
}
});
});
Publish data to Redis server, at the time of new post created
$data = [
'event' => 'newPost',
'data' => [
'title' => $postTitle,
'content' => $postContent
]
];
$redisChannel = "channel-" . $channelId; // As per our naming conventions
Redis::publish($redisChannel, json_encode($data));
The user is getting notification correctly for the channels they have been subscribed.
Problem 1: I am not sure this is the best solution to implement this notification thing.
Problem 2: When a user opens the same application in more than one browser tabs, it gets the notification for all of them. It should send only one notification. This is something related to user management at server side on redis end. I am not sure about this part too.
I really appreciate your suggestion/help on this. Thank you in advance.

Using passport-facebook without Mongoose User (No Mongo in the MEAN stack)

I'm very new to the MEAN stack, and this might seem to be very naive or wrong approach, but I want to ask that when we authenticate using passport-facebook strategy, using the following code:
var FacebookStrategy = require('passport-facebook').Strategy;
var User = require('../models/user');
var fbConfig = require('../fb.js');
module.exports = function(passport) {
passport.use('facebook', new FacebookStrategy({
clientID : fbConfig.appID,
clientSecret : fbConfig.appSecret,
callbackURL : fbConfig.callbackUrl
},
// facebook will send back the tokens and profile
function(access_token, refresh_token, profile, done) {
console.log('profile', profile);
// asynchronous
process.nextTick(function() {
// find the user in the database based on their facebook id
User.findOne({ 'id' : profile.id }, function(err, user) {
// if there is an error, stop everything and return that
// ie an error connecting to the database
if (err)
return done(err);
// if the user is found, then log them in
if (user) {
return done(null, user); // user found, return that user
} else {
// if there is no user found with that facebook id, create them
var newUser = new User();
// set all of the facebook information in our user model
newUser.fb.id = profile.id; // set the users facebook id
newUser.fb.access_token = access_token; // we will save the token that facebook provides to the user
newUser.fb.firstName = profile.name.givenName;
newUser.fb.lastName = profile.name.familyName; // look at the passport user profile to see how names are returned
//newUser.fb.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first
// save our user to the database
newUser.save(function(err) {
if (err)
throw err;
// if successful, return the new user
return done(null, newUser);
});
}
});
});
}));
};
I don't need to store the user information in any data store. I want to store the token only for the time the user is logged into my web application, basically I don't have the need to use Mongo, because all the data that will be displayed in the web application will come from Facebook api, for example the posts for a profile, the number of likes on a particular posts etc. I don't need to have a backend as such, because if I store the data in any data store such as Mongo, the next time the user login then the data will be stale (in a way the Facebook api is kind of my backend), and I also want that the updates for information on any posts done on Facebook should be updated realtime on my web application for e.g. if someone likes a post on the actual Facebook page the number of likes on my web application should also be updated in realtime, so it seems unnecessary to first bring the data from the Facebook SDK and then store it in Mongo, why not just give it to the controller and from there the view can present the data. If my approach is wrong please do correct me.
So basically every time the user logs in an access token is created and used for that session, when the user logs out the access token is destroyed and so completely eliminates the need for storing the token and any data that is brought in using the Facebook SDK.
Replace the function call
User.findOne({ 'id' : profile.id }, function(err, user) {
With facebook sdk authentication call and return the user object when it's validated.
return done(null, user);
Please refer...
https://github.com/jaredhanson/passport-facebook
you need to create a new user template in the model folder. I have created the following: user.js
var facebook = module.exports.facebook = {
id : String,
token : String,
email : String,
name : String
}
and then change the passport.serializeUser and passport.deserializeUser functions.
passport.serializeUser(function(user, done) {
done(null, user.facebook.id);
});
// used to deserialize the user
//passport.deserializeUser(function(id, done) {
passport.deserializeUser(function(id, done) {
done(null, { id: User.facebook.id, token: User.facebook.token, name: User.facebook.name, email: User.facebook.email})
});
then the function: process.nextTick(function() {} replace the content by this code :
var newUser = User;
// set all of the facebook information in our user model
newUser.facebook.id = profile.id; // set the users facebook id
newUser.facebook.token = token; // we will save the token that facebook provides to the user
newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; // look at the passport user profile to see how names are returned
newUser.facebook.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first
return done(null, newUser);
add the line profileFields: ['id', 'displayName', 'photos', 'emails', 'name'] in function passport.use(new FacebookStrategy({}
change the profile.ejs file by removing the local information div and changing the properties <% = user.facebook.id%> to <% = user.id%> and so on in the others.

Firebase: How can i use onDisconnect during logout?

How can i detect when a user logs out of firebase (either facebook, google or password) and trigger the onDisconnect method in the firebase presence system. .unauth() is not working. I would like to show a users online and offline status when they login and out, minimize the app (idle) - not just when the power off their device and remove the app from active applications on the device.
I'm using firebase simple login for angularjs/ angularfire
Im using code based off of this tutorial on the firebase site.
https://www.firebase.com/blog/2013-06-17-howto-build-a-presence-system.html
Please i need help with this!
Presence code:
var connectedRef = new Firebase(fb_connections);
var presenceRef = new Firebase(fb_url + 'presence/');
var presenceUserRef = new Firebase(fb_url + 'presence/'+ userID + '/status');
var currentUserPresenceRef = new Firebase(fb_url + 'users/'+ userID + '/status');
connectedRef.on("value", function(isOnline) {
if (isOnline.val()) {
// If we lose our internet connection, we want ourselves removed from the list.
presenceUserRef.onDisconnect().remove();
currentUserPresenceRef.onDisconnect().set("<span class='balanced'>☆</span>");
// Set our initial online status.
presenceUserRef.set("<span class='balanced'>★</span>");
currentUserPresenceRef.set("<span class='balanced'>★</span>");
}
});
Logout function:
var ref = new Firebase(fb_url);
var usersRef = ref.child('users');
service.logout = function(loginData) {
ref.unauth();
//Firebase.goOffline(); //not working
loggedIn = false;
seedUser = {};
clearLoginFromStorage();
saveLoginToStorage();
auth.logout();
};
The onDisconnect() code that you provide, will run automatically on the Firebase servers when the connection to the client is lost. To force the client to disconnect, you can call Firebase.goOffline().
Note that calling unauth() will simply sign the user out from the Firebase connection. It does not disconnect, since there might be data that the user still has access to.
Update
This works for me:
var fb_url = 'https://yours.firebaseio.com/';
var ref = new Firebase(fb_url);
function connect() {
Firebase.goOnline();
ref.authAnonymously(function(error, authData) {
if (!error) {
ref.child(authData.uid).set(true);
ref.child(authData.uid).onDisconnect().remove();
}
});
setTimeout(disconnect, 5000);
}
function disconnect() {
ref.unauth();
Firebase.goOffline();
setTimeout(connect, 5000);
}
connect();

Resources