How to export reference of central variable - javascript

I'm using Hapi.
I'm also using NES client to connect to another node instance. In order to access this socket from the routes, I attach it to the server variable like so:
exports = async () => {
//api
var server = new Hapi.Server({
host: process.env.WEB_HOST || '127.0.0.1',
port: process.env.WEB_PORT || '8080'
});
// register plugins
await server.register(plugins);
// add routes
await routes(server);
server.socket = new Socket(identifier); // uses NES
return server;
}
I want to access the socket from a library file. How do I do this without having to pass it the server variable each time? When I try to module.exports the server, it never gives me a reference, only a version of the variable at the time it was exported.

Have you tried server.decorate. If I understood correctly, you want to access your socket variable from routes. Why not just create basic plugin and create your socket instance and pass that into server object. For example;
exports.plugin = {
async register(server, options) {
server.decorate('server', 'socketConn', new Socket(identifier));
},
name: 'socket-connection'
};
and in your routes you can access this variable as request.server.socketConn.

Related

Unable to retrieve instance from Eureka server using JS client

I'm having a problem trying to get a service URL discover by eureka.
I'm using eureka-js-client to connect to Eureka and for testing purposes I've created two microservices, I've called it: ms1 and ms2.
What I've tried is:
Start Eureka server to allow services register into it
Start ms1 and register into Eureka
Start ms2, register into Eureka and get ms1 URL.
To accomplish this I've launched eureka server as a Spring Boot app using #EnableEurekaServer. This part works fine, I can access http://localhost:8761/ and see the dashboard.
Then, in my microservices I've this configuration
this._client = new Eureka({
instance: {
app: 'ms1',
instanceId: 'ms1',
hostName: 'localhost',
ipAddr: '127.0.0.1',
statusPageUrl: `http://localhost:${port ? port : this._port}`,
healthCheckUrl: `http://localhost:${port? port : this._port}/health`,
port: {
'$': port? port: this._port,
'#enabled': true,
},
vipAddress: 'myvip',
dataCenterInfo: {
'#class': 'com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo',
name: 'MyOwn',
},
},
eureka: {
host: 'localhost',
port: 8761,
servicePath: '/eureka/apps/'
},
})
And the same for ms2 changing the name.
When I run the project it output registered with eureka: ms1/ms1 and services seems to be registered in eureka correctly:
But now the problem is trying to get the URL of one of the two services. From either of the two services, if I try to get the Eureka instances I always get an empty list.
I have this code:
let instances: any = this.getClient().getInstancesByAppId(microserviceName);
let instance = null;
let url = ''
if (instances != null && instances.length > 0) {
instance = instances[0];
let protocol = instance.securePort["#enabled"] == "true" ? "https" : "http";
url = `${protocol}//${instance.ipAddr}:${instance.port.$}/`
}
Where in "microserviceName" variable I've tried:
"ms1"
"MS1"
"ms1/ms1"
But the response is always an empty array with this output:
Unable to retrieve instances for appId: ms1
So, what's the problem? Have I missed something? I think the flow is correct:
Start Eureka server.
Register services into server.
Look for instances in the server.
Thanks in advance.
Finally I solved my own issue. All was working good, the ms2 was able to find ms1 using the code I posted, so the problem was:
My ms2 file was like this:
EurekaService.getClient().start()
EurekaService.getUrl('ms1')
EurekaService.getClient()?.stop()
And it seems like EurekaService.getClient().start() does not block until it ends (or is available or whatever), so the client is not up and can't get the instance ms1.
Note that the method getUrl() has the code provided in the OP:
let instances: any = this.getClient().getInstancesByAppId(microserviceName);
let instance = null;
...
So I've changed the code like this:
start()
async function start(){
EurekaService.getClient().start()
await new Promise(f => setTimeout(f, 1000));
const url = EurekaService.getUrl('ms1')
console.log("url = ",url)
EurekaService.getClient()?.stop()
}
And works perfectly, the output log is:
registered with eureka: ms2/ms2
url = http//127.0.0.1:8002/
de-registered with eureka: ms2/ms2
So, start method is not async so I can't use await or .then(), I have to set a timeout and wait to complete.
I don't know if there is a better way to do this or by the nature of the architecture can't be controlled when is available.
By the way, for me, 1 second timeout is enough.

