Koajs and Socket.io integration - node.js

Please, need help with integration of sockets with this koajs skeleton
Here is the code from server side file (/src/index.js)
'use strict';
// 3rd party
require('dotenv').config(); // Load env vars from .env, always run this early
const koa = require('koa');
const bouncer = require('koa-bouncer');
const nunjucksRender = require('koa-nunjucks-render');
const debug = require('debug')('app:index');
// 1st party
const config = require('./config');
const mw = require('./middleware');
const belt = require('./belt');
const cancan = require('./cancan');
////////////////////////////////////////////////////////////
const app = koa();
app.poweredBy = false;
app.proxy = config.TRUST_PROXY;
////////////////////////////////////////////////////////////
// Configure view-layer (nunjucks)
//
// We can override options send directly to nunjucks.
// https://mozilla.github.io/nunjucks/api.html#configure
////////////////////////////////////////////////////////////
const nunjucksOptions = {
// `yield this.render('show_user')` will assume that a show_user.html exists
ext: '.html',
noCache: config.NODE_ENV === 'development',
// don't throw template errors in development if we try to render
// a null/undefined like {{ x }}. in theory, setting it to true prevents
// bugs and forces you to be explicit about {{ x or '' }}, but in reality,
// often more annoying than it's worth.
throwOnUndefined: false,
// globals are bindings we want to expose to all templates
globals: {
// let us use `can(USER, ACTION, TARGET)` authorization-checks in templates
can: cancan.can,
},
// filters are functions that we can pipe values to from nunjucks templates.
// e.g. {{ user.uname | md5 | toAvatarUrl }}
filters: {
json: x => JSON.stringify(x, null, ' '),
formatDate: belt.formatDate,
nl2br: belt.nl2br,
md5: belt.md5,
toAvatarUrl: belt.toAvatarUrl,
autolink: belt.autolink,
},
};
////////////////////////////////////////////////////////////
// Middleware
////////////////////////////////////////////////////////////
app.use(mw.ensureReferer());
app.use(require('koa-helmet')());
app.use(require('koa-compress')());
app.use(require('koa-static')('public'));
// Don't show logger in test mode
if (config.NODE_ENV !== 'test') {
app.use(require('koa-logger')());
}
app.use(require('koa-body')({ multipart: true }));
app.use(mw.methodOverride()); // Must come after body parser
app.use(mw.removeTrailingSlash());
app.use(mw.wrapCurrUser());
app.use(mw.wrapFlash('flash'));
app.use(bouncer.middleware());
app.use(mw.handleBouncerValidationError()); // Must come after bouncer.middleware()
app.use(nunjucksRender('views', nunjucksOptions));
// Provide a convience function for protecting our routes behind
// our authorization rules. If authorization check fails, 404 response.
//
// Usage:
//
// router.get('/topics/:id', function*() {
// const topic = yield db.getTopicById(this.params.id);
// this.assertAuthorized(this.currUser, 'READ_TOPIC', topic);
// ...
// });
app.use(function*(next) {
this.assertAuthorized = (user, action, target) => {
const isAuthorized = cancan.can(user, action, target);
const uname = (user && user.uname) || '<Guest>';
debug('[assertAuthorized] Can %s %s: %s', uname, action, isAuthorized);
this.assert(isAuthorized, 404);
};
yield* next;
});
////////////////////////////////////////////////////////////
// Routes
////////////////////////////////////////////////////////////
app.use(require('./routes').routes());
app.use(require('./routes/authentication').routes());
app.use(require('./routes/admin').routes());
////////////////////////////////////////////////////////////
// If we run this file directly (npm start, npm run start-dev, node src/index.js)
// then start the server. Else, if we require() this file (like from
// our tests), then don't start the server and instead just export the app.
if (require.main === module) {
app.listen(config.PORT, function() {
console.log('Listening on port', config.PORT);
});
} else {
module.exports = app;
}
On client side (/views/master.js) :
....
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
var socket = io();
socket.on('news', function (data) {
console.log('received news with data: ');
console.log(data);
});
function myclick () {
console.log("click");
socket.emit('click', { clickdata: 'someone clicked on the button' });
}
</script>
<button type="button" onclick="myclick();">Click Me and watch console at server and in browser.</button>
....
Please, can someone explain to me hot to integrate socket.io with koas server? I've installed ["socket.io": "^1.4.5"]. What are the next steps?
PS: Sory about my ugly english.

