How to seperate websocket/express server into different files? - javascript

I am having trouble wrapping my head around the best way to execute some code below from a separate file. I have it running well in my my app.js but obviously, I do not want to keep a bunch of code in only app.js and want to start moving things into other files but am at a loss on how to do so. My app.js more or less looks like this and is extremely bloated:
//web server
const express = require('express');
const app = express();
const port = 3000;
//raspberryPI
const { ChatClient } = require("dank-twitch-irc");
const { yay } = require('./JavaScript/yay');
const { readPin } = require('./JavaScript/readPin');
let ejs = require('ejs');
//listen on port 3000
app.listen(port, () => console.info('Listening on port', { port }));
app.use(express.static('./'))
app.set('view engine', 'ejs');
app.get('/', (req, res) => {
res.render('index');
})
//Setup raspberry pi stepper motor hat
let spec = {
steppers: [{ W1: 'M1', W2: 'M2' }, { W1: 'M3', W2: 'M4'}],
//steppers: [{ W1: 'M3', W2: 'M4' }],
};
const motorHat = require('motor-hat')(spec);
motorHat.init();
motorHat.steppers[0].setSteps(2048);
motorHat.steppers[0].setSpeed({ rpm: 5 });
let client = new ChatClient({
connection: {
type: "websocket",
secure: true,
}
});
//connected?
client.on("ready", () => console.log("Successfully connected to chat"));
client.on("close", (error) => {
if (error != null) {
console.error("Client closed due to error", error);
}
});
const keywordsList = [];
client.on("PRIVMSG", (msg, error) => {
console.log(`[#${msg.channelName}] ${msg.displayName}: ${msg.messageText}`);
const keywordFinder = /\b(^right)|(^left)|(^fire)\b/iy;
//empty array fixes null value
const keywords = msg.messageText.match(keywordFinder) || [];
if (msg.messageText === keywords[0]){
if (keywordsList.length>10){
keywordsList.shift();
}
keywordsList.push(`${msg.displayName}: ${msg.messageText}`);
console.log(keywordsList);
}
// keywordsList.forEach(keywords)
if (msg.messageText === "right") {
motorHat.steppers[0].stepSync('fwd', 12);
}
if (msg.messageText === "left") {
motorHat.steppers[0].stepSync('back', 12);
}
if (msg.messageText === "yay") {
yay();
}
if (msg.messageText === "pin") {
readPin();
}
if (error != null) {
console.error("Client closed due to error", error);
}
});
//connect to specific Twitch chat
client.connect();
client.join("yuhn");
and the code I am having trouble removing is this (sorry if redundant):
const keywordsList = [];
client.on("PRIVMSG", (msg, error) => {
console.log(`[#${msg.channelName}] ${msg.displayName}: ${msg.messageText}`);t
const keywordFinder = /\b(^right)|(^left)|(^fire)\b/iy;
//empty array fixes null value
const keywords = msg.messageText.match(keywordFinder) || [];
if (msg.messageText === keywords[0]){
if (keywordsList.length>10){
keywordsList.shift();
}
keywordsList.push(`${msg.displayName}: ${msg.messageText}`);
console.log(keywordsList);
}
// keywordsList.forEach(keywords)
if (msg.messageText === "right") {
motorHat.steppers[0].stepSync('fwd', 12);
}
if (msg.messageText === "left") {
motorHat.steppers[0].stepSync('back', 12);
}
if (msg.messageText === "yay") {
yay();
}
if (msg.messageText === "pin") {
readPin();
}
if (error != null) {
console.error("Client closed due to error", error);
}
});
What I thought I could do is just put the code in a separate file, make sure all my modules are connected (export client and import it into the separate files along with motorhat, etc) but it does not run. Any and all help is appreciated. I have attempted to read about this for a few hours now and I keep getting in depth guides to routing/express file structure...which is fine but I need to know if that's the direction to go in and if so, the first steps.

