Add node location access RPCs and operator visibility
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
import type { GatewayRequest, OutboundMessage } from '../protocol.js';
|
||||
import { makeError, makeResponse, ErrorCode, GATEWAY_PROTOCOL_VERSION, parseNodeRegisterParams } from '../protocol.js';
|
||||
import {
|
||||
makeError,
|
||||
makeResponse,
|
||||
ErrorCode,
|
||||
GATEWAY_PROTOCOL_VERSION,
|
||||
parseNodeRegisterParams,
|
||||
parseNodeLocationSetParams,
|
||||
parseNodeLocationGetParams,
|
||||
} from '../protocol.js';
|
||||
|
||||
export interface NodeRegistration {
|
||||
nodeId: string;
|
||||
@@ -12,14 +20,29 @@ export interface NodeRegistration {
|
||||
export interface NodeConnectionState {
|
||||
identity?: string;
|
||||
node?: NodeRegistration;
|
||||
location?: NodeLocation;
|
||||
}
|
||||
|
||||
export interface NodeLocation {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
accuracyMeters?: number;
|
||||
altitudeMeters?: number;
|
||||
headingDegrees?: number;
|
||||
speedMps?: number;
|
||||
source: 'gps' | 'network' | 'manual' | 'unknown';
|
||||
capturedAt: number;
|
||||
receivedAt: number;
|
||||
}
|
||||
|
||||
export interface NodeHandlerDeps {
|
||||
enabled: boolean;
|
||||
locationEnabled: boolean;
|
||||
allowedRoles: string[];
|
||||
featureGates: Record<string, boolean>;
|
||||
getConnectionState: (connectionId: string) => NodeConnectionState | undefined;
|
||||
setNodeRegistration: (connectionId: string, registration: NodeRegistration) => void;
|
||||
setNodeLocation: (connectionId: string, location: NodeLocation) => void;
|
||||
}
|
||||
|
||||
export function createNodeHandlers(deps: NodeHandlerDeps) {
|
||||
@@ -107,6 +130,74 @@ export function createNodeHandlers(deps: NodeHandlerDeps) {
|
||||
});
|
||||
},
|
||||
|
||||
'node.location.set': async (request: GatewayRequest): Promise<OutboundMessage> => {
|
||||
if (!deps.enabled) {
|
||||
return makeError(request.id, ErrorCode.AuthFailed, 'Node RPC is disabled');
|
||||
}
|
||||
if (!deps.locationEnabled) {
|
||||
return makeError(request.id, ErrorCode.AuthFailed, 'Node location access is disabled');
|
||||
}
|
||||
|
||||
const parsed = parseNodeLocationSetParams(request.params);
|
||||
if (!parsed) {
|
||||
return makeError(request.id, ErrorCode.InvalidRequest, 'Invalid node.location.set params');
|
||||
}
|
||||
|
||||
const state = deps.getConnectionState(parsed.connectionId);
|
||||
if (!state?.node) {
|
||||
return makeError(request.id, ErrorCode.AuthFailed, 'Node is not registered for this connection');
|
||||
}
|
||||
|
||||
const location: NodeLocation = {
|
||||
latitude: parsed.latitude,
|
||||
longitude: parsed.longitude,
|
||||
accuracyMeters: parsed.accuracyMeters,
|
||||
altitudeMeters: parsed.altitudeMeters,
|
||||
headingDegrees: parsed.headingDegrees,
|
||||
speedMps: parsed.speedMps,
|
||||
source: parsed.source ?? 'unknown',
|
||||
capturedAt: parsed.capturedAt ?? Date.now(),
|
||||
receivedAt: Date.now(),
|
||||
};
|
||||
deps.setNodeLocation(parsed.connectionId, location);
|
||||
|
||||
return makeResponse(request.id, {
|
||||
updated: true,
|
||||
node: {
|
||||
id: state.node.nodeId,
|
||||
role: state.node.role,
|
||||
},
|
||||
location,
|
||||
});
|
||||
},
|
||||
|
||||
'node.location.get': async (request: GatewayRequest): Promise<OutboundMessage> => {
|
||||
if (!deps.enabled) {
|
||||
return makeError(request.id, ErrorCode.AuthFailed, 'Node RPC is disabled');
|
||||
}
|
||||
if (!deps.locationEnabled) {
|
||||
return makeError(request.id, ErrorCode.AuthFailed, 'Node location access is disabled');
|
||||
}
|
||||
|
||||
const parsed = parseNodeLocationGetParams(request.params);
|
||||
if (!parsed) {
|
||||
return makeError(request.id, ErrorCode.InvalidRequest, 'Invalid node.location.get params');
|
||||
}
|
||||
|
||||
const state = deps.getConnectionState(parsed.connectionId);
|
||||
if (!state?.node) {
|
||||
return makeError(request.id, ErrorCode.AuthFailed, 'Node is not registered for this connection');
|
||||
}
|
||||
|
||||
return makeResponse(request.id, {
|
||||
node: {
|
||||
id: state.node.nodeId,
|
||||
role: state.node.role,
|
||||
},
|
||||
location: state.location ?? null,
|
||||
});
|
||||
},
|
||||
|
||||
'system.capabilities': async (request: GatewayRequest): Promise<OutboundMessage> => {
|
||||
const params = request.params as { connectionId?: string } | undefined;
|
||||
const connectionId = params?.connectionId;
|
||||
@@ -117,6 +208,7 @@ export function createNodeHandlers(deps: NodeHandlerDeps) {
|
||||
},
|
||||
nodes: {
|
||||
enabled: deps.enabled,
|
||||
locationEnabled: deps.locationEnabled,
|
||||
allowedRoles: deps.allowedRoles,
|
||||
registered: Boolean(state?.node),
|
||||
role: state?.node?.role,
|
||||
|
||||
Reference in New Issue
Block a user