How to read contents of a directory in linux on nodejs? - node.js

I am creating an application on nodejs that has to read contents of a folder in the install location. The installer creates the directory 'cert' at the install location.
My code is:
const dircert = './cert'
files = fs.readdirSync(dircert)
if (!files.length) {
***some code***
} else {
files.forEach(file => {
if (path.extname(file) == '.key') {
pathkey = path.resolve(dircert, file)
}
if (path.extname(file) == '.crt') {
pathcert = path.resolve(dircert, file)
}
})
This runs fine in windows but not in Linux. The installer is not able to read the contents. What do I need to change to ensure that it works in both? I am pretty new to linux.
The application is getting installed in ProgramFiles in windows but in /opt/AppName in linux. Please suggest.
Editing to add: So this cert folder is getting created under /opt/AppName/ in linux and under c:/ProgramFiles/company/App Name/ in Windows. While this code runs fine on windows, on linux this code tries to look for cert at root. How do I make sure it looks for the folder at the installed location which is /opt/AppName, and it should work on both the platforms.

It's unclear from your question how the files are being installed and how the code is being executed. Both of these matter in Linux. You can use the __dirname node variable with path.resolve() to get the directory of the current module. If cert is below the current module, you can use the following code to resolve the location:
const path = require('path');
const dircert = path.resolve(__dirname, 'cert');
If you're using ES Modules:
import path from 'path';
const dircert = path.resolve('cert').

Related

How to find where an application got installed in linux using nodejs

I installed a server on linux. The installation files gets installed under /opt/AppName/ while the service is under /etc/systemd/system/. I need to get the path of a folder 'cert' which is under /opt/AppName when the server starts. Now the process tries to look under current working directory or the root which obviously fails. How do I get the location where the install files are present?
Have tried:
const dircert = 'cert'
const certificateDirWin = path.resolve(`${process.cwd()}`, dircert)
and
const certificateDir = path.resolve(__dirname, dircert)
None of these work.
However if I hardcode
const certificateDirLin = path.resolve('/opt/AppName', dircert)
it works.
How do I get rid of the harcoded path.

How to access the base package form a node_module

I am looking to access a JSON config file that the user would place next to their package.json from a node_module package that I created. Is there a best approach to do this. I tried a relative import but that didn't really work and I am not sure how best to accomplish dynamic imports if the config file doesn't exist because I want to allow it to not exist as well.
Here is how I tried to handle dynamic imports though:
export const overrides = (function () {
try {
return require('../../../../../../overrides.json');
} catch (_err) {
return null;
}
})();
Also I tried fs but I get a browser config error I am not sure if that is something else. I should research but I didn't understand the docs around that.
using a library
This worked for me: find-package-json
Basically on any js file who needs the base, home or workspace path, do this:
var finder = require('find-package-json');
var path = require('path');
var f = finder(__dirname);
var rootDirectory = path.dirname(f.next().filename);
rootDirectory will be the location of the folder in which the main package.json exist.
If you want to optimize, get the appRootPath variable at the start of your app and store/propagate the variable to the hole nodejs system.
no libraries
Without any library, this worked for me:
console.log("root directory: "+require('path').resolve('./'));
This will get you the root directory of your nodejs app no matter if you are using npm run start or node foo/bar/index.js
More ways to get the root directory here:
Determine project root from a running node.js application
usage
If you achieve to obtain the root directory of your nodejs app and your file is at the package.json level, use this variable like this to locate any file at root level:
rootDirectory+"/overrides.json"

nodejs writeFile only working with admin permssions

I currently learning electron and I would like to write a config file in the same dir than the exe file. This is my code:
var save = fs.readFileSync(__dirname+"/config.json")
save = {
storein : savegame
}
fs.writeFile("./config.json", JSON.stringify(save), function(error){
//other stuff
})
But if I run this without administrator permissions, its not working, only if I run the .exe as an admin. If I use electron app it works too

Electron open file/directory in specific application

I'm building a sort of File explorer / Finder using Electron.
I want to open some file types with a specific application.
I've tried the approach from this answer:
Open external file with Electron
import { spawn } from 'child_process'
spawn('/path/to/app/superApp.app', ['/path/to/file'])
But when I do that, I get a EACCES error as follows.
Is this the right approach? If yes, how can I fix the issue? If not, what is the right approach?
You can open a file or folder through shell commands from the electron module. The commands work on both main and renderer process.
const {shell} = require('electron') // deconstructing assignment
shell.showItemInFolder('filepath') // Show the given file in a file manager. If possible, select the file.
shell.openPath('folderpath') // Open the given file in the desktop's default manner.
More info on https://github.com/electron/electron/blob/master/docs/api/shell.md
While the accepted answer does say how to open the folder in file explorer, it doesn't answer the question on how to open the folder WITH an external program like VSCode. It can be done like so:
import { spawn, SpawnOptions } from "child_process";
import { pathExists } from "fs-extra";
const editorPath = "C:/Program Files/Microsoft VS Code/Code.exe";
export const launchExternalEditor = async (
folderPath: string
): Promise<void> => {
const exists = await pathExists(editorPath);
if (!exists) {
console.error("editor not found");
}
const opts: SpawnOptions = {
// Make sure the editor processes are detached from the Desktop app.
// Otherwise, some editors (like Notepad++) will be killed when the
// Desktop app is closed.
detached: true,
};
spawn(editorPath, [folderPath], opts);
};
This will launch the folder in VSCode as it's root directory. Code is taken from this repository.
Note: this may require some tinkering depending on what program you are trying to use, but so far it worked properly with: VSCode, Visual Studio 2019, Intellij IDEA, NetBeans.
For display native system dialogs for opening and saving files, alerting, etc. you can use dialog module from electron package.
const electron = require('electron');
var filePath = __dirname;
console.log(electron.dialog.showOpenDialog)({
properties:['openFile'],
filters:[
{name:'Log', extentions:['csv', 'log']}
]
});
A very prompt explanation is provided at Electron Docs.

How can I bundle a precompiled binary with electron

I am trying to include a precompiled binary with an electron app. I began with electron quick start app and modified my renderer.js file to include this code that is triggered when a file is dropped on the body:
spawn = require('child_process').spawn,
ffmpeg = spawn('node_modules/.bin/ffmpeg', ['-i', clips[0], '-an', '-q:v', '1', '-vcodec', 'libx264', '-y', '-pix_fmt', 'yuv420p', '-vf', 'setsar=1,scale=trunc(iw/2)*2:trunc(ih/2)*2,crop=in_w:in_h-50:0:50', '/tmp/out21321.mp4']);
ffmpeg.stdout.on('data', data => {
console.log(`stdout: ${data}`);
});
ffmpeg.stderr.on('data', data => {
console.log(`stderr: ${data}`);
});
I have placed my precompiled ffmpeg binary in node_modules/.bin/. Everything works great in the dev panel, but when I use electron-packager to set up the app, it throws a spawn error ENOENT to the console when triggered. I did find a very similar question on SO, but the question doesn't seem to be definitively answered. The npm page on electron-packager does show that they can be bundled, but I cannot find any documentation on how to do so.
The problem is that electron-builder or electron-packager will bundle your dependency into the asar file. It seems that if the dependency has a binary into node_modules/.bin it is smart enough to not package it.
This is the documentation for asar packaging for electron-builder on that topic. It says
Node modules, that must be unpacked, will be detected automatically
I understand that it is related to existing binaries in node_modules/.bin.
If the module you are using is not automatically unpacked you can disable asar archiving completely or explicitly tell electron-builder to not pack certain files. You do so in your package.json file like this:
"build": {
"asarUnpack": [
"**/app/node_modules/some-module/*"
],
For your particular case
I ran into the same issue with ffmpeg and this is what I've done:
Use ffmpeg-static. This package bundles statically compiled ffmpeg binaries for Windows, Mac and Linux. It also provides a way to get the full path of the binary for the OS you are running: require('ffmpeg-static').path
This will work fine in development, but we still need to troubleshoot the distribution problem.
Tell electron-builder to not pack the ffmpeg-static module:
"build": {
"asarUnpack": [
"**/app/node_modules/ffmpeg-static/*"
],
Now we need to slightly change the code to get the right path to ffmpeg with this code: require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked') (if we are in development the replace() won't replace anything which is fine).
If you are using webpack (or other javascript bundler)
I ran into the issue that require('ffmpeg-static').path was returning a relative path in the renderer process. But the issue seemed to be that webpack changes the way the module is required and that prevents ffmpeg-static to provide a full path. In the Dev Tools the require('ffmpeg-static').path was working fine when run manually, but when doing the same in the bundled code I was always getting a relative path. So this is what I did.
In the main process add this before opening the BrowserWindow: global.ffmpegpath = require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked'). The code that runs in the main process is not bundled by webpack so I always get a full path with this code.
In the renderer process pick the value this way: require('electron').remote.getGlobal('ffmpegpath')
I know I'm a bit late but just wanted to mention ffbinaries npm package I created a while ago exactly for this purpose.
It'll allow you to download ffmpeg/ffplay/ffserver/ffprobe binaries to specified location either during application boot (so you don't need to bundle it with your application) or in a CI setup. It can autodetect platform, you can also specify it manually.
If anyone happens to need an answer to this question: I do have a solution to this, but I have no idea if this is considered best practice. I couldn't find any good documentation for including 3rd party precompiled binaries, so I just fiddled with it until it finally worked. Here's what I did (starting with the electron quick start, node.js v6):
From the app directory I ran the following commands to include the ffmpeg binary as a module:
mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
ln -s ../ffmpeg/ffmpeg node_modules/.bin/ffmpeg
(replace /usr/local/bin/ffmpeg with your current binary path, download it from here) Placing the link allowed electron-packager to include the binary I saved to node_modules/ffmpeg/.
Then to get the bundled app path I installed the npm package app-root-dir by running the following command:
npm i -S app-root-dir
Since I could then get the app path, I just appended the subfolder for my binary and spawned from there. This is the code that I placed in renderer.js:.
var appRootDir = require('app-root-dir').get();
var ffmpegpath=appRootDir+'/node_modules/ffmpeg/ffmpeg';
console.log(ffmpegpath);
const
spawn = require( 'child_process' ).spawn,
ffmpeg = spawn( ffmpegpath, ['-i',clips_input[0]]); //add whatever switches you need here
ffmpeg.stdout.on( 'data', data => {
console.log( `stdout: ${data}` );
});
ffmpeg.stderr.on( 'data', data => {
console.log( `stderr: ${data}` );
});
This is how I would do it:
Taking cues from tsuriga's answer, here is my code:
Note: replace or add OS path accordingly.
Create a directory ./resources/mac/bin
Place you binaries inside this folder
Create file ./app/binaries.js and paste the following code:
'use strict';
import path from 'path';
import { remote } from 'electron';
import getPlatform from './get-platform';
const IS_PROD = process.env.NODE_ENV === 'production';
const root = process.cwd();
const { isPackaged, getAppPath } = remote.app;
const binariesPath =
IS_PROD && isPackaged
? path.join(path.dirname(getAppPath()), '..', './Resources', './bin')
: path.join(root, './resources', getPlatform(), './bin');
export const execPath = path.resolve(path.join(binariesPath, './exec-file-name'));
Create file ./app/get-platform.js and paste the following code:
'use strict';
import { platform } from 'os';
export default () => {
switch (platform()) {
case 'aix':
case 'freebsd':
case 'linux':
case 'openbsd':
case 'android':
return 'linux';
case 'darwin':
case 'sunos':
return 'mac';
case 'win32':
return 'win';
}
};
Add the following code inside the ./package.json file:
"build": {
....
"extraFiles": [
{
"from": "resources/mac/bin",
"to": "Resources/bin",
"filter": [
"**/*"
]
}
],
....
},
import binary file path as:
import { execPath } from './binaries';
#your program code:
var command = spawn(execPath, arg, {});
Why this is better?
Most of the answers require an additional package called app-root-dir
The original answer doesn't handle the (env=production) build or the pre-packed versions properly. He/she has only taken care of development and post-packaged versions.

Resources