Push gateway updates to clients

This commit is contained in:
Owen
2026-09-25 11:51:38 -04:00
parent e92911e7c8
commit 90848581a1
2 changed files with 122 additions and 0 deletions
+40
View File
@@ -23,6 +23,10 @@ import {
import { and, count, eq, inArray, isNotNull, ne } from "drizzle-orm";
import { deletePeersBatch as newtDeletePeersBatch } from "@server/routers/newt/peers";
import {
sendGatewayDisable,
sendGatewaySitesUpdate
} from "@server/routers/olm/gateway";
import {
initPeerAddHandshakeBatch,
deletePeersBatch as olmDeletePeersBatch
@@ -536,6 +540,19 @@ async function rebuildClientAssociationsFromSiteResourceImpl(
);
}
// A client that loses access to a gateway resource (it was deleted, or the
// client's roles/users/machines no longer include it) can't keep using it
// as its gateway. The olm ignores this unless it selected this resource.
if (
siteResource.mode === "gateway" &&
clientSiteResourcesToRemove.length > 0
) {
await sendGatewayDisable(
clientSiteResourcesToRemove,
siteResource.siteResourceId
);
}
/////////// process the client-site associations ///////////
logger.debug(
@@ -2056,6 +2073,29 @@ export async function handleMessagingForUpdatedSiteResource(
);
}
// The olm only knows which gateway resource it selected and the sites it
// is currently using for it, so tell the clients that have access to this
// one what changed. Clients that lost access are handled by the rebuild.
if (existingSiteResource?.mode === "gateway") {
const clientIds = mergedAllClients.map((c) => c.clientId);
if (
updatedSiteResource.mode !== "gateway" ||
!updatedSiteResource.enabled
) {
await sendGatewayDisable(
clientIds,
updatedSiteResource.siteResourceId
);
} else {
await sendGatewaySitesUpdate(
clientIds,
updatedSiteResource.siteResourceId,
addedSiteIds,
removedSiteIds
);
}
}
logger.debug(
`handleMessagingForUpdatedSiteResource: DONE siteResourceId=${updatedSiteResource.siteResourceId}`
);
+82
View File
@@ -0,0 +1,82 @@
import { sendToClientsBatch } from "#dynamic/routers/ws";
import { db, olms } from "@server/db";
import { canCompress } from "@server/lib/clientVersionChecks";
import logger from "@server/logger";
import { inArray } from "drizzle-orm";
// The olm only tracks which gateway (exit node) site resource it selected, by
// its numeric siteResourceId, and the site IDs that resource currently
// resolves to. So all the server has to push is what changed for that one
// resource; the olm ignores a message whose siteResourceId isn't the one it
// selected, which stops a site added to some other gateway resource from
// being pulled into the client's gateway set. (Creates aren't pushed: a
// client has to select a gateway resource before it can be using it.)
async function sendGatewayMessageToClients(
clientIds: number[],
type: string,
data: Record<string, unknown>
): Promise<void> {
const uniqueClientIds = Array.from(new Set(clientIds));
if (uniqueClientIds.length === 0) {
return;
}
const olmRows = await db
.select({
olmId: olms.olmId,
version: olms.version
})
.from(olms)
.where(inArray(olms.clientId, uniqueClientIds));
const payloads = olmRows.map((olm) => ({
clientId: olm.olmId,
message: { type, data },
options: {
compress: canCompress(olm.version, "olm"),
incrementConfigVersion: true
}
}));
if (payloads.length === 0) {
return;
}
await sendToClientsBatch(payloads).catch((error) => {
logger.error(`Error sending ${type} messages to olms:`, error);
});
}
// Tells the olms of the given clients that sites were added to / removed from
// the gateway site resource, so those that selected it can adjust the set of
// sites they use as the gateway.
export async function sendGatewaySitesUpdate(
clientIds: number[],
siteResourceId: number,
addedSiteIds: number[],
removedSiteIds: number[]
): Promise<void> {
if (addedSiteIds.length === 0 && removedSiteIds.length === 0) {
return;
}
await sendGatewayMessageToClients(
clientIds,
"olm/wg/gateway/sites/update",
{ siteResourceId, addedSiteIds, removedSiteIds }
);
}
// Tells the olms of the given clients that the gateway site resource can no
// longer be used as a gateway (it was deleted, disabled, changed to another
// mode, or the client lost access to it), so those that selected it drop out
// of gateway mode.
export async function sendGatewayDisable(
clientIds: number[],
siteResourceId: number
): Promise<void> {
await sendGatewayMessageToClients(clientIds, "olm/wg/gateway/disable", {
siteResourceId
});
}