Files
mazacoin-explorer/backend/src/nodeHistory.js
T
sami7777 906b1a502d Initial commit: Mazacoin blockchain explorer v1.0
- Full-featured blockchain explorer with React frontend + Node.js backend
- Features: block/tx/address lookup, live node map, price ticker, rich list
- Real-time updates via WebSocket
- Comprehensive documentation (README, INSTALL, API, TROUBLESHOOTING)
- Docker deployment with docker-compose
- Deployed at https://maza.samiahmed7777.me
2026-03-09 06:52:46 +01:00

133 lines
3.0 KiB
JavaScript

const fs = require('fs').promises;
const path = require('path');
const HISTORY_FILE = path.join(__dirname, '..', 'data', 'nodes-history.json');
const MAX_AGE_HOURS = 24;
class NodeHistory {
constructor() {
this.nodes = new Map();
this.initialized = false;
}
async init() {
if (this.initialized) return;
try {
// Ensure data directory exists
const dataDir = path.dirname(HISTORY_FILE);
await fs.mkdir(dataDir, { recursive: true });
// Load existing history
try {
const data = await fs.readFile(HISTORY_FILE, 'utf8');
const parsed = JSON.parse(data);
this.nodes = new Map(Object.entries(parsed));
} catch (err) {
// File doesn't exist or is invalid, start fresh
this.nodes = new Map();
}
this.initialized = true;
} catch (err) {
console.error('Error initializing node history:', err);
this.nodes = new Map();
this.initialized = true;
}
}
async updateNode(ip, geoData, isActive = true) {
await this.init();
const now = Date.now();
const existing = this.nodes.get(ip);
if (existing) {
// Update existing node
this.nodes.set(ip, {
...existing,
...geoData,
lastSeen: now,
isActive
});
} else {
// Add new node
this.nodes.set(ip, {
ip,
...geoData,
firstSeen: now,
lastSeen: now,
isActive
});
}
}
async getRecentNodes() {
await this.init();
const cutoff = Date.now() - (MAX_AGE_HOURS * 60 * 60 * 1000);
const recent = [];
// First, mark all as inactive
for (const [ip, node] of this.nodes.entries()) {
if (node.lastSeen >= cutoff) {
recent.push({
...node,
isActive: false // Will be updated to true for currently connected nodes
});
}
}
return recent;
}
async cleanup() {
await this.init();
const cutoff = Date.now() - (MAX_AGE_HOURS * 60 * 60 * 1000);
// Remove nodes older than 24 hours
for (const [ip, node] of this.nodes.entries()) {
if (node.lastSeen < cutoff) {
this.nodes.delete(ip);
}
}
}
async save() {
await this.init();
try {
const obj = Object.fromEntries(this.nodes);
await fs.writeFile(HISTORY_FILE, JSON.stringify(obj, null, 2), 'utf8');
} catch (err) {
console.error('Error saving node history:', err);
}
}
async getStats() {
await this.init();
const cutoff = Date.now() - (MAX_AGE_HOURS * 60 * 60 * 1000);
const recent = Array.from(this.nodes.values()).filter(n => n.lastSeen >= cutoff);
const active = recent.filter(n => n.isActive);
const countries = {};
recent.forEach(node => {
if (node.country) {
countries[node.country] = (countries[node.country] || 0) + 1;
}
});
return {
total: recent.length,
active: active.length,
countries,
nodes: recent
};
}
}
module.exports = new NodeHistory();