pinggy 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -88,7 +88,7 @@ function parseUsers(positionalArgs, explicitToken) {
88
88
  }
89
89
  function parseType(finalConfig, values, inferredType) {
90
90
  const t = inferredType || values.type || finalConfig.tunnelType;
91
- if (t === "http" /* TunnelType.Http */ || t === "tcp" /* TunnelType.Tcp */ || t === "tls" /* TunnelType.Tls */ || t === "udp" /* TunnelType.Udp */) {
91
+ if (t === "http" /* TunnelType.Http */ || t === "tcp" /* TunnelType.Tcp */ || t === "tls" /* TunnelType.Tls */ || t === "udp" /* TunnelType.Udp */ || t === "tlstcp" /* TunnelType.TlsTcp */) {
92
92
  finalConfig.tunnelType = [t];
93
93
  }
94
94
  }
@@ -37,7 +37,7 @@ export function parseExtendedOptions(options, config) {
37
37
  // Whitelist IPs
38
38
  if (value) {
39
39
  const ips = value.split(",").map(ip => ip.trim()).filter(Boolean);
40
- const invalidIps = ips.filter(ip => !isValidIpV4Cidr(ip));
40
+ const invalidIps = ips.filter(ip => !(isValidIpV4Cidr(ip) || isValidIpV6Cidr(ip)));
41
41
  if (invalidIps.length > 0) {
42
42
  CLIPrinter.warn(`Invalid IP/CIDR(s) in whitelist: ${invalidIps.join(", ")}`);
43
43
  logger.warn(`Warning: Invalid IP/CIDR(s) in whitelist: ${invalidIps.join(", ")}`);
@@ -132,3 +132,18 @@ function isValidIpV4Cidr(input) {
132
132
  }
133
133
  return false;
134
134
  }
135
+ function isValidIpV6Cidr(input) {
136
+ // Check for CIDR notation
137
+ if (input.includes('/')) {
138
+ const [rawIp, mask] = input.split('/');
139
+ if (!rawIp || !mask)
140
+ return false;
141
+ // Strip zone index (e.g. %eth0) and surrounding brackets if present
142
+ const ip = rawIp.split('%')[0].replace(/^\[|\]$/g, '');
143
+ const isIp6 = isIP(ip) === 6;
144
+ const maskNum = parseInt(mask, 10);
145
+ const isMaskValid = !isNaN(maskNum) && maskNum >= 0 && maskNum <= 128;
146
+ return isIp6 && isMaskValid;
147
+ }
148
+ return false;
149
+ }
@@ -19,7 +19,9 @@ export const cliOptions = {
19
19
  // Logging options (CLI overrides env)
20
20
  loglevel: { type: 'string', description: 'Logging level: ERROR, INFO, DEBUG. Overrides PINGGY_LOG_LEVEL environment variable' },
21
21
  logfile: { type: 'string', description: 'Path to log file. Overrides PINGGY_LOG_FILE environment variable' },
22
- printlog: { type: 'boolean', short: 'g', description: 'Also print logs to stdout. Overrides PINGGY_LOG_STDOUT environment variable' },
22
+ v: { type: 'boolean', description: 'Print logs to stdout for Cli. Overrides PINGGY_LOG_STDOUT environment variable' },
23
+ vv: { type: 'boolean', description: 'Enable detailed logging for the Node.js SDK and Libpinggy, including both info and debug level logs.' },
24
+ vvv: { type: 'boolean', description: 'Enable all logs from Cli, SDK and internal components.' },
23
25
  // Save and load config
24
26
  saveconf: { type: 'string', description: 'Create the configuration file based on the options provided here' },
25
27
  conf: { type: 'string', description: 'Use the configuration file as base. Other options will be used to override this file' },
@@ -9,13 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
11
  import CLIPrinter from "../utils/printer.js";
12
+ import { TunnelManager } from "../tunnel_manager/TunnelManager.js";
12
13
  import chalk from "chalk";
13
14
  import TunnelTui from "../tui/index.js";
14
15
  import { withFullScreen } from "fullscreen-ink";
15
- import path from "node:path";
16
- import { Worker } from "node:worker_threads";
17
16
  import { getFreePort } from "../utils/getFreePort.js";
18
- import { fileURLToPath } from "url";
19
17
  import { logger } from "../logger.js";
20
18
  import React, { useState } from "react";
21
19
  const TunnelData = {
@@ -26,8 +24,6 @@ const TunnelData = {
26
24
  let activeTui = null;
27
25
  let disconnectState = null;
28
26
  let updateDisconnectState = null;
29
- const __filename = fileURLToPath(import.meta.url);
30
- const __dirname = path.dirname(__filename);
31
27
  const TunnelTuiWrapper = ({ finalConfig, urls, greet }) => {
32
28
  const [disconnectInfo, setDisconnectInfo] = useState(null);
33
29
  React.useEffect(() => {
@@ -40,118 +36,95 @@ const TunnelTuiWrapper = ({ finalConfig, urls, greet }) => {
40
36
  };
41
37
  export function startCli(finalConfig, manager) {
42
38
  return __awaiter(this, void 0, void 0, function* () {
39
+ var _a, _b, _c;
43
40
  if (!finalConfig.NoTUI && finalConfig.webDebugger === "") {
44
41
  // Need a webdebugger port
45
42
  const freePort = yield getFreePort(finalConfig.webDebugger || "");
46
43
  finalConfig.webDebugger = `localhost:${freePort}`;
47
44
  }
48
- const workerPath = path.resolve(__dirname, "../workers/worker.js");
49
45
  try {
50
- const worker = new Worker(workerPath, {
51
- workerData: { finalConfig },
46
+ const manager = TunnelManager.getInstance();
47
+ const tunnel = manager.createTunnel(finalConfig);
48
+ CLIPrinter.startSpinner("Connecting to Pinggy...");
49
+ if (!finalConfig.NoTUI) {
50
+ manager.registerStatsListener(tunnel.tunnelid, (tunnelId, stats) => {
51
+ var _a;
52
+ (_a = globalThis.__PINGGY_TUNNEL_STATS__) === null || _a === void 0 ? void 0 : _a.call(globalThis, stats);
53
+ });
54
+ }
55
+ manager.registerWorkerErrorListner(tunnel.tunnelid, (_tunnelid, error) => {
56
+ // The CLI terminates in this callback because these errors occur only when the tunnel worker
57
+ // exits, crashes, or encounters critical problems (e.g., authentication failure or primary forwarding failure).
58
+ CLIPrinter.error(`${error.message}`);
52
59
  });
53
- worker.on("message", (msg) => __awaiter(this, void 0, void 0, function* () {
54
- var _a, _b, _c, _d, _e;
55
- switch (msg.type) {
56
- case "created":
57
- CLIPrinter.startSpinner(msg.message);
58
- break;
59
- case "started":
60
- CLIPrinter.stopSpinnerSuccess(msg.message);
61
- CLIPrinter.success(chalk.bold("Tunnel established!"));
62
- CLIPrinter.print(chalk.gray("───────────────────────────────"));
63
- break;
64
- case "urls":
65
- TunnelData.urls = msg.urls;
66
- CLIPrinter.info(chalk.cyanBright("Remote URLs:"));
67
- ((_a = TunnelData.urls) !== null && _a !== void 0 ? _a : []).forEach((url) => CLIPrinter.print(" " + chalk.magentaBright(url)));
68
- CLIPrinter.print(chalk.gray("───────────────────────────────"));
69
- CLIPrinter.print(chalk.gray("\nPress Ctrl+C to stop the tunnel.\n"));
70
- break;
71
- case "greetmsg":
72
- TunnelData.greet = msg.message;
73
- if ((_b = TunnelData.greet) === null || _b === void 0 ? void 0 : _b.includes("not authenticated")) {
74
- // show unauthenticated warning
75
- CLIPrinter.warn(chalk.yellowBright(TunnelData.greet));
76
- }
77
- else if ((_c = TunnelData.greet) === null || _c === void 0 ? void 0 : _c.includes("authenticated as")) {
78
- // extract email
79
- const emailMatch = /authenticated as (.+)/.exec(TunnelData.greet);
80
- if (emailMatch) {
81
- const email = emailMatch[1];
82
- CLIPrinter.info(chalk.cyanBright("Authenticated as: " + email));
83
- }
84
- }
85
- CLIPrinter.print(chalk.gray("───────────────────────────────"));
86
- break;
87
- case "status":
88
- CLIPrinter.info(msg.message || "Status update from worker");
89
- break;
90
- case "usage":
91
- TunnelData.usage = msg.usage;
92
- (_d = globalThis.__PINGGY_TUNNEL_STATS__) === null || _d === void 0 ? void 0 : _d.call(globalThis, msg.usage);
93
- break;
94
- case "TUI":
95
- if (!finalConfig.NoTUI) {
96
- const tui = withFullScreen(_jsx(TunnelTuiWrapper, { finalConfig: finalConfig, urls: TunnelData.urls, greet: TunnelData.greet }));
97
- activeTui = tui;
98
- try {
99
- yield tui.start();
100
- yield tui.waitUntilExit();
101
- }
102
- catch (e) {
103
- logger.warn("TUI error", e);
104
- }
105
- finally {
106
- activeTui = null;
107
- }
108
- }
109
- break;
110
- case "warnings":
111
- CLIPrinter.warn(msg.message);
112
- break;
113
- case "disconnected":
114
- if (activeTui && updateDisconnectState) {
115
- disconnectState = {
116
- disconnected: true,
117
- error: msg.error,
118
- messages: msg.messages
119
- };
120
- updateDisconnectState(disconnectState);
121
- try {
122
- // Wait for Ink to fully exit
123
- yield activeTui.waitUntilExit();
124
- }
125
- catch (e) {
126
- logger.warn("Failed to wait for TUI exit", e);
127
- }
128
- finally {
129
- activeTui = null;
130
- }
131
- }
132
- if ((_e = msg.messages) === null || _e === void 0 ? void 0 : _e.length) {
133
- msg.messages.forEach((m) => CLIPrinter.print(m));
134
- }
60
+ yield manager.startTunnel(tunnel.tunnelid);
61
+ CLIPrinter.stopSpinnerSuccess("Connected to Pinggy");
62
+ CLIPrinter.success(chalk.bold("Tunnel established!"));
63
+ CLIPrinter.print(chalk.gray("───────────────────────────────"));
64
+ TunnelData.urls = yield manager.getTunnelUrls(tunnel.tunnelid);
65
+ TunnelData.greet = yield manager.getTunnelGreetMessage(tunnel.tunnelid);
66
+ CLIPrinter.info(chalk.cyanBright("Remote URLs:"));
67
+ ((_a = TunnelData.urls) !== null && _a !== void 0 ? _a : []).forEach((url) => CLIPrinter.print(" " + chalk.magentaBright(url)));
68
+ CLIPrinter.print(chalk.gray("───────────────────────────────"));
69
+ if ((_b = TunnelData.greet) === null || _b === void 0 ? void 0 : _b.includes("not authenticated")) {
70
+ // show unauthenticated warning
71
+ CLIPrinter.warn(chalk.yellowBright(TunnelData.greet));
72
+ }
73
+ else if ((_c = TunnelData.greet) === null || _c === void 0 ? void 0 : _c.includes("authenticated as")) {
74
+ // extract email
75
+ const emailMatch = /authenticated as (.+)/.exec(TunnelData.greet);
76
+ if (emailMatch) {
77
+ const email = emailMatch[1];
78
+ CLIPrinter.info(chalk.cyanBright("Authenticated as: " + email));
79
+ }
80
+ }
81
+ CLIPrinter.print(chalk.gray("───────────────────────────────"));
82
+ CLIPrinter.print(chalk.gray("\nPress Ctrl+C to stop the tunnel.\n"));
83
+ manager.registerDisconnectListener(tunnel.tunnelid, (tunnelId, error, messages) => __awaiter(this, void 0, void 0, function* () {
84
+ if (activeTui && updateDisconnectState) {
85
+ disconnectState = {
86
+ disconnected: true,
87
+ error: error,
88
+ messages: messages
89
+ };
90
+ updateDisconnectState(disconnectState);
91
+ try {
92
+ // Wait for Ink to fully exit
93
+ yield activeTui.waitUntilExit();
94
+ }
95
+ catch (e) {
96
+ logger.warn("Failed to wait for TUI exit", e);
97
+ }
98
+ finally {
99
+ activeTui = null;
100
+ messages.forEach(function (m) {
101
+ CLIPrinter.warn(m);
102
+ });
135
103
  // Exit ONLY after fullscreen ink has restored the terminal
136
104
  process.exit(0);
137
- break;
138
- case "error":
139
- CLIPrinter.error(msg.message || "Unknown error from worker");
140
- break;
105
+ }
106
+ }
107
+ else {
108
+ messages.forEach(function (m) {
109
+ CLIPrinter.warn(m);
110
+ });
111
+ process.exit(0);
141
112
  }
142
113
  }));
143
- worker.on("error", (err) => {
144
- logger.error("Worker thread error:", err);
145
- CLIPrinter.error(`${err}`);
146
- });
147
- worker.on("exit", (code) => {
148
- if (code !== 0) {
149
- CLIPrinter.error(`Worker stopped with exit code ${code}`);
114
+ if (!finalConfig.NoTUI) {
115
+ const tui = withFullScreen(_jsx(TunnelTuiWrapper, { finalConfig: finalConfig, urls: TunnelData.urls, greet: TunnelData.greet }));
116
+ activeTui = tui;
117
+ try {
118
+ yield tui.start();
119
+ yield tui.waitUntilExit();
150
120
  }
151
- else {
152
- console.log("Worker exited cleanly.");
121
+ catch (e) {
122
+ logger.warn("TUI error", e);
153
123
  }
154
- });
124
+ finally {
125
+ activeTui = null;
126
+ }
127
+ }
155
128
  }
156
129
  catch (err) {
157
130
  CLIPrinter.stopSpinnerFail("Failed to connect");
package/dist/index.js CHANGED
@@ -25,6 +25,16 @@ function main() {
25
25
  const { values, positionals, hasAnyArgs } = parseCliArgs(cliOptions);
26
26
  // Configure logger from CLI args
27
27
  configureLogger(values);
28
+ // Use the TunnelManager to start the tunnel
29
+ const manager = TunnelManager.getInstance();
30
+ // Keep the process alive and handle graceful shutdown
31
+ process.on('SIGINT', () => {
32
+ logger.info("SIGINT received: stopping tunnels and exiting");
33
+ console.log("\nStopping all tunnels...");
34
+ manager.stopAllTunnels();
35
+ console.log("Tunnels stopped. Exiting.");
36
+ process.exit(0);
37
+ });
28
38
  if (!hasAnyArgs || values.help) {
29
39
  printHelpMessage();
30
40
  return;
@@ -44,17 +54,7 @@ function main() {
44
54
  logger.debug("Building final config from CLI values and positionals", { values, positionals });
45
55
  const finalConfig = buildFinalConfig(values, positionals);
46
56
  logger.debug("Final configuration built", finalConfig);
47
- // Use the TunnelManager to start the tunnel
48
- const manager = TunnelManager.getInstance();
49
- const tunnel = yield startCli(finalConfig, manager);
50
- // Keep the process alive and handle graceful shutdown
51
- process.on('SIGINT', () => {
52
- logger.info("SIGINT received: stopping tunnels and exiting");
53
- console.log("\nStopping all tunnels...");
54
- manager.stopAllTunnels();
55
- console.log("Tunnels stopped. Exiting.");
56
- process.exit(0);
57
- });
57
+ yield startCli(finalConfig, manager);
58
58
  }
59
59
  catch (error) {
60
60
  logger.error("Unhandled error in CLI:", error);
package/dist/logger.js CHANGED
@@ -12,6 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import winston from "winston";
13
13
  import fs from "fs";
14
14
  import path from "path";
15
+ import { pinggy } from "@pinggy/pinggy";
15
16
  // Singleton logger instance
16
17
  let _logger = null;
17
18
  function getLogger() {
@@ -22,10 +23,15 @@ function getLogger() {
22
23
  }
23
24
  export const logger = getLogger();
24
25
  export function configureLogger(values, silent = false) {
26
+ var _a;
25
27
  // Parse values from CLI args
26
28
  const levelStr = values.loglevel || undefined;
27
29
  const filePath = values.logfile || process.env.PINGGY_LOG_FILE || undefined;
28
- const printlog = values.printlog;
30
+ const printlog = values.v || values.vvv || undefined;
31
+ const source = (_a = values.vvv) !== null && _a !== void 0 ? _a : false;
32
+ if (values.vv || values.vvv) {
33
+ enableLoggingByLogLevel();
34
+ }
29
35
  // Ensure log directory exists if file logging is enabled
30
36
  if (filePath) {
31
37
  const dir = path.dirname(filePath);
@@ -39,7 +45,8 @@ export function configureLogger(values, silent = false) {
39
45
  transports.push(new winston.transports.Console({
40
46
  format: winston.format.combine(winston.format.colorize(), winston.format.timestamp(), winston.format.printf((_a) => {
41
47
  var { level, message, timestamp } = _a, meta = __rest(_a, ["level", "message", "timestamp"]);
42
- return `${timestamp} [${level}] ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ""}`;
48
+ const srcLabel = source ? "[CLI] " : "";
49
+ return `${timestamp} ${srcLabel}[${level}] ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ""}`;
43
50
  })),
44
51
  }));
45
52
  }
@@ -65,3 +72,6 @@ export function configureLogger(values, silent = false) {
65
72
  log.silent = transports.length === 0 || silent === true;
66
73
  return log;
67
74
  }
75
+ function enableLoggingByLogLevel() {
76
+ pinggy.setDebugLogging(true);
77
+ }
@@ -14,168 +14,127 @@ export class TunnelOperations {
14
14
  constructor() {
15
15
  this.tunnelManager = TunnelManager.getInstance(); // Use singleton instance
16
16
  }
17
- handleStart(config) {
18
- try {
19
- // Convert TunnelConfig -> PinggyOptions
20
- const pinggyOpts = tunnelConfigToPinggyOptions(config);
21
- const tunnelResp = this.tunnelManager.createTunnel(Object.assign(Object.assign({}, pinggyOpts), { configid: config.configid, tunnelName: config.configname }));
22
- this.tunnelManager.startTunnel(tunnelResp.tunnelid);
23
- const tunnelPconfig = this.tunnelManager.getTunnelConfig("", tunnelResp.tunnelid);
24
- const tunnelGreetMsg = this.tunnelManager.getTunnelGreetMessage(tunnelResp.tunnelid);
25
- const tlsInfo = this.tunnelManager.getLocalserverTlsInfo(tunnelResp.tunnelid);
26
- // Construct TunnelResponse object
27
- const tunnelStatus = {
28
- tunnelid: tunnelResp.tunnelid,
29
- remoteurls: [],
30
- tunnelconfig: pinggyOptionsToTunnelConfig(tunnelPconfig, config.configid, tunnelResp.tunnelName, tlsInfo, tunnelGreetMsg),
31
- status: newStatus(this.tunnelManager.getTunnelStatus(tunnelResp.tunnelid), TunnelErrorCodeType.NoError, ""),
32
- stats: tunnelResp.instance.getLatestUsage()
17
+ // --- Helper to construct TunnelResponse ---
18
+ buildTunnelResponse(tunnelid, tunnelConfig, configid, tunnelName) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const [status, stats, tlsInfo, greetMsg, remoteurls] = yield Promise.all([
21
+ this.tunnelManager.getTunnelStatus(tunnelid),
22
+ this.tunnelManager.getTunnelStats(tunnelid),
23
+ this.tunnelManager.getLocalserverTlsInfo(tunnelid),
24
+ this.tunnelManager.getTunnelGreetMessage(tunnelid),
25
+ this.tunnelManager.getTunnelUrls(tunnelid)
26
+ ]);
27
+ return {
28
+ tunnelid,
29
+ remoteurls,
30
+ tunnelconfig: pinggyOptionsToTunnelConfig(tunnelConfig, configid, tunnelName, tlsInfo, greetMsg),
31
+ status: newStatus(status, TunnelErrorCodeType.NoError, ""),
32
+ stats
33
33
  };
34
- return tunnelStatus;
35
- }
36
- catch (error) {
37
- return newErrorResponse({
38
- code: ErrorCode.ErrorStartingTunnel,
39
- message: error instanceof Error ? error.message : 'Unknown error occurred while starting tunnel'
40
- });
41
- }
34
+ });
42
35
  }
43
- handleUpdateConfig(config) {
36
+ error(code, err, fallback) {
37
+ return newErrorResponse({
38
+ code,
39
+ message: err instanceof Error ? err.message : fallback
40
+ });
41
+ }
42
+ // --- Operations ---
43
+ handleStart(config) {
44
44
  return __awaiter(this, void 0, void 0, function* () {
45
45
  try {
46
46
  // Convert TunnelConfig -> PinggyOptions
47
- const pinggyOpts = tunnelConfigToPinggyOptions(config);
48
- const managedTunnel = yield this.tunnelManager.updateConfig(Object.assign(Object.assign({}, pinggyOpts), { configid: config.configid, tunnelName: config.configname }));
49
- if (!managedTunnel.tunnelConfig) {
50
- throw new Error("Failed to update tunnel configuration");
51
- }
52
- if (!managedTunnel.instance) {
53
- throw new Error("Tunnel instance not found after configuration update");
54
- }
55
- const tunnelStatus = {
56
- tunnelid: managedTunnel.tunnelid,
57
- remoteurls: [],
58
- tunnelconfig: pinggyOptionsToTunnelConfig(managedTunnel.tunnelConfig, config.configid, managedTunnel.tunnelName, this.tunnelManager.getLocalserverTlsInfo(managedTunnel.tunnelid), this.tunnelManager.getTunnelGreetMessage(managedTunnel.tunnelid)),
59
- status: newStatus(this.tunnelManager.getTunnelStatus(managedTunnel.tunnelid), TunnelErrorCodeType.NoError, ""),
60
- stats: managedTunnel.instance.getLatestUsage()
61
- };
62
- return tunnelStatus;
47
+ const opts = tunnelConfigToPinggyOptions(config);
48
+ const { tunnelid, instance, tunnelName } = this.tunnelManager.createTunnel(Object.assign(Object.assign({}, opts), { configid: config.configid, tunnelName: config.configname }));
49
+ this.tunnelManager.startTunnel(tunnelid);
50
+ const tunnelPconfig = yield this.tunnelManager.getTunnelConfig("", tunnelid);
51
+ const resp = this.buildTunnelResponse(tunnelid, tunnelPconfig, config.configid, tunnelName);
52
+ return resp;
53
+ }
54
+ catch (err) {
55
+ return this.error(ErrorCode.ErrorStartingTunnel, err, "Unknown error occurred while starting tunnel");
56
+ }
57
+ });
58
+ }
59
+ handleUpdateConfig(config) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ try {
62
+ const opts = tunnelConfigToPinggyOptions(config);
63
+ const tunnel = yield this.tunnelManager.updateConfig(Object.assign(Object.assign({}, opts), { configid: config.configid, tunnelName: config.configname }));
64
+ if (!tunnel.instance || !tunnel.tunnelConfig)
65
+ throw new Error("Invalid tunnel state after configuration update");
66
+ return this.buildTunnelResponse(tunnel.tunnelid, tunnel.tunnelConfig, config.configid, tunnel.tunnelName);
63
67
  }
64
- catch (error) {
65
- return newErrorResponse({
66
- code: ErrorCode.InternalServerError,
67
- message: error instanceof Error ? error.message : 'Failed to update tunnel configuration'
68
- });
68
+ catch (err) {
69
+ return this.error(ErrorCode.InternalServerError, err, "Failed to update tunnel configuration");
69
70
  }
70
71
  });
71
72
  }
72
73
  handleList() {
73
- try {
74
- const tunnels = this.tunnelManager.getAllTunnels();
75
- const result = tunnels.map(tunnel => {
76
- // try to get stats from manager first
77
- const statsFromManager = this.tunnelManager.getTunnelStats(tunnel.tunnelid);
78
- let stats = statsFromManager;
79
- // if null/undefined, return default stats
80
- if (!stats) {
81
- stats = newStats();
82
- }
83
- return {
84
- tunnelid: tunnel.tunnelid,
85
- remoteurls: tunnel.remoteurls,
86
- status: newStatus(this.tunnelManager.getTunnelStatus(tunnel.tunnelid), TunnelErrorCodeType.NoError, ""),
87
- stats: stats,
88
- // Convert PinggyOptions -> TunnelConfig
89
- tunnelconfig: pinggyOptionsToTunnelConfig(this.tunnelManager.getTunnelConfig("", tunnel.tunnelid), tunnel.configid, tunnel.tunnelName, this.tunnelManager.getLocalserverTlsInfo(tunnel.tunnelid), this.tunnelManager.getTunnelGreetMessage(tunnel.tunnelid))
90
- };
91
- });
92
- return result;
93
- }
94
- catch (error) {
95
- return newErrorResponse({
96
- code: ErrorCode.InternalServerError,
97
- message: error instanceof Error ? error.message : 'Failed to list tunnels'
98
- });
99
- }
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ try {
76
+ const tunnels = yield this.tunnelManager.getAllTunnels();
77
+ return Promise.all(tunnels.map((t) => __awaiter(this, void 0, void 0, function* () {
78
+ var _a;
79
+ const stats = ((_a = this.tunnelManager.getTunnelStats(t.tunnelid)) !== null && _a !== void 0 ? _a : newStats());
80
+ const [status, config, tlsInfo, greetMsg] = yield Promise.all([
81
+ this.tunnelManager.getTunnelStatus(t.tunnelid),
82
+ this.tunnelManager.getTunnelConfig("", t.tunnelid),
83
+ this.tunnelManager.getLocalserverTlsInfo(t.tunnelid),
84
+ this.tunnelManager.getTunnelGreetMessage(t.tunnelid)
85
+ ]);
86
+ return {
87
+ tunnelid: t.tunnelid,
88
+ remoteurls: t.remoteurls,
89
+ status: newStatus(status, TunnelErrorCodeType.NoError, ""),
90
+ stats,
91
+ tunnelconfig: pinggyOptionsToTunnelConfig(config, t.configid, t.tunnelName, tlsInfo, greetMsg)
92
+ };
93
+ })));
94
+ }
95
+ catch (err) {
96
+ return this.error(ErrorCode.InternalServerError, err, "Failed to list tunnels");
97
+ }
98
+ });
100
99
  }
101
100
  handleStop(tunnelid) {
102
- try {
103
- const { configid, tunnelid: stoppedTunnelId } = this.tunnelManager.stopTunnel(tunnelid);
104
- const tunnelInstance = this.tunnelManager.getManagedTunnel("", tunnelid);
105
- if (!tunnelInstance) {
106
- throw new Error(`Tunnel instance for ID "${tunnelid}" not found`);
101
+ return __awaiter(this, void 0, void 0, function* () {
102
+ try {
103
+ const { configid } = this.tunnelManager.stopTunnel(tunnelid);
104
+ const managed = this.tunnelManager.getManagedTunnel("", tunnelid);
105
+ if (!(managed === null || managed === void 0 ? void 0 : managed.tunnelConfig))
106
+ throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
107
+ return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, configid, managed.tunnelName);
107
108
  }
108
- if (!tunnelInstance.tunnelConfig) {
109
- throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
109
+ catch (err) {
110
+ return this.error(ErrorCode.TunnelNotFound, err, "Failed to stop tunnel");
110
111
  }
111
- const tunnelResponse = {
112
- tunnelid: stoppedTunnelId,
113
- remoteurls: [],
114
- tunnelconfig: pinggyOptionsToTunnelConfig(tunnelInstance.tunnelConfig, configid, tunnelInstance.tunnelName, this.tunnelManager.getLocalserverTlsInfo(tunnelid), this.tunnelManager.getTunnelGreetMessage(tunnelid)),
115
- status: newStatus(this.tunnelManager.getTunnelStatus(stoppedTunnelId), TunnelErrorCodeType.NoError, ""),
116
- stats: newStats()
117
- };
118
- return tunnelResponse;
119
- }
120
- catch (error) {
121
- return newErrorResponse({
122
- code: ErrorCode.TunnelNotFound,
123
- message: error instanceof Error ? error.message : 'Failed to stop tunnel'
124
- });
125
- }
112
+ });
126
113
  }
127
114
  handleGet(tunnelid) {
128
- try {
129
- const tunnelState = this.tunnelManager.getTunnelStatus(tunnelid);
130
- const tunnelInstance = this.tunnelManager.getManagedTunnel("", tunnelid);
131
- if (!tunnelInstance) {
132
- throw new Error(`Tunnel instance for ID "${tunnelid}" not found`);
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ try {
117
+ const managed = this.tunnelManager.getManagedTunnel("", tunnelid);
118
+ if (!(managed === null || managed === void 0 ? void 0 : managed.tunnelConfig))
119
+ throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
120
+ return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, managed.configid, managed.tunnelName);
133
121
  }
134
- if (!tunnelInstance.tunnelConfig) {
135
- throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
122
+ catch (err) {
123
+ return this.error(ErrorCode.TunnelNotFound, err, "Failed to get tunnel information");
136
124
  }
137
- // TODO: directly calling to getLatestUsage() may through error if tunnel stopped earlier
138
- const stats = this.tunnelManager.getTunnelStats(tunnelid) ? this.tunnelManager.getTunnelStats(tunnelid) : tunnelInstance.instance.getLatestUsage();
139
- const tunnelResponse = {
140
- tunnelid: tunnelid,
141
- remoteurls: this.tunnelManager.getTunnelUrls(tunnelid),
142
- status: newStatus(tunnelState, TunnelErrorCodeType.NoError, ""),
143
- stats: stats,
144
- tunnelconfig: pinggyOptionsToTunnelConfig(tunnelInstance.tunnelConfig, tunnelInstance.configid, tunnelInstance.tunnelName, this.tunnelManager.getLocalserverTlsInfo(tunnelid), this.tunnelManager.getTunnelGreetMessage(tunnelid))
145
- };
146
- return tunnelResponse;
147
- }
148
- catch (error) {
149
- return newErrorResponse({
150
- code: ErrorCode.TunnelNotFound,
151
- message: error instanceof Error ? error.message : 'Failed to get tunnel information'
152
- });
153
- }
125
+ });
154
126
  }
155
127
  handleRestart(tunnelid) {
156
128
  return __awaiter(this, void 0, void 0, function* () {
157
129
  try {
158
130
  yield this.tunnelManager.restartTunnel(tunnelid);
159
- const tunnelConfig = this.tunnelManager.getTunnelConfig("", tunnelid);
160
- const tunnelInstance = this.tunnelManager.getManagedTunnel("", tunnelid);
161
- if (!tunnelInstance) {
162
- throw new Error(`Tunnel instance for ID "${tunnelid}" not found`);
163
- }
164
- const stats = this.tunnelManager.getTunnelStats(tunnelid) ? this.tunnelManager.getTunnelStats(tunnelid) : tunnelInstance.instance.getLatestUsage();
165
- const tunnelResponse = {
166
- tunnelid: tunnelid,
167
- remoteurls: this.tunnelManager.getTunnelUrls(tunnelid),
168
- tunnelconfig: pinggyOptionsToTunnelConfig(tunnelConfig, tunnelInstance.configid, tunnelInstance.tunnelName, this.tunnelManager.getLocalserverTlsInfo(tunnelid), this.tunnelManager.getTunnelGreetMessage(tunnelid)),
169
- status: newStatus(this.tunnelManager.getTunnelStatus(tunnelid), TunnelErrorCodeType.NoError, ""),
170
- stats: stats
171
- };
172
- return tunnelResponse;
131
+ const managed = this.tunnelManager.getManagedTunnel("", tunnelid);
132
+ if (!(managed === null || managed === void 0 ? void 0 : managed.tunnelConfig))
133
+ throw new Error(`Tunnel config for ID "${tunnelid}" not found`);
134
+ return this.buildTunnelResponse(tunnelid, managed.tunnelConfig, managed.configid, managed.tunnelName);
173
135
  }
174
- catch (error) {
175
- return newErrorResponse({
176
- code: ErrorCode.TunnelNotFound,
177
- message: error instanceof Error ? error.message : 'Failed to restart tunnel'
178
- });
136
+ catch (err) {
137
+ return this.error(ErrorCode.TunnelNotFound, err, "Failed to restart tunnel");
179
138
  }
180
139
  });
181
140
  }