With my current understanding of your code, I would start by creating a file that contains the callback of your websocket event:
ws_callbacks.js
const motorHat = require('./main.js').motorHat
const keywordsList = [];
exports.PRIVMSG_callback = (msg, error) => {
console.log(`[#${msg.channelName}] ${msg.displayName}: ${msg.messageText}`);t
const keywordFinder = /\b(^right)|(^left)|(^fire)\b/iy;
//empty array fixes null value
const keywords = msg.messageText.match(keywordFinder) || [];
// Rest of your code
}
You can then access the callback by requiring ws_callbacks.js in your main file as so:
app.js
// ...
motorHat.init();
motorHat.steppers[0].setSteps(2048);
motorHat.steppers[0].setSpeed({ rpm: 5 });
exports.motorHat = motorHat
// ...
client.on("PRIVMSG", require('./ws_callbacks.js').PRIVMSG_callback )
// ...
Here, The callback accesses motorHat, which is why it is being exported within the app.js file.
Note that you can also export the other websocket callbacks (ready and close) in similar fashion.
If you prefer to keep all websocket-related code in a separate file, this is how I would write it:
chat.js
const { ChatClient } = require("dank-twitch-irc");
let client = new ChatClient({
connection: {
type: "websocket",
secure: true,
}
});
//connected?
client.on("ready", () => console.log("Successfully connected to chat"));
client.on("close", (error) => {
if (error != null) {
console.error("Client closed due to error", error);
}
});
client.on("PRIVMSG", require('./ws_callbacks.js').PRIVMSG_callback )
// Exporting client in case you want to use it somewhere else
exports.client = client
And then import it in app.js.
However, you have mentioned experiencing problems with importing and exporting client so I am unsure if this will work

Related

TypeError deleting a file with gridfs mongoose and node.js

I am developing an application that allows uploading and downloading music.
I can upload files, send them to the client... however, I have problems when it comes to deleting a bucket file...
I'am using "mongoose": "^6.2.1".
My controller, where podcastId is a ObjectId:
const connection = require('../database')
const mongoose = require('mongoose')
const Users = require('../models/Users')
const PodcastInfo = require('../models/PodcastInfo')
ctrPod.deletePodcast = async (req, res, next) => {
try {
const id = req.params.idPodInfo
const info = await PodcastInfo.findById(id)
const { userId, podcastId } = info
const gridFsBucket = new mongoose.mongo.GridFSBucket(connection, {
bucketName: 'podcasts',
});
gridFsBucket.delete(podcastId, (err) => {
console.log(err)
})
.
.
.
I get this error:
TypeError: Cannot use 'in' operator to search for 'client' in undefined
at getTopology
The problem appears here, \node_modules\mongodb\lib\utils.js:363:23) :
function getTopology(provider) {
if (`topology` in provider && provider.topology) {
return provider.topology;
}
else if ('client' in provider.s && provider.s.client.topology) {
return provider.s.client.topology;
}
else if ('db' in provider.s && provider.s.db.s.client.topology) {
return provider.s.db.s.client.topology;
}
throw new error_1.MongoNotConnectedError('MongoClient must be connected to perform this operation');
}
////////////////////////
delete(id, callback) {
return (0, utils_1.executeLegacyOperation)((0, utils_1.getTopology)(this.s.db), _delete, [this, id, callback], {
skipSessions: true
});
}
/////////////////////////////////////
What am I doing wrong?
I think the problem lies here:
const gridFsBucket = new mongoose.mongo.GridFSBucket(connection, {
bucketName: 'podcasts',
});
new mongoose.mongo.GridFSBucket(db,{bucketName}) takes in a db not a connection. Try:
const gridFsBucket = new mongoose.mongo.GridFSBucket(connection.db, {
bucketName: 'podcasts',
});

Module's function is not being copied

I have defined a function in a module named server.js and want to use it in sign-in.js. server.js exports only one function-emailExists.
module.exports.emailExists = emailExists;
So I import it using:
const server = require("./../server");
and wish to use as
const emailExists = server.emailExists;// but emailExists is undefined
console.log("emailExists is ", typeof emailExists);// result undefined
but
the below is working as expected
console.log("server.emailExists is ", typeof server.emailExists); // returns function
I thought maybe functions are not copied or referenced, but the below code works as i want the above example to work
//this works as expected
function x() {
console.log("from x");
}
const y = x;
console.log("y is", typeof y); // logs y is function
y(); // returns "from x"
I am not sure whether the difference in these two examples is because in first case function is being copied/ referenced from another module and in the second case, function from the same file is bieng copied/ referenced, as few days ago i created new project just to check this and fuction from other module was being copied, i.e. the below was working.
const y= require("./mod2.js").x //x is function in mod2
console.log(typeof y)// returned fucntion
server.js
const express = require("express");
const path = require("path");
const multer = require("multer");
const db = require("./server/database/index.js");
const http = require("http");
const app = express();
const PORT = 3000;
const router = require("./routes/sign-in.js");
app.use(express.static("./"));
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "./index.html"));
});
const upload = multer();
let OTP;
app.post("/sendOTP", upload.none(), async (req, res) => {
OTP = Math.ceil(Math.random() * 100000);
http
.get(
`http://myactualdomain.com:portthatiamusing/${OTP}/${req.body.email}`,
async (response) => {
console.log("got response from linux server");
let existingEmail = await emailExists(req.body.email);
if (existingEmail === true) {
res.json(existingEmail);
} else if (existingEmail === false) {
res.json(existingEmail);
} else {
res.json(await emailExists(req.body.email));
}
}
)
.on("error", (err) => {
res.json({ err });
});
});
async function emailExists(email) {
let sqlResponse = await db
.emailExists(email)
.then((data) => {
if (Object.keys(data).length === 0) {
return { exists: false };
} else {
return { exists: true };
}
})
.catch((err) => {
throw new Error("Error which checking if Email exists in MySQL");
});
return JSON.parse(sqlResponse.exists);
}
app.use(
"/",
(req, res, next) => {
req.OTP = OTP;
next();
},
router()
);
app.listen(PORT);
module.exports.emailExists = emailExists;
sign-in.js
const express = require("express");
const router = express.Router();
const multer = require("multer");
const upload = multer();
const db = require("../server/database/index.js");
const server = require("./../server");
const validateOTP = require("./validateOTP.js");
const emailExists = server.emailExists;
module.exports = () => {
router.use("/validateOTP", validateOTP());
router.post("/sign-in", upload.none(), async (req, res) => {
if (await server.emailExists(req.body.email)) {
let correctPassword = await db
.retreivePassword(req.body.email)
.then((password) => {
if (password[0].password === req.body.password) {
res.json({ successful: true });
} else {
res.json({ successful: false });
}
})
.catch((err) => {
res.json(err);
throw "Error checking password from MySql";
});
} else {
res.json({ successful: "email doesn't exist" });
}
//this works as expected
console.log("server.emailExists is ", typeof server.emailExists);
//this does not works as expected. expeted value function, actual value undefined
console.log("emailExists is ", typeof emailExists);
//this works as expected
function x() {
console.log("from x");
}
const y = x;
console.log("y is", typeof y); // logs y is function
y(); // returns "from x"
});
return router;
};