I've founded a solution here. Bellow is a general code sample
const Koa = require( 'koa' )
const IO = require( 'koa-socket' )
const app = new Koa()
const io = new IO()
app.use( ... )
io.attach( app )
io.on( 'join', ( ctx, data ) => {
console.log( 'join event fired', data )
})
app.listen( process.env.PORT || 3000 )

Related

Error: No responses defined for platform: null when listing users google calendar events

I'm trying to implement a reminders dialogflow agent in node js that reminds the user on his google calendar upcoming events. however I'm getting an No responses defined for platform: null error when calling the intent for listing the upcoming events.
This is my code:
const express = require('express');
const google = require('googleapis').google;
const jwt = require('jsonwebtoken');
const dfff = require('dialogflow-fulfillment')
const {googlec} = require('googleapis');
// Google's OAuth2 client
const OAuth2 = google.auth.OAuth2;
// Including our config file
const CONFIG = require('./config');
// Creating our express application
const app = express();
// Allowing ourselves to use cookies
const cookieParser = require('cookie-parser');
app.use(cookieParser());
// Setting up EJS Views
app.set('view engine', 'ejs');
app.set('views', __dirname);
console.log(dfff)
app.get('/', function (req, res) {
// Create an OAuth2 client object from the credentials in our config file
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
// Obtain the google login link to which we'll send our users to give us access
const loginLink = oauth2Client.generateAuthUrl({
access_type: 'offline', // Indicates that we need to be able to access data continously without the user constantly giving us consent
scope: CONFIG.oauth2Credentials.scopes // Using the access scopes from our config file
});
return res.render("index", { loginLink: loginLink });
});
app.get('/auth_callback', function (req, res) {
// Create an OAuth2 client object from the credentials in our config file
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
if (req.query.error) {
// The user did not give us permission.
return res.redirect('/error');
} else {
oauth2Client.getToken(req.query.code, function(err, token) {
if (err)
return res.redirect('/');
// Store the credentials given by google into a jsonwebtoken in a cookie called 'jwt'
res.cookie('jwt', jwt.sign(token, CONFIG.JWTsecret));
return res.redirect('/');
});
}
});
app.post('/', express.json(),(req,res)=>{
//if (!req.cookies.jwt) {
// We haven't logged in
//return res.redirect('/');
//}
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
const calendar = google.calendar({version: 'v3' , auth:oauth2Client});
const agent = new dfff.WebhookClient({
request : req,
response : res
})
function welcome(agent){
agent.add("Hi")
}
function listEvents(agent){
calendar.events.list({
'calendarId': 'primary',
'auth':oauth2Client,
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 10,
'singleEvents': true,
'orderBy': 'startTime'
}).then((err,response)=> {
let events = response.result.items;
if (events.length > 0) {
for (let i = 0; i < events.length; i++) {
var event = events[i];
var when = event.start.dateTime;
if (!when) {
when = event.start.date;
}
return agent.add('Be ready for '+ event.summary + ' event '+ 'at ' + when )
}}
else {
return agent.add('You dont have any upcoming events.');
}
});
}
let intenMap = new Map()
intenMap.set('Default_Welcome_Intent',welcome)
intenMap.set('Remind_me',listEvents)
agent.handleRequest(intenMap)
});
// Listen on the port defined in the config file
app.listen(CONFIG.port, function () {
console.log(`Listening on port ${CONFIG.port}`);
});
Whenever listEvents function is processed I'm getting (Error: No responses defined for platform null) any idea why?
The issue is that listEvents() does an asynchronous operation that returns a Promise (the call to calendar.events.list()), which you handle through the .then() block, but you don't have a way for the agent.handleRequest() function to know this and to wait for the Promise to complete. To do this, you need to return a Promise.
Fortunately, in your case, the solution is straightforward, since the call and then() chain return a Promise, you can just return it. This would be done by adding the return keyword before calendar.events.list(). It might look something like this:
function listEvents(agent){
return calendar.events.list({
// Parameters go here
}).then((err,response)=> {
// Code to handle response and return a message go here
});
}

