I have an app that, when it is starting, loads a bunch of files from a directory in the underlying OS, after this it presents a page (generated with PUG, if it makes any difference) that shows the the list of loaded files to the user and if one is clicked it shows the contents. One of the buttons on the page allows the user to create a new file (by spawning a system command based on the user's input).
My question is how do I reload the main page after the new file is created so that the web page now shows the new file that was added. In the future there will be a feature to delete a file as well, so I will also need to reload after this to show that the file has been deleted.
TIA for any assistance.
Code is short enough that I can include it here (please let me know if I am doing this the long difficult way, I am jut learning)
index.js
const express = require('express')
const app = express();
const port = 8000;
app.use(express.json());
app.use(express.static(__dirname + "/public"));
app.set('view engine', 'pug');
const { exec } = require('child_process');
// Read profile names
const profileFolder = '/home/pi/configs/';
const fs = require('fs');
var profiles = [];
function init() {
profiles = fs.readdirSync(profileFolder);
for (var i = 0; i < profiles.length; i++) {
try {
profile = fs.readFileSync(profileFolder + profiles[i], 'utf8');
profiles[i] = {
pname: profiles[i].split('.')[0],
profile: profile
};
}
catch (err) {
console.error(err);
}
};
}
app.get('/', function(req, res) {
init();
res.render(__dirname + '/views/index.pug', {vpnprofiles: profiles});
});
app.get('/addnew', function(req, res) {
let pname = req.query.newprofile;
console.log(`Adding new profile: ${pname}`);
exec("pivpn -a -n "+pname, (error, stdout, stderr) => {
if (error) {
console.log(`ERROR: ${error.message}`);
return;
}
if (stderr) {
console.log(`STDERR: ${stderr}`);
return;
}
console.log(`${stdout}`);
res.send("Done!");
});
return;
});
app.listen(port, () => {
console.log(`Server started on port ${port}!`)
});
/public/script.js
function genQRcode(p, n) {
const nl = '\n';
const rep = '<br>'
document.getElementById("qrcode").innerHTML = "";
document.getElementById("profile").innerHTML = "";
document.getElementById("title").innerHTML = "Profile Details (" + n + ")";
new QRCode(document.getElementById("qrcode"), p);
document.getElementById("profile").style.padding = "10px";
document.getElementById("profile").innerHTML = p.replaceAll(nl, rep);
}
// Create a new profile
function addn(pn) {
console.log(pn);
var newname = prompt("Enter profile name:", "");
if ((newname != null) && (newname != "")) {
var found = false;
for (var i = 0; i < pn.length; i++) {
if (pn[i]['pname'] == newname) {
found = true;
}
}
if (!found) {
fetch(`/addnew?newprofile=${newname}`).then((messages) => {console.log("messages");});
}
else { alert("profile already exists"); }
}
window.location = "http://192.168.1.3:8000/";
window.location.reload(true);
//return failse;
}
and finally the pug file
doctype html
html
head
meta(charset='utf-8')
title PiVPN Profiles
script !{addnew}
script(src='../scripts/qrcode.js')
script(src='../scripts/script.js')
link(rel="stylesheet", type="text/css", href="../styles/style.css")
body
.grid
.box.nav
a(href='/') Home
.btn(onclick=`addn(${JSON.stringify(vpnprofiles)});`) New Profile
.box.sidebar
h2 Profiles
ul
each prof in vpnprofiles
li(onclick=`genQRcode(${JSON.stringify(prof.profile)},${JSON.stringify(prof.pname)})`)=prof.pname
.box.content
h1#title Profile Details
p.qrcode#qrcode
p.profile#profile
.box.footer
p.small Version: 0.1a
Maybe I am reading this wrong, but can you make use of window.location = 'yourpage' (a client-side JS solution) or similar page refresh techniques?
Related
i have a problem, my Command Handler only recognize the top Folder inside my Commands Directory. Its supposed to show all of the available Folder in Commands Directory but it only showed the 'test' category which is the top one. any help would be really appreciated.
Folder/Directory Construction:
console.log output:
Command Handler Code:
const {readdirSync} = require('fs');
const ascii = require('ascii-table');
let table = new ascii("Commands");
table.setHeading('Category', 'Command', ' Load status');
var logged = false;
const path = require('node:path')
module.exports = (client) => {
readdirSync('./Commands/').forEach(dir => {
var commands = readdirSync(`./Commands/${dir}/`).filter(file => file.endsWith('.js'));
for(let file of commands){
let pull = require(`../Commands/${dir}/${file}`);
if(pull.name){
client.commands.set(pull.name, pull);
table.addRow(dir,file,'✔️ -> Command Loaded')
} else {
table.addRow(dir,file,'❌ -> Command Error')
continue;
}
if(pull.aliases && Array.isArray(pull.aliases)) pull.aliases.forEach(alias => client.aliases.set(alias, pull.name))
}
if(!logged) {
console.log(table.toString())
console.log(`[Command] Command Handler is Ready! | Total Commands: ${commands.length}`)
logged = true
}
});
}
I believe you are overwriting the commands variable after each folder has been looped through. Try this:
const {readdirSync} = require('fs');
const ascii = require('ascii-table');
let table = new ascii("Commands");
table.setHeading('Category', 'Command', ' Load status');
var logged = false;
const path = require('node:path')
module.exports = (client) => {
readdirSync('./Commands/').forEach(dir => {
var commands = []
commands.push(readdirSync(`./Commands/${dir}/`).filter(file => file.endsWith('.js')));
for(let file of commands){
let pull = require(`../Commands/${dir}/${file}`);
if(pull.name){
client.commands.set(pull.name, pull);
table.addRow(dir,file,'✔️ -> Command Loaded')
} else {
table.addRow(dir,file,'❌ -> Command Error')
continue;
}
if(pull.aliases && Array.isArray(pull.aliases)) pull.aliases.forEach(alias => client.aliases.set(alias, pull.name))
}
if(!logged) {
console.log(table.toString())
console.log(`[Command] Command Handler is Ready! | Total Commands: ${commands.length}`)
logged = true
}
});
}
If this doesn't help than it might still be that issue I referred to above but the edits I made might not be compatible with your code.
I always get an error message after adding the reload command.
Without the Reload Command, the bot works with all commands without any problems
Without the Reload Command, the bot works with all commands without any problems
I just can't find the mistake
The error message:
undefined commands found.
/home/runner/Discord-BreakpointBot/index.js:19
delete require.cache[require.resolve(`./commands/${f}`)];
^
TypeError: require.resolve is not a function
Code:
function loadCmds () {
fs.readdir('./commands/', (err, files) => {
if(err) console.error(err);
var jsfiles = files.filter(f => f.split('.').pop() === 'js');
if (jsfiles.length <=0) {return console.log('No commands found...')}
else {console.log(jsfiles.lenght + ' commands found.')}
jsfiles.forEach((f,i) => {
delete require.cache[require.resolve(`./commands/${f}`)];
var cmds = require(`./commands/${f}`);
console.log(`Command ${f} loading...`);
bot.commands.set(cmds.config.command, cmds);
})
})
}
bot.on('message', message => {
var sender = message.author;
var msg = message.content.toUpperCase();
var prefix ='>'
var cont = message.content.slice(prefix.length).split(" ");
var args = cont.slice(1);
if (!message.content.startsWith(prefix)) return;
var cmd = bot.commands.get(cont[0])
if (cmd) cmd.run(bot, message, args);
if (msg === prefix + 'RELOAD') {
message.channel.send({embed:{description:"All Commands Reloaded"}})
message.channel.send('All Commands Reloaded')
loadCmds()
}
});
loadCmds();
// Bot Launched
bot.on('ready', () => {
console.log('Bot Launched...')
bot.user.setStatus('Online')
bot.user.setActivity('https://www.twitch.tv');
});
I hope someone can help, thanks
I'm not 100% sure what causes your error but I can offer you my method of retrieving comamnds, which works pretty well:
const fileArray = [];
function readCommands(dir) {
const __dirname = rootDir;
// Read out all command files
const files = fs.readdirSync(path.join(__dirname, dir));
// Loop through all the files in ./commands
for (const file of files) {
// Get the status of 'file' (is it a file or directory?)
const stat = fs.lstatSync(path.join(__dirname, dir, file));
// If the 'file' is a directory, call the 'readCommands' function
// again with the path of the subdirectory
if (stat.isDirectory()) {
readCommands(path.join(dir, file));
}
else {
const fileDir = dir.replace('\\', '/');
fileArray.push(fileDir + '/' + file);
// fs.readdirSync(dir).filter(cmdFile => cmdFile.endsWith('.js'));
}
}
}
readCommands('commands');
for(const file of fileArray) {
const command = require(`../${file}`);
if(command.name) {
client.commands.set(command.name, command);
}
else {
continue;
}
}
This will recursively search the folder you specify when calling the function and then store it in fileArray.
This solution is more an alternative than an exact solution to the error you are experiencing
So I'm working on an electron app that should launch an external application when a button is pressed. It works, but if I close that app and press the button again, it'll start several instances of the process. Here's the code:
ipcMain.on("run_crystal", () => {
var cp = require("child_process");
executablePath = crystal + "\\CrystalDM\\Server\\CrystalDMServer.exe";
var Server_child = cp.spawn(executablePath);
executablePath = crystal + "\\CrystalDM\\Game\\CrystalDM.exe";
var parameters = ["test"];
var options = {cwd:crystal+"\\CrystalDM\\Game"};
console.log("a intrat in start game si urmeaza sa ruleze " + executablePath)
var Game_child = cp.execFile(executablePath, parameters, options, (error, stdout, stderr) =>{
console.log(stdout)
Game_child.kill("SIGINT");
Server_child.kill("SIGINT");
delete Game_child;
delete Server_child;
delete cp;
});
});
This piece of code might be called multiple time, and you forgot to remove the event listener after things done. I don't know extractly how to fix it, but try this:
ipcMain.once("run_crystal", () => {
[your code here]
});
Or:
ipcMain.on("run_crystal", () => {
[your code here]
ipcMain.removeAllListeners("run_crystal");
});
For now, I used this solution:
ipcMain.on("run_crystal", () => {
if(if_started == 0){
if_started = 1;
var cp = require("child_process");
console.log("mesajul a ajuns");
executablePath = crystal + "\\CrystalDM\\Server\\CrystalDMServer.exe";
var Server_child = cp.spawn(executablePath);
executablePath = crystal + "\\CrystalDM\\Game\\CrystalDM.exe";
var parameters = ["test"];
var options = {cwd:crystal+"\\CrystalDM\\Game"};
var Game_child = cp.execFile(executablePath, parameters, options,
(error, stdout, stderr) =>{
Game_child.kill("SIGINT");
Server_child.kill("SIGINT");
delete Game_child;
delete Server_child;
delete cp;
delete parameters;
delete options;
if_started = 0;
});
}
});
If you don't want to start a process multiple times, you have to keep track of already started processes.
let runningProcesses = {};
ipcMain.on("run_crystal", (event, arg) => {
var cp = require("child_process");
executablePath = crystal + "\\CrystalDM\\Server\\CrystalDMServer.exe";
if (runningProcesses[executablePath]){ // boolean value for a single process or int if you want to allow multiple instances runningProcesses[executablePath] < maxProcessCounts
event.sender.send('process-running', { process: executablePath })
} else {
runningProcesses[executablePath] = true; // boolean value for a single process or int if you want to allow multiple instances
var Server_child = cp.spawn(executablePath);
Server_child.on('close', (code, signal) => {
runningProcesses[executablePath] = false; // or delete runningProcesses[executablePath];
});
event.sender.send('process-started', { process: executablePath })
executablePath = crystal + "\\CrystalDM\\Game\\CrystalDM.exe";
var parameters = ["test"];
var options = {cwd:crystal+"\\CrystalDM\\Game"};
console.log("a intrat in start game si urmeaza sa ruleze " + executablePath)
var Game_child = cp.execFile(executablePath, parameters, options,(error, stdout, stderr) =>{
console.log(stdout)
Game_child.kill("SIGINT");
Server_child.kill("SIGINT");
delete Game_child;
delete Server_child;
delete cp;
});
}
});
I'm running the below node-rdkafka code in Eclipse as Node.js application. This is the sample code from https://blizzard.github.io/node-rdkafka/current/tutorial-producer_.html
I want to run this in a test server and call from iOS Mobile application.
I knew about running node.js app in AWS.
Question I: Is there any other options to run in a free test server environment like Tomcat?
Question II: Even If I am able to run this node.js app in a server, how do i call from a mobile application? Do I need to call producer.on('ready', function(arg) (or) What function i need to call from Mobile app?
var Kafka = require('node-rdkafka');
//console.log(Kafka.features);
//console.log(Kafka.librdkafkaVersion);
var producer = new Kafka.Producer({
'metadata.broker.list': 'localhost:9092',
'dr_cb': true
});
var topicName = 'MyTest';
//logging debug messages, if debug is enabled
producer.on('event.log', function(log) {
console.log(log);
});
//logging all errors
producer.on('event.error', function(err) {
console.error('Error from producer');
console.error(err);
});
//counter to stop this sample after maxMessages are sent
var counter = 0;
var maxMessages = 10;
producer.on('delivery-report', function(err, report) {
console.log('delivery-report: ' + JSON.stringify(report));
counter++;
});
//Wait for the ready event before producing
producer.on('ready', function(arg) {
console.log('producer ready.' + JSON.stringify(arg));
for (var i = 0; i < maxMessages; i++) {
var value = new Buffer('MyProducerTest - value-' +i);
var key = "key-"+i;
// if partition is set to -1, librdkafka will use the default partitioner
var partition = -1;
producer.produce(topicName, partition, value, key);
}
//need to keep polling for a while to ensure the delivery reports are received
var pollLoop = setInterval(function() {
producer.poll();
if (counter === maxMessages) {
clearInterval(pollLoop);
producer.disconnect();
}
}, 1000);
});
/*
producer.on('disconnected', function(arg) {
console.log('producer disconnected. ' + JSON.stringify(arg));
});*/
//starting the producer
producer.connect();
First of all, you need an HTTP server. ExpressJS can be used. Then, just tack on the Express code basically at the end, but move the producer loop into the request route.
So, start with what you had
var Kafka = require('node-rdkafka');
//console.log(Kafka.features);
//console.log(Kafka.librdkafkaVersion);
var producer = new Kafka.Producer({
'metadata.broker.list': 'localhost:9092',
'dr_cb': true
});
var topicName = 'MyTest';
//logging debug messages, if debug is enabled
producer.on('event.log', function(log) {
console.log(log);
});
//logging all errors
producer.on('event.error', function(err) {
console.error('Error from producer');
console.error(err);
});
producer.on('delivery-report', function(err, report) {
console.log('delivery-report: ' + JSON.stringify(report));
counter++;
});
//Wait for the ready event before producing
producer.on('ready', function(arg) {
console.log('producer ready.' + JSON.stringify(arg));
});
producer.on('disconnected', function(arg) {
console.log('producer disconnected. ' + JSON.stringify(arg));
});
//starting the producer
producer.connect();
Then, you can add this in the same file.
var express = require('express')
var app = express()
app.get('/', (req, res) => res.send('Ready to send messages!'))
app.post('/:maxMessages', function (req, res) {
if (req.params.maxMessages) {
var maxMessages = parseInt(req.params.maxMessages);
for (var i = 0; i < maxMessages; i++) {
var value = new Buffer('MyProducerTest - value-' +i);
var key = "key-"+i;
// if partition is set to -1, librdkafka will use the default partitioner
var partition = -1;
producer.produce(topicName, partition, value, key);
} // end for
} // end if
}); // end app.post()
app.listen(3000, () => console.log('Example app listening on port 3000!'))
I don't think the poll loop is necessary since you don't care about the counter anymore.
Now, connect your mobile app to http://<your server IP>:3000/ and send test messages with a POST request to http://<your server IP>:3000/10, for example, and adjust to change the number of messages to send
I might be late on this but this is how I did using promises and found it better than have a time out etc.
const postMessageToPublisher = (req, res) => {
return new Promise((resolve, reject) => {
producer.connect();
producer.setPollInterval(globalConfigs.producerPollingTime);
const actualBody = requestBody.data;
const requestBody = req.body;
const topicName = req.body.topicName;
const key = requestBody.key || uuid();
const partition = requestBody.partition || undefined;
const data = Buffer.from(JSON.stringify(udpatedBody));
/**
* Actual messages are sent here when the producer is ready
*/
producer.on(kafkaEvents.READY, () => {
try {
producer.produce(
topic,
partition,
message,
key // setting key user provided or UUID
);
} catch (error) {
reject(error);
}
});
// Register listener for debug information; only invoked if debug option set in driver_options
producer.on(kafkaEvents.LOG, log => {
logger.info('Producer event log notification for debugging:', log);
});
// Register error listener
producer.on(kafkaEvents.ERROR, err => {
logger.error('Error from producer:' + JSON.stringify(err));
reject(err);
});
// Register delivery report listener
producer.on(kafkaEvents.PUBLISH_ACKNOWLEDGMENT, (err, ackMessage) => {
if (err) {
logger.error(
'Delivery report: Failed sending message ' + ackMessage.value
);
logger.error('and the error is :', err);
reject({ value: ackMessage.value, error: err });
} else {
resolve({
teamName: globalConfigs.TeamNameService,
topicName: ackMessage.topic,
key: ackMessage.key.toString()
});
}
});
});
};
Please note that kafkaEvents contains my constants for the events we listen to and it is just a reference such as kafkaEvents.LOG is same as event.log
and also the calling function is expecting this to a promise and accordingly we user .then(data => 'send your response to user from here') and .catch(error => 'send error response to user
this is how I achieved it using promises
Ok, i am just starting to learn node.js and i am having a little difficulty getting a good grasp on the async nature of it and when/how to use callbacks to get data passed along like i need it.
The concept of what i am trying to build is this. I have a small node.js app that uses the FS package - defined as var fs = require("fs"). The app is responding to localhost:4000 right now. When i hit that url, the app will use fs.readdir() to get all of the virtual host files in the directory that i pass to readdir().
Next, the app loops through those files and parses each one line by line and word by word (quick and dirty for now). I am using fs.readFile() to read the file and then literally doing lines = data.toString().split("\n") and then var words = lines[l].split(" ") to get to the data in the file i need. Each virtual host file looks something like this:
<VirtualHost *:80>
ServerName some-site.local
DocumentRoot "/path/to/the/docroot"
<Directory "/path/to/the/docroot">
Options Indexes FollowSymLinks Includes ExecCGI
AllowOverride All
Require all granted
</Directory>
ErrorLog "logs/some-site.local-error_log"
</VirtualHost>
Here is my main.js file (routes file):
var express = require("express"),
router = express.Router(),
async = require("async"),
directoryReader = require(__dirname + "/../lib/directoryReader");
fileReader = require(__dirname + "/../lib/fileReader");
router.get("/", function(req, res) {
var response = [];
directoryReader.read(function(files) {
async.each(files, function(file, callback) {
fileReader.read(file, function(data) {
if (data.length > 0) {
response.push(data);
}
callback();
});
}, function(err){
if (err) throw err;
res.json(response);
});
});
});
module.exports = router;
My directoryReader.js file:
var fs = require("fs"),
fileReader = require(__dirname + "/../lib/fileReader");
var directoryReader = {
read: function(callback) {
var self = this;
fs.readdir("/etc/apache2/sites-available", function (err, files) {
if (err) throw err;
var output = [];
for(f in files) {
output.push(files[f]);
}
callback(output);
});
}
};
module.exports = directoryReader;
And my fileReader.js file:
var fs = require("fs");
var fileReader = {
read: function(file, callback) {
fs.readFile("/etc/apache2/sites-available/" + file, { encoding: "utf8" }, function (err, data) {
if (err) throw err;
var vHostStats = ["servername", "documentroot", "errorlog"],
lines = data.toString().split("\n"),
output = [];
for(l in lines) {
var words = lines[l].split(" ");
for(w in words) {
if (words[w].toLowerCase() == "<virtualhost") {
var site = {
"servername" : "",
"documentroot" : "",
"errorlog" : "",
"gitbranch" : ""
}
w++;
}
if (vHostStats.indexOf(words[w].toLowerCase()) !== -1) {
var key = words[w].toLowerCase();
w++;
site[key] = words[w];
}
if (words[w].toLowerCase() == "</virtualhost>" && site.documentroot != "") {
w++;
output.push(site);
var cmd = "cd " + site["documentroot"] + " && git rev-parse --abbrev-ref HEAD";
var branch = ...; // get Git branch based on the above command
site["gitbranch"] = branch;
}
}
}
callback(output);
});
}
};
module.exports = fileReader;
All of this code will spit out json. This all works fine, expect for one part. The line in the fileReader.js file:
var branch = ...; // get Git branch based on the above command
I am trying to get this code to run a shell command and get the Git branch based on the document root directory. I then want to take the branch returned and add the value to the gitbranch proptery of the current site object during the loop. Hope this makes sense. I know there are probably questions on SO that cover something similar to this and i have looked at many of them. I fear i am just not educated enough in node.js yet to apply the answers to those SO questions to my particular use case.
Please let me know if there's anything i can add that can help anyoe answer this question. I should note that this app is for personal uses only, so the solution really just has to work, not be super elegant.
UPDATE: (5/1/2015)
Probably not the best solution but i got what i wanted by using the new execSync added to v0.12.x
if (words[w].toLowerCase() == "</virtualhost>" && site.documentroot != "") {
var cmd = "cd " + site["documentroot"] + " && git rev-parse --abbrev-ref HEAD";
var branch = sh(cmd, { encoding: "utf8" });
site["gitbranch"] = branch.toString().trim();
w++;
output.push(site);
}