Why Math.js default takes multiply as operator when calculation an expression?

//Require module
const express = require('express');
const { evaluate, compile, parse } = require('mathjs');
// Express Initialize
const app = express();
const port = 8000;
app.listen(port, () => {
console.log('listen port 8000');
})
//create api
app.get('/hello_world', (req, res) => {
const expression = "A B A";
console.log(expression.length);
let response;
const scope = {
A: 5,
B: 4
}
try {
const parsedExp = parse(expression);
const compiled = parsedExp.compile();
const result = compiled.evaluate(scope);
response = {
"expression": parsedExp.toString(),
"variables": parsedExp.args,
"result": result
}
console.log("success");
res.send(JSON.stringify(response));
} catch (error) {
console.log(error);
res.send(JSON.stringify(error));
}
})
The code and calculation are working fine. but it's taking multiply by default. Is there a way we can stop this default behavior and throw an error message to the user that please enter your desired operator?
I tried even with normal javascript code by splitting with space and tried to check if of +,-,*,/,^ operator but the user can still give multiple spaces then writes another variable
Help appreciated
There is currently no option to disable implicit multiplication, but there is a (currently open) github issue for that. And in the comments of that issue there is a workaround to find any implicit multiplications and throw an error if one is found.
try {
const parsedExp = parse(expression);
parsedExp.traverse((node, path, parent) => {
if (node.type === 'OperatorNode' && node.op === '*' && node['implicit']) {
throw new Error('Invalid syntax: Implicit multiplication found');
}
});
...
} catch (error) {
console.log(error);
res.send(JSON.stringify(error));
}

