fix(tailscale): cap enable-flow health wait at 20s

Enable waited out the full 180s HEALTH_CHECK.timeoutMs when funnel URL
DNS was unreachable, making the toggle hang ~3 minutes before returning
success. waitForHealth now takes an optional timeoutMs; enable uses
HEALTH_CHECK.enableTimeoutMs (20s) while watchdog/cloudflare keep 180s.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
decolua
2026-09-23 12:12:10 +07:00
parent 95600db17b
commit e7c269b8c9
3 changed files with 6 additions and 4 deletions

View File

@@ -2,6 +2,7 @@
export const HEALTH_CHECK = {
intervalMs: 2000,
timeoutMs: 180000,
enableTimeoutMs: 20000, // Enable flow waits short; watchdog re-verifies afterwards
fetchTimeoutMs: 8000,
dnsTimeoutMs: 3000,
};

View File

@@ -18,12 +18,12 @@ export async function probeUrlAlive(url) {
}
}
export async function waitForHealth(url, cancelToken = { cancelled: false }) {
export async function waitForHealth(url, cancelToken = { cancelled: false }, { timeoutMs = HEALTH_CHECK.timeoutMs } = {}) {
const start = Date.now();
while (Date.now() - start < HEALTH_CHECK.timeoutMs) {
while (Date.now() - start < timeoutMs) {
if (cancelToken.cancelled) throw new Error("cancelled");
if (await probeUrlAlive(url)) return true;
await new Promise((r) => setTimeout(r, HEALTH_CHECK.intervalMs));
}
throw new Error(`Health check timeout after ${HEALTH_CHECK.timeoutMs}ms`);
throw new Error(`Health check timeout after ${timeoutMs}ms`);
}

View File

@@ -1,6 +1,7 @@
import { loadState, generateShortId } from "../shared/state.js";
import { startFunnel, stopFunnel, isTailscaleRunning, isTailscaleRunningStrict, isTailscaleLoggedIn, isTailscaleLoggedInStrict, startLogin, startDaemonWithPassword, provisionCert } from "./tailscale.js";
import { waitForHealth } from "./healthCheck.js";
import { HEALTH_CHECK } from "./config.js";
import { getSettings, updateSettings } from "@/lib/localDb";
import { getCachedPassword, loadEncryptedPassword, initDbHooks } from "@/mitm/manager";
@@ -88,7 +89,7 @@ export async function enableTailscale(localPort = 20128) {
// Verify funnel serves /api/health — timeout is non-fatal (DNS may still be propagating)
let reachableNow = false;
try {
await waitForHealth(result.tunnelUrl, token);
await waitForHealth(result.tunnelUrl, token, { timeoutMs: HEALTH_CHECK.enableTimeoutMs });
reachableNow = true;
} catch (he) {
if (!he.message.startsWith("Health check timeout")) throw he;