I am an ex-PHP Laravel developer and I am trying to explore other options right now, currently using Node.js, Sequelize, Koa and Next.js to build a small app.
When sequelize is initialized, a models folder gets created automatically and it has along the lines the following code.
...
fs
.readdirSync(__dirname)
.filter(file => {
console.log('Filer file: ', file)
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
...
Which is working fine when imported in the main server.js file.
I am trying to figure out what is the best way to include this in any of the other files, in my case I need to have access to it via the newly release API routes from Next.js /api/{endpoint} (https://nextjs.org/docs/api-routes/introduction).
If I import the file there __dirname (from the main sequelize models file) is the root directory of my machine which makes me think that if this code is automatically generated code by their CLI tool I am probably doing something wrong.
One way I found to make this work is by passing the db object via the req object in the server.js file.
router.get('/api/*', async (ctx) => {
/**
* There has to be a better way
*/
ctx.req.db = db
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
});
Is there better way to handle all of this?
Related
I'm building and trying do deploying a packaged electron app. FOr the packaging i used
electron-packager
electron-installer-debian
electron-installer-dmg
electron-winstaller
and I'm facing a little issue where I have to store tha appa datas somewhere in my user computer.
I saw that the good practice is to use the the folder in the path that is returned by the electron method app.getPath('userData').
from the docs
It is The directory for storing the app's configuration files, which by default it is the appData directory appended with the app name.
%APPDATA% on Windows
$XDG_CONFIG_HOME or ~/.config on Linux
~/Library/Application Support on macOS
By my tests sometimes this folder is not created automatically when the app is installed and other times yes and I'm wondering if i should create it or not.
Right now i'm quitting the app if this folder isn't present in the pc with the following code
var DatasPath = app.getPath('userData')
if (!fs.existsSync(DatasPath)){
process.exit()
}
So the question is
should i create the DatasPath folder with fs.mkdirSync(DatasPath); when it is not present or it is 'bad practice to do so', and if I can create the folder i have to warning the user the i have just added that folder?
(Expanding my reply from a "comment" to an "answer")
i don't know if i'm supposed to create it or not so i automatically
make the app quit if there is not that folder
It seems you are taking "userData" too literally? It is not an actual "folder" named "userData – it is a path to where the operating system stores data for that application. Electron currently runs on 3 operating systems and each one does things differently. For our convenience, Electron hides those differences by creating the wrapper method app.getPath(name) so the same code will work on each OS.
Try this: put the line below in your main.js script:
console.log(app.getPath('userData'));
/Users/*********/Library/Application Support/MyCoolApp
(the "*********" will be your user account name.)
UPDATED:
Run the code below in main.js and then look in the folder specified by the "userData" path
const fs = require("fs");
const path = require('path');
var datasPath = app.getPath('userData')
var data = "I am the cheese"
var filePath = path.join(datasPath, "savedData.txt")
fs.writeFileSync(filePath, data)
At pathConfig.js
function getAppDataPath() {
switch (process.platform) {
case "darwin": {
return path.join(process.env.HOME, "Library", "Application Support", "myApp");
}
case "win32": {
return path.join(process.env.APPDATA, "myApp");
}
case "linux": {
return path.join(process.env.HOME, ".myApp");
}
default: {
console.log("Unsupported platform!");
process.exit(1);
}
}
}
const appPath = __dirname;
const appDataPath =
!process.env.NODE_ENV || process.env.NODE_ENV === "production"
? getAppDataPath() // Live Mode
: path.join(appPath, "AppData"); // Dev Mode
if (!fs.existsSync(appDataPath)) {
// If the AppData dir doesn't exist at expected Path. Then Create
// Maybe the case when the user runs the app first.
fs.mkdirSync(appDataPath);
}
In each operating system the appData folder has a different path and the perfect way of getting this path is by calling app.getPath('userData') in the main process.
But there is a package that can handle this for you, it stores data in a JSON file and update it in every change.
In my opinion this package is much better than handling everything by your self.
Read more :
https://www.npmjs.com/package/electron-data-holder
According to the expo sqlite documentation for react-native I can initialize a db like so:
const db = SQLite.openDatabase('db.db');
This works and I can update the db like so:
update() {
db.transaction(tx => {
tx.executeSql(
`select * from items where done = ?;`,
[this.props.done ? 1 : 0],
(_, { rows: { _array } }) => this.setState({ items: _array })
);
});
}
From my limited understanding this creates a database in the device. And then it's manipulated keeping all the db local.
I have a database with all the necessary tables already setup. How can I have it use the current database I already have setup?
For example: (not correct syntax)
const db = SQLite.openDatabaseIShipWithApp('mypath/mydb.db');
I couldn't find any documentation to help me with this.
The only reason I mention the above is because I already have the db with the tables and data.
Any help would be appreciated!
I was able to achieve this by using expo's FileSystem.downloadAsync:
first I import it since I'm using expo managed app:
import { FileSystem } from 'expo';
Then I download it from a server like so:
// load DB for expo
FileSystem.downloadAsync(
'http://example.com/downloads/data.sqlite',
FileSystem.documentDirectory + 'data.sqlite'
)
.then(({ uri }) => {
console.log('Finished downloading to ', uri)
})
.catch(error => {
console.error(error);
})
The first parameter is the uri for the location, the second one is where I'd like to place it. Here I am using documentDirectory.
If using local prepopulated database in assets:
import * as FileSystem from "expo-file-system";
import {Asset} from "expo-asset";
async function openDatabaseIShipWithApp() {
const internalDbName = "dbInStorage.sqlite"; // Call whatever you want
const sqlDir = FileSystem.documentDirectory + "SQLite/";
if (!(await FileSystem.getInfoAsync(sqlDir + internalDbName)).exists) {
await FileSystem.makeDirectoryAsync(sqlDir, {intermediates: true});
const asset = Asset.fromModule(require("../assets/database/mydb.sqlite"));
await FileSystem.downloadAsync(asset.uri, sqlDir + internalDbName);
}
this.database = SQLite.openDatabase(internalDbName);
}
This creates the SQLite directory and database if not exists. Otherwise FileSystem.downloadAsync() will throw an error on fresh installed app.
Some remarks:
You cannot use variable in require() (only string). See e.g. this.
You have to explicitly allow file extension .db or .sqlite to be loadable in Expo, see this. You have to create a file metro.config.js in root:
const defaultAssetExts = require("metro-config/src/defaults/defaults").assetExts;
module.exports = {
resolver: {
assetExts: [
...defaultAssetExts,
"db", "sqlite"
]
}
};
And may add following to app.json
"expo": {
"assetBundlePatterns": [
"**/*"
]
}
If want to delete loaded database (e.g. for testing) you have to clear whole Expo App data in Phone settings (deleting cache not sufficient). Or write a method like this:
async function removeDatabase() {
const sqlDir = FileSystem.documentDirectory + "SQLite/";
await FileSystem.deleteAsync(sqlDir + "dbInStorage.sqlite", {idempotent: true});
}
It's pretty straight forward
If you bundle your app, you have to move the Database from the asset folder to the document directory first. In order to do that, check if a folder named SQLite exists. If not, create it. Why do you need a folder called SQLite? That is because SQLite.openDatabase(databaseName) looks per default in FileSystem.documentDirectory + 'SQLite'. Then, when the folder is created, you can download the database from the asset folder. Make sure you have your database in a folder called asset. Locate the foler asset under src/asset of your app document tree. Also, make sure to configure your app.json and metro.config.js.
import * as SQLite from 'expo-sqlite';
import * as FileSystem from 'expo-file-system';
import { Asset } from 'expo-asset';
const FOO = 'foo.db'
if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + 'SQLite')).exists) {
await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + 'SQLite');
};
await FileSystem.downloadAsync(
// the name 'foo.db' is hardcoded because it is used with require()
Asset.fromModule(require('../../asset/foo.db')).uri,
// use constant FOO constant to access 'foo.db' whereever possible
FileSystem.documentDirectory + `SQLite/${FOO}`
);
// Then you can use the database like this
SQLite.openDatabase(FOO).transaction(...);
// app.json
{
"name": "Your App name",
"displayName": "Your App name",
"assetBundlePatterns": [
"assets/**"
],
"packagerOpts": {
"assetExts": ["db"]
}
}
// metro config
const { getDefaultConfig } = require('#expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
module.exports = {
resolver: {
assetExts: [...defaultConfig.resolver.assetExts, 'db', 'json'],
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
This is all extracted from the documentation of expo.
I don't believe this is possible in expo. There is a way to use an existing database if you are using a bare android project which involves writing some native code to copy the database from the project assets to the standard location on the phone (/data/ etc) for your application.
https://medium.com/#johann.pardanaud/ship-an-android-app-with-a-pre-populated-database-cd2b3aa3311f
I have always personally created the database myself with CREATE TABLE IF NOT EXISTS since sqlite requires you to define the schema before you query it. If you need to seed the database, this step would then be followed by additional steps to insert the required data.
In most cases, you will need to also check your reference data and update it from the server at regular intervals (it might even change from publishing your apk to someone downloading the app) and this code would also work when there is no reference data in the database.
There are a couple of services which try and take his hassle away from you (e.g. parse) but you will need to decide if you are happy with them hosting your data.) I haven't used them so not sure how this works exactly but I'm told it tries to solve the offline first type problems.
Remember that in future iterations you may need to modify the structure for future versions (to add fields etc) so you will probably need to define some code that loads when the application is first started, checks the database version and applies any changes that are required to bring the database up to the appropriate level.
I want to display the current git tag on my app's login page,
Its built using react.
Im trying to use the 'git-rev-sync' library to do this.
but it doesnt seem to work on the client side because I keep getting errors like
'cannot find module 'children process', it works on the server side where Im able to console.log and print the tag
anyone know how to achieve this? Open to any solutions with any library
import version from 'git-rev-sync'
...
class Login extends Component {
...
render ()
...
return (
<div> my version: {version.tag()} </div>
) }
Thanks
I decided to use git-revision-webpack-plugin which creates a VERSION file (among other files) in the dist folder, and then I read the file from my client side react app:
add this to your webpack.js:
const GitRevisionPlugin = require('git-revision-webpack-plugin')
module.exports = {
plugins: [
new GitRevisionPlugin({
lightweightTags: true //I added this to get the tags as well
})
]
}
then my client side looks like this:
const [revision, setRevision] = useState('')
const fetchRevision = async () => {
let result = await fetch('/dist/VERSION')
let txt = await result.text()
txt = txt.replace(/^(.*?)(?:\-.*)?$/, '$1') //I only care for the tag.
setRevision(txt)
}
useEffect(() => {
fetchRevision()
}, [])
and then you can render the revision
One thing to notice, depending on your server, you may need to tell it to serve this VERSION file as is, so for example in express, you might find you need this:
server.get('*', (req, res, next) => {
if (/^\/dist\/*/.test(req.originalUrl)) {
const relative = req.originalUrl.replace(/\/dist(\/.*)/, '$1')
const filename = path.join(compiler.outputPath, relative)
compiler.outputFileSystem.readFile(filename, (err, result) => {
if (err) {
return next(err)
}
res.send(result)
res.end()
})
}
...
})
Hope this helps for future use.
If you used create-react-app#0.2.3 > to generate your app.
create-react-app scripts use environment variables that start with the REACT_APP_ symbol in the root .env file. create-react-app - Adding custom environment variables is a good place to dig into the details.
or just include the following in your .env file.
.env
REACT_APP_VERSION=$npm_package_version
and access it on your react login component by referring to {process.env.REACT_APP_VERSION}
I try to download an uploaded file from a data folder, OUT of the asset folder in a Sails.js 0.12 app.
I ended up installing Skipper-disk npm to do so with :
var SkipperDisk = require('skipper-disk');
var fileAdapter = SkipperDisk(/* optional opts */);
res.attachment('./data/'+req.param('product_id')+ '/' + req.param('file'));
fileAdapter.read('./data/'+req.param('product_id')+ '/' + req.param('file'))
.on('error', function (err){
return res.serverError(err);
})
.pipe(res);
I'm a bit disappointed about it because I wanted to find a way to do it without installing more dependencies (skipper-disk). I tried things like : sailsjs send a file on the fly with res.attachment(), How do I authenticate access to assets folder in sails.js, Uploading multiple files with Sails.js 0.10 and Skipper using Dropzone.js, Sails.js Download File To Client, and many more, but nothing else work.
Is there another way than skipper-disk to make the upload work ?
Also, I needed more than one file downloaded with the previous controller action, but my request from the front end only opens the first file selected.
Here's that code :
files.forEach(function(file) {
window.open('/product/' + id + '/download/' + file, '_blank');
});
Is there a way to get multiple files from the back end ?
Well for my first question, I'm not sure if it's the best way to do it, but this works fine without Skipper-disk (the server on which the app is running is a pain when I need to install a new npm, so I try to do without !) :
const Path = require('path');
const fs = require('fs');
fs.createReadStream(Path.resolve('./data/'+req.param('product_id')+ '/' + req.param('file')))
.on('error', function (err) {
return res.serverError(err);
})
.pipe(res);
I am new to Browserify and trying the following:
I created a node server and trying to get a package called 'openbci' running on the browser.
so I have the following file structure:
Myapp
-...
-public
--app.js
--index.html
--openBCI.js
--...
--javascript
---openBCI
----bundle.js
---...
-node_modules
--openbci
---openBCIBoard.js
--browserify
--...
my app.js file sets the server to serve the public folder
// app.js
var express = require('express');
var app = express();
app.use(express.static('public'));
app.listen(myPort);
then I created the following openBCI.js
// openBCI.js
var OpenBCIBoard = require('openbci').OpenBCIBoard;
exports.OpenBCIBoard = OpenBCIBoard;
and finally launched the browserify command:
$ browserify public/openBCI.js > public/javascript/openBCI/bundle.js
but once called in my index.html file, I got an Uncaught TypeError: exists is not a function at Function.getRoot:
exports.getRoot = function getRoot (file) {
var dir = dirname(file)
, prev
while (true) {
if (dir === '.') {
// Avoids an infinite loop in rare cases, like the REPL
dir = process.cwd()
}
**if (exists(join(dir, 'package.json')) || exists(join(dir, 'node_modules'))) {**
// Found the 'package.json' file or 'node_modules' dir; we're done
return dir
}
if (prev === dir) {
// Got to the top
throw new Error('Could not find module root given file: "' + file
+ '". Do you have a `package.json` file? ')
}
// Try the parent dir next
prev = dir
dir = join(dir, '..')
}
}
It appears that it could not find the original path for the module.
Could you please tell me what is to change? Or if I understood at all how browserify works ? :)
I notice a few things that seem strange about the code.
exists is undefined in JavaScript or node. It appears to be an alias of fs.exists - is that right?
If so, fs.exists is deprecated. Per the documentation, you can achieve the same effect with fs.stat or fs.access. Note however that you should either supply a callback (preferable) or use the Sync version of these methods.
If you are trying to use file system tools in the browser you are going to run into problems because you are attempting to access the server's file system from the browser. There is a plugin, browserify-fs, that gives you an equivalent to fs in the browser. However, this seems to access the browser's local IndexedDB, not the storage on your server.
I would suggest running code that relies on server-side files on the server, rather than in the browser.