GitHub Action commited on
Commit
ff4bc35
·
1 Parent(s): d354bfb

Sync from GitHub with Git LFS

Browse files
Files changed (2) hide show
  1. agents/peer_sync.py +53 -36
  2. agents/tools/storage.py +13 -5
agents/peer_sync.py CHANGED
@@ -1,29 +1,64 @@
1
  # agents/peer_sync.py
2
 
3
- """
4
- peer_sync.py — фоновый процесс синхронизации пиров в сети HMP
5
- с поддержкой tcp://, udp:// и any:// адресов
6
- """
7
-
8
  import socket
9
  import threading
10
  import time
11
  import json
12
  import uuid
13
- import requests
14
  from tools.storage import Storage
15
 
16
  storage = Storage()
 
17
 
18
  # ======================
19
- # UDP Discovery
20
  # ======================
 
 
 
 
 
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  def udp_discovery_listener(udp_port: int):
23
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
24
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
25
  sock.bind(("", udp_port))
26
-
27
  while True:
28
  data, addr = sock.recvfrom(1024)
29
  try:
@@ -31,39 +66,26 @@ def udp_discovery_listener(udp_port: int):
31
  peer_id = msg.get("id")
32
  name = msg.get("name", "unknown")
33
  addresses = msg.get("addresses", [f"{addr[0]}:{msg.get('tcp_port', udp_port)}"])
34
-
35
- # Обрабатываем все адреса через normalize_address
36
  normalized_addresses = []
37
  for a in addresses:
38
  norm = storage.normalize_address(a)
39
  if norm is None:
40
  continue
41
  proto, hostport = norm.split("://")
42
- # UDP и any учитываются для UDP discovery
43
  if proto in ["udp", "any"]:
44
  normalized_addresses.append(norm)
45
-
46
  if normalized_addresses:
47
- storage.add_or_update_peer(peer_id, name, normalized_addresses, source="discovery", status="online")
48
-
49
  except Exception as e:
50
  print("[PeerSync] Ошибка при обработке UDP пакета:", e)
51
 
52
  def udp_discovery_sender(agent_id: str, agent_name: str, udp_port: int, tcp_port: int):
53
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
54
  sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
55
-
56
- msg = {
57
- "id": agent_id,
58
- "name": agent_name,
59
- "tcp_port": tcp_port,
60
- "addresses": [f"127.0.0.1:{tcp_port}"]
61
- }
62
-
63
  last_broadcast = 0
64
  DISCOVERY_INTERVAL = 60
65
  BROADCAST_INTERVAL = 600
66
-
67
  while True:
68
  now = time.time()
69
  if int(now) % DISCOVERY_INTERVAL == 0:
