mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-25 14:26:39 +00:00
Add version and send it down
This commit is contained in:
@@ -25,6 +25,7 @@ export interface AuthenticatedWebSocket extends WebSocket {
|
||||
connectionId?: string;
|
||||
isFullyConnected?: boolean;
|
||||
pendingMessages?: Buffer[];
|
||||
configVersion?: number;
|
||||
}
|
||||
|
||||
export interface TokenPayload {
|
||||
@@ -36,6 +37,7 @@ export interface TokenPayload {
|
||||
export interface WSMessage {
|
||||
type: string;
|
||||
data: any;
|
||||
configVersion?: number;
|
||||
}
|
||||
|
||||
export interface HandlerResponse {
|
||||
@@ -50,10 +52,11 @@ export interface HandlerContext {
|
||||
senderWs: WebSocket;
|
||||
client: Newt | Olm | RemoteExitNode | undefined;
|
||||
clientType: ClientType;
|
||||
sendToClient: (clientId: string, message: WSMessage) => Promise<boolean>;
|
||||
sendToClient: (clientId: string, message: WSMessage, options?: SendMessageOptions) => Promise<boolean>;
|
||||
broadcastToAllExcept: (
|
||||
message: WSMessage,
|
||||
excludeClientId?: string
|
||||
excludeClientId?: string,
|
||||
options?: SendMessageOptions
|
||||
) => Promise<void>;
|
||||
connectedClients: Map<string, WebSocket[]>;
|
||||
}
|
||||
@@ -62,6 +65,11 @@ export type MessageHandler = (
|
||||
context: HandlerContext
|
||||
) => Promise<HandlerResponse | void>;
|
||||
|
||||
// Options for sending messages with config version tracking
|
||||
export interface SendMessageOptions {
|
||||
incrementConfigVersion?: boolean;
|
||||
}
|
||||
|
||||
// Redis message type for cross-node communication
|
||||
export interface RedisMessage {
|
||||
type: "direct" | "broadcast";
|
||||
@@ -69,4 +77,5 @@ export interface RedisMessage {
|
||||
excludeClientId?: string;
|
||||
message: WSMessage;
|
||||
fromNodeId: string;
|
||||
options?: SendMessageOptions;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
TokenPayload,
|
||||
WebSocketRequest,
|
||||
WSMessage,
|
||||
AuthenticatedWebSocket
|
||||
AuthenticatedWebSocket,
|
||||
SendMessageOptions
|
||||
} from "./types";
|
||||
import { validateSessionToken } from "@server/auth/sessions/app";
|
||||
|
||||
@@ -34,6 +35,8 @@ const NODE_ID = uuidv4();
|
||||
|
||||
// Client tracking map (local to this node)
|
||||
const connectedClients: Map<string, AuthenticatedWebSocket[]> = new Map();
|
||||
// Config version tracking map (clientId -> version)
|
||||
const clientConfigVersions: Map<string, number> = new Map();
|
||||
// Helper to get map key
|
||||
const getClientMapKey = (clientId: string) => clientId;
|
||||
|
||||
@@ -84,14 +87,34 @@ const removeClient = async (
|
||||
// Local message sending (within this node)
|
||||
const sendToClientLocal = async (
|
||||
clientId: string,
|
||||
message: WSMessage
|
||||
message: WSMessage,
|
||||
options: SendMessageOptions = {}
|
||||
): Promise<boolean> => {
|
||||
const mapKey = getClientMapKey(clientId);
|
||||
const clients = connectedClients.get(mapKey);
|
||||
if (!clients || clients.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const messageString = JSON.stringify(message);
|
||||
|
||||
// Increment config version if requested
|
||||
if (options.incrementConfigVersion) {
|
||||
const currentVersion = clientConfigVersions.get(clientId) || 0;
|
||||
const newVersion = currentVersion + 1;
|
||||
clientConfigVersions.set(clientId, newVersion);
|
||||
// Update version on all client connections
|
||||
clients.forEach((client) => {
|
||||
client.configVersion = newVersion;
|
||||
});
|
||||
}
|
||||
|
||||
// Include config version in message
|
||||
const configVersion = clientConfigVersions.get(clientId) || 0;
|
||||
const messageWithVersion = {
|
||||
...message,
|
||||
configVersion
|
||||
};
|
||||
|
||||
const messageString = JSON.stringify(messageWithVersion);
|
||||
clients.forEach((client) => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(messageString);
|
||||
@@ -102,14 +125,31 @@ const sendToClientLocal = async (
|
||||
|
||||
const broadcastToAllExceptLocal = async (
|
||||
message: WSMessage,
|
||||
excludeClientId?: string
|
||||
excludeClientId?: string,
|
||||
options: SendMessageOptions = {}
|
||||
): Promise<void> => {
|
||||
connectedClients.forEach((clients, mapKey) => {
|
||||
const [type, id] = mapKey.split(":");
|
||||
if (!(excludeClientId && id === excludeClientId)) {
|
||||
const clientId = mapKey; // mapKey is the clientId
|
||||
if (!(excludeClientId && clientId === excludeClientId)) {
|
||||
// Handle config version per client
|
||||
if (options.incrementConfigVersion) {
|
||||
const currentVersion = clientConfigVersions.get(clientId) || 0;
|
||||
const newVersion = currentVersion + 1;
|
||||
clientConfigVersions.set(clientId, newVersion);
|
||||
clients.forEach((client) => {
|
||||
client.configVersion = newVersion;
|
||||
});
|
||||
}
|
||||
// Include config version in message for this client
|
||||
const configVersion = clientConfigVersions.get(clientId) || 0;
|
||||
const messageWithVersion = {
|
||||
...message,
|
||||
configVersion
|
||||
};
|
||||
clients.forEach((client) => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(JSON.stringify(message));
|
||||
client.send(JSON.stringify(messageWithVersion));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -119,10 +159,11 @@ const broadcastToAllExceptLocal = async (
|
||||
// Cross-node message sending
|
||||
const sendToClient = async (
|
||||
clientId: string,
|
||||
message: WSMessage
|
||||
message: WSMessage,
|
||||
options: SendMessageOptions = {}
|
||||
): Promise<boolean> => {
|
||||
// Try to send locally first
|
||||
const localSent = await sendToClientLocal(clientId, message);
|
||||
const localSent = await sendToClientLocal(clientId, message, options);
|
||||
|
||||
logger.debug(
|
||||
`sendToClient: Message type ${message.type} sent to clientId ${clientId}`
|
||||
@@ -133,10 +174,11 @@ const sendToClient = async (
|
||||
|
||||
const broadcastToAllExcept = async (
|
||||
message: WSMessage,
|
||||
excludeClientId?: string
|
||||
excludeClientId?: string,
|
||||
options: SendMessageOptions = {}
|
||||
): Promise<void> => {
|
||||
// Broadcast locally
|
||||
await broadcastToAllExceptLocal(message, excludeClientId);
|
||||
await broadcastToAllExceptLocal(message, excludeClientId, options);
|
||||
};
|
||||
|
||||
// Check if a client has active connections across all nodes
|
||||
@@ -146,6 +188,11 @@ const hasActiveConnections = async (clientId: string): Promise<boolean> => {
|
||||
return !!(clients && clients.length > 0);
|
||||
};
|
||||
|
||||
// Get the current config version for a client
|
||||
const getClientConfigVersion = (clientId: string): number => {
|
||||
return clientConfigVersions.get(clientId) || 0;
|
||||
};
|
||||
|
||||
// Get all active nodes for a client
|
||||
const getActiveNodes = async (
|
||||
clientType: ClientType,
|
||||
@@ -434,5 +481,6 @@ export {
|
||||
getActiveNodes,
|
||||
disconnectClient,
|
||||
NODE_ID,
|
||||
cleanup
|
||||
cleanup,
|
||||
getClientConfigVersion
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user