How to pull out handler using module exports?

I am building a node application, and trying to neatly organize my code. I wrote a serial module that imports the serial libs and handles the connection. My intention was to write a basic module and then reuse it over and over again in different projects as needed. The only part that changes per use is how the incoming serial data is handled. For this reason I would like to pull out following handler and redefine it as per the project needs. How can I use module exports to redefine only this section of the file?
I have tried added myParser to exports, but that gives me a null and I would be out of scope.
Handler to redefine/change/overload for each new project
myParser.on('data', (data) => {
console.log(data)
//DO SOMETHING WITH DATA
});
Example usage: main.js
const serial = require('./serial');
const dataParser = require('./dataParser');
const serial = require('./serial');
//call connect with CL args
serial.connect(process.argv[2], Number(process.argv[3]))
serial.myParser.on('data',(data) => {
//Do something unique with data
if (dataParser.parse(data) == 0)
serial.send('Error');
});
Full JS Module below serial.js
const SerialPort = require('serialport');
const ReadLine = require('#serialport/parser-readline');
const _d = String.fromCharCode(13); //char EOL
let myPort = null;
let myParser = null;
function connect(port, baud) {
let portName = port || `COM1`;
let baudRate = baud || 115200;
myPort = new SerialPort(portName, {baudRate: baudRate})
myParser = myPort.pipe(new ReadLine({ delimiter: '\n'}))
//Handlers
myPort.on('open', () => {
console.log(`port ${portName} open`)
});
myParser.on('data', (data) => {
console.log(data)
});
myPort.on('close', () => {
console.log(`port ${portName} closed`)
});
myPort.on('error', (err) => {
console.error('port error: ' + err)
});
}
function getPorts() {
let portlist = [];
SerialPort.list((err, ports) => {
ports.forEach(port => {
portlist.push(port.comName)
});
})
return portlist;
}
function send(data) {
myPort.write(JSON.stringify(data) + _d, function (err) {
if (err) {
return console.log('Error on write: ', err.message);
}
console.log(`${data} sent`);
});
}
function close() {
myPort.close();
}
module.exports = {
connect, getPorts, send, close
}
The problem is that a module is used where a class or a factory would be appropriate. myParser cannot exist without connect being called, so it doesn't make sense to make it available as module property, it would be unavailable by default, and multiple connect calls would override it.
It can be a factory:
module.exports = function connect(port, baud) {
let portName = port || `COM1`;
let baudRate = baud || 115200;
let myPort = new SerialPort(portName, {baudRate: baudRate})
let myParser = myPort.pipe(new ReadLine({ delimiter: '\n'}))
//Handlers
myPort.on('open', () => {
console.log(`port ${portName} open`)
});
myParser.on('data', (data) => {
console.log(data)
});
myPort.on('close', () => {
console.log(`port ${portName} closed`)
});
myPort.on('error', (err) => {
console.error('port error: ' + err)
});
function getPorts() {
let portlist = [];
SerialPort.list((err, ports) => {
ports.forEach(port => {
portlist.push(port.comName)
});
})
return portlist;
}
function send(data) {
myPort.write(JSON.stringify(data) + _d, function (err) {
if (err) {
return console.log('Error on write: ', err.message);
}
console.log(`${data} sent`);
});
}
function close() {
myPort.close();
}
return {
myParser, getPorts, send, close
};
}
So it could be used like:
const serial = require('./serial');
const connection = serial(...);
connection.myParser.on('data',(data) => {
//Do something unique with data
if (dataParser.parse(data) == 0)
connection.send('Error');
});

How to make a function that can be called in other functions in Cloud Functions?