@@ -77,27 +99,25 @@ def udp_discovery_sender(agent_id: str, agent_name: str, udp_port: int, tcp_port
77
  # ======================
78
  # Peer Exchange (TCP)
79
  # ======================
80
-
81
  def peer_exchange():
82
  PEER_EXCHANGE_INTERVAL = 120
83
  while True:
84
  peers = storage.get_online_peers(limit=50)
85
  for peer in peers:
86
  peer_id, addresses = peer["id"], peer["addresses"]
 
 
 
 
87
  try:
88
  addr_list = json.loads(addresses)
89
  for addr in addr_list:
90
- # Нормализация адреса через storage
91
  norm = storage.normalize_address(addr)
92
  if norm is None:
93
  continue
94
-
95
  proto, hostport = norm.split("://")
96
-
97
- # TCP и any обрабатываются как TCP
98
  if proto not in ["tcp", "any"]:
99
- continue # пропускаем UDP
100
-
101
  try:
102
  host, port = hostport.split(":")
103
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -106,25 +126,21 @@ def peer_exchange():
106
  s.sendall(b"PEER_EXCHANGE_REQUEST")
107
  s.close()
108
  print(f"[PeerSync] Успешно подключились к {peer_id} ({norm})")
109
- break # успешное подключение — остальные адреса не пробуем
110
  except Exception as e:
111
  print(f"[PeerSync] Не удалось подключиться к {peer_id} ({norm}): {e}")
112
-
113
  except Exception as e:
114
  print(f"[PeerSync] Ошибка обработки адресов {peer_id} ({addresses}): {e}")
115
-
116
  time.sleep(PEER_EXCHANGE_INTERVAL)
117
 
118
  # ======================
119
  # Основной запуск
120
  # ======================
121
-
122
  def start_sync():
123
  print("[PeerSync] Запуск фоновой синхронизации")
124
-
125
  udp_port = int(storage.get_config_value("udp_port", 4000))
126
  tcp_port = int(storage.get_config_value("tcp_port", 5000))
127
- agent_id = storage.get_config_value("agent_id", "unknown-agent")
128
  agent_name = storage.get_config_value("agent_name", "HMP-Agent")
129
 
130
  storage.load_bootstrap()
@@ -132,6 +148,7 @@ def start_sync():
132
  threading.Thread(target=udp_discovery_listener, args=(udp_port,), daemon=True).start()
133
  threading.Thread(target=udp_discovery_sender, args=(agent_id, agent_name, udp_port, tcp_port), daemon=True).start()
134
  threading.Thread(target=peer_exchange, daemon=True).start()
 
135
 
136
  while True:
137
  time.sleep(60)
 
1
  # agents/peer_sync.py
2
 
 
 
 
 
 
3
  import socket
4
  import threading
5
  import time
6
  import json
7
  import uuid
8
+ import ipaddress
9
  from tools.storage import Storage
10
 
11
  storage = Storage()
12
+ my_id = storage.get_config_value("agent_id", "")
13
 
14
  # ======================
15
+ # LAN Discovery
16
  # ======================
17
+ def lan_discovery(udp_port: int):
18
+ """
19
+ Периодический поиск локальных агентов в сети.
20
+ """
21
+ DISCOVERY_INTERVAL = 300 # каждые 5 минут
22
 
23
+ while True:
24
+ local_ip = get_local_ip()
25
+ if not local_ip:
26
+ time.sleep(DISCOVERY_INTERVAL)
27
+ continue
28
+
29
+ net = ipaddress.ip_network(local_ip + '/24', strict=False) # /24 по умолчанию
30
+ for ip in net.hosts():
31
+ if str(ip) == local_ip:
32
+ continue # пропускаем себя
33
+ try:
34
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
35
+ sock.settimeout(0.5)
36
+ msg = json.dumps({"ping": "HMP"}).encode("utf-8")
37
+ sock.sendto(msg, (str(ip), udp_port))
38
+ sock.close()
39
+ except:
40
+ continue
41
+
42
+ time.sleep(DISCOVERY_INTERVAL)
43
+
44
+ def get_local_ip():
45
+ """Возвращает локальный IP хоста."""
46
+ try:
47
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
48
+ s.connect(("8.8.8.8", 80))
49
+ ip = s.getsockname()[0]
50
+ s.close()
51
+ return ip
52
+ except:
53
+ return None
54
+
55
+ # ======================
56
+ # UDP Discovery
57
+ # ======================
58
  def udp_discovery_listener(udp_port: int):
59
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
60
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
61
  sock.bind(("", udp_port))
 
62
  while True:
63
  data, addr = sock.recvfrom(1024)
64
  try:
 
66
  peer_id = msg.get("id")
67
  name = msg.get("name", "unknown")
68
  addresses = msg.get("addresses", [f"{addr[0]}:{msg.get('tcp_port', udp_port)}"])
 
 
69
  normalized_addresses = []
70
  for a in addresses:
71
  norm = storage.normalize_address(a)
72
  if norm is None:
73
  continue
74
  proto, hostport = norm.split("://")
 
75
  if proto in ["udp", "any"]:
76
  normalized_addresses.append(norm)
 
77
  if normalized_addresses:
78
+ storage.add_or_update_peer(peer_id, name, normalized_addresses, "discovery", "online")
 
79
  except Exception as e:
80
  print("[PeerSync] Ошибка при обработке UDP пакета:", e)
81
 
82
  def udp_discovery_sender(agent_id: str, agent_name: str, udp_port: int, tcp_port: int):
83
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
84
  sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
85
+ msg = {"id": agent_id, "name": agent_name, "tcp_port": tcp_port, "addresses": [f"127.0.0.1:{tcp_port}"]}
 
 
 
 
 
 
 
86
  last_broadcast = 0
87
  DISCOVERY_INTERVAL = 60
88
  BROADCAST_INTERVAL = 600
 
89
  while True:
90
  now = time.time()
91
  if int(now) % DISCOVERY_INTERVAL == 0:
 
99
  # ======================
100
  # Peer Exchange (TCP)
101
  # ======================
 
102
  def peer_exchange():
103
  PEER_EXCHANGE_INTERVAL = 120
104
  while True:
105
  peers = storage.get_online_peers(limit=50)
106
  for peer in peers:
107
  peer_id, addresses = peer["id"], peer["addresses"]
108
+
109
+ if peer_id == my_id:
110
+ continue # пропускаем собственный агент
111
+
112
  try:
113
  addr_list = json.loads(addresses)
114
  for addr in addr_list:
 
115
  norm = storage.normalize_address(addr)
116
  if norm is None:
117
  continue
 
118
  proto, hostport = norm.split("://")
 
 
119
  if proto not in ["tcp", "any"]:
120
+ continue
 
121
  try:
122
  host, port = hostport.split(":")
123
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
126
  s.sendall(b"PEER_EXCHANGE_REQUEST")
127
  s.close()
128
  print(f"[PeerSync] Успешно подключились к {peer_id} ({norm})")
129
+ break
130
  except Exception as e:
131
  print(f"[PeerSync] Не удалось подключиться к {peer_id} ({norm}): {e}")
 
132
  except Exception as e:
133
  print(f"[PeerSync] Ошибка обработки адресов {peer_id} ({addresses}): {e}")
 
134
  time.sleep(PEER_EXCHANGE_INTERVAL)
135
 
136
  # ======================
137
  # Основной запуск
138
  # ======================
 
139
  def start_sync():
140
  print("[PeerSync] Запуск фоновой синхронизации")
 
141
  udp_port = int(storage.get_config_value("udp_port", 4000))
142
  tcp_port = int(storage.get_config_value("tcp_port", 5000))
143
+ agent_id = storage.get_config_value("agent_id", str(uuid.uuid4()))
144
  agent_name = storage.get_config_value("agent_name", "HMP-Agent")
145
 
146
  storage.load_bootstrap()
 
148
  threading.Thread(target=udp_discovery_listener, args=(udp_port,), daemon=True).start()
149
  threading.Thread(target=udp_discovery_sender, args=(agent_id, agent_name, udp_port, tcp_port), daemon=True).start()
150
  threading.Thread(target=peer_exchange, daemon=True).start()
151
+ threading.Thread(target=lan_discovery, args=(udp_port,), daemon=True).start() # новый поток
152
 
153
  while True:
154
  time.sleep(60)
agents/tools/storage.py CHANGED
@@ -7,11 +7,13 @@ import json
7
  import uuid
8
  import time
9
 
10
- from datetime import datetime, timedelta, UTC
11
  from werkzeug.security import generate_password_hash, check_password_hash
12
  from tools.identity import generate_did
13
  from tools.crypto import generate_keypair
14
 
 
 
15
  SCRIPTS_BASE_PATH = "scripts"
16
 
17
  class Storage:
@@ -724,6 +726,14 @@ class Storage:
724
 
725
  return value
726
 
 
 
 
 
 
 
 
 
727
  # Web-интерфейс и API
728
  def write_note(self, content, user_did="anon", source="user", hidden=0):
729
  timestamp = datetime.now(UTC).isoformat()
@@ -896,18 +906,16 @@ class Storage:
896
  c.execute("SELECT id, addresses FROM agent_peers WHERE status='online' LIMIT ?", (limit,))
897
  return c.fetchall()
898
 
 
899
  def normalize_address(self, addr: str) -> str:
900
- """Нормализует адрес в формате tcp://, udp:// или any://"""
901
  addr = addr.strip()
902
  if not addr:
903
  return None
904
-
905
  if "://" not in addr:
906
- # Универсальная запись → считаем, что оба порта используются
907
  return f"any://{addr}"
908
-
909
  return addr
910
 
 
911
  def load_bootstrap(self, bootstrap_file="bootstrap.txt"):
912
  """
913
  Загружает узлы из bootstrap.txt.
 
7
  import uuid
8
  import time
9
 
10
+ from datetime import datetime, timedelta, UTC, timezone
11
  from werkzeug.security import generate_password_hash, check_password_hash
12
  from tools.identity import generate_did
13
  from tools.crypto import generate_keypair
14
 
15
+ UTC = timezone.utc
16
+
17
  SCRIPTS_BASE_PATH = "scripts"
18
 
19
  class Storage:
 
726
 
727
  return value
728
 
729
+ def set_config_value(self, key, value):
730
+ c = self.conn.cursor()
731
+ c.execute("""
732
+ INSERT INTO config(key, value) VALUES (?, ?)
733
+ ON CONFLICT(key) DO UPDATE SET value=excluded.value
734
+ """, (key, value))
735
+ self.conn.commit()
736
+
737
  # Web-интерфейс и API
738
  def write_note(self, content, user_did="anon", source="user", hidden=0):
739
  timestamp = datetime.now(UTC).isoformat()
 
906
  c.execute("SELECT id, addresses FROM agent_peers WHERE status='online' LIMIT ?", (limit,))
907
  return c.fetchall()
908
 
909
+ # Нормализация адресов
910
  def normalize_address(self, addr: str) -> str:
 
911
  addr = addr.strip()
912
  if not addr:
913
  return None
 
914
  if "://" not in addr:
 
915
  return f"any://{addr}"
 
916
  return addr
917
 
918
+ # Bootstrap
919
  def load_bootstrap(self, bootstrap_file="bootstrap.txt"):
920
  """
921
  Загружает узлы из bootstrap.txt.