Session variables in ExpressJS

I am a web developer with a PHP background that moved recently to JS.
In PHP, we used to have the $_SESSION global variable which make your session variables accessible from any 'place' in your code (Great feature!).
Now, working with an ExpressJS application, I am using express-session package to create session variables.
The issue is that session variables in ExpressJS are NOT global, they are property of the request object (req.session). So they are only accessible in functions that have a req parameter (AKA middlewares and route functions).
The question is: Is it possible to make session variables in ExpressJS global ala PHP, so any 'helper' function can handle them?
Yes, but you should not do that. You can create globals by appending properties to the global object. For example: global.someData = req.session.someData, but the global object is similar to the GLOBAL from PHP.
In that case, especially if you have async functions, the variables of one request will mess up with the code running for another request (e.g. Alice would get the Bob's variables).
The good practice is to pass the req.session object wherever that is needed and access it.
You can use CLS (Continuation-local-storage) to achieve this.
All you have to do is create a namespace and bind it to each request.
Now, add the below code to your server.js where you initialize your server
//server.js
var express = require('express');
var app = express();
var createNameSpace = require('continuation-local-storage').createNamespace;
var myNameSpace = createNameSpace('myNameSpace');
// Assign your sessionVariable to each request as 'sessionId'
app.use(function(req, res, next) {
myNameSpace.run(function() {
myNameSpace.set('sessionId', sessionVariable);
next();
});
});
Add the following code anywhere in you application to access your sessionVariable
var getNamespace = require('continuation-local-storage').getNamespace;
var myNameSpace = getNamespace('myNameSpace');
var sessionId = myNameSpace.get('sessionId'); // access your sessionId (sessionVariable)
Note that using CLS can be tricky sometimes.
Once your session variable is setup (either on your login function or wherever you are setting it up) it is then a global variable on your index.js file.
I setup my session like so:
var students = {Id: result[key].Id,Username: result[key].Username,Email: result[key].Email,Phone: result[key].Phone,First_Name: result[key].First_Name,Surname: result[key].Surname,RoleName: result[key].RoleName, Company: result[key].CompanyName };
Users.push(students);
req.session.user = students;
You can access the stored session by setting up a var like the below:
var session = req.session.user
This will set the session to a session variable where you need it inside your function.
Then when you render a page you just need to add the session to that render and you will have access to it on that page.
res.render('/', { title: 'Hello World', session: session});

Socket.io middlewhere functions

I am trying to seperate logic in my socket.io server but i am experiance some issues.
say for instance i have the following:
io.on('connection', function (socket) {
var fileModule = require('./costum_modules/FileModule.js')(io);
app.use(fileModule);
});
Now inside the fileModule i have the following code:
var fileModule = function (socket) {
socket.on('userData', function(msg){
var i = 0;
});
}
module.exports = new fileModule();
Sadly the socket i undefined.
My question is can i do it like this or is it not possible to pass a singleton to another file and make it read from the same object?
You can use other files to break up your logic, but there are a couple issues with your code.
First, I think Hacketo is correct that you don't want to do new fileModule(), just:
module.exports = fileModule;
Second, when call require, you are passing the global socketIO object (io). You should pass it the socket you get in the connection handler. E.g.
require('./costum_modules/FileModule.js')(socket);
I think that will work to move some of your socket.io message handling code into a module. Now your socket will respond to userData messages from a client. However, unless you have some custom application, I don't think app.use is going to do what you expect. You can't hook web socket handlers into an Express/Restify/Connect/Whatever middleware chain. But you could write a middleware that sends a message to your socket server.
If you are just trying to share the session between your app and socket server, try this SO answer.

Socket IO 1.2 Query Parameters

I can't figure out how to retrieve query parameters on the server side for socket.io
1.2.1
Here's my client side code
var socket = io('http://localhost:3000/',{_query:"sid=" + $('#sid').attr('data-sid') + "&serial=" + $('#serial_tracker').text()});
and the server side:
io.use(function(socket,next){ //find out if user is logged in
var handshake = socket.request;
console.log(socket.request._query);
handshake.sid = handshake.query.sid;
}
socket.request._query is:
{ EIO: '3', transport: 'polling', t: '1419909065555-0' }
Does anyone know how query parameters work in socket io 1.2.1?
Thanks for any help and if you need any more information, just ask me.
When sending handshake query data to socket.io, use the following property name in the object:
{
query: 'token=12345'
}
I see above you used _query for a property name instead.
You should be able to access the query information at socket.request._query at that point. I'm not sure if there is a better way to get a hold of that data? I'm guessing yes, since they put an underscore in front of it, but I haven't found a better way yet.
Here's the full example of a connect query that is working for me (forgive the formatting, I'm copy/pasting this out of different node modules into an inline solution).
Server (using socket 1.2.1 nodejs):
var restify = require('restify');
var api = restify.createServer();
var socketio = require('socket.io');
var io = socketio.listen(api.server); // api is an instance of restify, listening on localhost:3000
io.use(function(socket, next) {
// socket.request._query.token is accessible here, for me, and will be '12345'
next();
});
api.listen(3000, function() {
console.log('%s listening at %s', api.name, api.url);
});
Client (chrome browser using the client library located at https://cdn.socket.io/socket.io-1.2.1.js):
var socket = io.connect('http://localhost:3000/', { query: 'token=12345' });

Exception when using a server route and onBeforeAction

I'm seeing strange behavior when trying to add pdf file generation.
The following code, on the if statement, throws:
both\routes.js
Router.onBeforeAction(function () { if (!Meteor.user() || Meteor.loggingIn()) {
this.redirect('welcome.view'); } else {
Meteor.call("userFileDirectory", function (error, result) {
if (error)
throw error;
else
console.log(result);
});
this.next(); } }, { except: ['welcome.view'] });
Error: Meteor.userId can only be invoked in method calls. Use
this.userId in publish functions. at Object.Meteor.userId
(packages/accounts-base/accounts_server.js:19:1) at Object.Meteor.user
(packages/accounts-base/accounts_server.js:24:1) at [object
Object].Router.onBeforeAction.except
(app/both/3-router/routes.js:10:15) at
packages/iron:router/lib/router.js:277:1 at [object
Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
at [object Object].hookWithOptions
(packages/iron:router/lib/router.js:276:1) at boundNext
(packages/iron:middleware-stack/lib/middleware_stack.js:251:1) at
runWithEnvironment (packages/meteor/dynamics_nodejs.js:108:1) at
packages/meteor/dynamics_nodejs.js:121:1 at [object Object].dispatch
(packages/iron:middleware-stack/lib/middleware_stack.js:275:1)
Only when I add this code into the file, and the /pdf route is taken:
Router.route('/pdf', function() {
var filePath = process.env.PWD + "/server/.files/users/test.pdf";
console.log(filePath);
var fs = Npm.require('fs');
var data = fs.readFileSync(filePath);
this.response.write(data);
this.response.end();
}, {
where: 'server'
});
The above code works fine; the pdf is rendered to the screen and no exception is thrown, when I take out the onBeforeAction code.
The opposite is also true, if I take out the server route, there is no route that causes an exception.
This occurs because the route you're using is a server side route. The technique Meteor uses to authenticate a user is done via the DDP protocol, over websockets.
When your browser makes a GET/POST request to the server it doesn't have any information regarding the user's authentication state.
You use Meteor.user() in your Route.onBeforeAction but it has no access to this information.
The solution to this is find an alternative way to authenticate the user. One such method is to use cookie's.
This is known issue with Meteor's authentication system, see: https://github.com/EventedMind/iron-router/issues/649
A better way than cookies could be a named collection of Meteor that stores userId and some sessionId:
You can store current userId on the client side before the call to the server:
var sessionId = Random.id();
col = new Mongo.Collection('session');
col.insert({
sessionId: sid,
userId: Meteor.userId(),
issued: new Date()
});
And then pass sessionId to the server through a GET/POST request and read it on the server:
var sid = this.request.query.sid;
var user = col.findOne({sessionId: sid}); // returns an object
Using a separate parameter is better than using userId itself because you can revoke this sessionId after some time or immediately after the server call.
Proper allow/deny permissions are required to prevent anyone from updating the collection. Also, please note that you can't trust new Date() on the client's side.

Categories

Resources