remote desktop only shuts off whoever is currently connected instead of all. File browser stuff is now per user.
This commit is contained in:
@@ -315,17 +315,24 @@ export function handleRemoteDesktopViewerMessage(ws: ServerWebSocket<SocketData>
|
|||||||
logger.debug(`[rd] ignoring duplicate desktop_start for client ${clientId}`);
|
logger.debug(`[rd] ignoring duplicate desktop_start for client ${clientId}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "desktop_stop":
|
case "desktop_stop": {
|
||||||
sendDesktopCommand(target, "desktop_stop", {});
|
const otherViewers = sessionManager.getRdSessionsForClient(clientId)
|
||||||
if (state.isStreaming) {
|
.filter(s => s.id !== ws.data.sessionId);
|
||||||
state.isStreaming = false;
|
if (otherViewers.length === 0) {
|
||||||
rdStreamingState.set(clientId, state);
|
sendDesktopCommand(target, "desktop_stop", {});
|
||||||
logger.debug(`[rd] stopped streaming for client ${clientId}`);
|
if (state.isStreaming) {
|
||||||
|
state.isStreaming = false;
|
||||||
|
rdStreamingState.set(clientId, state);
|
||||||
|
logger.debug(`[rd] stopped streaming for client ${clientId}`);
|
||||||
|
} else {
|
||||||
|
rdStreamingState.set(clientId, { ...state, isStreaming: false });
|
||||||
|
logger.debug(`[rd] stop requested while not streaming for client ${clientId}`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rdStreamingState.set(clientId, { ...state, isStreaming: false });
|
logger.debug(`[rd] ignoring desktop_stop for client ${clientId} - ${otherViewers.length} other viewer(s) still active`);
|
||||||
logger.debug(`[rd] stop requested while not streaming for client ${clientId}`);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case "desktop_select_display": {
|
case "desktop_select_display": {
|
||||||
const newDisplay = Number(payload.display) || 0;
|
const newDisplay = Number(payload.display) || 0;
|
||||||
if (state.display !== newDisplay) {
|
if (state.display !== newDisplay) {
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ type WsViewerClusterDeps = {
|
|||||||
consumeHttpDownloadPayload: (payload: any) => Promise<void> | void;
|
consumeHttpDownloadPayload: (payload: any) => Promise<void> | void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fileBrowserCommandSessions = new Map<string, string>();
|
||||||
|
|
||||||
|
function trackFileBrowserCommand(commandId: string, sessionId: string): void {
|
||||||
|
fileBrowserCommandSessions.set(commandId, sessionId);
|
||||||
|
setTimeout(() => fileBrowserCommandSessions.delete(commandId), 10 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function decodeViewerPayload(raw: string | ArrayBuffer | Uint8Array): any | null {
|
function decodeViewerPayload(raw: string | ArrayBuffer | Uint8Array): any | null {
|
||||||
if (typeof raw === "string") {
|
if (typeof raw === "string") {
|
||||||
try {
|
try {
|
||||||
@@ -85,39 +92,41 @@ export function handleFileBrowserViewerMessage(ws: ServerWebSocket<SocketData>,
|
|||||||
if (typeof payload.commandType !== "string") return;
|
if (typeof payload.commandType !== "string") return;
|
||||||
logger.debug(`[DEBUG] Handling command type: ${payload.commandType}`);
|
logger.debug(`[DEBUG] Handling command type: ${payload.commandType}`);
|
||||||
const actualPayload = payload.payload || {};
|
const actualPayload = payload.payload || {};
|
||||||
|
const routedId = payload.id || commandId;
|
||||||
|
if (ws.data.sessionId) trackFileBrowserCommand(routedId, ws.data.sessionId);
|
||||||
switch (payload.commandType) {
|
switch (payload.commandType) {
|
||||||
case "file_read":
|
case "file_read":
|
||||||
logger.debug(`[DEBUG] Forwarding file_read to client ${clientId}:`, actualPayload.path);
|
logger.debug(`[DEBUG] Forwarding file_read to client ${clientId}:`, actualPayload.path);
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_read", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_read", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_read");
|
metrics.recordCommand("file_read");
|
||||||
break;
|
break;
|
||||||
case "file_write":
|
case "file_write":
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_write", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_write", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_write");
|
metrics.recordCommand("file_write");
|
||||||
break;
|
break;
|
||||||
case "file_search":
|
case "file_search":
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_search", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_search", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_search");
|
metrics.recordCommand("file_search");
|
||||||
break;
|
break;
|
||||||
case "file_copy":
|
case "file_copy":
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_copy", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_copy", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_copy");
|
metrics.recordCommand("file_copy");
|
||||||
break;
|
break;
|
||||||
case "file_move":
|
case "file_move":
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_move", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_move", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_move");
|
metrics.recordCommand("file_move");
|
||||||
break;
|
break;
|
||||||
case "file_chmod":
|
case "file_chmod":
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_chmod", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_chmod", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_chmod");
|
metrics.recordCommand("file_chmod");
|
||||||
break;
|
break;
|
||||||
case "file_execute":
|
case "file_execute":
|
||||||
logger.debug(`[DEBUG] Forwarding file_execute to client ${clientId}:`, actualPayload.path);
|
logger.debug(`[DEBUG] Forwarding file_execute to client ${clientId}:`, actualPayload.path);
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_execute", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_execute", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_execute");
|
metrics.recordCommand("file_execute");
|
||||||
break;
|
break;
|
||||||
case "file_upload_http":
|
case "file_upload_http":
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_upload_http", id: payload.id || commandId, payload: actualPayload } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_upload_http", id: routedId, payload: actualPayload } as any));
|
||||||
metrics.recordCommand("file_upload");
|
metrics.recordCommand("file_upload");
|
||||||
logAudit({
|
logAudit({
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
@@ -137,6 +146,7 @@ export function handleFileBrowserViewerMessage(ws: ServerWebSocket<SocketData>,
|
|||||||
|
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
case "file_list":
|
case "file_list":
|
||||||
|
if (ws.data.sessionId) trackFileBrowserCommand(commandId, ws.data.sessionId);
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_list", id: commandId, payload: { path: payload.path || "" } } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_list", id: commandId, payload: { path: payload.path || "" } } as any));
|
||||||
metrics.recordCommand("file_list");
|
metrics.recordCommand("file_list");
|
||||||
logAudit({
|
logAudit({
|
||||||
@@ -150,6 +160,7 @@ export function handleFileBrowserViewerMessage(ws: ServerWebSocket<SocketData>,
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "file_download":
|
case "file_download":
|
||||||
|
if (ws.data.sessionId) trackFileBrowserCommand(commandId, ws.data.sessionId);
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_download", id: commandId, payload: { path: payload.path || "" } } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_download", id: commandId, payload: { path: payload.path || "" } } as any));
|
||||||
metrics.recordCommand("file_download");
|
metrics.recordCommand("file_download");
|
||||||
logAudit({
|
logAudit({
|
||||||
@@ -177,6 +188,7 @@ export function handleFileBrowserViewerMessage(ws: ServerWebSocket<SocketData>,
|
|||||||
}
|
}
|
||||||
case "file_delete": {
|
case "file_delete": {
|
||||||
const deleteCommandId = payload.commandId || commandId;
|
const deleteCommandId = payload.commandId || commandId;
|
||||||
|
if (ws.data.sessionId) trackFileBrowserCommand(deleteCommandId, ws.data.sessionId);
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_delete", id: deleteCommandId, payload: { path: payload.path || "" } } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_delete", id: deleteCommandId, payload: { path: payload.path || "" } } as any));
|
||||||
metrics.recordCommand("file_delete");
|
metrics.recordCommand("file_delete");
|
||||||
logAudit({
|
logAudit({
|
||||||
@@ -192,6 +204,7 @@ export function handleFileBrowserViewerMessage(ws: ServerWebSocket<SocketData>,
|
|||||||
}
|
}
|
||||||
case "file_mkdir": {
|
case "file_mkdir": {
|
||||||
const mkdirCommandId = payload.commandId || commandId;
|
const mkdirCommandId = payload.commandId || commandId;
|
||||||
|
if (ws.data.sessionId) trackFileBrowserCommand(mkdirCommandId, ws.data.sessionId);
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_mkdir", id: mkdirCommandId, payload: { path: payload.path || "" } } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_mkdir", id: mkdirCommandId, payload: { path: payload.path || "" } } as any));
|
||||||
metrics.recordCommand("file_mkdir");
|
metrics.recordCommand("file_mkdir");
|
||||||
logAudit({
|
logAudit({
|
||||||
@@ -207,6 +220,7 @@ export function handleFileBrowserViewerMessage(ws: ServerWebSocket<SocketData>,
|
|||||||
}
|
}
|
||||||
case "file_zip": {
|
case "file_zip": {
|
||||||
const zipCommandId = payload.commandId || commandId;
|
const zipCommandId = payload.commandId || commandId;
|
||||||
|
if (ws.data.sessionId) trackFileBrowserCommand(zipCommandId, ws.data.sessionId);
|
||||||
target.ws.send(encodeMessage({ type: "command", commandType: "file_zip", id: zipCommandId, payload: { path: payload.path || "" } } as any));
|
target.ws.send(encodeMessage({ type: "command", commandType: "file_zip", id: zipCommandId, payload: { path: payload.path || "" } } as any));
|
||||||
metrics.recordCommand("file_zip");
|
metrics.recordCommand("file_zip");
|
||||||
logAudit({
|
logAudit({
|
||||||
@@ -239,6 +253,9 @@ export function handleFileBrowserMessage(clientId: string, payload: any, deps: W
|
|||||||
void deps.consumeHttpDownloadPayload(payload);
|
void deps.consumeHttpDownloadPayload(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const payloadCommandId = typeof payload?.commandId === "string" ? payload.commandId : undefined;
|
||||||
|
const ownerSessionId = payloadCommandId ? fileBrowserCommandSessions.get(payloadCommandId) : undefined;
|
||||||
|
|
||||||
let hasSession = false;
|
let hasSession = false;
|
||||||
for (const session of sessionManager.getFileBrowserSessionsByClient(clientId)) {
|
for (const session of sessionManager.getFileBrowserSessionsByClient(clientId)) {
|
||||||
if (!hasSession) {
|
if (!hasSession) {
|
||||||
@@ -250,6 +267,9 @@ export function handleFileBrowserMessage(clientId: string, payload: any, deps: W
|
|||||||
if (isHttpDownload) {
|
if (isHttpDownload) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (ownerSessionId && session.id !== ownerSessionId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (payload.type === "file_download" && payload.data) {
|
if (payload.type === "file_download" && payload.data) {
|
||||||
const data = payload.data instanceof Uint8Array ? payload.data : new Uint8Array(payload.data);
|
const data = payload.data instanceof Uint8Array ? payload.data : new Uint8Array(payload.data);
|
||||||
safeSendViewer(session.viewer, { ...payload, data });
|
safeSendViewer(session.viewer, { ...payload, data });
|
||||||
|
|||||||
Reference in New Issue
Block a user