Why is the request handler called continuously (like every second) in node?

I've got this (complete code – well, sort of):
const fs = require('fs');
const http = require('http');
const url = require('url');
const path = require('path');
const Handlebars = require('handlebars');
const Router = require('./router');
const Utils = require('./utils');
const utils = new Utils();
const views = require('./views');
let hostname,
server,
router = new Router();
const port = ( process.argv.length > 2 ) ? parseInt(process.argv[process.argv.length - 1]) : 3000;
function install ( config ) {
let install_processes = [
installControllers( config ),
installModels( config ),
views( config )
];
return Promise.all( install_processes );
}
function installControllers ( config ) {
// To keep example compact, this return a Promise
}
function installModels ( config ) {
// To keep example compact, this return a Promise
}
function handleRequest ( req, res ) {
let r = global.routes.find( ( obj ) => obj.route === req.url );
if ( r ) {
let template = global.routes.views.layout,
output = template( req.body );
r.action();
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end( output );
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end("404: page not found, " + req.url);
}
}
var fw = (function ( ƒ ) {
ƒ.init = function Init ( config ) {
config = config;
hostname = config.hostname ? config.hostname : '127.0.0.1';
install( config )
.then(function () {
ƒ.startServer();
})
.catch(function ( err ) {
console.error("Error", err);
})
}
ƒ.startServer = function () {
server = http.createServer( handleRequest ).listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}`);
});
}
return ƒ;
}( fw || {} ));
module.exports = fw;
Please note, that variables and handlers are set elsewhere.
Everything works fine. But why is the handleRequest handler called every second (without any incoming requests. And it's actually called TWICE within that second)? If I add a console.log it gets output every second or so.
I would expect it to be called ONLY when actual requests are made to the server. Is this some sort of timeout handler that runs in the background or the like?
Why is handle triggered twice when starting the server, that is?
Try adding this to a file (wtf.js) and run: node wtf 9000;
```
const http = require('http');
function handle ( req, res ) {
console.log("Handling request");
}
var fw = (function () {
http.createServer( handle ).listen(9000, '127.0.0.1', () => {
console.log("Server running");
});
}());
module.exports = fw;
```
And actually, while writing this post I can see in the console, that it gets triggered again ... minutes later. Something spooky is going in here.
In case you run into the same 'situation':
Normally working in FireFox. At some point I had opened Chrome to use the debugger, and forgot all about it. THAT was the bugger continuously sending requests in the background ... sigh! Yes, web dev IS hard.
So check all possible incoming channels ; )

Execute a middleware one-time only at server startup in Koa v2

I created this middleware which executing only once when any route in the website gets the first hit from a visitor:
// pg-promise
const db = require('./db/pgp').db;
const pgp = require('./db/pgp').pgp;
app.use(async (ctx, next) => {
try {
ctx.db = db;
ctx.pgp = pgp;
} catch (err) {
debugErr(`PGP ERROR: ${err.message}` || err);
}
await next();
});
// One-Time middleware
// https://github.com/expressjs/express/issues/2457
const oneTime = (fn) => {
try {
let done = false;
const res = (ctx, next) => {
if (done === false) {
fn(ctx, next);
done = true;
}
next();
};
return res;
} catch (err) {
debugErr(`oneTime ERROR: ${err.message}` || err);
}
};
const oneTimeQuery = async (ctx) => {
const result = await ctx.db.proc('version', [], a => a.version);
debugLog(result);
};
app.use(oneTime(oneTimeQuery));
This code executing on the first-time only when a user visiting the website, resulting:
app:log Listening on port 3000 +13ms
app:req GET / 200 - 24ms +2s
23:07:15 connect(postgres#postgres)
23:07:15 SELECT * FROM version()
23:07:15 disconnect(postgres#postgres)
app:log PostgreSQL 9.6.2, compiled by Visual C++ build 1800, 64-bit +125ms
My problem is that I want to execute it at the server start, when there's no any visit on the site.
The future purpose of this code will be to check the existence of tables in the database.
Solution:
Placing this in ./bin/www before the const server = http.createServer(app.callback()); declaration helped:
const query = async () => {
const db = require('../db/pgp').db;
const pgp = require('../db/pgp').pgp;
const result = await db.proc('version', [], a => a.version);
debugLog(`www: ${result}`);
pgp.end(); // for immediate app exit, closing the connection pool (synchronous)
};
query();
You could start your application using a js script that requires your app and uses node's native http module to fire up the server. Exactly like in koa-generator (click).
This is in your app.js file:
const app = require('koa')();
...
module.exports = app;
And then this is in your script to fire up the server:
const app = require('./app');
const http = require('http');
[this is the place where you should run your code before server starts]
const server = http.createServer(app.callback());
server.listen(port);
Afterwards you start your application with:
node [script_name].js
Of course keep in mind the async nature of node when doing it this way. What I mean by that - run the 'listen' method on 'server' variable in callback/promise.

NodeJS - How to test index.js without module.exports

I'm testing my NodeJs project using Mocha and I have a file, index.js that is the main file without module.exports that is run like a CLI
index.js
// CLI tools
var bluebird = require('bluebird');
var gigatool = require('./lib/gigatool');
var debug = require('debug')('index');
var size = 20;
var page = process.env.BATCH;
var startDate = process.env.START;
var dataDir = process.env.DATADIR;
debug(page, startDate, dataDir);
// requires parameters
if (!process.env.BATCH) {
throw new Error('BATCH environment variable is needed');
}
tool = gigatool(size, page, dataDir);
bluebird.all([tool.clean(startDate), tool.continuous()])
.finally(function(){
process.exit(0);
});
test.js
'use strict';
var chai = require('chai');
var fs = require('fs');
var noop = require('lodash.noop');
var rimraf = require('rimraf');
var async = require('async');
var rimraf = require('rimraf');
var expect = chai.expect;
describe.only('Integration', function() {
var dataDir = './countries';
var path = dataDir + '/Albania';
describe('clean run', function() {
this.timeout(10000);
before(function() {
process.env.BATCH = 1;
process.env.DEBUG = '*';
require('../../index');
});
after(function(done) {
// rimraf(dataDir, done);
});
});
});
if I run require('./index'), it will run the module and then continue to move forward, how can i wait for it to end before i run test cases?
Note: It is calling some apis
You need to test your whole application at once, this is still testing but hardly "unit" testing unless your code is a unit ("the unix way"). For this reason your code should start with:
var Promise= require("bluebird");
var exec= Promise.promisify(require("child_process").exec);
var run = function(args){
return exec("node", ["../../index.js"].concat(args)).get("stdout");
};
Which would make your tests test the actual inputs on the file:
describe('your code', function() {
it('should work with params a,b', function(){
return run(['a','b']).then(function(out){ // note the Mocha promise syntax
assert.equal(out, 'your expected stdout');
});
});
});
Unfortunately, there is no way to unit test individual aspects of a CLI Node script as you have it. Instead, what I've done in the past is have conditional execution based on whether the script was used via require or called from the command line:
// index.js
var foo = require('foo');
var bar = require('bar');
// ...
// determine if this script is being required as a module or is CLI
var IS_EXECUTING = (require.main === module);
var methods = {
init: function(args) {
methods.auditArgs(args);
methods.doSomeStuff(arg1, arg2);
methods.doOtherStuff();
},
auditArgs: function(args) {/* ... */},
doSomeStuff: function(arg1, arg2) {/* ... */},
// ...
};
// At the bottom we either begin execution or return a function which can
// be called in a test harness when ready...
if (IS_EXECUTING) {
methods.init(process.argv);
} else {
module.exports = function (mockMethods) {
// you could have some code here to mock out provided methods
// for example:
methods.auditArgs = mockMethods.auditArgs || methods.auditArgs;
// then return the "API" for this script...
return methods;
};
}
In your test harness then you would simply require the file and when ready, use it like you would any other module. But when called from the command line the code will just execute normally:
// in test.js
'use strict';
var chai = require('chai');
// ...
var appFactory = require('index');
var expect = chai.expect;
describe('initialization', function() {
var app;
beforeEach(function() {
app = appFactory({
auditArgs = chai.spy(function() { });
// other mock method implementations, spies, etc
});
});
it('should call necessary methods on init', function() {
expect(app.auditArgs).to.have.been.called(1);
// ...
});
});

WebRTC Video sharing app doesn't work

I have been trying to share a video stream between two clients over WebRTC for about a week now and I have no idea how to proceed any further. I'm frustrated and could really use some help from the more experienced. Please help me get this running.
I am using Websockets and NodeJS. I will post all of my code below:
Server Code ( on NodeJS )
"use strict";
/** Requires **/
var webSocketServer = require('websocket').server,
expr = require("express"),
xpress = expr(),
server = require('http').createServer(xpress);
// Configure express
xpress.configure(function() {
xpress.use(expr.static(__dirname + "/public"));
xpress.set("view options", {layout: false});
});
// Handle GET requests to root directory
xpress.get('/', function(req, res) {
res.sendfile(__dirname + '/public/index.html');
});
// WebSocket Server
var wsServer = new webSocketServer({
httpServer: server
});
// Set up the http server
server.listen(8000, function(err) {
if(!err) { console.log("Listening on port 8000"); }
});
var clients = [ ];
/** On connection established */
wsServer.on('request', function(request) {
// Accept connection - you should check 'request.origin' to make sure that client is connecting from your website
var connection = request.accept(null, request.origin);
var self = this;
// We need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
// Event Listener for when Clients send a message to the Server
connection.on('message', function(message) {
var parsedMessage = JSON.parse(message.utf8Data);
if ( parsedMessage.kind == 'senderDescription' ) {
wsServer.broadcastUTF(JSON.stringify({ kind:'callersDescription', callerData: parsedMessage }));
}
});
});
Index.html loads and immediately runs VideoChatApp.js
function VideoChatApp() {
this.connection = null;
this.runConnection();
}
_p = VideoChatApp.prototype;
/** Initialize the connection and sets up the event listeners **/
_p.runConnection = function(){
// To allow event listeners to have access to the correct scope
var self = this;
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
// if browser doesn't support WebSocket, just show some notification and exit
if (!window.WebSocket) { return; }
/** Where to make the connection **/
var host = location.origin.replace(/^http/, 'ws');
console.log(host);
this.connection = new WebSocket(host);
/** Once the connection is established **/
this.connection.onopen = function () {
console.log("Web Socket Connection Established");
self.onConnectionEstablished();
};
/** If there was a problem with the connection */
this.connection.onerror = function (error) {
console.log("ERROR with the connection *sadface*");
};
}; // end runConnection
_p.onConnectionEstablished = function() {
// My connection to the nodejs server
var websocketConnection = this.connection;
// Some local variables for use later
var mediaConstraints = {
optional: [],
mandatory: {
OfferToReceiveVideo: true
}
};
var offerer, answerer;
this.theLocalStream = null;
var amITheCaller = false;
var localVideoTag = document.getElementById('localVideoTag');
var remoteVideoTag = document.getElementById('remoteVideoTag');
window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
window.URL = window.webkitURL || window.URL;
window.iceServers = {
iceServers: [{
url: 'stun:23.21.150.121'
}]
};
var callButton = document.getElementById("callButton");
callButton.onclick = callClicked;
function callClicked() {
amITheCaller = true;
setUpOffer();
}
offerer = new RTCPeerConnection(window.iceServers);
answerer = new RTCPeerConnection(window.iceServers);
/** Start Here - Set up my local stream **/
getUserMedia(function (stream) {
hookUpLocalStream(stream);
});
function getUserMedia(callback) {
navigator.getUserMedia({
video: true
}, callback, onerror);
function onerror(e) {
console.error(e);
}
}
function hookUpLocalStream(localStream) {
this.theLocalStream = localStream;
callButton.disabled = false;
localVideoTag.src = URL.createObjectURL(localStream);
localVideoTag.play();
};
/* When you click call, then we come here. Here I want to set up the offer and send it. */
function setUpOffer() {
var stream = theLocalStream;
offerer.addStream(stream);
offerer.onaddstream = function (event) {
console.log("onaddstream callback was called");
};
offerer.onicecandidate = function (event) {
if (!event || !event.candidate) return;
answerer.addIceCandidate(event.candidate);
};
offerer.createOffer(function (offer) {
offerer.setLocalDescription(offer);
console.log("------------------- What I am sending: -------------------------");
console.log(offer);
console.log(stream);
console.log("-----------------------------------------------------------------\n");
var jsonMsg = JSON.stringify( {kind:'senderDescription', streamInfo: offer, theStream: stream} );
websocketConnection.send( jsonMsg );
//answererPeer(offer, stream);
}, onSdpError, mediaConstraints);
}
/* Respond to a call */
function answererPeer(offer, stream) {
answerer.addStream(stream);
answerer.onaddstream = function (event) {
remoteVideoTag.src = URL.createObjectURL(event.stream);
remoteVideoTag.play();
};
answerer.onicecandidate = function (event) {
if (!event || !event.candidate) return;
offerer.addIceCandidate(event.candidate);
};
answerer.setRemoteDescription(offer, onSdpSucces, onSdpError);
answerer.createAnswer(function (answer) {
answerer.setLocalDescription(answer);
offerer.setRemoteDescription(answer, onSdpSucces, onSdpError);
}, onSdpError, mediaConstraints);
}
function onSdpError(e) {
console.error('onSdpError', e);
}
function onSdpSucces() {
console.log('onSdpSucces');
}
websocketConnection.onmessage = function (messageFromServer) {
console.log(" ------------------------ Message from server: -------------------- ");
var parsedMessage = JSON.parse(messageFromServer.data);
if(parsedMessage.callerData.kind = "senderDescription") {
console.log("Received a senderDescription");
console.log(parsedMessage.callerData.streamInfo);
console.log(parsedMessage.callerData.theStream);
console.log("-------------------------------------------------------------------\n");
answererPeer(parsedMessage.callerData.streamInfo, parsedMessage.callerData.theStream);
}
};
};// end onConnectionEstablished()
Finally, here are my errors:
I am not sure if this is still interesting for you, but I have some very good experience with WebRTC using PeerJS as a wrapper around it. It takes care of all the stuff you don't want to do (http://peerjs.com/). There is the client library as well as a very nice signaling server for nodejs (https://github.com/peers/peerjs-server). You can easy extend this server in your own node server.
This may not explain why your approach failed, but gets WebRTC running easily.
You can start with code that is already working and completely open. Check out easyrtc.com we have a client api, signalling server and working code. And if you have problems with that code ask us for help on Google Groups for easyrtc.

Resources