Desktop Top Save Location For User Storage? - node.js

While writing a node-webkit application I came across needing to save users uploaded photos through the built in html file input. I can save photos easy enough where I wish via a nice post here on node file uploading and node-webkit's file dialog changes simple enough.
The question really is there a best practice for saving user generated content for a desktop app or is the application folder (OS specific) reasonable to use with say the application or company name? Security is not much of a concern here in this case.

node-webkit (at this time) has an application folder under under process.env.LOCALAPPPATH (for windows users anyway) which could be used.
Another option which is viable is to use an application directory in the exe directory of the program.
IE for node with process global.
var path = require('path');
var appDir = path.dirname( process.execPath ) + path.sep + 'data' + path.sep;
//might produce something like c:\\programs\\node-webkit\\data\\

Related

In Electron, how to allow users to pick a file path

I have an Electron App in which when users click on a button I want to open file explorer (or Finder on Mac) for users to choose a path in their file system. Then I want to use this path to save a file.
The second part is quiet easy to achieve. I just need to use writeFile() from node File System API.
However I have gone through the full list of node File System API and I have found nothing allowing me to do the first part.
You should use dialogue module. Simple example for showing the file explorer will be:
const {dialog} = require('electron')
console.log(dialog.showOpenDialog({properties: ['openFile', 'openDirectory', 'multiSelections']}))
Check this link for more information.

Exporting sqlite3 db file from inside electron app. Is this possible?

I've got an app I've put together in Electron which saves data using sqlite3. Everything works as expected. I'd like to be able to export/save the actual database file so I can share it with others, treating it sort of like a save file.
I assume that if this is possible then I need to be using fs as well, which is fine.
Even better, can I just create the database file outside the compiled app from the start? And if so what's the best way to accomplish that?
Otherwise I can switch over to kripken/sql.js or something like that, but I'd rather not put the time in to make those changes if there's an easy way to just have the existing sqlite database file get saved to the user's computer outside the app.
I'm an idiot.
Instead of storing the file internally in the packaged app like this…
const dbPath = path.resolve(__dirname, 'data.db')
…I'm just storing it in the filesystem like this…
const {app} = require('electron').remote;
const dbPath = path.resolve(app.getPath('userData'), 'data.db');
…so that it's accessible from the start.
I'm leaving this question up because I'd be interested if there is a way to have a save file dialogue for an extant file in the packaged app, but in the mean time this is my answer.

How to handle data import and export from a Windows Universal app

I am developing a Windows Universal app that collects results of races. It saves each race result in a sql-lite database in an application folder so the user can view previous results. I have further requirements, however, for saving and opening race results.
I need to be able to export the results of a race as a CSV file so that they can be opened by a third-party application that might be running on a separate machine on a different operating system.
I need to be able to export the results as an HTML file that can be uploaded/included in the user's own web site.
I need the user to be able to print the results (which I was thinking could just be done by printing the HTML file from a browser)
I would like the user to be able to choose to import the results of a race created by my own legacy application in my own format.
It seems, however, that we are restricted in a Windows Universal app to saving files to just very specific folders under very specific circumstances if we have requested that app capability. Therefore I am getting access denied errors both saving and reading files using the FileOpenPicker and FileSavePicker.
I think I probably need to view the export and import of results in a different way, but after a lot of searching I have not been able to come up with the right and recommended solution to this. So the question is how should I be handling the import and export of results? Should I be using the user's documents folder, or their OneDrive? Do I need to create a web application for my app so that the user can store results in the cloud and download them from there?
CSV and HTML are both text files with some encoding. So your question is about how to read/write files with JS.
Here is example how to create html page with FileSavePicker:
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
savePicker.fileTypeChoices.insert("Web page", [".html"]);
savePicker.suggestedFileName = "New page";
savePicker.pickSaveFileAsync().then(function (file)
{
if (file) {
var _WriteThis="<!DOCTYPE html>" + "\r\n";
_WriteThis = _WriteThis + "<html><head><title>Web page title</title>" + "\r\n";
// .....
Windows.Storage.FileIO.writeTextAsync(file, _WriteThis, Windows.Storage.Streams.UnicodeEncoding.utf8);
}
});
This example doesn't required any special rules and you can save file anywhere on you PC HDD or USD stick without setting capabilities in manifest (except system folders)
Same way you can save in csv format

Where should I store cache of a custom CLI npm module?

