From ac87345b7a98f1320146fd5692e85f5a19955633 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 13 Aug 2025 21:35:06 -0700 Subject: [PATCH] Seperate get relays --- server/routers/gerbil/getAllRelays.ts | 218 +++++++++++++++----------- 1 file changed, 125 insertions(+), 93 deletions(-) diff --git a/server/routers/gerbil/getAllRelays.ts b/server/routers/gerbil/getAllRelays.ts index a64fd78f..6eaf87e2 100644 --- a/server/routers/gerbil/getAllRelays.ts +++ b/server/routers/gerbil/getAllRelays.ts @@ -1,6 +1,15 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { clients, exitNodes, newts, olms, Site, sites, clientSites } from "@server/db"; +import { + clients, + exitNodes, + newts, + olms, + Site, + sites, + clientSites, + ExitNode +} from "@server/db"; import { db } from "@server/db"; import { eq } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; @@ -10,7 +19,7 @@ import { fromError } from "zod-validation-error"; // Define Zod schema for request validation const getAllRelaysSchema = z.object({ - publicKey: z.string().optional(), + publicKey: z.string().optional() }); // Type for peer destination @@ -44,103 +53,27 @@ export async function getAllRelays( const { publicKey } = parsedParams.data; if (!publicKey) { - return next(createHttpError(HttpCode.BAD_REQUEST, 'publicKey is required')); + return next( + createHttpError(HttpCode.BAD_REQUEST, "publicKey is required") + ); } // Fetch exit node - const [exitNode] = await db.select().from(exitNodes).where(eq(exitNodes.publicKey, publicKey)); + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.publicKey, publicKey)); if (!exitNode) { - return next(createHttpError(HttpCode.NOT_FOUND, "Exit node not found")); + return next( + createHttpError(HttpCode.NOT_FOUND, "Exit node not found") + ); } - // Fetch sites for this exit node - const sitesRes = await db.select().from(sites).where(eq(sites.exitNodeId, exitNode.exitNodeId)); + const mappings = await generateRelayMappings(exitNode); - if (sitesRes.length === 0) { - return res.status(HttpCode.OK).send({ - mappings: {} - }); - } - - // Initialize mappings object for multi-peer support - const mappings: { [key: string]: ProxyMapping } = {}; - - // Process each site - for (const site of sitesRes) { - if (!site.endpoint || !site.subnet || !site.listenPort) { - continue; - } - - // Find all clients associated with this site through clientSites - const clientSitesRes = await db - .select() - .from(clientSites) - .where(eq(clientSites.siteId, site.siteId)); - - for (const clientSite of clientSitesRes) { - if (!clientSite.endpoint) { - continue; - } - - // Add this site as a destination for the client - if (!mappings[clientSite.endpoint]) { - mappings[clientSite.endpoint] = { destinations: [] }; - } - - // Add site as a destination for this client - const destination: PeerDestination = { - destinationIP: site.subnet.split("/")[0], - destinationPort: site.listenPort - }; - - // Check if this destination is already in the array to avoid duplicates - const isDuplicate = mappings[clientSite.endpoint].destinations.some( - dest => dest.destinationIP === destination.destinationIP && - dest.destinationPort === destination.destinationPort - ); - - if (!isDuplicate) { - mappings[clientSite.endpoint].destinations.push(destination); - } - } - - // Also handle site-to-site communication (all sites in the same org) - if (site.orgId) { - const orgSites = await db - .select() - .from(sites) - .where(eq(sites.orgId, site.orgId)); - - for (const peer of orgSites) { - // Skip self - if (peer.siteId === site.siteId || !peer.endpoint || !peer.subnet || !peer.listenPort) { - continue; - } - - // Add peer site as a destination for this site - if (!mappings[site.endpoint]) { - mappings[site.endpoint] = { destinations: [] }; - } - - const destination: PeerDestination = { - destinationIP: peer.subnet.split("/")[0], - destinationPort: peer.listenPort - }; - - // Check for duplicates - const isDuplicate = mappings[site.endpoint].destinations.some( - dest => dest.destinationIP === destination.destinationIP && - dest.destinationPort === destination.destinationPort - ); - - if (!isDuplicate) { - mappings[site.endpoint].destinations.push(destination); - } - } - } - } - - logger.debug(`Returning mappings for ${Object.keys(mappings).length} endpoints`); + logger.debug( + `Returning mappings for ${Object.keys(mappings).length} endpoints` + ); return res.status(HttpCode.OK).send({ mappings }); } catch (error) { logger.error(error); @@ -151,4 +84,103 @@ export async function getAllRelays( ) ); } -} \ No newline at end of file +} + +export async function generateRelayMappings(exitNode: ExitNode) { + // Fetch sites for this exit node + const sitesRes = await db + .select() + .from(sites) + .where(eq(sites.exitNodeId, exitNode.exitNodeId)); + + if (sitesRes.length === 0) { + return {}; + } + + // Initialize mappings object for multi-peer support + const mappings: { [key: string]: ProxyMapping } = {}; + + // Process each site + for (const site of sitesRes) { + if (!site.endpoint || !site.subnet || !site.listenPort) { + continue; + } + + // Find all clients associated with this site through clientSites + const clientSitesRes = await db + .select() + .from(clientSites) + .where(eq(clientSites.siteId, site.siteId)); + + for (const clientSite of clientSitesRes) { + if (!clientSite.endpoint) { + continue; + } + + // Add this site as a destination for the client + if (!mappings[clientSite.endpoint]) { + mappings[clientSite.endpoint] = { destinations: [] }; + } + + // Add site as a destination for this client + const destination: PeerDestination = { + destinationIP: site.subnet.split("/")[0], + destinationPort: site.listenPort + }; + + // Check if this destination is already in the array to avoid duplicates + const isDuplicate = mappings[clientSite.endpoint].destinations.some( + (dest) => + dest.destinationIP === destination.destinationIP && + dest.destinationPort === destination.destinationPort + ); + + if (!isDuplicate) { + mappings[clientSite.endpoint].destinations.push(destination); + } + } + + // Also handle site-to-site communication (all sites in the same org) + if (site.orgId) { + const orgSites = await db + .select() + .from(sites) + .where(eq(sites.orgId, site.orgId)); + + for (const peer of orgSites) { + // Skip self + if ( + peer.siteId === site.siteId || + !peer.endpoint || + !peer.subnet || + !peer.listenPort + ) { + continue; + } + + // Add peer site as a destination for this site + if (!mappings[site.endpoint]) { + mappings[site.endpoint] = { destinations: [] }; + } + + const destination: PeerDestination = { + destinationIP: peer.subnet.split("/")[0], + destinationPort: peer.listenPort + }; + + // Check for duplicates + const isDuplicate = mappings[site.endpoint].destinations.some( + (dest) => + dest.destinationIP === destination.destinationIP && + dest.destinationPort === destination.destinationPort + ); + + if (!isDuplicate) { + mappings[site.endpoint].destinations.push(destination); + } + } + } + } + + return mappings; +}