bubenshchykov / ngrok

Expose your localhost to the web. Node wrapper for ngrok.
https://ngrok.com
2.33k stars 317 forks source link

set binPath for Electron #218

Closed realrecordzLab closed 3 years ago

realrecordzLab commented 3 years ago

I've installed the library to create a tunnel between my Electron app and internet. I'm not sure if I'm doing it in the right way but I'm unable to use ngrok.

const { ipcMain } = require('electron');
const path = require('path');
const http = require('http');
const ngrok = require('ngrok');

let server;

const createServer = () => {
  server = http.createServer( (req, res) => {
    res.writeHead(200);
    res.end('Server ok!');
  }).listen(5539);
  console.log('createServer log:\n', server);  
}

ipcMain.on('start-server', (event, data) =>  {
  console.log('message from renderer!');
  createServer();
  ngrok.connect({
    proto: 'http', 
    port: 5539, 
    binPath: path => path.replace('app.asar', 'app.asar.unpacked/bin')
  }, (err, url) => {
    if( err ) console.log(err);
    event.reply('server-status', { details: `Tunnel link ${url}` });
  });  
});

When I run the app in serve mode I always get this error:

Uncaught Exception:
Error: spawn /Users/dev/Sites/chrok/dist_electron/bin/ngrok ENOENT
    at Process.ChildProcess._handle.onexit (internal/child_process.js:267:19)
    at onErrorNT (internal/child_process.js:469:16)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)

I've also tried to set the binPath but without success. Any idea of how to make things working?

philnash commented 3 years ago

I see that line about setting the binPath in electron comes from the README. It does specifically say that it is for production deploys of an electron app. Are you finding this doesn't work when developing the app?

I have played around with this package in an electron app before and I didn't not package it for production. I found I did not need to set the binPath in development at all.

So, I would try removing the binPath for now and then coming back to this when you package from production.

Let me know how you get on with that.

realrecordzLab commented 3 years ago

I see that line about setting the binPath in electron comes from the README. It does specifically say that it is for production deploys of an electron app. Are you finding this doesn't work when developing the app?

I have played around with this package in an electron app before and I didn't not package it for production. I found I did not need to set the binPath in development at all.

So, I would try removing the binPath for now and then coming back to this when you package from production.

Let me know how you get on with that.

The error happens also if the binPath isn't set inside the config options when I try serve mode, not only in production.

philnash commented 3 years ago

What is the error when you don't provide a binPath?

realrecordzLab commented 3 years ago

What is the error when you don't provide a binPath?

I'ts the same of the issue

Uncaught Exception:
Error: spawn /Users/dev/Sites/chrok/dist_electron/bin/ngrok ENOENT
    at Process.ChildProcess._handle.onexit (internal/child_process.js:267:19)
    at onErrorNT (internal/child_process.js:469:16)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)
philnash commented 3 years ago

It looks like you are maybe trying to package the application somehow, is that right? Your code is looking for the ngrok binary in the dist_electron directory. I guess you are using electron-builder for this? I haven't used that before, so I don't know how to help straight away.

Can you look through the dist_electron directory and see whether the ngrok binary is located somewhere else that you can refer to using the binPath option?

realrecordzLab commented 3 years ago

I'm using vue-cli-plugin-electron-builder and in serve mode to test the app there are only two files generated from webpack . in production all modules and files are bundled inside the app.asar file. Not sure if the ngrok binary is copied because the bundled folder that is the unpacked version of the app have an empty node_modules folder

Il giorno lun 29 mar 2021 alle ore 00:05 Phil Nash @.***> ha scritto:

It looks like you are maybe trying to package the application somehow, is that right? Your code is looking for the ngrok binary in the dist_electron directory. I guess you are using electron-builder for this? I haven't used that before, so I don't know how to help straight away.

Can you look through the dist_electron directory and see whether the ngrok binary is located somewhere else that you can refer to using the binPath option?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/bubenshchykov/ngrok/issues/218#issuecomment-808966687, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMKZMUS5TE7BD4LLL4XXJS3TF6RZZANCNFSM4Z45XA6Q .

philnash commented 3 years ago

Can you create a minimal reproduction of this error that I can take a look at?

Also, notably with this project, the ngrok executable for the relevant system is downloaded at install time with a postinstall script. So it shouldn't be copied into the build, unless you are copying in a different ngrok executable per OS build. If the postinstall step is not run by the application, you may instead want to download the executable yourself using the functions exported by download.js.

