Fix bug Tunnel
This commit is contained in:
@@ -113,6 +113,12 @@ export async function ensureCloudflared() {
|
||||
}
|
||||
|
||||
let cloudflaredProcess = null;
|
||||
let unexpectedExitHandler = null;
|
||||
|
||||
/** Register a callback to be called when cloudflared exits unexpectedly after connecting */
|
||||
export function setUnexpectedExitHandler(handler) {
|
||||
unexpectedExitHandler = handler;
|
||||
}
|
||||
|
||||
export async function spawnCloudflared(tunnelToken) {
|
||||
const binaryPath = await ensureCloudflared();
|
||||
@@ -127,7 +133,9 @@ export async function spawnCloudflared(tunnelToken) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let connectionCount = 0;
|
||||
let resolved = false;
|
||||
const timeout = setTimeout(() => {
|
||||
resolved = true;
|
||||
resolve(child);
|
||||
}, 90000);
|
||||
|
||||
@@ -135,7 +143,8 @@ export async function spawnCloudflared(tunnelToken) {
|
||||
const msg = data.toString();
|
||||
if (msg.includes("Registered tunnel connection")) {
|
||||
connectionCount++;
|
||||
if (connectionCount >= 4) {
|
||||
if (connectionCount >= 4 && !resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
resolve(child);
|
||||
}
|
||||
@@ -146,14 +155,27 @@ export async function spawnCloudflared(tunnelToken) {
|
||||
child.stderr.on("data", handleLog);
|
||||
|
||||
child.on("error", (err) => {
|
||||
clearTimeout(timeout);
|
||||
reject(err);
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
|
||||
child.on("exit", (code) => {
|
||||
clearTimeout(timeout);
|
||||
if (connectionCount === 0) {
|
||||
reject(new Error(`cloudflared exited with code ${code}`));
|
||||
cloudflaredProcess = null;
|
||||
clearPid();
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
if (connectionCount === 0) {
|
||||
reject(new Error(`cloudflared exited with code ${code}`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Notify reconnect handler if tunnel died after successful connection
|
||||
if (unexpectedExitHandler) {
|
||||
unexpectedExitHandler();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import crypto from "crypto";
|
||||
import { loadState, saveState, clearState } from "./state.js";
|
||||
import { spawnCloudflared, killCloudflared, isCloudflaredRunning } from "./cloudflared.js";
|
||||
import { spawnCloudflared, killCloudflared, isCloudflaredRunning, setUnexpectedExitHandler } from "./cloudflared.js";
|
||||
import { getSettings, updateSettings } from "@/lib/localDb";
|
||||
|
||||
const TUNNEL_WORKER_URL = process.env.TUNNEL_WORKER_URL || "https://tunnel.9router.com";
|
||||
@@ -8,6 +8,9 @@ const MACHINE_ID_SALT = "9router-tunnel-salt";
|
||||
const API_KEY_SECRET = "9router-tunnel-api-key-secret";
|
||||
const SHORT_ID_LENGTH = 6;
|
||||
const SHORT_ID_CHARS = "abcdefghijklmnpqrstuvwxyz23456789";
|
||||
const RECONNECT_DELAYS_MS = [5000, 15000, 30000];
|
||||
|
||||
let isReconnecting = false;
|
||||
|
||||
function generateShortId() {
|
||||
let result = "";
|
||||
@@ -80,9 +83,43 @@ export async function enableTunnel() {
|
||||
|
||||
await updateSettings({ tunnelEnabled: true, tunnelUrl: hostname });
|
||||
|
||||
// Register exit handler for auto-reconnect on unexpected crash/sleep-wake
|
||||
setUnexpectedExitHandler(() => scheduleReconnect(0));
|
||||
|
||||
return { success: true, tunnelUrl: hostname, shortId };
|
||||
}
|
||||
|
||||
async function scheduleReconnect(attempt) {
|
||||
if (isReconnecting) return;
|
||||
isReconnecting = true;
|
||||
|
||||
const delay = RECONNECT_DELAYS_MS[Math.min(attempt, RECONNECT_DELAYS_MS.length - 1)];
|
||||
console.log(`[Tunnel] Unexpected exit detected, reconnecting in ${delay / 1000}s (attempt ${attempt + 1})...`);
|
||||
|
||||
await new Promise((r) => setTimeout(r, delay));
|
||||
|
||||
try {
|
||||
const settings = await getSettings();
|
||||
if (!settings.tunnelEnabled) {
|
||||
console.log("[Tunnel] Tunnel disabled, skipping reconnect");
|
||||
isReconnecting = false;
|
||||
return;
|
||||
}
|
||||
await enableTunnel();
|
||||
console.log("[Tunnel] Reconnected successfully");
|
||||
isReconnecting = false;
|
||||
} catch (err) {
|
||||
console.log(`[Tunnel] Reconnect attempt ${attempt + 1} failed:`, err.message);
|
||||
isReconnecting = false;
|
||||
const nextAttempt = attempt + 1;
|
||||
if (nextAttempt < RECONNECT_DELAYS_MS.length) {
|
||||
scheduleReconnect(nextAttempt);
|
||||
} else {
|
||||
console.log("[Tunnel] All reconnect attempts exhausted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function disableTunnel() {
|
||||
const state = loadState();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user