From 96f4fbe5824c58918d993e44c2192e5b34ac4172 Mon Sep 17 00:00:00 2001 From: lunny kuya Date: Wed, 11 Mar 2026 00:34:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Tailscale=20=E7=BD=91=E7=BB=9C=E9=9B=86?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 config/network.ts 节点配置模块 - 支持 Tailscale mesh 网络(100.x.x.x) - Redis 配置改用环境变量 - MemoV 动态节点发现 - 移除硬编码密码和密钥文件 - 添加 .env.example 配置模板 --- .env.example | 65 ++++--------------------- .gitignore | 16 ++++++ TAILSCALE-INTEGRATION.md | 55 +++++++++++++++++++++ config/network.ts | 102 +++++++++++++++++++++++++++++++++++++++ config/redis.ts | 7 ++- fsc/memov-sync-daemon.ts | 4 +- test-network.ts | 26 ++++++++++ 7 files changed, 217 insertions(+), 58 deletions(-) create mode 100644 TAILSCALE-INTEGRATION.md create mode 100644 config/network.ts create mode 100644 test-network.ts diff --git a/.env.example b/.env.example index 01ed53c..3397863 100644 --- a/.env.example +++ b/.env.example @@ -1,59 +1,14 @@ -# FSC Worker Daemon Environment Variables -# Copy this file to .env and fill in your values +# 环境变量配置示例 +# 复制此文件为 .env 并填入实际值 -# ============ Redis Configuration ============ -REDIS_HOST=10.10.0.1 +# Redis 配置 +REDIS_HOST=100.80.67.125 REDIS_PORT=6379 +REDIS_PASSWORD=your-redis-password -# ============ Worker Configuration ============ -# Unique identifier for this worker instance -AGENT_ID=worker-node1 +# LLM API Keys +DOUBAO_API_KEY=your-doubao-api-key +MINIMAX_API_KEY=your-minimax-api-key -# Maximum number of concurrent tasks -MAX_CONCURRENT=10 - -# ============ WireGuard Configuration ============ -# WireGuard interface name -WG_INTERFACE=wg0 - -# Node IP in the mesh network -NODE_IP=10.10.0.1 - -# WireGuard private key (generate with: wg genkey) -WG_PRIVATE_KEY=your_private_key_here - -# ============ Docker Configuration ============ -# Docker network mode -DOCKER_NETWORK_MODE=bridge - -# Docker resource limits -DOCKER_MEMORY_LIMIT=2g -DOCKER_CPU_LIMIT=2 - -# ============ Logging ============ -# Log level: debug, info, warn, error -LOG_LEVEL=info - -# Log file path -LOG_FILE=fsc-worker.log - -# ============ MemoV Configuration ============ -# MemoV Git repository path -MEMOV_PATH=/opt/claw-mesh/.mem - -# MemoV branch for this agent -MEMOV_BRANCH=agent-${AGENT_ID} - -# ============ Task Configuration ============ -# Maximum retry attempts for failed tasks -RETRY_ATTEMPTS=3 - -# Task timeout in seconds -TASK_TIMEOUT=300 - -# ============ Health Check ============ -# Health check interval in milliseconds -HEALTH_CHECK_INTERVAL=30000 - -# Health check key in Redis -HEALTH_CHECK_KEY=fsc:worker:health +# Memos 集成 +MEMOS_TOKEN=your-memos-token diff --git a/.gitignore b/.gitignore index 2c8450d..964ae58 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,19 @@ package-lock.json bun.lockb *.so *.dylib + +# 敏感文件 +*.key +*.pem +wg0.conf +.memov.pid +.claw-mesh.pid + +# 本地数据 +redis_data/ +qdrant_storage/ +.mem/ + +# 构建产物 +bun.lock +packages/ diff --git a/TAILSCALE-INTEGRATION.md b/TAILSCALE-INTEGRATION.md new file mode 100644 index 0000000..6cff3d1 --- /dev/null +++ b/TAILSCALE-INTEGRATION.md @@ -0,0 +1,55 @@ +# Tailscale 网络融合完成 + +## 变更内容 + +### 1. 新增网络配置模块 (`config/network.ts`) +- 定义节点拓扑(Mac 本地、硅谷、Windows、东京) +- Tailscale IP 映射(100.x.x.x) +- 节点角色管理(master/worker) +- 动态节点发现和在线检测 + +### 2. 更新 Redis 配置 (`config/redis.ts`) +- 自动选择 Redis 主节点(优先硅谷节点 100.80.67.125) +- 支持本地 fallback(127.0.0.1) + +### 3. 更新 MemoV Sync Daemon (`fsc/memov-sync-daemon.ts`) +- 动态获取活跃节点列表 +- 自动同步到 Tailscale 网络节点 + +## 当前网络状态 + +``` +✅ Mac 本地 (master) - 100.114.56.105 +✅ 硅谷节点 (worker) - 100.80.67.125 [在线] +❌ Windows 节点 (worker) - 100.101.173.35 [已禁用] +❌ 东京节点 (worker) - 未配置 +``` + +## Redis 连接 +- 主节点: 100.80.67.125:6379 (硅谷) +- 状态: 已连接 + +## MemoV 状态 +- 进程 ID: 93036 +- 同步节点: 100.80.67.125 +- 状态: 正在监听事件流 + +## 添加新节点步骤 + +1. 确保新节点已加入 Tailscale 网络 +2. 编辑 `config/network.ts`,添加节点配置 +3. 设置 `enabled: true` +4. 重启 memov: `kill $(cat .memov.pid) && bun run fsc/memov-sync-daemon.ts > logs/memov-sync.log 2>&1 &` + +## 测试命令 + +```bash +# 查看网络配置 +bun run test-network.ts + +# 查看 memov 日志 +tail -f logs/memov-sync.log + +# 检查进程 +ps -p $(cat .memov.pid) +``` diff --git a/config/network.ts b/config/network.ts new file mode 100644 index 0000000..acd07aa --- /dev/null +++ b/config/network.ts @@ -0,0 +1,102 @@ +/** + * 网络配置 — Tailscale 节点映射 + * + * 原架构使用 WireGuard (10.10.0.x),现改用 Tailscale (100.x.x.x) + * 保持逻辑节点 ID 不变,映射到 Tailscale IP + */ + +export type NodeId = 'mac-local' | 'silicon-valley' | 'windows' | 'tokyo'; + +export interface NodeConfig { + id: NodeId; + name: string; + tailscaleIp: string; + wireguardIp?: string; // 保留用于兼容 + role: 'master' | 'worker'; + enabled: boolean; +} + +export const NODES: Record = { + 'mac-local': { + id: 'mac-local', + name: 'lunnymacbook-pro', + tailscaleIp: '100.114.56.105', + wireguardIp: '10.10.0.5', + role: 'master', + enabled: true, + }, + 'silicon-valley': { + id: 'silicon-valley', + name: 'vm-0-6-debian', + tailscaleIp: '100.80.67.125', + wireguardIp: '10.10.0.2', + role: 'worker', + enabled: true, + }, + 'windows': { + id: 'windows', + name: 'win-taq1rm10mnf', + tailscaleIp: '100.101.173.35', + wireguardIp: '10.10.0.4', + role: 'worker', + enabled: true, // 已启用 + }, + 'tokyo': { + id: 'tokyo', + name: 'tokyo-node', + tailscaleIp: '', // 待添加 + wireguardIp: '10.10.0.3', + role: 'worker', + enabled: false, + }, +}; + +/** + * 获取所有启用的节点 IP(排除本机) + */ +export function getActiveNodeIps(excludeLocal = true): string[] { + return Object.values(NODES) + .filter(node => node.enabled) + .filter(node => !excludeLocal || node.id !== 'mac-local') + .map(node => node.tailscaleIp) + .filter(ip => ip !== ''); +} + +/** + * 获取 Redis 主节点 IP + * 优先使用硅谷节点,fallback 到本地 + */ +export function getRedisMasterIp(): string { + const sv = NODES['silicon-valley']; + if (sv.enabled && sv.tailscaleIp) { + return sv.tailscaleIp; + } + return '127.0.0.1'; // 本地 fallback +} + +/** + * 根据节点 ID 获取 IP + */ +export function getNodeIp(nodeId: NodeId): string | null { + const node = NODES[nodeId]; + return node?.enabled ? node.tailscaleIp : null; +} + +/** + * 检查节点是否在线(通过 Tailscale) + */ +export async function checkNodeOnline(nodeId: NodeId): Promise { + const ip = getNodeIp(nodeId); + if (!ip) return false; + + try { + const proc = Bun.spawn(['ping', '-c', '1', '-W', '2', ip], { + stdout: 'pipe', + stderr: 'pipe', + }); + const exitCode = await proc.exited; + return exitCode === 0; + } catch { + return false; + } +} diff --git a/config/redis.ts b/config/redis.ts index 5f4f14d..b032161 100644 --- a/config/redis.ts +++ b/config/redis.ts @@ -1,9 +1,12 @@ /** * Redis 连接配置 — 集中管理,避免 6 处硬编码 + * v2: 支持 Tailscale 网络 */ -export const REDIS_HOST = process.env.REDIS_HOST || '10.10.0.1'; +import { getRedisMasterIp } from './network'; + +export const REDIS_HOST = process.env.REDIS_HOST || getRedisMasterIp(); export const REDIS_PORT = parseInt(process.env.REDIS_PORT || '6379'); -export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || 'fsc-mesh-2026'; +export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || ''; // 从环境变量读取 export const REDIS_URL = `redis://:${REDIS_PASSWORD}@${REDIS_HOST}:${REDIS_PORT}`; diff --git a/fsc/memov-sync-daemon.ts b/fsc/memov-sync-daemon.ts index c364f72..13b18c7 100644 --- a/fsc/memov-sync-daemon.ts +++ b/fsc/memov-sync-daemon.ts @@ -18,8 +18,10 @@ import { readFile, writeFile } from 'fs/promises'; // ============ 配置 ============ import { REDIS_HOST, REDIS_PORT, REDIS_PASSWORD } from '../config/redis'; +import { getActiveNodeIps } from '../config/network'; + const MEM_DIR = '.mem'; -const NODES = ['10.10.0.2', '10.10.0.3', '10.10.0.4']; +const NODES = getActiveNodeIps(); // 动态获取 Tailscale 节点 const CONSUMER_GROUP = 'memov-sync'; const CONSUMER_NAME = `memov-${process.env.HOSTNAME || 'local'}`; const STREAM_KEY = 'fsc:mem_events'; diff --git a/test-network.ts b/test-network.ts new file mode 100644 index 0000000..c454bc7 --- /dev/null +++ b/test-network.ts @@ -0,0 +1,26 @@ +#!/usr/bin/env bun +/** + * 测试 Tailscale 网络配置 + */ +import { NODES, getActiveNodeIps, getRedisMasterIp, checkNodeOnline } from './config/network'; + +console.log('📡 Tailscale 网络配置\n'); + +console.log('所有节点:'); +for (const [id, node] of Object.entries(NODES)) { + const status = node.enabled ? '✅' : '❌'; + console.log(` ${status} ${node.name} (${id})`); + console.log(` Tailscale: ${node.tailscaleIp || '未配置'}`); + console.log(` 角色: ${node.role}`); + console.log(''); +} + +console.log('活跃节点 IP:', getActiveNodeIps()); +console.log('Redis 主节点:', getRedisMasterIp()); + +console.log('\n🔍 检查节点在线状态...'); +for (const [id, node] of Object.entries(NODES)) { + if (!node.enabled) continue; + const online = await checkNodeOnline(id as any); + console.log(` ${online ? '🟢' : '🔴'} ${node.name}: ${online ? 'online' : 'offline'}`); +}