Open millsp opened 2 years ago
Since process.cwd()
returns the system root in Electron (which is correct behavior), this will search all users's files and therefore trigger multiple access permissions alerts during first start. "Do you want this app to access your photos? Your contacts? etc.". This leads to user confusion ("Why does this app need my photos?"). It would be great if a path could be passed to prisma client directly.
Is there a way to reliably detect Electron so we could differentiate if we use process.cwd()
vs. require('electron').app.getAppPath()
? Or is require('electron').app.getAppPath()
not even always the right answer in Electron context? (We always try to avoid adding configuration options when possible.)
Apologies, this is a rather long answer, but using require('electron').app.getAppPath()
will not work as expected.
require('electron').app.getAppPath()
is not a viable solution. It would make it impossible to securely expose Prisma to the renderer process during preload, since require('electron').app.getAppPath()
is not accessible in a preload script (which you would use to expose select node.js apis and code like prisma to the browser).
We expose prisma via ContextBridge from the main process to the renderer process. This in line with what other uses do and allows for a very convenient, yet secure, use of prisma in Electron (e. g. https://github.com/prisma/prisma/discussions/7889?sort=top#discussioncomment-1618858)
So we can simply use window.prisma.myCollection.create()
in the browser window.
Since you cannot use app.getAppPath in a preload script, we expose the app path and some other prisma related stuff like query engine paths via IPC, which we can then read from the preload script.
// main.ts process, where electron.app.getAppPath() is accessible
ipcMain.on('config:get-app-path', (event) => {
event.returnValue = app.getAppPath();
});
// shortened for brevity
// we also get the query engine path and the db location here, exposed in a similar fashion
// preload script where we can access IPC
import { ipcRenderer } from 'electron';
import { PrismaClient } from '@prisma/client';
const dbPath = ipcRenderer.sendSync('config:get-prisma-db-path');
const qePath = ipcRenderer.sendSync('config:get-prisma-qe-path');
export const prisma = new PrismaClient({
datasources: {
db: {
url: `file:${dbPath}`,
},
},
__internal: {
engine: {
// @ts-expect-error
binaryPath: qePath,
},
},
});
Then we use a custom postinstall script that replaces the process.cwd()
call with an IPC call that will return the app path.
const replace = require('replace-in-file');
const path = require('path');
const options = {
files: path.join(__dirname, '../node_modules/', '.prisma', 'client', 'index.js'),
from: 'findSync(process.cwd()',
to: `findSync(require("electron").ipcRenderer.sendSync('config:get-app-path')`,
};
const results = replace.sync(options);
Since IPC handlers are done in user land and other people might structure their projects differently, I'm afraid there is no "one-fits-all" solution here. Therefore my suggestion to expose a path config variable. BTW: This very same issue should be applicable to all use cases where you use prisma for a custom CLI (like internal tooling), where process.cwd()
would return the path from where the CLI command is called instead of the origin, where the CLI command's code exist.
// preload script
const dbPath = ipcRenderer.sendSync('config:get-prisma-db-path');
const qePath = ipcRenderer.sendSync('config:get-prisma-qe-path');
const appPath = ipcRenderer.sendSync('config:get-app-path');
export const prisma = new PrismaClient({
datasources: {
db: {
url: `file:${dbPath}`,
},
},
__internal: {
cwd: appPath, // this does not exist
engine: {
// @ts-expect-error
binaryPath: qePath,
},
},
});
To answer your original question, you can detect electron like this:
// https://github.com/cheton/is-electron/blob/master/index.js
// https://github.com/electron/electron/issues/2288
function isElectron() {
// Renderer process
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
return true;
}
// Main process
if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
return true;
}
// Detect the user agent when the `nodeIntegration` option is set to false
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
return true;
}
return false;
}
module.exports = isElectron;
There is another, but related, issue:
The generated prisma client defines the search paths in findSync in a OS-specific manner.
Example after using the build script by @awohletz
const dirname = findSync(require('electron').app.getAppPath(), [
"src/main/prisma-app-client",
"main/prisma-app-client",
], ['d'], ['d'], 1)[0] || __dirname
This causes an error on windows where the search path would be
const dirname = findSync(require('electron').app.getAppPath(), [
"src\\main\\prisma-app-client",
"main\\prisma-app-client",
], ['d'], ['d'], 1)[0] || __dirname
It would be helpful if the matches would be written in a cross-platform conform manner.
Originally posted by @awohletz in https://github.com/prisma/prisma/issues/8484#issuecomment-969403133