Added a "remote diagnostics" tool

This commit is contained in:
David Carley
2022-09-12 00:05:57 +00:00
parent d151e668e2
commit 55595e5fb8
6 changed files with 3006 additions and 4 deletions

243
scripts/support.js Executable file
View File

@@ -0,0 +1,243 @@
#!/usr/bin/env node
const inquirer = require("inquirer");
const { SSM } = require("@aws-sdk/client-ssm");
const { initSignalHandlers } = require("./util");
const fetch = require("node-fetch");
let ssm;
const Commands = [
"code",
"ssh",
"web",
"disconnect"
];
initSignalHandlers();
main();
async function main() {
await getAWSCredentials();
const { command } = await inquirer.prompt({
type: "list",
name: "command",
choices: Commands
});
switch (command) {
case "code":
return await commandCode();
case "ssh":
return await commandSsh();
case "web":
return await commandWeb();
case "disconnect":
return await commandDisconnect();
}
}
async function commandCode() {
await closeTunnels();
await updateNgrokAuthToken();
const code = `000000${Math.random() * 999999}`.slice(-6);
await saveParam("code", code);
console.log(`The code is: ${code}`);
}
async function commandSsh() {
const tunnels = await loadTunnels();
if (!tunnels.length) {
console.log("There are no tunnels!");
return;
}
const sshTunnel = tunnels.find(tunnel => tunnel.proto === "tcp");
const [ , host, port ] = sshTunnel.public_url.match(/tcp:\/\/([^:]+):(\d+)/);
console.log("Run this:");
console.log();
console.log(`ssh bbmc@${host} -p ${port}`);
console.log();
}
async function commandWeb() {
const url = await getWebUrl();
console.log(`Web interface: ${url}`);
console.log();
}
async function commandDisconnect() {
const tunnels = await loadTunnels();
if (!tunnels.length) {
console.log("There are no tunnels!");
process.exit(1);
}
const webTunnel = tunnels.find(tunnel => tunnel.proto === "https");
const response = await fetch(`${webTunnel.public_url}/api/remote-diagnostics?command=disconnect`, {
headers: {
Authorization: `Basic ${Buffer.from("onefinity:onefinity").toString("base64")}`
}
});
console.log();
console.log(await response.text());
console.log();
console.log("We now need a new ngrok auth token.");
await promptForNewAuthToken();
}
async function updateNgrokAuthToken() {
const storedAuthToken = await loadParam("ngrok-auth-token", "");
const apiKey = await loadParam("ngrok-api-key");
const response = await fetch("https://api.ngrok.com/credentials", {
headers: {
"Authorization": `Bearer ${apiKey}`,
"Ngrok-Version": "2"
}
});
const result = await response.json();
let authToken = result.credentials[0].token;
if (authToken === storedAuthToken) {
console.warn("The nGrok AuthToken is stale.");
const { ignore } = await inquirer.prompt({
type: "confirm",
name: "ignore"
});
if (!ignore) {
authToken = await promptForNewAuthToken();
}
}
await saveParam("ngrok-auth-token", authToken);
}
async function promptForNewAuthToken() {
console.warn("To re-issue, visit: https://dashboard.ngrok.com/get-started/your-authtoken");
const { newAuthToken } = await inquirer.prompt({
type: "input",
name: "newAuthToken"
});
return newAuthToken;
}
function getParamName(name) {
return `/onefinity-support/${name}`;
}
async function loadParam(name, defaultValue = undefined) {
name = getParamName(name);
try {
const response = await ssm.getParameter({ Name: name });
return response.Parameter.Value;
} catch (error) {
if (error.name !== "ParameterNotFound") {
console.log(`Error getting parameter "${name}"`, JSON.stringify({
name: error.name,
message: error.message,
stack: error.stack,
cause: error.cause
}, null, 4));
}
if (defaultValue === undefined) {
throw error;
}
return defaultValue;
}
}
async function saveParam(name, value) {
await ssm.putParameter({
Name: getParamName(name),
Value: value,
DataType: "text",
Overwrite: true,
Tier: "Standard",
Type: "String"
});
}
async function getAWSCredentials() {
const { accessKeyId, secretAccessKey } = await inquirer.prompt([
{
type: "input",
name: "accessKeyId"
},
{
type: "input",
name: "secretAccessKey"
}
]);
ssm = new SSM({
credentials: {
accessKeyId,
secretAccessKey
},
region: "us-east-1"
});
}
async function loadTunnels() {
const apiKey = await loadParam("ngrok-api-key");
const response = await fetch("https://api.ngrok.com/tunnels", {
headers: {
Authorization: `Bearer ${apiKey}`,
"Ngrok-Version": 2
}
});
const { tunnels } = await response.json();
return tunnels;
}
async function getWebUrl() {
const tunnels = await loadTunnels();
if (!tunnels.length) {
console.log("There are no tunnels!");
process.exit(1);
}
const webTunnel = tunnels.find(tunnel => tunnel.proto === "https");
const url = new URL(webTunnel.public_url);
url.username = "onefinity";
url.password = "onefinity";
url.protocol = "http";
return url.toString();
}
async function closeTunnels() {
const tunnels = await loadTunnels();
if (!tunnels?.length) {
return;
}
console.error("There are tunnels open:", JSON.stringify(tunnels, null, 4));
console.error("Giving up");
process.exit(1);
}