Let NodeJS application update itself using NPM - javascript

Hej There,
I'm trying to add some non-conventional functionality to my NodeJS application but I'm having some trouble.
What I'm trying to do is the following:
I want to update my server code from the client. (An auto-update functionality if you will.)
My first attempt was to utilize the NPM API and run:
npm.commands.install([package], function(err, data)
But of course this results in an error telling me NPM can not install while the server is running.
My second attempt was spawning NPM update using the following code:
spawnProcess('npm', ['update'], { cwd: projectPath }, done);
The spawnProcess function is a generic spawn function:
var projectPath = path.resolve(process.cwd());
var spawnProcess = function(command, args, options, callback) {
var spawn = require('child_process').spawn;
var process = spawn(command, args, options);
var err = false;
process.stdout.on('data', function(data) {
console.log('stdout', data.toString());
});
process.stderr.on('data', function(data) {
err = true;
console.log('stderr', data.toString());
});
if (typeof callback === 'function') {
process.on('exit', function() {
if (!err) {
return callback();
}
});
}
};
But this gives me a stderr followed by a 'CreateProcessW: can not find file' error.
I don't quite know what I'm doing wrong.
If all else fails I thought it might be possible to write a shellscript killing Node, updating the application and then rebooting it. Something like:
kill -9 45728
npm update
node server
But I don't know if this is a plausible solution and how I would go about executing it from my node server.
I'd rather have the spawn function working of course.
Any help is welcome.
Thanks in advance!

So I finally fixed this issue. If someone is interested how I did it, this is how:
I built a function using the NPM api to check if the current version is up to date:
exports.checkVersion = function(req, res) {
npm.load([], function (err, npm) {
npm.commands.search(["mediacenterjs"], function(err, data){
if (err){
console.log('NPM search error ' + err);
return;
} else{
var currentInfo = checkCurrentVersion();
for (var key in data) {
var obj = data[key];
if(obj.name === 'mediacenterjs' && obj.version > currentInfo.version){
var message = 'New version '+obj.version+' Available';
res.json(message);
}
}
}
});
});
}
var checkCurrentVersion = function(){
var info = {};
var data = fs.readFileSync('./package.json' , 'utf8');
try{
info = JSON.parse(data);
}catch(e){
console.log('JSON Parse Error', e);
}
return info;
};
If the version is not up to date I initiate a download (in my case the github master repo url) using node-wget:
var wget = require('wget');
var download = wget.download(src, output, options);
download.on('error', function(err) {
console.log('Error', err);
callback(output);
});
download.on('end', function(output) {
console.log(output);
callback(output);
});
download.on('progress', function(progress) {
console.log(progress * 100);
});
The callback kicks off the unzip function bases on #John Munsch 'Self help' script but I added a check to see if there was a previous unzip attempt and if so, I delete the folder:
if(fs.existsSync(dir) === false){
fs.mkdirSync(dir);
} else {
rimraf(dir, function (err) {
if(err) {
console.log('Error removing temp folder', err);
} else {
fileHandler.downloadFile(src, output, options, function(output){
console.log('Done', output);
unzip(req, res, output, dir);
});
}
});
}
console.log("Unzipping New Version...");
var AdmZip = require("adm-zip");
var zip = new AdmZip(output);
zip.extractAllTo(dir, true);
fs.openSync('./configuration/update.js', 'w');
The openSync function kicks off my 'NodeMon' based file (https://github.com/jansmolders86/mediacenterjs/blob/master/server.js) which kills the server because it is listing for changes to that specific file. Finally it restart and starts the following functions:
function installUpdate(output, dir){
console.log('Installing update...');
var fsExtra = require("fs.extra");
fsExtra.copy(dir+'/mediacenterjs-master', './', function (err) {
if (err) {
console.error('Error', err);
} else {
console.log("success!");
cleanUp(output, dir);
}
});
}
function cleanUp(output, dir) {
console.log('Cleanup...');
var rimraf = require('rimraf');
rimraf(dir, function (e) {
if(e) {
console.log('Error removing module', e .red);
}
});
if(fs.existsSync(output) === true){
fs.unlinkSync(output);
console.log('Done, restarting server...')
server.start();
}
}
Thanks to everyone that helped me out!

Untested!
Have you tried the "prestart" npm script handle? Of course this would mean that you'd use npm to run your server: npm start
In your package.json (if you have) :
{ "scripts" :
{
"start" : "node server.js",
"prestart" : "scripts/customScript.js"
}
}
NPM start command is implicitly defaulted to "start": "node server.js". If your server script is called server.js, there is no need to include this. You can do your npm install on the customScript.js or might be able to call npm install directly though I haven't tested this.
You can also assign/read your handler using the environment variable process.env.npm_package_scripts_prestart

It doesn't use NPM to accomplish it but here's a working experiment I put together a while back because I wanted to be able to build a self-updating app using Node.js for installation on people's machines (think apps like SABnzbd+, Couch Potato, or the Plex Media Server).
The Github for the example code is here: https://github.com/JohnMunsch/selfhelp
I got the example to the point where it would automatically notify you when a new version was available and then download, update, and restart itself. Even if you don't end up using it as-is, you should be able to get some ideas from it.

Related

Error: ENOENT: no such file or directory, uv_chdir at process.chdir when creating a directory and changing into it

I'm trying to write a small app that installs some files and modules in a new folder, but I keep getting this error:
{ Error: ENOENT: no such file or directory, uv_chdir
at process.chdir (/home/aboardwithabag/LaunchProject/node_modules/graceful-fs/polyfills.js:20:9)
at cd (/home/aboardwithabag/LaunchProject/index.js:26:13)
Below is my code. Can someone help me out?
// node LaunchProject projectName
// Installs a server, node modules, and index page.
// not working due to issues with chdir.
const cp = require('child_process');
const fse = require('fs-extra');
// const path = require('path');
const project = process.argv[2];
let server ="";
let home = "";
function make (cb){
fse.mkdirs(project, function(err){
if (err){
console.error(err);
}
});
cb;
}
function cd(cb){
try{
process.chdir('/'+project);
cb;
} catch (err) {
console.error(err);
return;
}}
function install(cb){
cp.exec('npm install express', function(err){
if (err){
console.error(err);
} else {
console.log('Express Installed.');
cp.exec('npm install ejs', function(err){
if (err){
console.error(err);
} else{
console.log('Ejs Installed.');
fse.outputFile('index.js', server);
fse.outputFile('public/index.html', home);
}});
}
});
cb;
}
make(cd(install(console.log(project + ' created.'))));
unless the folder name you assign to the project variable (in this case it seems to be "uv_chdir") is located at the root folder of your HDD, below line will give the error:
process.chdir('/'+project);
make sure you give correct path to the program arguments. (in this case argv[2])
Or you may remove the leading '/' and make the path relative.
It seems there are some issues with this code.
cb callbacks provided as function arguments need to be called not after the async calls, but inside the callbacks of these calls. For example:
function make (cb){
fse.mkdirs(project, function(err){
if (err){
console.error(err);
}
cb();
});
}
The last call chain make(cd(install(console.log(project + ' created.')))); would work only with sync calls in reversed order and only if they returned needed callbacks.
That is why your new dir is not ready when you try to use it: your async functions do not actually wait for each other.
You do not call your callbacks as cb(), just mention them as cb. You should call them.
With minimal changess, your code can be refactored in this way:
'use strict';
const cp = require('child_process');
const fse = require('fs-extra');
const project = process.argv[2];
let server = '';
let home = '';
make(cd, install, () => { console.log(project + ' created.'); });
function make(cb1, cb2, cb3) {
fse.mkdirs(project, (err) => {
if (err) {
console.error(err);
}
cb1(cb2, cb3);
});
}
function cd(cb1, cb2) {
try {
process.chdir('/' + project);
cb1(cb2);
} catch (err) {
console.error(err);
}
}
function install(cb1) {
cp.exec('npm install express', (err) => {
if (err) {
console.error(err);
} else {
console.log('Express Installed.');
cp.exec('npm install ejs', (err) => {
if (err) {
console.error(err);
} else {
console.log('Ejs Installed.');
fse.outputFile('index.js', server);
fse.outputFile('public/index.html', home);
cb1();
}
});
}
});
}
But it is rather brittle and unnecessarily complicated in this form. Maybe it would be simpler to inline your functions each in other.
when I use PM2,i got this error "no such file or directory, uv_chdir"
the resolvent is :
first,I use 'pm2 delete' to delete old process
second,I use 'pm2 start',then ok
ps : just change your code or use 'pm2 reload' or 'pm2 restart' would not be ok.
more detail , you can see "https://blog.csdn.net/u013934914/article/details/51145134"

How to resolve stream is not writable on node server for binary.js(BinaryStream.write)?

I am working on file upload node server, for one large file after upload I am getting below error
Error: Stream is not writable
at BinaryStream.write (/node_modules/binaryjs/lib/stream.js:84:11)
at /src/K/Cli/upload/lib/media.js:120:36
at ChildProcess.exithandler (child_process.js:742:7)
at ChildProcess.emit (events.js:110:17)
at maybeClose (child_process.js:1015:16)
at Process.ChildProcess._handle.onexit (child_process.js:1087:5)
I am using node version v0.10.48
Reference code picked from https://github.com/rajkissu/binaryjs-upload-stream
I am unable to find the way how to resolve this.
the problem is for 1% case below code goes to console.log('Debug 4) case and upon executing stream.write({end: true}); it stop node server ie., I get bad gateway, since thrown error is not handled.
and on front end I am using data from node server read content id and set done, but since node server stop and I am not able to get content id from node server.
bs = new BinaryServer({ port: 9004 });
bs.on('connection', function (client) {
client.on('stream', function (stream, meta) {
upload(stream, meta);
});
});
function upload(stream, meta) {
var file = fs.createWriteStream(uploadPath + '/' + meta.name);
stream.pipe(file);
stream.on('end', function () {
// console.log(__dirname);
var cli_base=__dirname+'/../../';
var cmd = '<upload systme cmd>';
console.log('Uploading End and running command ' + cmd);
try {
stream.write({uploaded: true});
}catch (e) {
console.log('Debug 3, Streem On End Event : '+e.message,meta);
// stream.write({end: true});
}
if(!meta.hasOwnProperty('executed')){
meta.executed=true;
child = exec(cmd, function (error, stdout, stderr) {
try{
console.log(error, JSON.parse(stdout), stderr);
var command_output = JSON.parse(stdout);
if(command_output.hasOwnProperty('content_id')){
stream.write({end: true, content_id: command_output.content_id,files:command_output.file});
console.log(command_output.content_id);
}
}catch (e) {
console.log('Debug 4, Streem On End Event : '+e.message,meta,child);
stream.write({end: true});
}
});
}
});
}
I found solution for your problem, use options while creating file object as below
var options = {flags: 'w', encoding: 'utf8',fd: null,mode: '0666'};
var file = fs.createWriteStream(uploadPath + '/' + meta.name,options);
Node reference https://nodejs.org/docs/v0.12.5/api/fs.html#fs_fs_createwritestream_path_options
Encoding options for createWriteStream https://nodejs.org/docs/v0.12.5/api/fs.html#fs_fs_writefile_filename_data_options_callback

Meteor Error: ENOTEMPTY: directory not empty

When I am trying to load the following package in Meteor https://github.com/vsivsi/meteor-job-collection
It gets downloaded 100% and extracted, but at the time of loading it throws the following error:
{ [
Error: ENOTEMPTY: directory not empty, rmdir 'C:\Users\LALITS~1\AppData\Local\Temp\mt-16riklk\npm\job\node_modules']
errno: -4051,
code: 'ENOTEMPTY',
syscall: 'rmdir',
path: 'C:\\Users\\LALITS~1\\AppData\\Local\\Temp\\mt-16riklk\\npm\\job\\node_modules' }
I am using windows 8.1 64 bit.
I have tried to delete the folder manually, but again it created a new one and throws the same error. Can anyone tell me what is the problem? Am I missing something?
Thanks in advance.
Your issue looks like this known Meteor bug:
https://github.com/meteor/meteor/issues/8663. This bug occurs under Windows when updating to the next Meteor version.
Maybe you can try the proposed solution, which is to edit the following file:
C:\Users\[yourName]\AppData\Local\.meteor\packages\meteor-tool\[yourMeteorVersion]\mt-os.windows.x86_32\tools\fs\files.js
...and replace functions files.rm_recursive_async and files.rm_recursive with this code:
files.rm_recursive_async = function (path) {
return new Promise(function (resolve, reject) {
rimraf(files.convertToOSPath(path), function (err) {
err && console.log(err);
resolve();
//return err ? reject(err) : resolve();
});
});
}; // Like rm -r.
files.rm_recursive = Profile("files.rm_recursive", function (path) {
try {
rimraf.sync(files.convertToOSPath(path));
} catch (e) {
if (e.code === "ENOTEMPTY" && canYield()) {
files.rm_recursive_async(path).await();
return;
}
console.log(e);
//throw e;
}
}); // Makes all files in a tree read-only.

A single list of users shared between several process using PM2 and Nodejs

What is the best way to have a single list of users which is shared between several processes?. The processes are initiated using PM2.
The processes will have access to the list in order to add, remove, and check if a user exist in the list already.
The easiest way is to use redis(or memocache, even mongodb) to store those user list.
Or you will have to handle very complex IPC in your case, since pm2 uses node cluter, based on child_process.
You can use an in-memory data store like Redis.
Redis runs as a separate process and serves requests on a TCP port(by default 6379). Redis is a key-value data store and can be used by all your node processes.
Here's how you can do it:
List item
Install redis. (https://redis.io/)
Install node client for redis:
npm install --save redis
Now you can use redis to store your application state data and share it accross processes.
Refer this link for code example.
i just wrote a Job tracking logger for large web crawler system up to 1200 instances using redis.
Ok! Let's do it!
First you will need define it:
const redis = require("redis");
const client_redis = redis.createClient({
retry_strategy: function(options) {
if (options.error && options.error.code === "ECONNREFUSED") {
// End reconnecting on a specific error and flush all commands with
// a individual error
return new Error("The server refused the connection");
}
if (options.total_retry_time > 1000 * 60 * 60) {
// End reconnecting after a specific timeout and flush all commands
// with a individual error
return new Error("Retry time exhausted");
}
if (options.attempt > 10) {
// End reconnecting with built in error
return undefined;
}
// reconnect after
return Math.min(options.attempt * 100, 3000);
},
});
This function for update and create log.
function create_and_update_log(productName2, url2, proc, msg) {
var data_value = {
id: 'BESTBUY::DATA_LOG::'+md5(productName2 + url2),
totalrv: 'WAIT',
product: productName2,
url: url2,
process: proc,
task: msg,
timestamp: moment().format('DD/MM/YYYY HH:mm:ss')
};
client_redis.set('BESTBUY::DATA_LOG::'+md5(productName2 + url2), JSON.stringify(data_value));
}
This function for query all data
async function get_log_redis() {
return new Promise(function(resolve, reject) {
try {
var logger_data = {
logger: []
};
client_redis.multi()
.keys('BESTBUY::DATA_LOG::*', function(err, replies) {
replies.forEach(function(reply, index) {
client_redis.get(reply, function(err, data) {
if (!data.includes("Total reviews left: 0")) {
logger_data.logger.push(JSON.parse(data));
}
if (index == replies.length - 1) {
resolve(logger_data);
}
});
});
})
.exec(function(err, replies) {});
} catch (err) {
console.log(err);
}
});
}
Remember to replace :
BESTBUY::DATA_LOG::
... with what you want to define.
And the final is how to fetch all log belong to my key name begin with "BESTBUY::DATA_LOG::"
var log_obj_data = "";
(async () => {
var log_obj_data = await get_log_redis();
response.writeHead(200, {
"Content-Type": "application/json"
});
response.end(JSON.stringify(log_obj_data));
})();

Webshot doesn't save any image

I try to use node package Webshot, it executes the function, print "OK" to console, but never save any file to the folder. What am I missing here?
if (Meteor.isServer) {
var webshot = Meteor.npmRequire('webshot');
Meteor.methods({
'snapshot':function () {
webshot('google.com', './google.png', function (err) {
if (err) return console.log(err);
console.log('OK');
});
}
})
}
I just installed webshot and tried it.
You will find the .png in .meteor/local/build/programs/server or thereabouts

Categories

Resources