I am developing an npm module, where user can interact with it through a terminal by executing commands:
> mymodule init
> mymodule do stuff
When executing certain commands user is being asked for some data, which will be used by the module. Since this data won't really change while using the module and since these commands can be executed pretty frequently, it is not the best option to ask user for the data any time he runs a command. So I've decided to cache this data, and as soon as it should live through multiple module calls, the easiest way to store it that I see is a file (the data structure allows to store it in a simple JSON). But I am to quite sure where should this file go on a user's machine.
Where in the file system should I store a cache in a form of a file or multiple files for a custom npm module, considering that the module itself can be installed globally, on multiple operation systems and can be used in multiple projects at the same time?
I was thinking about storing it in a module's folder, but it might be tricky in case of global installation + multi-project use. The second idea was to store it in OS specific tmp storage, but I am not quite sure about it too. I am also wondering if there are some alternatives to file storage in this case.
I would create a hidden folder in the user's home directory (starting with a dot). For instance, /home/user/.mymodule/config.cfg. The user's home directory isn't going anywhere, and the dot will make sure it's out of the user's way unless they go looking for it.
This is the standard way that most software stores user configs, including SSH, Bash, Nano, Wine, Ruby, Gimp, and even NPM itself.
On some systems you can cache to ~/.cache by create a sub-directory to store your cache data, though its much more common for applications to create a hidden directory in the users home directory. On modern windows machines you can use create a directory in C:/Users/someUser/AppData. In Windows using a . suffix will not hide a file. I'd recommend you do something platform agnostic like so:
var path = require('path');
function getAppDir(appName, cb) {
var plat = process.platform;
var homeDir = process.env[(plat == 'win32') ? 'USERPROFILE' : 'HOME'];
var appDir;
if(plat == 'win32') {
appDir = path.join(homeDir, 'AppData', appName);
}
else {
appDir = path.join(homeDir, '.' + appName);
}
fs.mkdir(appDir, function(err) {
if(err) return cb(err);
cb(null, appDir);
})
}
Just declare a function to get the app directory. This should handle most systems, but if you run into a case where it does not it should be easy to fix because you can just create some kind of alternate logic here. Lets say you want to allow a user to specify a custom location for app data in a config file later on, you could easily add that logic. For now this should suite most of your cases for most all Unix/Linux systems and Windows Vista and up.
Storing in system temp folder, depending on the system, your cache could be lost on an interval(cron) or on reboot. Using the global install path would lead to some issues. If you need this data to be unique per project, then you can extend that functionality to allow you to store this data in the project root, but not the module root. It would be best not to store it in the module root, even if its just installed as a local/project module, because then the user doesn't have the ability to include this folder in their repositories without including the entire module.
So in the event that you need to store this cached data relevant to a project, then you should do so in the project root not the node_modules. Otherwise store it in the users home directory in a system agnostic way.
First you need to know in what kind of SO you are running:
Your original idea is not bad, because global modules are not really global in all SO and in all virtual enviroments.
Using /home/user may not work in Windows. In windows you have to check process.ENV.HOMEPAHT
I recommend you a chain of checks to determine the best place.
Let the user take the control. Chose your own env variable. Supouse MYMOD_HOME. You firts check if process.ENV.MYMOD_HOME exists, and use it
Check if windows standard process.ENV.LOCALAPPDATA
Check if windows standard process.ENV.HOMEPATH
Check if exists '/home/user' or '~'
Otherwise use __dirname
In all cases create a directory ./mymodule

Temporary File Download

Is there a service that creates basically a one-time download of a file, preferably something I can use from NodeJS?
I've done some research on FilePicker, and haven't found anything about regenerating the link it gives you for a file. There may be a way to do this with NodeJS, but I'm using Meteor at the same time so many Node things probably will conflict.
You could build it with meteor. Using meteor-router with meteorite & use server side routing to deliver the files.
You need a collection to keep track of downloaded files:
Server JS
var downloads = new Meteor.Collection("downloads");
//create a link
downloads.insert({url:"/mydownload.zip",downloaded:false})
Meteor.Router.add('/file/:id', 'GET', function(id) {
download = downloads.findOne(id);
if( download) {
if(dowload.downloaded) {
this.response.send("You've already downloaded me")
}
else
{
//I guess you could just redirect or stream the file for an extra layer of surety
this.response.redirect(download.url);
}
}
});
On the client you can use /files/{{_id}} with _id of the file from downloads the person has as the link
My recommendation would also be to add custom server-side logic to count # of uploads (or just flag a file as downloaded/not downloaded) and respond accordingly. The closest you could do with Filepicker.io would be using the security policies to restrict downloading the file to a specific time interval.
in addition to using the router package
in Meteor.startup you can add
var require = __meteor_bootstrap__.require;
fs = require( 'fs' );
the fs variable should be declared on the server only. the fs package is used by Meteor and does not need to be added separately.
once you have done this, you can create files with Meteor.uuid() as their name which makes them unique and very difficult to guess. It is also possible to delete the file after a certain amount of time by using Meteor.setTimeout
the question is: where do the files to be downloaded come from?
Solution using Heroku Cloud and NodeJS Meteor Hooks
Heroku in particular is actually great for temporary file download links: they offer a "temporary scratchpad" filesystem that is reset every time the program restarts, and each running Node server cannot see the files other instances have created.
Each dyno gets its own ephemeral filesystem, with a fresh copy of the
most recently deployed code. During the dyno’s lifetime its running
processes can use the filesystem as a temporary scratchpad, but no
files that are written are visible to processes in any other dyno and
any files written will be discarded the moment the dyno is stopped or
restarted.
Taken from the Heroku documentation: https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem
Thus, any files written to the "filesystem" will be temporary.
This allows for a very easy solution to this problem: you can simply use NodeJS filesystem manipulation to create temporary files on the server, serve them once (or for a limited time), and then remove them so they cannot be downloaded again.
This in combination with something like $.download() will make a seamless experience which in turn prevents unauthorized downloads.

Resources