realrecordzLab commented 3 years ago

Can you create a minimal reproduction of this error that I can take a look at?

Also, notably with this project, the ngrok executable for the relevant system is downloaded at install time with a postinstall script. So it shouldn't be copied into the build, unless you are copying in a different ngrok executable per OS build. If the postinstall step is not run by the application, you may instead want to download the executable yourself using the functions exported by download.js.

The reprodution is the one of the code at the top issue.

philnash commented 3 years ago

I have never created a vue-cli-plugin-electron-builder application before, so I'd appreciate a bit more help reproducing this issue if I'm going to help you solve it. Thanks!

realrecordzLab commented 3 years ago

what info can help you to reproduce the issue? As I know it's the same of setup a normal electron app

philnash commented 3 years ago

I've never run a vue-cli-plugin-electron-builder project before. What do I do to create the application? Where do I put the code above for it to run? How do I run the application?

I need instructions to get to the point of reproducing the error, then I can try to work out how to fix it for you.

realrecordzLab commented 3 years ago

I've never run a vue-cli-plugin-electron-builder project before. What do I do to create the application? Where do I put the code above for it to run? How do I run the application?

I need instructions to get to the point of reproducing the error, then I can try to work out how to fix it for you.

You need to have vue-cli installed, create a new project uting it or using the vue ui that have a GUI to do the same things of the pure cli.

After you've added the plugin and ngrok as project dependencies you need to create a preload.js file to be able to use the ipc from the renderer. More info are here

The code I've pasted into the issue is imported by the background.js file that is created when you add the vue electron builder plugin to the project to create the electron needed setup. I put it inside a file called server.js. You can use import './server' to load it. On the front-end I'm simply sending an event when the mounted hook is fired window.ipcRenderer.send('start-server'). In the same mounted hook I'm also listenong for response from ipcMain in this way window.ipcRenderer.receive('server-status', (data) => { ... })

philnash commented 3 years ago

Is there a chance you could create this example project and upload it to GitHub so that I can just clone it and get to debugging?

realrecordzLab commented 3 years ago

Is there a chance you could create this example project and upload it to GitHub so that I can just clone it and get to debugging?

here is the exact copy of the files that I'm working on https://github.com/realrecordzLab/temp-electron/ let me know when you've cloned so I can remove the repo.

philnash commented 3 years ago

I have cloned the repo. Will see what I can find.

realrecordzLab commented 3 years ago

I have cloned the repo. Will see what I can find.

Any news about the problem?

philnash commented 3 years ago

Ok, I had a play with it and figured out the issue.

The problem is that when you build the application, in either dev or production, the binary from the ngrok package is not part of the build. However, this is likely something that you want, since the binary that you have downloaded on the system on which you are building the application is not necessarily the one that is going to run on the system the app is eventually run on.

So, what you need to do instead is use the built in function to download the ngrok binary for the system your application is running on. The source of the download function is here.

I apologise that I didn't work this out earlier, it's exactly what I have to do with this package when I use it in a VSCode plugin that I have written. You can see that when my extension activates I attempt to download the binary. I have written a wrapper function around the download function that shows progress and checks to see if the binary is already present, which might be something similar to what you want to do in your electron app too.

I hope this helps. I will close the issue as it's not a bug with this package, but do feel free to keep messaging if I can help further.

realrecordzLab commented 3 years ago

Ok, I had a play with it and figured out the issue.

The problem is that when you build the application, in either dev or production, the binary from the ngrok package is not part of the build. However, this is likely something that you want, since the binary that you have downloaded on the system on which you are building the application is not necessarily the one that is going to run on the system the app is eventually run on.

So, what you need to do instead is use the built in function to download the ngrok binary for the system your application is running on. The source of the download function is here.

I apologise that I didn't work this out earlier, it's exactly what I have to do with this package when I use it in a VSCode plugin that I have written. You can see that when my extension activates I attempt to download the binary. I have written a wrapper function around the download function that shows progress and checks to see if the binary is already present, which might be something similar to what you want to do in your electron app too.

I hope this helps. I will close the issue as it's not a bug with this package, but do feel free to keep messaging if I can help further.

Ok, so this isn't a bug in the library but only the desire beahviour. If I understand, the library will not download the ngrok executable and needs to be instructed to do it based on the platform where the final electron app will run, rught? I saw the download ngork binary link youn've suggested, it include a callback, can you show an example for electron usage? I'm not planning to have a callback but only to implement the download of the binary after first run of the app. A bit of help will be appreciated. Thank you for the help :)

