mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 14:13:04 +00:00
271 lines
7.2 KiB
Go
271 lines
7.2 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
"github.com/multiformats/go-multiaddr"
|
|
)
|
|
|
|
// NetworkInfoImpl implements NetworkInfo
|
|
type NetworkInfoImpl struct {
|
|
client *Client
|
|
}
|
|
|
|
// GetPeers returns information about connected peers
|
|
func (n *NetworkInfoImpl) GetPeers(ctx context.Context) ([]PeerInfo, error) {
|
|
if !n.client.isConnected() {
|
|
return nil, fmt.Errorf("client not connected")
|
|
}
|
|
|
|
if err := n.client.requireAccess(ctx); err != nil {
|
|
return nil, fmt.Errorf("authentication required: %w - run CLI commands to authenticate automatically", err)
|
|
}
|
|
|
|
// Get peers from LibP2P host
|
|
host := n.client.host
|
|
if host == nil {
|
|
return nil, fmt.Errorf("no host available")
|
|
}
|
|
|
|
// Get connected peers
|
|
connectedPeers := host.Network().Peers()
|
|
peers := make([]PeerInfo, 0, len(connectedPeers)+1) // +1 for self
|
|
|
|
// Add connected peers
|
|
for _, peerID := range connectedPeers {
|
|
// Get peer addresses
|
|
peerInfo := host.Peerstore().PeerInfo(peerID)
|
|
|
|
// Convert multiaddrs to strings
|
|
addrs := make([]string, len(peerInfo.Addrs))
|
|
for i, addr := range peerInfo.Addrs {
|
|
addrs[i] = addr.String()
|
|
}
|
|
|
|
peers = append(peers, PeerInfo{
|
|
ID: peerID.String(),
|
|
Addresses: addrs,
|
|
Connected: true,
|
|
LastSeen: time.Now(), // LibP2P doesn't track last seen, so use current time
|
|
})
|
|
}
|
|
|
|
// Add self node
|
|
selfPeerInfo := host.Peerstore().PeerInfo(host.ID())
|
|
selfAddrs := make([]string, len(selfPeerInfo.Addrs))
|
|
for i, addr := range selfPeerInfo.Addrs {
|
|
selfAddrs[i] = addr.String()
|
|
}
|
|
|
|
// Insert self node at the beginning of the list
|
|
selfPeer := PeerInfo{
|
|
ID: host.ID().String(),
|
|
Addresses: selfAddrs,
|
|
Connected: true,
|
|
LastSeen: time.Now(),
|
|
}
|
|
|
|
// Prepend self to the list
|
|
peers = append([]PeerInfo{selfPeer}, peers...)
|
|
|
|
return peers, nil
|
|
}
|
|
|
|
// GetStatus returns network status
|
|
func (n *NetworkInfoImpl) GetStatus(ctx context.Context) (*NetworkStatus, error) {
|
|
if !n.client.isConnected() {
|
|
return nil, fmt.Errorf("client not connected")
|
|
}
|
|
|
|
if err := n.client.requireAccess(ctx); err != nil {
|
|
return nil, fmt.Errorf("authentication required: %w - run CLI commands to authenticate automatically", err)
|
|
}
|
|
|
|
host := n.client.host
|
|
if host == nil {
|
|
return nil, fmt.Errorf("no host available")
|
|
}
|
|
|
|
// Get actual network status
|
|
connectedPeers := host.Network().Peers()
|
|
|
|
// Try to get database size from RQLite (optional - don't fail if unavailable)
|
|
var dbSize int64 = 0
|
|
dbClient := n.client.database
|
|
if conn, err := dbClient.getRQLiteConnection(); err == nil {
|
|
// Query database size (rough estimate)
|
|
if result, err := conn.QueryOne("SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()"); err == nil {
|
|
for result.Next() {
|
|
if row, err := result.Slice(); err == nil && len(row) > 0 {
|
|
if size, ok := row[0].(int64); ok {
|
|
dbSize = size
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to get IPFS peer info (optional - don't fail if unavailable)
|
|
ipfsInfo := queryIPFSPeerInfo()
|
|
|
|
// Try to get IPFS Cluster peer info (optional - don't fail if unavailable)
|
|
ipfsClusterInfo := queryIPFSClusterPeerInfo()
|
|
|
|
return &NetworkStatus{
|
|
NodeID: host.ID().String(),
|
|
PeerID: host.ID().String(),
|
|
Connected: true,
|
|
PeerCount: len(connectedPeers),
|
|
DatabaseSize: dbSize,
|
|
Uptime: time.Since(n.client.startTime),
|
|
IPFS: ipfsInfo,
|
|
IPFSCluster: ipfsClusterInfo,
|
|
}, nil
|
|
}
|
|
|
|
// queryIPFSPeerInfo queries the local IPFS API for peer information
|
|
// Returns nil if IPFS is not running or unavailable
|
|
func queryIPFSPeerInfo() *IPFSPeerInfo {
|
|
// IPFS API typically runs on port 4501 in our setup
|
|
client := &http.Client{Timeout: 2 * time.Second}
|
|
resp, err := client.Post("http://localhost:4501/api/v0/id", "", nil)
|
|
if err != nil {
|
|
return nil // IPFS not available
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil
|
|
}
|
|
|
|
var result struct {
|
|
ID string `json:"ID"`
|
|
Addresses []string `json:"Addresses"`
|
|
}
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Filter addresses to only include public/routable ones
|
|
var swarmAddrs []string
|
|
for _, addr := range result.Addresses {
|
|
// Skip loopback and private addresses for external discovery
|
|
if !strings.Contains(addr, "127.0.0.1") && !strings.Contains(addr, "/ip6/::1") {
|
|
swarmAddrs = append(swarmAddrs, addr)
|
|
}
|
|
}
|
|
|
|
return &IPFSPeerInfo{
|
|
PeerID: result.ID,
|
|
SwarmAddresses: swarmAddrs,
|
|
}
|
|
}
|
|
|
|
// queryIPFSClusterPeerInfo queries the local IPFS Cluster API for peer information
|
|
// Returns nil if IPFS Cluster is not running or unavailable
|
|
func queryIPFSClusterPeerInfo() *IPFSClusterPeerInfo {
|
|
// IPFS Cluster API typically runs on port 9094 in our setup
|
|
client := &http.Client{Timeout: 2 * time.Second}
|
|
resp, err := client.Get("http://localhost:9094/id")
|
|
if err != nil {
|
|
return nil // IPFS Cluster not available
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil
|
|
}
|
|
|
|
var result struct {
|
|
ID string `json:"id"`
|
|
Addresses []string `json:"addresses"`
|
|
}
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Filter addresses to only include public/routable ones for cluster discovery
|
|
var clusterAddrs []string
|
|
for _, addr := range result.Addresses {
|
|
// Skip loopback addresses - only keep routable addresses
|
|
if !strings.Contains(addr, "127.0.0.1") && !strings.Contains(addr, "/ip6/::1") {
|
|
clusterAddrs = append(clusterAddrs, addr)
|
|
}
|
|
}
|
|
|
|
return &IPFSClusterPeerInfo{
|
|
PeerID: result.ID,
|
|
Addresses: clusterAddrs,
|
|
}
|
|
}
|
|
|
|
// ConnectToPeer connects to a specific peer
|
|
func (n *NetworkInfoImpl) ConnectToPeer(ctx context.Context, peerAddr string) error {
|
|
if !n.client.isConnected() {
|
|
return fmt.Errorf("client not connected")
|
|
}
|
|
|
|
if err := n.client.requireAccess(ctx); err != nil {
|
|
return fmt.Errorf("authentication required: %w - run CLI commands to authenticate automatically", err)
|
|
}
|
|
|
|
host := n.client.host
|
|
if host == nil {
|
|
return fmt.Errorf("no host available")
|
|
}
|
|
|
|
// Parse the multiaddr
|
|
ma, err := multiaddr.NewMultiaddr(peerAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid multiaddr: %w", err)
|
|
}
|
|
|
|
// Extract peer info
|
|
peerInfo, err := peer.AddrInfoFromP2pAddr(ma)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extract peer info: %w", err)
|
|
}
|
|
|
|
// Connect to the peer
|
|
if err := host.Connect(ctx, *peerInfo); err != nil {
|
|
return fmt.Errorf("failed to connect to peer: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DisconnectFromPeer disconnects from a specific peer
|
|
func (n *NetworkInfoImpl) DisconnectFromPeer(ctx context.Context, peerID string) error {
|
|
if !n.client.isConnected() {
|
|
return fmt.Errorf("client not connected")
|
|
}
|
|
|
|
if err := n.client.requireAccess(ctx); err != nil {
|
|
return fmt.Errorf("authentication required: %w - run CLI commands to authenticate automatically", err)
|
|
}
|
|
|
|
host := n.client.host
|
|
if host == nil {
|
|
return fmt.Errorf("no host available")
|
|
}
|
|
|
|
// Parse the peer ID
|
|
pid, err := peer.Decode(peerID)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid peer ID: %w", err)
|
|
}
|
|
|
|
// Close the connection to the peer
|
|
if err := host.Network().ClosePeer(pid); err != nil {
|
|
return fmt.Errorf("failed to disconnect from peer: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|