feature: Add support for getting inbound connection.

Also fixes any bugs I found.
This commit is contained in:
greysoh 2024-05-07 18:35:04 -04:00
parent b99d6a89dc
commit a9fcdea037
Signed by: imterah
GPG key ID: 8FA7DD57BA6CEA37
8 changed files with 146 additions and 17 deletions

View file

@ -4,7 +4,7 @@ import { run as connection } from "./commands/connections.js";
import { run as backends } from "./commands/backends.js";
import { run as users } from "./commands/users.js";
export type PrintLine = (...str: string[]) => void;
export type PrintLine = (...str: any[]) => void;
type Command = (
args: string[],

View file

@ -3,12 +3,25 @@ import type { Axios } from "axios";
import { SSHCommand } from "../libs/patchCommander.js";
import type { PrintLine } from "../commands.js";
// https://stackoverflow.com/questions/37938504/what-is-the-best-way-to-find-all-items-are-deleted-inserted-from-original-arra
function difference(a: any[], b: any[]) {
return a.filter(x => b.indexOf(x) < 0);
};
export async function run(
argv: string[],
println: PrintLine,
axios: Axios,
apiKey: string,
token: string,
) {
let resolve: (value: unknown) => void;
let reject: (value: unknown) => void;
const promise = new Promise((ourResolve, ourReject) => {
resolve = ourResolve;
reject = ourReject;
});
const program = new SSHCommand(println);
program.description("Manages connections for NextNet");
program.version("v0.1.0-preprod");
@ -76,6 +89,102 @@ export async function run(
getInbound.argument("<id>", "Tunnel ID to view inbound connections of");
getInbound.option("-t, --tail", "Live-view of connection list");
getInbound.action(async(idStr: string, options: {
tail?: boolean
}): Promise<void> => {
type InboundConnectionSuccess = {
success: true,
data: {
ip: string,
port: number,
connectionDetails: {
sourceIP: string,
sourcePort: number,
destPort: number,
enabled: boolean
}
}[]
};
const id = parseInt(idStr);
if (Number.isNaN(id)) {
println("ID (%s) is not a number\n", idStr);
return;
}
if (options.tail) {
let previousEntries: string[] = [];
while (true) {
const response = await axios.post("/api/v1/forward/connections", {
token,
id
});
if (response.status != 200) {
if (process.env.NODE_ENV != "production") console.log(response);
if (response.data.error) {
println(`Error: ${response.data.error}\n`);
} else {
println("Error requesting connections!\n");
}
return resolve(null);
}
const { data }: InboundConnectionSuccess = response.data;
const simplifiedArray: string[] = data.map((i) => `${i.ip}:${i.port}`);
const insertedItems: string[] = difference(simplifiedArray, previousEntries);
const removedItems: string[] = difference(previousEntries, simplifiedArray);
insertedItems.forEach((i) => println("CONNECTED: %s\n", i));
removedItems.forEach((i) => println("DISCONNECTED: %s\n", i));
previousEntries = simplifiedArray;
await new Promise((i) => setTimeout(i, 2000));
}
} else {
const response = await axios.post("/api/v1/forward/connections", {
token,
id
});
if (response.status != 200) {
if (process.env.NODE_ENV != "production") console.log(response);
if (response.data.error) {
println(`Error: ${response.data.error}\n`);
} else {
println("Error requesting connections!\n");
}
return resolve(null);
}
const { data }: InboundConnectionSuccess = response.data;
if (data.length == 0) {
println("There are currently no connected clients.\n");
return resolve(null);
}
println("Connected clients (for source: %s:%s):\n", data[0].connectionDetails.sourceIP, data[0].connectionDetails.sourcePort);
for (const entry of data) {
println(" - %s:%s\n", entry.ip, entry.port);
}
console.log(response.data);
}
return resolve(null);
});
const removeTunnel = new SSHCommand(println, "rm");
removeTunnel.description("Removes a tunnel");
removeTunnel.argument("<id>", "Tunnel ID to remove");
@ -88,4 +197,7 @@ export async function run(
program.addCommand(removeTunnel);
program.parse(argv);
if (program.hasRecievedExitSignal) return;
await promise;
}

View file

@ -80,8 +80,8 @@ server.on("connection", client => {
"Welcome to NextNet LOM. Run 'help' to see commands.\r\n\r\n~$ ",
);
function println(...str: string[]) {
stream.write(format(...str).replaceAll("\n", "\r\n"));
function println(...data: any[]) {
stream.write(format(...data).replaceAll("\n", "\r\n"));
};
while (true) {

View file

@ -35,7 +35,7 @@ export class SSHCommand extends Command {
if (process.env.NODE_ENV != "production") {
println(
"Caught irrecoverable action (command help call) in patchCommander\n",
"Caught irrecoverable crash (command help call) in patchCommander\n",
);
} else {
println("Aborted\n");
@ -46,12 +46,22 @@ export class SSHCommand extends Command {
}
}
_exit() {
recvExitDispatch() {
this.hasRecievedExitSignal = true;
let parentElement = this.parent;
while (parentElement instanceof SSHCommand) {
parentElement.hasRecievedExitSignal = true;
parentElement = parentElement.parent;
};
}
_exit() {
this.recvExitDispatch();
}
_exitCallback() {
this.hasRecievedExitSignal = true;
this.recvExitDispatch();
}
action(fn: (...args: any[]) => void | Promise<void>): this {
@ -63,7 +73,7 @@ export class SSHCommand extends Command {
// @ts-ignore
this._actionHandler = async (...args: any[]): Promise<void> => {
if (args[0][0] == "--help" || args[0][0] == "-h") return;
if (this.hasRecievedExitSignal) return;
await oldActionHandler(...args);
};