philnash commented 3 years ago

If I understand, the library will not download the ngrok executable and needs to be instructed to do it based on the platform where the final electron app will run, right?

That's correct. In normal use, this library will download the correct ngrok executable in a postinstall script. But, because you are building an electron app, that doesn't happen. Instead, you need to download the executable within your application.

I saw the download ngrok binary link you've suggested, it include a callback, can you show an example for electron usage?

I have not written this code within an electron app, so my best example is the VS Code extension that I have already linked you to. Technically that means it is running within electron, it's just loaded as an extension.

The code to download the executable runs every time the extension is activated, but it does a check to see if the executable is already present and exits early if it is.

I'm not sure how else I can help. Have you tried to implement similar in your electon app yet?

realrecordzLab commented 3 years ago

If I understand, the library will not download the ngrok executable and needs to be instructed to do it based on the platform where the final electron app will run, right?

That's correct. In normal use, this library will download the correct ngrok executable in a postinstall script. But, because you are building an electron app, that doesn't happen. Instead, you need to download the executable within your application.

I saw the download ngrok binary link you've suggested, it include a callback, can you show an example for electron usage?

I have not written this code within an electron app, so my best example is the VS Code extension that I have already linked you to. Technically that means it is running within electron, it's just loaded as an extension.

The code to download the executable runs every time the extension is activated, but it does a check to see if the executable is already present and exits early if it is.

I'm not sure how else I can help. Have you tried to implement similar in your electon app yet?

I'm not sure if I'm implementing ngork binary download in the right way but I'm getting this error in console

(node:3847) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.

null undefined
ngrok - cached download found at /Users/dev/.ngrok/aHR0cHM6Ly9iaW4uZXF1aW5veC5pby9jLzRWbUR6QTdpYUhiL25ncm9rLXN0YWJsZS1kYXJ3aW4tYW1kNjQuemlw.zip
ngrok - unpacking binary

ngrok - binary unpacked to /Users/dev/Sites/chrok/dist_electron/bin/ngrok

(node:3847) UnhandledPromiseRejectionWarning: Error: App threw an error during load
    at Socket.eval (webpack:///./node_modules/ngrok/process.js?:72:10)

    at Socket.emit (events.js:315:20)
    at addChunk (_stream_readable.js:295:12)
    at readableAddChunk (_stream_readable.js:271:9)
    at Socket.Readable.push (_stream_readable.js:212:10)
    at Pipe.onStreamRead (internal/stream_base_commons.js:186:23)
(node:3847) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:3847) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I've implemented this code block when the ipcMain process receive the start-server comand. It's supposed to check if the path where ngrok binary is supposed to be saved and if not download it. Anyway I'm not able to see the reply message with the tunnel address in front-end

ipcMain.on('start-server', (event, data) =>  {
  console.log('message from renderer!');
  createServer();

  if( !fs.existsSync( binPath ) ){
    fs.mkdir(binPath, (error, path) => {
      console.log(error, path);
      const ngk = new Promise( (resolve, reject) =>
        downloadNgrok((error) => (error ? reject(error) : resolve()))
      );
      ngk.then( () => {
        ngrok.connect({
          proto: 'http', 
          port: 15089, 
          //binPath: __dirname => __dirname.replace('app.asar', 'app.asar.unpacked')
        }, (err, url) => {
          if( err ) console.log(err);
          event.reply('server-status', { details: `Tunnel link ${url}` });
        }); 
      });
    });
  } else {
    ngrok.connect({
      proto: 'http', 
      port: 15089, 
      //binPath: __dirname => __dirname.replace('app.asar', 'app.asar.unpacked')
    }, (err, url) => {
      if( err ) console.log(err);
      event.reply('server-status', { details: `Tunnel link ${url}` });
    });  
  }

});
philnash commented 3 years ago

Have you confirmed that the ngrok executable has been downloaded to /Users/dev/Sites/chrok/dist_electron/bin/ngrok?

What happens if you use that path when you try to start ngrok?

realrecordzLab commented 3 years ago

Have you confirmed that the ngrok executable has been downloaded to /Users/dev/Sites/chrok/dist_electron/bin/ngrok?

What happens if you use that path when you try to start ngrok?

I didn't tested yet, I'm refactoring the code to make it more modular. I will try, and let you know