diff --git a/install/config/config.yml b/install/config/config.yml index c4145fa2..5f81c141 100644 --- a/install/config/config.yml +++ b/install/config/config.yml @@ -30,7 +30,7 @@ orgs: email: smtp_host: "{{.EmailSMTPHost}}" smtp_port: {{.EmailSMTPPort}} - smtp_user: "{{.EmailSMTPUser}allow_base_domain_resources}" + smtp_user: "{{.EmailSMTPUser}}" smtp_pass: "{{.EmailSMTPPass}}" no_reply: "{{.EmailNoReply}}" {{end}} diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index 5950aa93..71a6fd5c 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -258,101 +258,13 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { }; }; -/** - * Selects the most suitable exit node from a list of ping results. - * - * The selection algorithm follows these steps: - * - * 1. **Filter Invalid Nodes**: Excludes nodes with errors or zero weight. - * - * 2. **Sort by Latency**: Sorts valid nodes in ascending order of latency. - * - * 3. **Preferred Selection**: - * - If the lowest-latency node has sufficient capacity (≥10% weight), - * check if a previously connected node is also acceptable. - * - The previously connected node is preferred if its latency is within - * 30ms or 15% of the best node’s latency. - * - * 4. **Fallback to Next Best**: - * - If the lowest-latency node is under capacity, find the next node - * with acceptable capacity. - * - * 5. **Final Fallback**: - * - If no nodes meet the capacity threshold, fall back to the node - * with the highest weight (i.e., most available capacity). - * - */ function selectBestExitNode( pingResults: ExitNodePingResult[] ): ExitNodePingResult | null { - const MIN_CAPACITY_THRESHOLD = 0.1; - const LATENCY_TOLERANCE_MS = 30; - const LATENCY_TOLERANCE_PERCENT = 0.15; - - // Filter out invalid nodes - const validNodes = pingResults.filter((n) => !n.error && n.weight > 0); - - if (validNodes.length === 0) { - logger.error("No valid exit nodes available"); + if (!pingResults || pingResults.length === 0) { + logger.warn("No ping results provided"); return null; } - // Sort by latency (ascending) - const sortedNodes = validNodes - .slice() - .sort((a, b) => a.latencyMs - b.latencyMs); - const lowestLatencyNode = sortedNodes[0]; - - logger.info( - `Lowest latency node: ${lowestLatencyNode.exitNodeName} (${lowestLatencyNode.latencyMs} ms, weight=${lowestLatencyNode.weight.toFixed(2)})` - ); - - // If lowest latency node has enough capacity, check if previously connected node is acceptable - if (lowestLatencyNode.weight >= MIN_CAPACITY_THRESHOLD) { - const previouslyConnectedNode = sortedNodes.find( - (n) => - n.wasPreviouslyConnected && n.weight >= MIN_CAPACITY_THRESHOLD - ); - - if (previouslyConnectedNode) { - const latencyDiff = - previouslyConnectedNode.latencyMs - lowestLatencyNode.latencyMs; - const percentDiff = latencyDiff / lowestLatencyNode.latencyMs; - - if ( - latencyDiff <= LATENCY_TOLERANCE_MS || - percentDiff <= LATENCY_TOLERANCE_PERCENT - ) { - logger.info( - `Sticking with previously connected node: ${previouslyConnectedNode.exitNodeName} ` + - `(${previouslyConnectedNode.latencyMs} ms), latency diff = ${latencyDiff.toFixed(1)}ms ` + - `/ ${(percentDiff * 100).toFixed(1)}%.` - ); - return previouslyConnectedNode; - } - } - - return lowestLatencyNode; - } - - // Otherwise, find the next node (after the lowest) that has enough capacity - for (let i = 1; i < sortedNodes.length; i++) { - const node = sortedNodes[i]; - if (node.weight >= MIN_CAPACITY_THRESHOLD) { - logger.info( - `Lowest latency node under capacity. Using next best: ${node.exitNodeName} ` + - `(${node.latencyMs} ms, weight=${node.weight.toFixed(2)})` - ); - return node; - } - } - - // Fallback: pick the highest weight node - const fallbackNode = validNodes.reduce((a, b) => - a.weight > b.weight ? a : b - ); - logger.warn( - `No nodes with ≥10% weight. Falling back to highest capacity node: ${fallbackNode.exitNodeName}` - ); - return fallbackNode; + return pingResults[0]; } diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 79a56130..654f0c4a 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; import { db, exitNodes } from "@server/db"; -import { and, eq, inArray } from "drizzle-orm"; +import { and, eq, inArray, or, isNull } from "drizzle-orm"; import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; @@ -27,13 +27,9 @@ export async function traefikConfigProvider( }) .from(exitNodes) .where(eq(exitNodes.name, exitNodeName)); - if (!exitNode) { - logger.error( - `Exit node with name ${exitNodeName} not found in the database` - ); - return []; + if (exitNode) { + currentExitNodeId = exitNode.exitNodeId; } - currentExitNodeId = exitNode.exitNodeId; } else { const [exitNode] = await tx .select({ @@ -42,12 +38,9 @@ export async function traefikConfigProvider( .from(exitNodes) .limit(1); - if (!exitNode) { - logger.error("No exit node found in the database"); - return []; + if (exitNode) { + currentExitNodeId = exitNode.exitNodeId; } - - currentExitNodeId = exitNode.exitNodeId; } } @@ -68,7 +61,7 @@ export async function traefikConfigProvider( siteId: sites.siteId, type: sites.type, subnet: sites.subnet, - exitNodeId: sites.exitNodeId, + exitNodeId: sites.exitNodeId }, enabled: resources.enabled, stickySession: resources.stickySession, @@ -77,7 +70,12 @@ export async function traefikConfigProvider( }) .from(resources) .innerJoin(sites, eq(sites.siteId, resources.siteId)) - .where(eq(sites.exitNodeId, currentExitNodeId)); + .where( + or( + eq(sites.exitNodeId, currentExitNodeId), + isNull(sites.exitNodeId) + ) + ); // Get all resource IDs from the first query const resourceIds = resourcesWithRelations.map((r) => r.resourceId); @@ -284,7 +282,8 @@ export async function traefikConfigProvider( } else if (site.type === "newt") { if ( !target.internalPort || - !target.method || !site.subnet + !target.method || + !site.subnet ) { return false; }