guanghulab/server/app/modules/agent-handshake.js

187 lines
4.6 KiB
JavaScript

'use strict';
const crypto = require('crypto');
const { v4: uuidv4 } = require('uuid');
// ─── 常量 ───
const HANDSHAKE_EXPIRE_MS = 5 * 60 * 1000; // 5分钟
const HEARTBEAT_INTERVAL_MS = 30 * 1000; // 30秒
const CONNECTION_EXPIRE_MS = 24 * 60 * 60 * 1000; // 24小时
// ─── 内存存储 ───
const pendingHandshakes = new Map(); // handshakeId → { initiatorId, targetId, createdAt, tempKey }
const activeConnections = new Map(); // connectionId → { initiatorId, targetId, createdAt, lastHeartbeat, sharedKey }
/**
* 初始化握手
* @param {string} initiatorId - 发起方Agent ID
* @param {string} targetId - 目标Agent ID
* @param {string} token - 会话token
* @returns {{handshakeId: string, tempKey: string, expiresAt: string}}
*/
function initiateHandshake(initiatorId, targetId, token) {
if (!initiatorId || !targetId || !token) {
throw new Error('缺少必要参数');
}
// 验证会话token
// TODO: 集成现有会话验证系统
const handshakeId = uuidv4();
const tempKey = crypto.randomBytes(32).toString('hex');
const expiresAt = Date.now() + HANDSHAKE_EXPIRE_MS;
pendingHandshakes.set(handshakeId, {
initiatorId,
targetId,
tempKey,
createdAt: Date.now(),
expiresAt
});
return {
handshakeId,
tempKey,
expiresAt: new Date(expiresAt).toISOString()
};
}
/**
* 完成握手
* @param {string} handshakeId - 握手ID
* @param {string} targetId - 目标Agent ID
* @param {string} targetKey - 目标Agent临时密钥
* @param {string} token - 会话token
* @returns {{connectionId: string, sharedKey: string, expiresAt: string}}
*/
function completeHandshake(handshakeId, targetId, targetKey, token) {
if (!handshakeId || !targetId || !targetKey || !token) {
throw new Error('缺少必要参数');
}
const handshake = pendingHandshakes.get(handshakeId);
if (!handshake) {
throw new Error('无效的握手ID');
}
if (Date.now() > handshake.expiresAt) {
pendingHandshakes.delete(handshakeId);
throw new Error('握手已过期');
}
if (handshake.targetId !== targetId) {
throw new Error('目标Agent不匹配');
}
// 验证临时密钥
if (handshake.tempKey !== targetKey) {
throw new Error('无效的临时密钥');
}
// 生成共享密钥
const connectionId = uuidv4();
const sharedKey = crypto.randomBytes(32).toString('hex');
const expiresAt = Date.now() + CONNECTION_EXPIRE_MS;
activeConnections.set(connectionId, {
initiatorId: handshake.initiatorId,
targetId: handshake.targetId,
sharedKey,
createdAt: Date.now(),
lastHeartbeat: Date.now(),
expiresAt
});
// 清理握手
pendingHandshakes.delete(handshakeId);
return {
connectionId,
sharedKey,
expiresAt: new Date(expiresAt).toISOString()
};
}
/**
* 心跳检测
* @param {string} connectionId - 连接ID
* @param {string} token - 会话token
* @returns {{alive: boolean, expiresAt: string}}
*/
function heartbeat(connectionId, token) {
if (!connectionId || !token) {
throw new Error('缺少必要参数');
}
const connection = activeConnections.get(connectionId);
if (!connection) {
throw new Error('无效的连接ID');
}
if (Date.now() > connection.expiresAt) {
activeConnections.delete(connectionId);
throw new Error('连接已过期');
}
connection.lastHeartbeat = Date.now();
return {
alive: true,
expiresAt: new Date(connection.expiresAt).toISOString()
};
}
/**
* 断开连接
* @param {string} connectionId - 连接ID
* @param {string} token - 会话token
* @returns {{success: boolean}}
*/
function disconnect(connectionId, token) {
if (!connectionId || !token) {
throw new Error('缺少必要参数');
}
activeConnections.delete(connectionId);
return { success: true };
}
/**
* 验证连接
* @param {string} connectionId - 连接ID
* @param {string} sharedKey - 共享密钥
* @returns {{valid: boolean, initiatorId?: string, targetId?: string}}
*/
function validateConnection(connectionId, sharedKey) {
if (!connectionId || !sharedKey) {
return { valid: false };
}
const connection = activeConnections.get(connectionId);
if (!connection) {
return { valid: false };
}
if (Date.now() > connection.expiresAt) {
activeConnections.delete(connectionId);
return { valid: false };
}
if (connection.sharedKey !== sharedKey) {
return { valid: false };
}
return {
valid: true,
initiatorId: connection.initiatorId,
targetId: connection.targetId
};
}
module.exports = {
initiateHandshake,
completeHandshake,
heartbeat,
disconnect,
validateConnection
};