[GLADA] GLADA-CAB-20260415-001 step3 1. 创建了agent-handshake.js模块,实现Agent握手协议的核心逻辑_2. 修改了server.js,添加了Agent握手协议的API端点_3. 更新了package.json,添加了uuid依赖和新的脚本命令_4. 实现了完整的握手流程:初始化→完成→心跳→断开_5. 添加了连接验证和密钥管理功能
This commit is contained in:
parent
ec89bb0b5d
commit
f05d89417a
187
server/app/modules/agent-handshake.js
Normal file
187
server/app/modules/agent-handshake.js
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
'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
|
||||||
|
};
|
||||||
@ -25,7 +25,8 @@
|
|||||||
"pg": "^8.13.0",
|
"pg": "^8.13.0",
|
||||||
"nodemailer": "^7.0.11",
|
"nodemailer": "^7.0.11",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"cos-nodejs-sdk-v5": "^2.11.19"
|
"cos-nodejs-sdk-v5": "^2.11.19",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user