Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,4 @@ To add your node as a seed:

## License

MIT
MIT
19 changes: 3 additions & 16 deletions routing_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ func (rt *RoutingTable) CacheSystem(sys *System, learnedFrom uuid.UUID, verified
if shouldUpdate {
existing.System = sys
existing.LearnedAt = now
if verified && rt.storage != nil {
// Always persist updates with newer InfoVersion to storage
// The storage layer has its own InfoVersion check to prevent stale overwrites
if rt.storage != nil {
rt.storage.SavePeerSystem(sys)
}
}
Expand Down Expand Up @@ -515,18 +517,3 @@ func (rt *RoutingTable) PruneCache(maxAge time.Duration) int {

return pruned
}

// SaveSnapshot saves the routing table state to storage
func (rt *RoutingTable) SaveSnapshot() error {
if rt.storage == nil {
return nil
}

nodes := rt.GetAllRoutingTableNodes()
for _, sys := range nodes {
if err := rt.storage.SavePeerSystem(sys); err != nil {
return err
}
}
return nil
}
95 changes: 0 additions & 95 deletions storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,38 +429,6 @@ func (s *Storage) SaveAttestation(attestation *Attestation, receivedBy uuid.UUID
return err
}

// GetAttestations retrieves all attestations for a system
func (s *Storage) GetAttestations(systemID uuid.UUID) ([]*Attestation, error) {
rows, err := s.db.Query(`
SELECT from_system_id, to_system_id, timestamp, message_type, signature, public_key
FROM attestations
WHERE from_system_id = ? OR to_system_id = ?
ORDER BY timestamp ASC
`, systemID.String(), systemID.String())

if err != nil {
return nil, err
}
defer rows.Close()

var attestations []*Attestation
for rows.Next() {
var att Attestation
var fromID, toID string

err := rows.Scan(&fromID, &toID, &att.Timestamp, &att.MessageType, &att.Signature, &att.PublicKey)
if err != nil {
continue
}

att.FromSystemID = uuid.MustParse(fromID)
att.ToSystemID = uuid.MustParse(toID)
attestations = append(attestations, &att)
}

return attestations, nil
}

// GetAttestationCount returns the count of verified attestations
func (s *Storage) GetAttestationCount(systemID uuid.UUID) (int, error) {
var count int
Expand Down Expand Up @@ -610,16 +578,6 @@ func (s *Storage) GetDatabaseStats() (map[string]interface{}, error) {
return stats, nil
}

// CountKnownSystems returns the total number of unique systems we've heard about
func (s *Storage) CountKnownSystems() int {
var count int
err := s.db.QueryRow(`SELECT COUNT(*) FROM peer_systems`).Scan(&count)
if err != nil {
return 0
}
return count
}

// GetAllPeerSystems returns all cached peer system info (not just direct peers)
func (s *Storage) GetAllPeerSystems() ([]*System, error) {
rows, err := s.db.Query(`
Expand Down Expand Up @@ -829,59 +787,6 @@ type TopologyEdge struct {
ToName string `json:"to_name"`
}

// GetRecentTopology returns inferred connections from recent attestations
func (s *Storage) GetRecentTopology(maxAge time.Duration) ([]TopologyEdge, error) {
cutoff := time.Now().Add(-maxAge).Unix()

rows, err := s.db.Query(`
SELECT DISTINCT from_system_id, to_system_id
FROM attestations
WHERE timestamp > ?
`, cutoff)
if err != nil {
return nil, err
}
defer rows.Close()

// Collect unique edges (deduplicate A→B and B→A)
edgeMap := make(map[string]TopologyEdge)

for rows.Next() {
var fromID, toID string
if err := rows.Scan(&fromID, &toID); err != nil {
continue
}

// Create canonical edge key (smaller ID first) to deduplicate
var key string
if fromID < toID {
key = fromID + ":" + toID
} else {
key = toID + ":" + fromID
}

if _, exists := edgeMap[key]; !exists {
fromName := s.getSystemName(fromID)
toName := s.getSystemName(toID)

edgeMap[key] = TopologyEdge{
FromID: fromID,
FromName: fromName,
ToID: toID,
ToName: toName,
}
}
}

// Convert map to slice
edges := make([]TopologyEdge, 0, len(edgeMap))
for _, edge := range edgeMap {
edges = append(edges, edge)
}

return edges, nil
}

// getSystemName looks up a system name from peer_systems cache
func (s *Storage) getSystemName(systemID string) string {
var name string
Expand Down
59 changes: 38 additions & 21 deletions web-interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ const indexTemplate = `<!DOCTYPE html>
function onMouseDown(event) {
if (!scope.enabled) return;
event.preventDefault();
markMapInteraction();
if (event.button === 0) {
state = STATE.ROTATE;
rotateStart.set(event.clientX, event.clientY);
Expand Down Expand Up @@ -876,6 +877,7 @@ const indexTemplate = `<!DOCTYPE html>
function onWheel(event) {
if (!scope.enabled || !scope.enableZoom) return;
event.preventDefault();
markMapInteraction();
if (event.deltaY < 0) scale *= Math.pow(0.95, scope.zoomSpeed);
else if (event.deltaY > 0) scale /= Math.pow(0.95, scope.zoomSpeed);
scope.update();
Expand Down Expand Up @@ -921,6 +923,18 @@ const indexTemplate = `<!DOCTYPE html>
let currentKnownSystems = [...knownSystems];
let currentLivePeerIDs = new Set(livePeerIDs);

// Track user interaction to avoid disrupting browsing
let lastMapInteraction = 0;
const MAP_INTERACTION_COOLDOWN = 60000; // Don't refresh map for 60s after interaction

function markMapInteraction() {
lastMapInteraction = Date.now();
}

function isUserBrowsingMap() {
return (Date.now() - lastMapInteraction) < MAP_INTERACTION_COOLDOWN;
}

async function fetchConnections() {
try {
const resp = await fetch('/api/connections');
Expand Down Expand Up @@ -1497,27 +1511,30 @@ const indexTemplate = `<!DOCTYPE html>
document.getElementById('stat-galaxy').textContent = totalSystems + ' total';
document.getElementById('galaxy-title').textContent = 'Galaxy Map (' + totalSystems + ' systems)';

// Update live peer IDs set from peers response
currentLivePeerIDs = new Set(peers.map(p => p.id));

// Update known systems for map (convert to map format)
currentKnownSystems = systems.map(s => ({
id: s.id,
name: s.name,
x: s.x,
y: s.y,
z: s.z,
color: s.stars?.primary?.color || '#ffffff',
starClass: s.stars?.primary?.class || 'M',
starDesc: s.stars?.primary?.description || ''
}));

// Fetch fresh connections
const connectionsResp = await fetch('/api/connections');
cachedConnections = await connectionsResp.json() || [];

// Rebuild the 3D map with updated data
rebuildMapContent();
// Only update map data if user isn't actively browsing
if (!isUserBrowsingMap()) {
// Update live peer IDs set from peers response
currentLivePeerIDs = new Set(peers.map(p => p.id));

// Update known systems for map (convert to map format)
currentKnownSystems = systems.map(s => ({
id: s.id,
name: s.name,
x: s.x,
y: s.y,
z: s.z,
color: s.stars?.primary?.color || '#ffffff',
starClass: s.stars?.primary?.class || 'M',
starDesc: s.stars?.primary?.description || ''
}));

// Fetch fresh connections
const connectionsResp = await fetch('/api/connections');
cachedConnections = await connectionsResp.json() || [];

// Rebuild the 3D map with updated data
rebuildMapContent();
}

} catch (err) {
console.error('Failed to refresh stats:', err);
Expand Down