Refactor Tunnel

This commit is contained in:
decolua committed 2026-05-26 11:35:39 +07:00
1 parent 96a9a2b36d
commit b876e0225a
35 files changed
+642 -574

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
import dns from "dns";
// Force public DNS to bypass OS negative cache (mDNSResponder holds NXDOMAIN)
const resolver = new dns.promises.Resolver();
resolver.setServers(["1.1.1.1", "1.0.0.1", "8.8.8.8"]);
// Try custom public DNS first, fall back to OS resolver
// (Cloudflare DNS may not resolve all hostnames, e.g. *.ts.net)
export async function resolveDns(hostname, timeoutMs) {
const tryResolver = (fn) => Promise.race([
fn(),
new Promise((_, rej) => setTimeout(() => rej(new Error("dns timeout")), timeoutMs)),
]).then(() => true).catch(() => false);
if (await tryResolver(() => resolver.resolve4(hostname))) return true;
return tryResolver(() => dns.promises.resolve4(hostname));
}
+26
View File
@@ -0,0 +1,26 @@
import net from "net";
const INTERNET_CHECK = {
host: "1.1.1.1",
port: 443,
timeoutMs: 3000,
};
export function checkInternet() {
return new Promise((resolve) => {
const socket = new net.Socket();
let done = false;
const finish = (ok) => {
if (done) return;
done = true;
try { socket.destroy(); } catch { /* ignore */ }
resolve(ok);
};
socket.setTimeout(INTERNET_CHECK.timeoutMs);
socket.once("connect", () => finish(true));
socket.once("timeout", () => finish(false));
socket.once("error", () => finish(false));
try { socket.connect(INTERNET_CHECK.port, INTERNET_CHECK.host); }
catch { finish(false); }
});
}
+41
View File
@@ -0,0 +1,41 @@
import fs from "fs";
import path from "path";
import { DATA_DIR } from "@/lib/dataDir.js";
const TUNNEL_DIR = path.join(DATA_DIR, "tunnel");
const STATE_FILE = path.join(TUNNEL_DIR, "state.json");
const SHORT_ID_LENGTH = 6;
const SHORT_ID_CHARS = "abcdefghijklmnpqrstuvwxyz23456789";
export function ensureTunnelDir() {
if (!fs.existsSync(TUNNEL_DIR)) fs.mkdirSync(TUNNEL_DIR, { recursive: true });
}
export function loadState() {
try {
if (fs.existsSync(STATE_FILE)) return JSON.parse(fs.readFileSync(STATE_FILE, "utf8"));
} catch { /* ignore corrupt state */ }
return null;
}
export function saveState(state) {
ensureTunnelDir();
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}
export function clearState() {
try {
if (fs.existsSync(STATE_FILE)) fs.unlinkSync(STATE_FILE);
} catch { /* ignore */ }
}
export function generateShortId() {
let result = "";
for (let i = 0; i < SHORT_ID_LENGTH; i++) {
result += SHORT_ID_CHARS.charAt(Math.floor(Math.random() * SHORT_ID_CHARS.length));
}
return result;
}
export { TUNNEL_DIR };
+5
View File
@@ -0,0 +1,5 @@
// Watchdog + network monitor timings (shared by both services)
export const RESTART_COOLDOWN_MS = 120000;
export const NETWORK_SETTLE_MS = 2500;
export const WATCHDOG_INTERVAL_MS = 60000;
export const NETWORK_CHECK_INTERVAL_MS = 5000;