I have the following case, when deleting any data, I need to delete the app badges (at the moment I delete them using silent push notication and reduce the app badges number with the cloud function) if the user who sent the request has deleted. But since the user who deleted could send several requests to different users in different places, so I decided that I need to create a function that will be called in firebase database trigger functions and also it will help not to duplicate the same code everywhere .
The function will be approximate such
function adminRemoveAppBadge(userID, dataID, categoryID) {
};
And for example, call it in this function
module.exports = functions.database.ref('/cards/{cardID}/interestedUsers/{interestedUserID}').onWrite(event => {
const currentData = event.data.current;
const prevData = event.data.previous;
const cardID = event.params.cardID;
const interestedUserID = event.params.interestedUserID;
if (currentData.val() && !prevData.val()) {
// value created
return console.log('cardInterestedUserHandler - created');
} else if (!currentData.val() && prevData.val()) {
// value removed
console.log('cardInterestedUserHandler - removed', currentData.val());
const cardRef = admin.database().ref("cards").child(cardID);
const cardRefPromise = cardRef.once("value", function(snap, error) {
if (error) {
return error;
};
if (snap.val()) {
const cardJSON = snap.val();
const cardOwnerID = cardJSON["ownerID"];
if (cardOwnerID) {
const cardOwnerAppBadgesRef = admin.database().ref("userAppBadges").child(cardOwnerID).child("appBadgeModels").orderByChild("dataID").equalTo(cardID);
const cardOwnerAppBadgesRefPromise = cardOwnerAppBadgesRef.once("value", function (cardOwnerAppBadgesRefSnap, error) {
if (error) {
return error;
};
if (cardOwnerAppBadgesRefSnap.val()) {
var deletingPromises = [];
cardOwnerAppBadgesRefSnap.forEach(function(cardOwnerAppBadgesRefSnapChild) {
const appBadgeModelJSON = cardOwnerAppBadgesRefSnapChild.val();
const appBadgeModelID = appBadgeModelJSON["id"];
const senderID = appBadgeModelJSON["senderID"];
if (appBadgeModelID && senderID) {
if (senderID == interestedUserID) {
const cardOwnerAppBadgeRef = admin.database().ref("userAppBadges").child(cardOwnerID).child("appBadgeModels").child(cardOwnerAppBadgeModelID);
const cardOwnerAppBadgeRefPromise = cardOwnerAppBadgeRef.remove();
deletingPromises.push(cardOwnerAppBadgeRefPromise);
// to call
adminRemoveAppBadge
};
} else {
console.log("cardOwnerAppBadgeModelID == null");
};
});
return Promise.all(deletingPromises);
};
});
return Promise.all([cardOwnerAppBadgesRefPromise]);
} else {
return console.log("owner id == null");
};
};
});
return Promise.all([cardRefPromise]);
} else {
return console.log('cardInterestedUserHandler - updated');
};
});
Also functions are in different files. How can I call it in other firebase cloud functions and how do I deploy this function?
Update I tried to do so one of the options as written here and here, but when I tried to do deploy I got an error Cannot find module 'AppBadges/adminRemoveAppBadge.js'.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.adminRemoveAppBadge = function (userID, dataID, categoryID) {
console.log("adminRemoveAppBadge nil");
};
Requested this function so
var adminRemoveAppBadgeModule = require("AppBadges/adminRemoveAppBadge.js");
and call this functions so
adminRemoveAppBadgeModule.adminRemoveAppBadge(cardOwnerID, cardID, 0);
Google Functions are just JS - so normal routes to include code work.
I place my "library" functions in a folder /lib
So my functions folder looks like this:
/functions
/lib
BuildImage.js
SendEmail.js
index.js
package.json
...etc...
within my index.js I just include my code:
const SendMail = require('./lib/SendMail')
const sendMail = new SendMail({
database: db,
mailgun: mailgun
})
exports.sendContactUsMessage = functions.database.ref('/contact-messages/{itemId}').onWrite(sendMail.send(event))
EDIT Added /lib/SendMail.js code:
module.exports = class SendMail {
constructor(config) {
if (!config) {
throw new Error ('config is empty. Must pass database and mailgun settings')
}
if (!config.database) {
throw new Error('config.database is empty. Must pass config.database')
}
if (!config.mailgun) {
throw 'config.mailgun is empty. Must pass config.mailgun'
}
this.database = config.database
this.mailgun = config.mailgun
}
sendEmail (emailData) {
return new Promise((resolve, reject) => {
this.mailgun.messages().send(emailData, (error, body) => {
if (error) {
if (debug) {
console.log(error)
}
reject(error)
} else {
if (debug) {
console.log(body)
}
resolve(body)
}
})
})
}
...
}

Categories