/** * Simple JSON file-based cache for DNS records. * No native dependencies — works on any Node version. * * Structure of dns-cache.json: * { * "cloudflare": { * "zone123": { * "synced_at": "2024-01-01T00:00:00.000Z", * "records": [ { id, type, name, content, ttl, priority }, ... ] * } * } * } */ const fs = require('fs'); const path = require('path'); const CACHE_PATH = process.env.DB_PATH || path.join(__dirname, '..', 'dns-cache.json'); // ─── Load / save ───────────────────────────────────────────────────────────── function load() { try { return JSON.parse(fs.readFileSync(CACHE_PATH, 'utf8')); } catch { return {}; } } function save(data) { fs.writeFileSync(CACHE_PATH, JSON.stringify(data, null, 2), 'utf8'); } // ─── Public API ────────────────────────────────────────────────────────────── function getRecords(provider, zoneId) { const data = load(); return data[provider]?.[zoneId]?.records ?? []; } function getSyncedAt(provider, zoneId) { const data = load(); return data[provider]?.[zoneId]?.synced_at ?? null; } // Returns { [zoneId]: { synced_at, record_count } } for all cached zones of a provider function getProviderSyncStatus(provider) { const data = load(); const zones = data[provider] ?? {}; const result = {}; for (const [zoneId, zone] of Object.entries(zones)) { result[zoneId] = { synced_at: zone.synced_at ?? null, record_count: (zone.records ?? []).length, }; } return result; } function getZoneName(provider, zoneId) { const data = load(); return data[provider]?.[zoneId]?.zone_name ?? zoneId; } function replaceZoneRecords(provider, zoneId, records, zoneName) { const data = load(); if (!data[provider]) data[provider] = {}; data[provider][zoneId] = { zone_name: zoneName ?? data[provider]?.[zoneId]?.zone_name ?? zoneId, synced_at: new Date().toISOString(), records: records.map(r => ({ id: r.id, type: r.type, name: r.name, content: r.content, ttl: r.ttl ?? null, priority: r.priority ?? null, })), }; save(data); } function upsertRecord(provider, zoneId, record) { const data = load(); if (!data[provider]) data[provider] = {}; if (!data[provider][zoneId]) data[provider][zoneId] = { synced_at: null, records: [] }; const records = data[provider][zoneId].records; const idx = records.findIndex(r => r.id === record.id); const entry = { id: record.id, type: record.type, name: record.name, content: record.content, ttl: record.ttl ?? null, priority: record.priority ?? null, }; if (idx >= 0) records[idx] = entry; else records.push(entry); save(data); } function deleteRecord(provider, zoneId, recordId) { const data = load(); const zone = data[provider]?.[zoneId]; if (!zone) return; zone.records = zone.records.filter(r => r.id !== recordId); save(data); } /** * Returns all cached zones across all providers as a flat list. * Groups duplicate zone names so you can spot the same domain on multiple providers. */ function getAllZones() { const data = load(); const byName = {}; for (const [provider, zones] of Object.entries(data)) { for (const [zoneId, zone] of Object.entries(zones)) { const name = zone.zone_name ?? zoneId; if (!byName[name]) byName[name] = []; byName[name].push({ provider, zone_id: zoneId, record_count: (zone.records ?? []).length, synced_at: zone.synced_at ?? null, }); } } return Object.entries(byName) .map(([name, entries]) => ({ name, entries, duplicate: entries.length > 1 })) .sort((a, b) => a.name.localeCompare(b.name)); } function getStats() { const data = load(); const typeCounts = {}; let totalZones = 0; let zonesWithMx = 0; const perProvider = {}; for (const [provider, zones] of Object.entries(data)) { const zoneCount = Object.keys(zones).length; totalZones += zoneCount; perProvider[provider] = { zones: zoneCount }; for (const zone of Object.values(zones)) { const records = zone.records ?? []; const hasMx = records.some(r => r.type === 'MX'); if (hasMx) zonesWithMx++; for (const r of records) { typeCounts[r.type] = (typeCounts[r.type] || 0) + 1; } } } const recordTypes = Object.entries(typeCounts) .map(([type, count]) => ({ type, count })) .sort((a, b) => b.count - a.count); return { totalZones, zonesWithMx, recordTypes, perProvider }; } module.exports = { getRecords, getSyncedAt, getZoneName, getProviderSyncStatus, getAllZones, replaceZoneRecords, upsertRecord, deleteRecord, getStats };