From 80da640084ce164a237d0df0516b55887d864e59 Mon Sep 17 00:00:00 2001 From: Josh Guyette Date: Sun, 19 Mar 2023 00:34:14 -0500 Subject: [PATCH] updates --- .gitignore | 3 +- README.md | 4 +- ngenv | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++ ngrok.js | 88 --------------------------- package.json | 20 ++++--- 5 files changed, 181 insertions(+), 98 deletions(-) create mode 100755 ngenv delete mode 100644 ngrok.js diff --git a/.gitignore b/.gitignore index ec55de5..96e7070 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ yarn.lock -package.json.lock \ No newline at end of file +package.json.lock +.env diff --git a/README.md b/README.md index 610bf39..ea6e793 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ngrok-to-dotenv -This is a ngrok start-up wrapper that uses a dotenv file (at '../.env' relative to this project's folder) to get the authtoken and save the https address; it's a node cli tool that you can use with projects that need ngrok. You clone it into your project's folder, and add to the project's package.json a run script to call "cd ngrok-to-dotenv && node ngrok". Be sure to add the folder to your .gitignore and install the one package which is ngrok and command-line-args, using 'npm i' or 'yarn'. The 'node ngrok' script supports two arguments --proto or -P (string, the protocol), --port or -p (number, the port number). +This is a ngrok start-up wrapper that uses a dotenv file (at './.env') to get the authtoken and save the https address; it's a node cli tool that you can use with projects that need ngrok. You clone it into your project's folder, and add to the project's package.json a run script to call "cd ngrok-to-dotenv && node ngrok". Be sure to add the folder to your .gitignore and install the one package which is ngrok and command-line-args, using 'npm i' or 'yarn'. The 'node ngrok' script supports two arguments --proto or -P (string, the protocol), --port or -p (number, the port number). -By default 'node ngrok' (no arguments) is equivalent to 'node ngrok -P http -p 3000' and 'node ngrok --proto http --port 3000'. \ No newline at end of file +By default 'node ngrok' (no arguments) is equivalent to 'node ngrok -P http -p 3000' and 'node ngrok --proto http --port 3000'. diff --git a/ngenv b/ngenv new file mode 100755 index 0000000..e445f22 --- /dev/null +++ b/ngenv @@ -0,0 +1,164 @@ +#!/usr/bin/env node +const ngrok = require("ngrok"); +const fs = require("fs"); +const os = require("os"); +const { parse } = require("yaml"); + +// Parse the command line options +const commandLineArgs = require("command-line-args"); +const optionDefinitions = [ + { name: "proto", alias: "P", type: String }, + { name: "port", alias: "p", type: Number }, + { name: "env", alias: "e", type: String }, +]; +const options = commandLineArgs(optionDefinitions); +const dotenvFile = options?.env ?? "./.env"; + +// vars +let authToken = null; // string || null + +// Main (an async) function +(async function () { + // Get the ngrok config file path + let ngrokConfig = getNgrokConfig(); + + // Check if the ngrok config file exists + if (!fs.existsSync(ngrokConfig)) { + throw new Error("No ngrok config file found"); + } + + // Read the ngrok config file + try { + const { authToken } = readNgrokConfig(ngrokConfig); + global.authToken = authToken; + } catch (err) { + console.error(err); + process.exit(1); + } + + // Start ngrok + const proto = options?.proto ?? "http"; + const addr = options?.port ? options.port : 3000; + try { + const url = await ngrok.connect({ + authtoken: authToken, + proto, + addr, + onLogEvent: (data) => { + // combine the key-value pairs into a single object + const dataObj = parseLogLine(data); + // console.log(`[${dataObj.lvl}] ${dataObj.msg}`); + // console.log(dataObj); + }, + }); + writeDotEnv(url); // Sync the .env file + console.log(`Started ngrok: protocol '${proto}', addr '${addr}'`); + console.log(`Your current ngrok url is ${url}.`); + console.log(`Your .env file has been updated.`); + } catch (err) { + console.error(err.message); + process.exit(1); + } +})(); // end main async function + +function getNgrokConfig() { + // Newer versions of ngrok + let ngrokConfig = os.homedir() + "/.ngrok2/ngrok.yml"; + if (fs.existsSync(ngrokConfig)) { + return ngrokConfig; + } + + // Alternative location for linux + ngrokConfig = os.homedir() + "/.config/ngrok/ngrok.yml"; + if (fs.existsSync(ngrokConfig)) { + return ngrokConfig; + } + + // MacOS + ngrokConfig = os.homedir() + "/Library/Application Support/ngrok/ngrok.yml"; + if (fs.existsSync(ngrokConfig)) { + return ngrokConfig; + } + + // Windows + ngrokConfig = os.homedir() + "/AppData/Local/ngrok/ngrok.yml"; + if (fs.existsSync(ngrokConfig)) { + return ngrokConfig; + } + + throw new Error("ngrok config file not found"); +} + +function readNgrokConfig(ngrokConfig) { + const data = fs.readFileSync(ngrokConfig, "utf8"); + + let authToken; + + // Convert string to string array, split at newlines + let lines = data.split("\n"); // string[] + + // find the auth token (format: authtoken: token_goes_here) + lines.forEach((element) => { + const [name, value] = element.split(": "); + if (name === "authtoken") authToken = value; + }); + + // No token found + if (!authToken) { + // https://dashboard.ngrok.com/get-started/your-authtoken + throw new Error("Setup NGROK_AUTHTOKEN in your .env file"); + } + + return { authToken }; +} + +function readDotEnv() { + const data = fs.readFileSync(dotenvFile, "utf8"); + + // Convert string to string array, split at newlines + let lines = data.split("\n"); // string[] + + return lines; +} + +function writeDotEnv(url) { + // Read the .env file + let lines = readDotEnv(dotenvFile); + + // Rebuild lines with the new url + let found = false; + lines = lines.map((element) => { + const [name] = element.split("="); + if (name === "NGROK_SERVERHOST") { + found = true; + return `${name}=${url}`; + } + return element; + }); + + // Is this variable already in the .env, if not add it. + if (!found) { + lines.unshift(`NGROK_SERVERHOST=${url}`); + } + + // convert back to string format + let writeData = ""; + lines.forEach((element) => { + writeData += element + "\n"; + }); + writeData = writeData.slice(0, writeData.length - 1); + + // Write the new .env file + fs.writeFileSync(dotenvFile, writeData); +} + +function parseLogLine(line) { + const regex = /(\S+)=("[^"]+"|\S+)/g; + const logEntry = {}; + let match; + while ((match = regex.exec(line)) !== null) { + logEntry[match[1]] = match[2].replace(/"/g, ""); + } + logEntry.t = new Date(logEntry.t); + return logEntry; +} diff --git a/ngrok.js b/ngrok.js deleted file mode 100644 index 25a8410..0000000 --- a/ngrok.js +++ /dev/null @@ -1,88 +0,0 @@ -const ngrok = require("ngrok"); -const fs = require("fs"); -const { exec } = require("child_process"); - -const dotenvFile = "../.env"; - -const optionDefinitions = [ - { name: "proto", alias: 'P', type: String }, - { name: "port", alias: 'p', type: Number }, -]; - -const commandLineArgs = require("command-line-args"); -const options = commandLineArgs(optionDefinitions); - -console.log(options); - -// Read the .env file -fs.readFile(dotenvFile, "utf8", (err, data) => { - if (err) { - console.error(err); - return; - } - - // Convert string to string array, split at newlines - let lines = data.split("\n"); // string[] - let authtoken = null; // string || null - - // find the auth token - lines.forEach((element) => { - const [name, value] = element.split("="); - if (name === "NGROK_AUTHTOKEN") authtoken = value; - }); - - // No token found - if (!authtoken) { - console.error("Setup NGROK_AUTHTOKEN in your .env file"); - return; - } - - // Start ngrok - (async function () { - const proto = options?.proto ?? "http"; - const addr = options?.addr ? options.addr : 3000; - - // Start ngrok - try { - const url = await ngrok.connect({ authtoken, proto, addr }); - - // Rebuild lines with the new url - let found = false; - lines = lines.map((element) => { - const [name] = element.split("="); - if (name === "NGROK_SERVERHOST") { - found = true; - return `${name}=${url}`; - } - return element; - }); - - // Is this variable already in the .env, if not add it. - if (!found) { - lines.unshift(`NGROK_SERVERHOST=${url}`) - } - - // convert back to string format - let writeData = ""; - lines.forEach((element) => { - writeData += element + "\n"; - }); - writeData = writeData.slice(0, writeData.length - 1); - - // Write the new .env file - fs.writeFile(dotenvFile, writeData, (err) => { - if (err) { - console.error(err); - return; - } - console.clear(); - console.log(`Started ngrok: protocol '${proto}', addr '${addr}'`); - console.log(`Your current ngrok url is ${url}.`); - console.log(`Your .env file has been updated.`); - }); - } catch (err) { - console.error(err.message); - process.exit(1); - } - })(); -}); diff --git a/package.json b/package.json index c7b1a22..0346969 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,22 @@ { - "name": "ngrok-to-dotenv", - "version": "1.0.0", - "description": "ngrok start-up wrapper that uses a dotenv file to get the authtoken and save the https address", - "main": "ngrok.js", + "name": "ngenv", + "version": "1.1.1", + "description": "Writes ngrok tunnel url to .env file", + "main": "ngenv", "license": "MIT", + "bin": { + "ngenv": "./ngenv" + }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "debug": "node --inspect ngrok.js", - "start": "node ngrok" + "debug": "node --inspect ngenv", + "start": "node ngenv" }, "dependencies": { "command-line-args": "^5.2.0", "ngrok": "^4.2.2" + }, + "peerDependencies": { + "yaml": "*" } -} +} \ No newline at end of file