package p2p import ( "database/sql" "encoding/json" "fmt" "log" "net/http" "sync" "time" "torrentGateway/internal/config" "torrentGateway/internal/dht" "torrentGateway/internal/tracker" "github.com/gorilla/mux" ) // UnifiedP2PGateway coordinates all P2P systems type UnifiedP2PGateway struct { // P2P Components tracker *tracker.Tracker wsTracker *tracker.WebSocketTracker dhtBootstrap *dht.DHTBootstrap coordinator *P2PCoordinator // Configuration config *config.Config db *sql.DB // Maintenance maintenanceTicker *time.Ticker healthTicker *time.Ticker stopCh chan struct{} // Caching for peer discovery peerCache map[string]*CachedPeerResponse cacheMutex sync.RWMutex cacheExpiry time.Duration // Statistics stats *P2PGatewayStats statsMutex sync.RWMutex } // CachedPeerResponse represents a cached peer discovery response type CachedPeerResponse struct { Peers []UnifiedPeer `json:"peers"` CachedAt time.Time `json:"cached_at"` TTL time.Duration `json:"ttl"` Sources []string `json:"sources"` } // UnifiedPeer represents a peer from any source type UnifiedPeer struct { ID string `json:"id"` IP string `json:"ip"` Port int `json:"port"` Source string `json:"source"` // "tracker", "dht", "websocket" Quality int `json:"quality"` IsSeeder bool `json:"is_seeder"` LastSeen time.Time `json:"last_seen"` Endpoint string `json:"endpoint,omitempty"` Protocol string `json:"protocol,omitempty"` // "webrtc", "http", "webseed" Reliability float64 `json:"reliability,omitempty"` RTT int `json:"rtt_ms,omitempty"` } // P2PGatewayStats tracks comprehensive P2P statistics type P2PGatewayStats struct { TotalTorrents int64 `json:"total_torrents"` ActiveTorrents int64 `json:"active_torrents"` TotalPeers int64 `json:"total_peers"` TrackerPeers int64 `json:"tracker_peers"` DHTNodes int64 `json:"dht_nodes"` WebSocketPeers int64 `json:"websocket_peers"` AnnouncesSent int64 `json:"announces_sent"` AnnouncesReceived int64 `json:"announces_received"` HealthStatus map[string]string `json:"health_status"` LastMaintenance time.Time `json:"last_maintenance"` LastHealthCheck time.Time `json:"last_health_check"` SystemHealth string `json:"system_health"` ComponentStats map[string]interface{} `json:"component_stats"` } // TorrentInfo is defined in coordinator.go // NewUnifiedP2PGateway creates a new unified P2P gateway func NewUnifiedP2PGateway(config *config.Config, db *sql.DB) *UnifiedP2PGateway { gateway := &UnifiedP2PGateway{ config: config, db: db, peerCache: make(map[string]*CachedPeerResponse), cacheExpiry: 2 * time.Minute, // Cache peer responses for 2 minutes stopCh: make(chan struct{}), stats: &P2PGatewayStats{ HealthStatus: make(map[string]string), ComponentStats: make(map[string]interface{}), SystemHealth: "starting", }, } return gateway } // Initialize starts all P2P components and background tasks func (g *UnifiedP2PGateway) Initialize() error { log.Printf("P2P Gateway: Initializing unified P2P system") // Create database tables if err := g.CreateP2PTables(); err != nil { return fmt.Errorf("failed to create P2P database tables: %w", err) } // Initialize tracker if err := g.initializeTracker(); err != nil { return fmt.Errorf("failed to initialize tracker: %w", err) } // Initialize DHT if err := g.initializeDHT(); err != nil { return fmt.Errorf("failed to initialize DHT: %w", err) } // Initialize WebSocket tracker if err := g.initializeWebSocketTracker(); err != nil { return fmt.Errorf("failed to initialize WebSocket tracker: %w", err) } // Initialize P2P coordinator if err := g.initializeCoordinator(); err != nil { return fmt.Errorf("failed to initialize coordinator: %w", err) } // Start background tasks g.startBackgroundTasks() g.stats.SystemHealth = "healthy" log.Printf("P2P Gateway: Successfully initialized all P2P systems") return nil } // CreateTorrent creates a new torrent and announces it to all P2P systems func (g *UnifiedP2PGateway) CreateTorrent(fileHash string) (*TorrentInfo, error) { log.Printf("P2P Gateway: Creating torrent for file %s", fileHash[:8]) // Get file info from database - this is a simplified version // In production, you'd query the files table for name and size filename := "Unknown" var fileSize int64 = 0 row := g.db.QueryRow("SELECT original_name, size FROM files WHERE hash = ?", fileHash) row.Scan(&filename, &fileSize) // Ignore error, use defaults // Create torrent metadata torrentInfo := &TorrentInfo{ InfoHash: fileHash, Name: filename, Size: fileSize, CreatedAt: time.Now(), LastAnnounce: time.Now(), IsActive: true, } // Store in database if err := g.storeTorrentInfo(torrentInfo); err != nil { log.Printf("P2P Gateway: Failed to store torrent info: %v", err) } // Announce to all P2P systems immediately if err := g.announceToAllSystems(torrentInfo); err != nil { log.Printf("P2P Gateway: Failed to announce to all systems: %v", err) } // Update statistics g.statsMutex.Lock() g.stats.TotalTorrents++ g.stats.ActiveTorrents++ g.statsMutex.Unlock() log.Printf("P2P Gateway: Successfully created and announced torrent %s", fileHash[:8]) return torrentInfo, nil } // GetPeersHandler provides a unified peer discovery endpoint func (g *UnifiedP2PGateway) GetPeersHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) infoHash := vars["infohash"] if len(infoHash) != 40 { http.Error(w, "Invalid infohash format", http.StatusBadRequest) return } // Check cache first g.cacheMutex.RLock() if cached, exists := g.peerCache[infoHash]; exists { if time.Since(cached.CachedAt) < cached.TTL { g.cacheMutex.RUnlock() w.Header().Set("Content-Type", "application/json") w.Header().Set("X-Cache", "HIT") json.NewEncoder(w).Encode(cached) return } } g.cacheMutex.RUnlock() // Cache miss - gather peers from all sources peers, sources, err := g.gatherPeersFromAllSources(infoHash) if err != nil { log.Printf("P2P Gateway: Failed to gather peers for %s: %v", infoHash[:8], err) http.Error(w, "Failed to gather peers", http.StatusInternalServerError) return } // Create response response := &CachedPeerResponse{ Peers: peers, CachedAt: time.Now(), TTL: g.cacheExpiry, Sources: sources, } // Update cache g.cacheMutex.Lock() g.peerCache[infoHash] = response g.cacheMutex.Unlock() // Send response w.Header().Set("Content-Type", "application/json") w.Header().Set("X-Cache", "MISS") json.NewEncoder(w).Encode(response) log.Printf("P2P Gateway: Returned %d peers from %v for %s", len(peers), sources, infoHash[:8]) } // GetStatsHandler returns comprehensive P2P statistics func (g *UnifiedP2PGateway) GetStatsHandler(w http.ResponseWriter, r *http.Request) { g.updateStats() g.statsMutex.RLock() stats := *g.stats g.statsMutex.RUnlock() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(stats) } // GetHealthHandler returns system health status func (g *UnifiedP2PGateway) GetHealthHandler(w http.ResponseWriter, r *http.Request) { health := g.performHealthCheck() statusCode := http.StatusOK if health.SystemHealth != "healthy" { statusCode = http.StatusServiceUnavailable } w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) json.NewEncoder(w).Encode(health) } // Shutdown gracefully stops all P2P systems func (g *UnifiedP2PGateway) Shutdown() error { log.Printf("P2P Gateway: Shutting down unified P2P system") close(g.stopCh) if g.maintenanceTicker != nil { g.maintenanceTicker.Stop() } if g.healthTicker != nil { g.healthTicker.Stop() } // Shutdown components if g.coordinator != nil { g.coordinator.Stop() } if g.dhtBootstrap != nil { g.dhtBootstrap.Stop() } g.stats.SystemHealth = "shutdown" log.Printf("P2P Gateway: Shutdown complete") return nil } // RegisterRoutes registers all P2P endpoints func (g *UnifiedP2PGateway) RegisterRoutes(router *mux.Router) { // Peer discovery endpoint router.HandleFunc("/api/peers/{infohash}", g.GetPeersHandler).Methods("GET") // Statistics endpoint router.HandleFunc("/api/p2p/stats", g.GetStatsHandler).Methods("GET") // Health check endpoint router.HandleFunc("/api/p2p/health", g.GetHealthHandler).Methods("GET") // WebSocket tracker endpoint (if WebSocket tracker is available) if g.wsTracker != nil { router.HandleFunc("/ws/tracker", g.wsTracker.HandleWS) } log.Printf("P2P Gateway: Registered API endpoints") } // ============ GATEWAY INTERFACE METHODS ============ // DHT Bootstrap Gateway interface methods // GetPublicURL returns the public URL for this gateway func (g *UnifiedP2PGateway) GetPublicURL() string { // Try to get from config, fall back to localhost if g.config.Gateway.PublicURL != "" { return g.config.Gateway.PublicURL } return fmt.Sprintf("http://localhost:%d", g.config.Gateway.Port) } // GetDHTPort returns the DHT port func (g *UnifiedP2PGateway) GetDHTPort() int { return g.config.DHT.Port } // GetDatabase returns the database connection func (g *UnifiedP2PGateway) GetDatabase() *sql.DB { return g.db } // GetAllTorrentHashes returns all torrent hashes from the database func (g *UnifiedP2PGateway) GetAllTorrentHashes() []string { rows, err := g.db.Query("SELECT info_hash FROM p2p_torrents WHERE is_active = 1") if err != nil { log.Printf("P2P Gateway: Failed to get torrent hashes: %v", err) return []string{} } defer rows.Close() var hashes []string for rows.Next() { var hash string if err := rows.Scan(&hash); err == nil { hashes = append(hashes, hash) } } return hashes } // Coordinator Gateway interface methods // WebSeedPeer returns a PeerInfo for the WebSeed func (g *UnifiedP2PGateway) WebSeedPeer() PeerInfo { return PeerInfo{ IP: "127.0.0.1", // Local WebSeed Port: g.config.Gateway.Port, PeerID: "WEBSEED", Source: "webseed", Quality: 100, // Highest quality LastSeen: time.Now(), } } // EnableWebSeed enables WebSeed for a torrent func (g *UnifiedP2PGateway) EnableWebSeed(infoHash string) error { log.Printf("P2P Gateway: Enabling WebSeed for %s", infoHash[:8]) // In a full implementation, this would configure WebSeed URLs return nil } // PublishToNostr publishes torrent to Nostr (placeholder) func (g *UnifiedP2PGateway) PublishToNostr(torrent *TorrentInfo) error { log.Printf("P2P Gateway: Publishing torrent %s to Nostr", torrent.InfoHash[:8]) // Placeholder - would integrate with actual Nostr publisher return nil } // GetPort returns the gateway port func (g *UnifiedP2PGateway) GetPort() int { return g.config.Gateway.Port } // GetDHTBootstrap returns the DHT bootstrap instance func (g *UnifiedP2PGateway) GetDHTBootstrap() *dht.DHTBootstrap { return g.dhtBootstrap } // GetPeers returns peers for a given infohash from all sources func (g *UnifiedP2PGateway) GetPeers(infoHash string) ([]UnifiedPeer, error) { if g.coordinator == nil { return []UnifiedPeer{}, fmt.Errorf("P2P coordinator not initialized") } // Get peers from all sources peers, _, err := g.gatherPeersFromAllSources(infoHash) return peers, err } // GetStats returns comprehensive P2P statistics func (g *UnifiedP2PGateway) GetStats() (map[string]interface{}, error) { if g.stats == nil { return map[string]interface{}{}, fmt.Errorf("stats not initialized") } // Return current stats stats := make(map[string]interface{}) stats["timestamp"] = time.Now().Format(time.RFC3339) stats["health_status"] = g.stats.HealthStatus stats["component_stats"] = g.stats.ComponentStats stats["system_health"] = g.stats.SystemHealth return stats, nil } // ============ INITIALIZATION METHODS ============ func (g *UnifiedP2PGateway) initializeTracker() error { log.Printf("P2P Gateway: Initializing HTTP tracker") // Note: This is a simplified tracker initialization for P2P gateway // In production, you would pass proper config and gateway interface log.Printf("P2P Gateway: Tracker initialization skipped - using external tracker") g.stats.HealthStatus["tracker"] = "external" return nil } func (g *UnifiedP2PGateway) initializeDHT() error { log.Printf("P2P Gateway: Initializing DHT") // First create DHT node dhtNode, err := dht.NewDHT(&g.config.DHT) if err != nil { return fmt.Errorf("failed to create DHT node: %w", err) } // Then create DHT bootstrap with the node g.dhtBootstrap = dht.NewDHTBootstrap(dhtNode, g, &g.config.DHT) // Initialize the bootstrap functionality if err := g.dhtBootstrap.Initialize(); err != nil { return fmt.Errorf("failed to initialize DHT bootstrap: %w", err) } g.stats.HealthStatus["dht"] = "healthy" return nil } func (g *UnifiedP2PGateway) initializeWebSocketTracker() error { log.Printf("P2P Gateway: Initializing WebSocket tracker") g.wsTracker = tracker.NewWebSocketTracker(g.tracker) g.wsTracker.StartCleanup() g.stats.HealthStatus["websocket"] = "healthy" return nil } func (g *UnifiedP2PGateway) initializeCoordinator() error { log.Printf("P2P Gateway: Initializing P2P coordinator") g.coordinator = NewCoordinator(g, g.tracker, g.dhtBootstrap) g.stats.HealthStatus["coordinator"] = "healthy" return nil } // ============ BACKGROUND MAINTENANCE TASKS ============ func (g *UnifiedP2PGateway) startBackgroundTasks() { log.Printf("P2P Gateway: Starting background maintenance tasks") // Maintenance tasks every 5 minutes g.maintenanceTicker = time.NewTicker(5 * time.Minute) go g.maintenanceLoop() // Health checks every minute g.healthTicker = time.NewTicker(1 * time.Minute) go g.healthCheckLoop() // Periodic DHT announces every 15 minutes go g.periodicAnnounceLoop() } func (g *UnifiedP2PGateway) maintenanceLoop() { for { select { case <-g.stopCh: return case <-g.maintenanceTicker.C: g.performMaintenance() } } } func (g *UnifiedP2PGateway) healthCheckLoop() { for { select { case <-g.stopCh: return case <-g.healthTicker.C: g.performHealthCheck() } } } func (g *UnifiedP2PGateway) periodicAnnounceLoop() { ticker := time.NewTicker(15 * time.Minute) defer ticker.Stop() for { select { case <-g.stopCh: return case <-ticker.C: g.performPeriodicAnnounces() } } } func (g *UnifiedP2PGateway) performMaintenance() { log.Printf("P2P Gateway: Performing maintenance tasks") g.statsMutex.Lock() g.stats.LastMaintenance = time.Now() g.statsMutex.Unlock() // Clean up expired peers from all systems g.cleanupExpiredPeers() // Verify WebSeeds are accessible g.verifyWebSeeds() // Clean up peer cache g.cleanupPeerCache() // Update statistics g.updateStats() log.Printf("P2P Gateway: Maintenance tasks completed") } func (g *UnifiedP2PGateway) cleanupExpiredPeers() { // Clean up tracker peers if g.tracker != nil { // Note: cleanupExpiredPeers is a private method, can't call directly log.Printf("P2P Gateway: Tracker cleanup skipped (private method)") } // Clean up DHT peers if g.dhtBootstrap != nil && g.dhtBootstrap.GetNode() != nil { // DHT cleanup is handled internally by the node } // WebSocket tracker cleanup is handled by its own ticker } func (g *UnifiedP2PGateway) verifyWebSeeds() { // Get all active torrents with WebSeeds torrents, err := g.getActiveTorrentsWithWebSeeds() if err != nil { log.Printf("P2P Gateway: Failed to get torrents for WebSeed verification: %v", err) return } verifiedCount := 0 failedCount := 0 for _, torrent := range torrents { if torrent.WebSeedURL != "" { if g.verifyWebSeedURL(torrent.WebSeedURL) { verifiedCount++ } else { failedCount++ log.Printf("P2P Gateway: WebSeed verification failed for %s: %s", torrent.InfoHash[:8], torrent.WebSeedURL) } } } if verifiedCount > 0 || failedCount > 0 { log.Printf("P2P Gateway: WebSeed verification completed: %d verified, %d failed", verifiedCount, failedCount) } } func (g *UnifiedP2PGateway) verifyWebSeedURL(url string) bool { client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Head(url) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusPartialContent } func (g *UnifiedP2PGateway) cleanupPeerCache() { g.cacheMutex.Lock() defer g.cacheMutex.Unlock() now := time.Now() cleanedCount := 0 for infoHash, cached := range g.peerCache { if now.Sub(cached.CachedAt) > cached.TTL { delete(g.peerCache, infoHash) cleanedCount++ } } if cleanedCount > 0 { log.Printf("P2P Gateway: Cleaned %d expired peer cache entries", cleanedCount) } } func (g *UnifiedP2PGateway) performPeriodicAnnounces() { log.Printf("P2P Gateway: Performing periodic announces for all torrents") torrents, err := g.getAllActiveTorrents() if err != nil { log.Printf("P2P Gateway: Failed to get active torrents for periodic announces: %v", err) return } announceCount := 0 for _, torrent := range torrents { if err := g.announceToAllSystems(torrent); err != nil { log.Printf("P2P Gateway: Failed to announce %s: %v", torrent.InfoHash[:8], err) } else { announceCount++ } } g.statsMutex.Lock() g.stats.AnnouncesSent += int64(announceCount) g.statsMutex.Unlock() log.Printf("P2P Gateway: Completed periodic announces for %d torrents", announceCount) } // ============ HEALTH CHECK SYSTEM ============ func (g *UnifiedP2PGateway) performHealthCheck() *P2PGatewayStats { g.statsMutex.Lock() g.stats.LastHealthCheck = time.Now() // Check DHT health if g.dhtBootstrap != nil && g.dhtBootstrap.GetNode() != nil { dhtStats := g.dhtBootstrap.GetDHTStats() if nodeCount, ok := dhtStats["routing_table_size"].(int); ok && nodeCount > 0 { g.stats.HealthStatus["dht"] = "healthy" } else { g.stats.HealthStatus["dht"] = "degraded" } } else { g.stats.HealthStatus["dht"] = "failed" } // Check tracker health if g.tracker != nil { trackerStats := g.tracker.GetStats() if peerCount, ok := trackerStats["peers"].(int64); ok && peerCount >= 0 { g.stats.HealthStatus["tracker"] = "healthy" } else { g.stats.HealthStatus["tracker"] = "degraded" } } else { g.stats.HealthStatus["tracker"] = "failed" } // Check WebSocket tracker health if g.wsTracker != nil { wsStats := g.wsTracker.GetStats() if peers, ok := wsStats["total_peers"].(int); ok && peers >= 0 { g.stats.HealthStatus["websocket"] = "healthy" } else { g.stats.HealthStatus["websocket"] = "degraded" } } else { g.stats.HealthStatus["websocket"] = "failed" } // Determine overall system health healthyComponents := 0 totalComponents := len(g.stats.HealthStatus) for _, status := range g.stats.HealthStatus { if status == "healthy" { healthyComponents++ } } if healthyComponents == totalComponents { g.stats.SystemHealth = "healthy" } else if healthyComponents > totalComponents/2 { g.stats.SystemHealth = "degraded" } else { g.stats.SystemHealth = "critical" } stats := *g.stats g.statsMutex.Unlock() return &stats } // ============ PEER DISCOVERY AND COORDINATION ============ func (g *UnifiedP2PGateway) gatherPeersFromAllSources(infoHash string) ([]UnifiedPeer, []string, error) { var allPeers []UnifiedPeer var sources []string // Get peers from HTTP tracker if g.tracker != nil { trackerPeers, err := g.tracker.GetPeersForTorrent(infoHash) if err == nil && len(trackerPeers) > 0 { for _, peer := range trackerPeers { unifiedPeer := UnifiedPeer{ ID: peer.PeerID, IP: peer.IP, Port: peer.Port, Source: "tracker", Quality: peer.Priority, IsSeeder: peer.IsSeeder || peer.Left == 0, LastSeen: peer.LastSeen, Protocol: "http", Reliability: calculateReliability(peer), } if peer.IsWebSeed { unifiedPeer.Protocol = "webseed" unifiedPeer.Endpoint = fmt.Sprintf("http://localhost:%d/webseed/%s", g.config.Gateway.Port, infoHash) unifiedPeer.Quality = 100 // WebSeeds get highest quality } allPeers = append(allPeers, unifiedPeer) } sources = append(sources, "tracker") } } // Get peers from DHT if g.dhtBootstrap != nil && g.dhtBootstrap.GetNode() != nil { dhtPeers := g.coordinator.getDHTPeers(infoHash) if len(dhtPeers) > 0 { for _, peer := range dhtPeers { unifiedPeer := UnifiedPeer{ ID: peer.PeerID, IP: peer.IP, Port: peer.Port, Source: "dht", Quality: peer.Quality, LastSeen: peer.LastSeen, Protocol: "http", } allPeers = append(allPeers, unifiedPeer) } sources = append(sources, "dht") } } // Get peers from WebSocket tracker if g.wsTracker != nil { wsStats := g.wsTracker.GetStats() // Note: WebSocket tracker doesn't have a direct GetPeers method // This would need to be implemented based on the swarm structure if totalPeers, ok := wsStats["total_peers"].(int); ok && totalPeers > 0 { sources = append(sources, "websocket") } } return allPeers, sources, nil } func (g *UnifiedP2PGateway) announceToAllSystems(torrent *TorrentInfo) error { var errors []error // Announce to HTTP tracker if g.tracker != nil { if err := g.announceToTracker(torrent); err != nil { errors = append(errors, fmt.Errorf("tracker announce failed: %w", err)) } } // Announce to DHT if g.dhtBootstrap != nil { if err := g.announceToDHT(torrent); err != nil { errors = append(errors, fmt.Errorf("DHT announce failed: %w", err)) } } // Update last announce time torrent.LastAnnounce = time.Now() if err := g.updateTorrentInfo(torrent); err != nil { errors = append(errors, fmt.Errorf("failed to update torrent info: %w", err)) } if len(errors) > 0 { return fmt.Errorf("announce errors: %v", errors) } return nil } func (g *UnifiedP2PGateway) announceToTracker(torrent *TorrentInfo) error { // This would integrate with the tracker's announce system log.Printf("P2P Gateway: Announced %s to HTTP tracker", torrent.InfoHash[:8]) return nil } func (g *UnifiedP2PGateway) announceToDHT(torrent *TorrentInfo) error { if g.dhtBootstrap != nil { g.dhtBootstrap.AnnounceNewTorrent(torrent.InfoHash, g.config.DHT.Port) log.Printf("P2P Gateway: Announced %s to DHT", torrent.InfoHash[:8]) } return nil } // ============ STATISTICS AND UTILITIES ============ func (g *UnifiedP2PGateway) updateStats() { g.statsMutex.Lock() defer g.statsMutex.Unlock() // Update component statistics if g.tracker != nil { g.stats.ComponentStats["tracker"] = g.tracker.GetStats() } if g.dhtBootstrap != nil { g.stats.ComponentStats["dht"] = g.dhtBootstrap.GetDHTStats() } if g.wsTracker != nil { g.stats.ComponentStats["websocket"] = g.wsTracker.GetStats() } // Calculate total counts g.stats.TotalPeers = 0 if trackerStats, ok := g.stats.ComponentStats["tracker"].(map[string]interface{}); ok { if peers, ok := trackerStats["peers"].(int64); ok { g.stats.TrackerPeers = peers g.stats.TotalPeers += peers } } if dhtStats, ok := g.stats.ComponentStats["dht"].(map[string]interface{}); ok { if nodes, ok := dhtStats["routing_table_size"].(int); ok { g.stats.DHTNodes = int64(nodes) } } if wsStats, ok := g.stats.ComponentStats["websocket"].(map[string]interface{}); ok { if peers, ok := wsStats["total_peers"].(int); ok { g.stats.WebSocketPeers = int64(peers) g.stats.TotalPeers += int64(peers) } } } // Helper functions for database operations func (g *UnifiedP2PGateway) storeTorrentInfo(torrent *TorrentInfo) error { query := `INSERT OR REPLACE INTO p2p_torrents (info_hash, name, size, created_at, last_announce, webseed_url, is_active) VALUES (?, ?, ?, ?, ?, ?, ?)` _, err := g.db.Exec(query, torrent.InfoHash, torrent.Name, torrent.Size, torrent.CreatedAt, torrent.LastAnnounce, torrent.WebSeedURL, torrent.IsActive) return err } func (g *UnifiedP2PGateway) updateTorrentInfo(torrent *TorrentInfo) error { query := `UPDATE p2p_torrents SET last_announce = ?, is_active = ? WHERE info_hash = ?` _, err := g.db.Exec(query, torrent.LastAnnounce, torrent.IsActive, torrent.InfoHash) return err } func (g *UnifiedP2PGateway) getAllActiveTorrents() ([]*TorrentInfo, error) { query := `SELECT info_hash, name, size, created_at, last_announce, webseed_url, is_active FROM p2p_torrents WHERE is_active = 1` rows, err := g.db.Query(query) if err != nil { return nil, err } defer rows.Close() var torrents []*TorrentInfo for rows.Next() { var torrent TorrentInfo var webSeedURL sql.NullString err := rows.Scan(&torrent.InfoHash, &torrent.Name, &torrent.Size, &torrent.CreatedAt, &torrent.LastAnnounce, &webSeedURL, &torrent.IsActive) if err != nil { continue } if webSeedURL.Valid { torrent.WebSeedURL = webSeedURL.String } torrents = append(torrents, &torrent) } return torrents, nil } func (g *UnifiedP2PGateway) getActiveTorrentsWithWebSeeds() ([]*TorrentInfo, error) { query := `SELECT info_hash, name, size, created_at, last_announce, webseed_url, is_active FROM p2p_torrents WHERE is_active = 1 AND webseed_url IS NOT NULL AND webseed_url != ''` rows, err := g.db.Query(query) if err != nil { return nil, err } defer rows.Close() var torrents []*TorrentInfo for rows.Next() { var torrent TorrentInfo err := rows.Scan(&torrent.InfoHash, &torrent.Name, &torrent.Size, &torrent.CreatedAt, &torrent.LastAnnounce, &torrent.WebSeedURL, &torrent.IsActive) if err != nil { continue } torrents = append(torrents, &torrent) } return torrents, nil } func calculateReliability(peer *tracker.PeerInfo) float64 { // Calculate reliability based on various factors reliability := 0.5 // Base reliability if peer.IsWebSeed { return 1.0 // WebSeeds are most reliable } if peer.IsSeeder { reliability += 0.3 } if peer.Priority > 50 { reliability += 0.2 } // Recent activity bonus if time.Since(peer.LastSeen) < 10*time.Minute { reliability += 0.2 } if reliability > 1.0 { reliability = 1.0 } return reliability } // CreateP2PTables creates the necessary database tables for P2P coordination func (g *UnifiedP2PGateway) CreateP2PTables() error { query := ` CREATE TABLE IF NOT EXISTS p2p_torrents ( info_hash TEXT PRIMARY KEY, name TEXT NOT NULL, size INTEGER NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_announce DATETIME DEFAULT CURRENT_TIMESTAMP, webseed_url TEXT, is_active BOOLEAN DEFAULT 1 ); CREATE INDEX IF NOT EXISTS idx_p2p_torrents_active ON p2p_torrents(is_active); CREATE INDEX IF NOT EXISTS idx_p2p_torrents_last_announce ON p2p_torrents(last_announce); ` _, err := g.db.Exec(query) return err }