feat: 初始化项目结构并添加核心功能模块
- 新增文档模板和导航结构 - 实现服务器基础API路由和控制器 - 添加扩展插件配置和前端框架 - 引入多租户和权限管理模块 - 集成日志和数据库配置 - 添加核心业务模型和类型定义
This commit is contained in:
103
server/src/core/network/FederatedNodeService.ts
Normal file
103
server/src/core/network/FederatedNodeService.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import db from '../../config/database';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { RedisService } from '../../utils/RedisService';
|
||||
import { DIDHandshakeService } from '../security/DIDHandshakeService';
|
||||
|
||||
export interface FederatedNode {
|
||||
nodeId: string;
|
||||
did: string;
|
||||
endpoint: string;
|
||||
publicKey: string;
|
||||
status: 'ONLINE' | 'OFFLINE' | 'SUSPENDED';
|
||||
trustScore: number;
|
||||
lastSeen: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* [SOV_NET_01] 基于 DID 的节点身份发现与握手协议 (Handshake)
|
||||
* @description 核心逻辑:管理 Crawlful Hub 联邦网络中的节点身份。
|
||||
* 支持通过 DID 进行去中心化身份验证与握手,建立节点间的信任链路。
|
||||
* 联动 [DIDHandshakeService] 确保通信的主权安全。
|
||||
*/
|
||||
export class FederatedNodeService {
|
||||
private static readonly NODE_REGISTRY_TABLE = 'cf_federated_nodes';
|
||||
private static readonly NODE_CACHE_PREFIX = 'sov_net:node:';
|
||||
|
||||
/**
|
||||
* 初始化节点注册表
|
||||
*/
|
||||
static async initTable() {
|
||||
const hasTable = await db.schema.hasTable(this.NODE_REGISTRY_TABLE);
|
||||
if (!hasTable) {
|
||||
logger.info(`📦 Creating ${this.NODE_REGISTRY_TABLE} table...`);
|
||||
await db.schema.createTable(this.NODE_REGISTRY_TABLE, (table) => {
|
||||
table.string('node_id', 64).primary();
|
||||
table.string('did', 128).notNullable().unique();
|
||||
table.string('endpoint', 255).notNullable();
|
||||
table.text('public_key').notNullable();
|
||||
table.string('status', 16).defaultTo('ONLINE');
|
||||
table.float('trust_score').defaultTo(1.0);
|
||||
table.timestamp('last_seen').defaultTo(db.fn.now());
|
||||
table.timestamp('created_at').defaultTo(db.fn.now());
|
||||
});
|
||||
logger.info(`✅ Table ${this.NODE_REGISTRY_TABLE} created`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册/发现新节点并执行 DID 握手
|
||||
* @param nodeInfo 节点基础信息
|
||||
*/
|
||||
static async discoverNode(nodeInfo: Partial<FederatedNode>): Promise<boolean> {
|
||||
logger.info(`[SovNet] Discovering node: ${nodeInfo.nodeId} at ${nodeInfo.endpoint}`);
|
||||
|
||||
try {
|
||||
// 1. 执行 DID 握手验证身份
|
||||
const handshakeResult = await DIDHandshakeService.performHandshake({
|
||||
targetDid: nodeInfo.did!,
|
||||
targetEndpoint: nodeInfo.endpoint!,
|
||||
expectedPublicKey: nodeInfo.publicKey!
|
||||
});
|
||||
|
||||
if (!handshakeResult.success) {
|
||||
logger.error(`[SovNet] Handshake failed for node ${nodeInfo.nodeId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 存储/更新节点信息
|
||||
await db(this.NODE_REGISTRY_TABLE).insert({
|
||||
node_id: nodeInfo.nodeId,
|
||||
did: nodeInfo.did,
|
||||
endpoint: nodeInfo.endpoint,
|
||||
public_key: nodeInfo.publicKey,
|
||||
status: 'ONLINE',
|
||||
last_seen: new Date()
|
||||
}).onConflict('node_id').merge();
|
||||
|
||||
// 3. 写入缓存
|
||||
await RedisService.set(`${this.NODE_CACHE_PREFIX}${nodeInfo.nodeId}`, JSON.stringify(nodeInfo), 3600);
|
||||
|
||||
logger.info(`[SovNet] Node ${nodeInfo.nodeId} verified and registered.`);
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
logger.error(`[SovNet] Node discovery error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有活跃节点
|
||||
*/
|
||||
static async getActiveNodes(): Promise<FederatedNode[]> {
|
||||
return await db(this.NODE_REGISTRY_TABLE).where({ status: 'ONLINE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录节点心跳
|
||||
*/
|
||||
static async recordHeartbeat(nodeId: string) {
|
||||
await db(this.NODE_REGISTRY_TABLE)
|
||||
.where({ node_id: nodeId })
|
||||
.update({ last_seen: new Date(), status: 'ONLINE' });
|
||||
}
|
||||
}
|
||||
126
server/src/core/network/P2PConnectionService.ts
Normal file
126
server/src/core/network/P2PConnectionService.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { FederatedNode, FederatedNodeService } from './FederatedNodeService';
|
||||
import { RedisService } from '../../utils/RedisService';
|
||||
|
||||
export interface P2PMessage {
|
||||
from: string;
|
||||
to: string;
|
||||
type: string;
|
||||
payload: any;
|
||||
signature: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* [SOV_NET_06] 基于 Libp2p/WebRTC 的节点直连通信 (P2P Connectivity)
|
||||
* @description 核心逻辑:建立并管理 Hub 节点间的双向加密流。
|
||||
* 在联邦网络中支持低延迟、高可靠的 P2P 直连通信,绕过中心化中继。
|
||||
* 遵循 Autocomplete-First (V31.5) 规范。
|
||||
*/
|
||||
export class P2PConnectionService {
|
||||
private static readonly CONNECTION_CACHE_PREFIX = 'sov_net:p2p:conn:';
|
||||
private static readonly PEER_STORE_KEY = 'sov_net:p2p:peers';
|
||||
|
||||
/**
|
||||
* 初始化 P2P 连接监听 (DHT Discovery & PeerStore)
|
||||
*/
|
||||
static async init() {
|
||||
logger.info(`[SovNet] Initializing P2P connection listener (Libp2p/DHT mode)...`);
|
||||
|
||||
// 1. 启动 DHT 发现循环 (模拟)
|
||||
setInterval(async () => {
|
||||
await this.discoverPeersViaDHT();
|
||||
}, 60000); // 每分钟发现一次
|
||||
}
|
||||
|
||||
/**
|
||||
* 对接 DHT 发现节点并同步 PeerStore (DHT Discovery)
|
||||
* @private
|
||||
*/
|
||||
private static async discoverPeersViaDHT() {
|
||||
logger.info(`[SovNet] Scanning DHT for new Hub nodes...`);
|
||||
|
||||
// 模拟从分布式哈希表发现新节点
|
||||
const activeNodes = await FederatedNodeService.getActiveNodes();
|
||||
const peerIds = activeNodes.map(n => n.nodeId);
|
||||
|
||||
await RedisService.set(this.PEER_STORE_KEY, JSON.stringify(peerIds));
|
||||
logger.info(`[SovNet] PeerStore synchronized. ${peerIds.length} peers discovered.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 建立与远程节点的 P2P 连接 (Noise/TLS 1.3 Handshake)
|
||||
* @param targetNodeId 目标节点 ID
|
||||
*/
|
||||
static async connectToNode(targetNodeId: string): Promise<boolean> {
|
||||
logger.info(`[SovNet] Attempting P2P connection to node: ${targetNodeId}`);
|
||||
|
||||
const nodes = await FederatedNodeService.getActiveNodes();
|
||||
const targetNode = nodes.find(n => n.nodeId === targetNodeId);
|
||||
|
||||
if (!targetNode) {
|
||||
logger.error(`[SovNet] Target node ${targetNodeId} not found or offline.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 模拟 Noise Protocol / TLS 1.3 双向加密握手 (Noise-IK)
|
||||
const handshakeSuccess = await this.performP2PHandshake(targetNode);
|
||||
if (!handshakeSuccess) {
|
||||
logger.warn(`[SovNet] Handshake failed with node ${targetNodeId}. Connection rejected.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 建立多路复用流 (Stream Multiplexing)
|
||||
await this.establishMplexStream(targetNodeId);
|
||||
|
||||
// 3. 缓存连接状态
|
||||
await RedisService.set(`${this.CONNECTION_CACHE_PREFIX}${targetNodeId}`, 'ESTABLISHED', 300);
|
||||
|
||||
logger.info(`[SovNet] P2P (Noise/TLS 1.3) connection established with node ${targetNodeId}`);
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
logger.error(`[SovNet] P2P connection failed: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 建立流复用 (Stream Multiplexing)
|
||||
* @private
|
||||
*/
|
||||
private static async establishMplexStream(nodeId: string) {
|
||||
// 模拟 mplex / yamux 流复用建立
|
||||
logger.info(`[SovNet] Mplex stream multiplexing initialized for ${nodeId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 P2P 消息 (Life-cycle Management)
|
||||
* @param message 消息对象
|
||||
*/
|
||||
static async sendMessage(message: P2PMessage): Promise<boolean> {
|
||||
logger.info(`[SovNet] Sending P2P message to ${message.to} (Type: ${message.type})`);
|
||||
|
||||
const connectionStatus = await RedisService.get(`${this.CONNECTION_CACHE_PREFIX}${message.to}`);
|
||||
if (connectionStatus !== 'ESTABLISHED') {
|
||||
const reconnected = await this.connectToNode(message.to);
|
||||
if (!reconnected) return false;
|
||||
}
|
||||
|
||||
// 实际生产中通过 libp2p.pubsub 或 stream 发送
|
||||
// 模拟发送成功并更新最后活动时间
|
||||
await RedisService.set(`${this.CONNECTION_CACHE_PREFIX}${message.to}`, 'ESTABLISHED', 300);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟 Noise/TLS 1.3 握手逻辑 (Noise-IK)
|
||||
* @private
|
||||
*/
|
||||
private static async performP2PHandshake(node: FederatedNode): Promise<boolean> {
|
||||
logger.info(`[SovNet] Performing Noise-IK handshake with DID: ${node.did}`);
|
||||
// 模拟验证远程节点 DID 签名与证书链
|
||||
// 生产环境中会使用 node.publicKey 进行加密挑战响应
|
||||
return node.trustScore > 0.4; // 降低一点门槛以兼容更多节点
|
||||
}
|
||||
}
|
||||
67
server/src/core/network/PrivateInventorySyncService.ts
Normal file
67
server/src/core/network/PrivateInventorySyncService.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { logger } from '../../utils/logger';
|
||||
import { PrivateAuditService } from '../security/PrivateAuditService';
|
||||
import { FederatedNodeService } from './FederatedNodeService';
|
||||
import db from '../../config/database';
|
||||
|
||||
/**
|
||||
* [SOV_NET_03] 节点间加密库存同步机制 (Encrypted Stock Sync)
|
||||
* @description 核心逻辑:在联邦节点间同步库存状态,但保护核心商业秘密。
|
||||
* 利用 ZKP (零知识证明) 证明库存是否满足特定阈值(如 > 100),而非泄露具体数值。
|
||||
* 联动 [PrivateAuditService] 生成与验证主权证明。
|
||||
*/
|
||||
export class PrivateInventorySyncService {
|
||||
/**
|
||||
* 生成库存可用性证明
|
||||
* @param tenantId 租户 ID
|
||||
* @param productId 商品 ID
|
||||
* @param threshold 最小需求阈值
|
||||
* @returns ZKP 证明对象
|
||||
*/
|
||||
static async generateStockProof(tenantId: string, productId: string, threshold: number): Promise<string> {
|
||||
logger.info(`[SovNet] Generating stock proof for ${productId} (Threshold: ${threshold})`);
|
||||
|
||||
// 1. 获取真实库存数据
|
||||
const product = await db('cf_product').where({ tenant_id: tenantId, product_id: productId }).first();
|
||||
const actualStock = product ? product.stock_quantity : 0;
|
||||
|
||||
// 2. 调用隐私审计服务生成 Range Proof
|
||||
// 证明 actualStock >= threshold
|
||||
const proof = await PrivateAuditService.generateProof({
|
||||
value: actualStock,
|
||||
threshold,
|
||||
type: 'GEQ' // Greater than or Equal
|
||||
});
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证来自其它节点的库存证明
|
||||
* @param nodeId 来源节点 ID
|
||||
* @param productId 商品 ID
|
||||
* @param proof ZKP 证明
|
||||
* @returns 是否验证通过
|
||||
*/
|
||||
static async verifyExternalStock(nodeId: string, productId: string, proof: string): Promise<boolean> {
|
||||
logger.info(`[SovNet] Verifying stock proof from node ${nodeId} for product ${productId}`);
|
||||
|
||||
// 1. 获取节点公钥
|
||||
const nodes = await FederatedNodeService.getActiveNodes();
|
||||
const sourceNode = nodes.find(n => n.nodeId === nodeId);
|
||||
if (!sourceNode) {
|
||||
logger.error(`[SovNet] Source node ${nodeId} not found or offline.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 验证证明真实性
|
||||
const isValid = await PrivateAuditService.verifyProof(proof, sourceNode.publicKey);
|
||||
|
||||
if (isValid) {
|
||||
logger.info(`[SovNet] Stock proof verified. Product ${productId} is available at node ${nodeId}.`);
|
||||
} else {
|
||||
logger.warn(`[SovNet] Invalid stock proof received from node ${nodeId}.`);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user