Extract public control plane routes
This commit is contained in:
+15
-142
@@ -27,11 +27,8 @@ import {
|
|||||||
parseControlPlaneDeviceAssignRequest,
|
parseControlPlaneDeviceAssignRequest,
|
||||||
parseControlPlaneDeviceIdentifyRequest,
|
parseControlPlaneDeviceIdentifyRequest,
|
||||||
parseControlPlaneDeviceRemoveRequest,
|
parseControlPlaneDeviceRemoveRequest,
|
||||||
parseControlPlaneDeviceRenameRequest,
|
|
||||||
parseControlPlaneDeviceSessionRequest,
|
|
||||||
parseControlPlanePublicAuthGrantRequest,
|
parseControlPlanePublicAuthGrantRequest,
|
||||||
parseControlPlanePublicAuthGrantRevokeRequest,
|
parseControlPlanePublicAuthGrantRevokeRequest,
|
||||||
parseControlPlanePublicSessionRequest,
|
|
||||||
parseControlPlaneSurfaceSessionRequest,
|
parseControlPlaneSurfaceSessionRequest,
|
||||||
parseControlPlaneErrorsRequest,
|
parseControlPlaneErrorsRequest,
|
||||||
parseControlPlaneLogsRequest,
|
parseControlPlaneLogsRequest,
|
||||||
@@ -39,14 +36,12 @@ import {
|
|||||||
parseControlPlaneReplEvalRequest,
|
parseControlPlaneReplEvalRequest,
|
||||||
parseControlPlaneRunRequest,
|
parseControlPlaneRunRequest,
|
||||||
type ControlPlaneCutoverResponse,
|
type ControlPlaneCutoverResponse,
|
||||||
type ControlPlaneDeviceSessionResponse,
|
|
||||||
type ControlPlaneDeviceTarget,
|
type ControlPlaneDeviceTarget,
|
||||||
type ControlPlaneDevicesResponse,
|
type ControlPlaneDevicesResponse,
|
||||||
type ControlPlaneErrorsResponse,
|
type ControlPlaneErrorsResponse,
|
||||||
type ControlPlaneLogsResponse,
|
type ControlPlaneLogsResponse,
|
||||||
type ControlPlanePublicAuthGrantRevokeResponse,
|
type ControlPlanePublicAuthGrantRevokeResponse,
|
||||||
type ControlPlanePublicAuthGrantResponse,
|
type ControlPlanePublicAuthGrantResponse,
|
||||||
type ControlPlanePublicSessionResponse,
|
|
||||||
type ControlPlaneReplLogEntry,
|
type ControlPlaneReplLogEntry,
|
||||||
type ControlPlaneRunResponse,
|
type ControlPlaneRunResponse,
|
||||||
type ControlPlaneSurfaceSessionResponse,
|
type ControlPlaneSurfaceSessionResponse,
|
||||||
@@ -87,17 +82,15 @@ import {
|
|||||||
import {
|
import {
|
||||||
createPublicAuthGrant,
|
createPublicAuthGrant,
|
||||||
createPublicSessionToken,
|
createPublicSessionToken,
|
||||||
exchangePublicAuthGrant,
|
|
||||||
loadPublicAuthGrants,
|
loadPublicAuthGrants,
|
||||||
normalizePublicAccessTarget,
|
normalizePublicAccessTarget,
|
||||||
parsePublicSessionToken,
|
|
||||||
publicSessionAllowsDeviceTarget,
|
|
||||||
revokePublicAuthGrant,
|
revokePublicAuthGrant,
|
||||||
} from "./src/public-auth-grants.js";
|
} from "./src/public-auth-grants.js";
|
||||||
import {
|
import {
|
||||||
createReplManager,
|
createReplManager,
|
||||||
listReplSessionsForControlPlane,
|
listReplSessionsForControlPlane,
|
||||||
} from "./src/repl-manager.js";
|
} from "./src/repl-manager.js";
|
||||||
|
import { handlePublicControlPlaneRoute } from "./src/public-control-routes.js";
|
||||||
import { createRunningPackageManager } from "./src/running-packages.js";
|
import { createRunningPackageManager } from "./src/running-packages.js";
|
||||||
import { createStateContextRegistry } from "./src/state-contexts.js";
|
import { createStateContextRegistry } from "./src/state-contexts.js";
|
||||||
import { createSubscriptionManager } from "./src/subscriptions.js";
|
import { createSubscriptionManager } from "./src/subscriptions.js";
|
||||||
@@ -1157,140 +1150,20 @@ if (CONTROL_PLANE_SECRET) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (await handlePublicControlPlaneRoute({
|
||||||
request.method === "POST" &&
|
broadcastDevicesChanged: () =>
|
||||||
request.url === CONTROL_PLANE_ROUTES.deviceSession
|
broadcastControlPlaneStatusInvalidated("devices-changed"),
|
||||||
) {
|
findDeviceForControlPlane,
|
||||||
try {
|
getOrCreateDevice,
|
||||||
const body = await readJsonBody(request);
|
isDeviceConnected,
|
||||||
const params = parseControlPlaneDeviceSessionRequest(body);
|
markDeviceSeenThisSession,
|
||||||
if (!params) {
|
request,
|
||||||
sendJson(response, 400, { error: "Invalid device session request" });
|
requireDevice,
|
||||||
return;
|
response,
|
||||||
}
|
saveDeviceState,
|
||||||
const publicSession = parsePublicSessionToken(params.sessionToken);
|
sendDeviceState,
|
||||||
if (
|
tryValidateDeviceTarget,
|
||||||
!publicSession ||
|
})) {
|
||||||
!publicSessionAllowsDeviceTarget(publicSession, params.requestedTarget)
|
|
||||||
) {
|
|
||||||
sendJson(response, 401, { error: "Unauthorized" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const device = getOrCreateDevice(params.deviceId);
|
|
||||||
let changed = false;
|
|
||||||
let persistedChanged = false;
|
|
||||||
if (markDeviceSeenThisSession(device.deviceId)) {
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
device.lastSeenAt = nowIso();
|
|
||||||
if ((params.userAgent ?? null) !== device.lastUserAgent) {
|
|
||||||
device.lastUserAgent = params.userAgent ?? null;
|
|
||||||
changed = true;
|
|
||||||
persistedChanged = true;
|
|
||||||
}
|
|
||||||
const nextViewport = params.viewport ?? null;
|
|
||||||
if (JSON.stringify(device.viewport) !== JSON.stringify(nextViewport)) {
|
|
||||||
device.viewport = nextViewport;
|
|
||||||
changed = true;
|
|
||||||
persistedChanged = true;
|
|
||||||
}
|
|
||||||
if (params.requestedTarget !== undefined) {
|
|
||||||
const validatedTarget = tryValidateDeviceTarget(
|
|
||||||
params.requestedTarget ?? null,
|
|
||||||
);
|
|
||||||
if (!validatedTarget.ok) {
|
|
||||||
sendJson(response, 400, { error: validatedTarget.error });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (JSON.stringify(device.assignment) !== JSON.stringify(validatedTarget.target)) {
|
|
||||||
device.assignment = validatedTarget.target;
|
|
||||||
changed = true;
|
|
||||||
persistedChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
if (persistedChanged) {
|
|
||||||
saveDeviceState();
|
|
||||||
}
|
|
||||||
broadcastControlPlaneStatusInvalidated("devices-changed");
|
|
||||||
sendDeviceState(device.deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: ControlPlaneDeviceSessionResponse = {
|
|
||||||
device: findDeviceForControlPlane(device.deviceId)!,
|
|
||||||
};
|
|
||||||
sendJson(response, 200, result);
|
|
||||||
} catch (error) {
|
|
||||||
sendJson(response, 500, {
|
|
||||||
error: error instanceof Error ? error.message : String(error),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
request.method === "POST" &&
|
|
||||||
request.url === CONTROL_PLANE_ROUTES.publicSession
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const body = await readJsonBody(request);
|
|
||||||
const params = parseControlPlanePublicSessionRequest(body);
|
|
||||||
if (!params) {
|
|
||||||
sendJson(response, 400, { error: "Invalid public session request" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const session = exchangePublicAuthGrant(params.authGrant);
|
|
||||||
if (!session) {
|
|
||||||
sendJson(response, 401, { error: "Unauthorized" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result: ControlPlanePublicSessionResponse = session;
|
|
||||||
sendJson(response, 200, result);
|
|
||||||
} catch (error) {
|
|
||||||
sendJson(response, 500, {
|
|
||||||
error: error instanceof Error ? error.message : String(error),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
request.method === "POST" &&
|
|
||||||
request.url === CONTROL_PLANE_ROUTES.deviceRename
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const body = await readJsonBody(request);
|
|
||||||
const params = parseControlPlaneDeviceRenameRequest(body);
|
|
||||||
if (!params) {
|
|
||||||
sendJson(response, 400, { error: "Invalid device rename request" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const device = requireDevice(params.deviceId);
|
|
||||||
if (
|
|
||||||
!isAuthorizedControlPlaneRequest(request) &&
|
|
||||||
!isDeviceConnected(device.deviceId)
|
|
||||||
) {
|
|
||||||
sendJson(response, 401, { error: "Unauthorized" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const normalizedName = params.name?.trim().length
|
|
||||||
? params.name.trim()
|
|
||||||
: null;
|
|
||||||
if (device.name !== normalizedName) {
|
|
||||||
device.name = normalizedName;
|
|
||||||
saveDeviceState();
|
|
||||||
broadcastControlPlaneStatusInvalidated("devices-changed");
|
|
||||||
sendDeviceState(device.deviceId);
|
|
||||||
}
|
|
||||||
sendJson(response, 200, {
|
|
||||||
device: findDeviceForControlPlane(device.deviceId),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
sendJson(response, 500, {
|
|
||||||
error: error instanceof Error ? error.message : String(error),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
import type http from "node:http";
|
||||||
|
import {
|
||||||
|
CONTROL_PLANE_ROUTES,
|
||||||
|
parseControlPlaneDeviceRenameRequest,
|
||||||
|
parseControlPlaneDeviceSessionRequest,
|
||||||
|
parseControlPlanePublicSessionRequest,
|
||||||
|
type ControlPlaneDevice,
|
||||||
|
type ControlPlaneDeviceSessionResponse,
|
||||||
|
type ControlPlanePublicSessionResponse,
|
||||||
|
} from "@quixos/control-plane-protocol";
|
||||||
|
import {
|
||||||
|
isAuthorizedControlPlaneRequest,
|
||||||
|
readJsonBody,
|
||||||
|
sendJson,
|
||||||
|
} from "./http.js";
|
||||||
|
import {
|
||||||
|
exchangePublicAuthGrant,
|
||||||
|
parsePublicSessionToken,
|
||||||
|
publicSessionAllowsDeviceTarget,
|
||||||
|
} from "./public-auth-grants.js";
|
||||||
|
import { nowIso } from "./util.js";
|
||||||
|
|
||||||
|
export const handlePublicControlPlaneRoute = async ({
|
||||||
|
broadcastDevicesChanged,
|
||||||
|
findDeviceForControlPlane,
|
||||||
|
getOrCreateDevice,
|
||||||
|
isDeviceConnected,
|
||||||
|
markDeviceSeenThisSession,
|
||||||
|
request,
|
||||||
|
requireDevice,
|
||||||
|
response,
|
||||||
|
saveDeviceState,
|
||||||
|
sendDeviceState,
|
||||||
|
tryValidateDeviceTarget,
|
||||||
|
}: {
|
||||||
|
broadcastDevicesChanged: () => void;
|
||||||
|
findDeviceForControlPlane: (deviceId: string) => ControlPlaneDevice | undefined;
|
||||||
|
getOrCreateDevice: (deviceId: string) => any;
|
||||||
|
isDeviceConnected: (deviceId: string) => boolean;
|
||||||
|
markDeviceSeenThisSession: (deviceId: string) => boolean;
|
||||||
|
request: http.IncomingMessage;
|
||||||
|
requireDevice: (deviceId: string) => any;
|
||||||
|
response: http.ServerResponse;
|
||||||
|
saveDeviceState: () => void;
|
||||||
|
sendDeviceState: (deviceId: string) => void;
|
||||||
|
tryValidateDeviceTarget: (
|
||||||
|
target: any,
|
||||||
|
) =>
|
||||||
|
| { ok: true; target: any }
|
||||||
|
| { ok: false; error: string };
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
request.method === "POST" &&
|
||||||
|
request.url === CONTROL_PLANE_ROUTES.deviceSession
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const body = await readJsonBody(request);
|
||||||
|
const params = parseControlPlaneDeviceSessionRequest(body);
|
||||||
|
if (!params) {
|
||||||
|
sendJson(response, 400, { error: "Invalid device session request" });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const publicSession = parsePublicSessionToken(params.sessionToken);
|
||||||
|
if (
|
||||||
|
!publicSession ||
|
||||||
|
!publicSessionAllowsDeviceTarget(publicSession, params.requestedTarget)
|
||||||
|
) {
|
||||||
|
sendJson(response, 401, { error: "Unauthorized" });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = getOrCreateDevice(params.deviceId);
|
||||||
|
let changed = false;
|
||||||
|
let persistedChanged = false;
|
||||||
|
if (markDeviceSeenThisSession(device.deviceId)) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
device.lastSeenAt = nowIso();
|
||||||
|
if ((params.userAgent ?? null) !== device.lastUserAgent) {
|
||||||
|
device.lastUserAgent = params.userAgent ?? null;
|
||||||
|
changed = true;
|
||||||
|
persistedChanged = true;
|
||||||
|
}
|
||||||
|
const nextViewport = params.viewport ?? null;
|
||||||
|
if (JSON.stringify(device.viewport) !== JSON.stringify(nextViewport)) {
|
||||||
|
device.viewport = nextViewport;
|
||||||
|
changed = true;
|
||||||
|
persistedChanged = true;
|
||||||
|
}
|
||||||
|
if (params.requestedTarget !== undefined) {
|
||||||
|
const validatedTarget = tryValidateDeviceTarget(
|
||||||
|
params.requestedTarget ?? null,
|
||||||
|
);
|
||||||
|
if (!validatedTarget.ok) {
|
||||||
|
sendJson(response, 400, { error: validatedTarget.error });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (JSON.stringify(device.assignment) !== JSON.stringify(validatedTarget.target)) {
|
||||||
|
device.assignment = validatedTarget.target;
|
||||||
|
changed = true;
|
||||||
|
persistedChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
if (persistedChanged) {
|
||||||
|
saveDeviceState();
|
||||||
|
}
|
||||||
|
broadcastDevicesChanged();
|
||||||
|
sendDeviceState(device.deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: ControlPlaneDeviceSessionResponse = {
|
||||||
|
device: findDeviceForControlPlane(device.deviceId)!,
|
||||||
|
};
|
||||||
|
sendJson(response, 200, result);
|
||||||
|
} catch (error) {
|
||||||
|
sendJson(response, 500, {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
request.method === "POST" &&
|
||||||
|
request.url === CONTROL_PLANE_ROUTES.publicSession
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const body = await readJsonBody(request);
|
||||||
|
const params = parseControlPlanePublicSessionRequest(body);
|
||||||
|
if (!params) {
|
||||||
|
sendJson(response, 400, { error: "Invalid public session request" });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const session = exchangePublicAuthGrant(params.authGrant);
|
||||||
|
if (!session) {
|
||||||
|
sendJson(response, 401, { error: "Unauthorized" });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const result: ControlPlanePublicSessionResponse = session;
|
||||||
|
sendJson(response, 200, result);
|
||||||
|
} catch (error) {
|
||||||
|
sendJson(response, 500, {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
request.method === "POST" &&
|
||||||
|
request.url === CONTROL_PLANE_ROUTES.deviceRename
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const body = await readJsonBody(request);
|
||||||
|
const params = parseControlPlaneDeviceRenameRequest(body);
|
||||||
|
if (!params) {
|
||||||
|
sendJson(response, 400, { error: "Invalid device rename request" });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const device = requireDevice(params.deviceId);
|
||||||
|
if (
|
||||||
|
!isAuthorizedControlPlaneRequest(request) &&
|
||||||
|
!isDeviceConnected(device.deviceId)
|
||||||
|
) {
|
||||||
|
sendJson(response, 401, { error: "Unauthorized" });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const normalizedName = params.name?.trim().length
|
||||||
|
? params.name.trim()
|
||||||
|
: null;
|
||||||
|
if (device.name !== normalizedName) {
|
||||||
|
device.name = normalizedName;
|
||||||
|
saveDeviceState();
|
||||||
|
broadcastDevicesChanged();
|
||||||
|
sendDeviceState(device.deviceId);
|
||||||
|
}
|
||||||
|
sendJson(response, 200, {
|
||||||
|
device: findDeviceForControlPlane(device.deviceId),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
sendJson(response, 500, {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user