Files
p2ns/includes/admin/admin-backend/routes/local-dns.js
T
2025-12-17 20:05:50 -05:00

176 lines
6.2 KiB
JavaScript

const fs = require('fs').promises;
const dns = require('dns').promises;
const state = require('../../../infrastructure/state');
const { logError, logWarn, logInfo } = require('../../../infrastructure/logger');
const { saveSelectorCache } = require('../cache');
const { broadcast } = require('../websocket');
const localDnsFile = process.env.LOCAL_DNS_FILE || 'cache/local_dns.json';
async function handleLocalDnsRoutes(req, res) {
const urlPath = req.urlPath || new URL(req.url, `https://${req.headers.host}`).pathname;
const method = req.method;
if (method === 'GET' && urlPath === '/api/local-dns') {
try {
let records = state.localDnsRecords || [];
let conflicts = [];
const domains = new Set([...state.domainsWithBoth, ...state.versionPreferences.keys()]);
for (const domain of domains) {
let publicIP = state.publicIpForDomain[domain];
if (!publicIP && state.versionPreferences.has(domain)) {
try {
const ips = await dns.resolve4(domain);
publicIP = ips[0] || 'N/A';
state.publicIpForDomain[domain] = publicIP;
} catch (err) {
logWarn('Admin', `Failed to resolve public IP for ${domain}: ${err.message}`);
publicIP = 'N/A';
}
}
conflicts.push({
domain,
version: state.versionPreferences.get(domain) || 'p2p',
publicIP: publicIP || 'N/A'
});
}
records = records.map((rec, index) => ({ ...rec, index }));
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ records, conflicts }));
} catch (err) {
logError('Admin', `Failed to fetch local DNS and conflicts: ${err.message}`);
res.writeHead(500);
res.end(JSON.stringify({ error: 'Failed to fetch local DNS and conflicts' }));
}
return true;
}
if (method === 'GET' && urlPath === '/api/selector-cache') {
try {
const preferences = Object.fromEntries(state.versionPreferences);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(preferences));
} catch (err) {
logError('Admin', `Failed to fetch selector cache: ${err.message}`);
res.writeHead(500);
res.end(JSON.stringify({ error: 'Failed to fetch selector cache' }));
}
return true;
}
if (method === 'POST' && urlPath === '/api/add-local-dns') {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', async () => {
try {
const record = JSON.parse(body);
if (!record.name || !record.type || !record.ttl || isNaN(record.ttl)) {
throw new Error('Missing or invalid required fields: name, type, ttl');
}
record.class = record.class || 'IN';
let records = state.localDnsRecords || [];
records.push(record);
await fs.writeFile(localDnsFile, JSON.stringify(records, null, 2));
state.localDnsRecords = records;
broadcast({ type: 'update-local-dns' });
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('OK');
} catch (err) {
logError('Admin', `Failed to add local DNS record: ${err.message}`);
res.writeHead(500);
res.end(err.message);
}
});
return true;
}
if (method === 'POST' && urlPath === '/api/update-local-dns') {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', async () => {
try {
const { index, record } = JSON.parse(body);
if (!record.name || !record.type || !record.ttl || isNaN(record.ttl)) {
throw new Error('Missing or invalid required fields: name, type, ttl');
}
let records = state.localDnsRecords || [];
if (index >= 0 && index < records.length) {
record.class = record.class || 'IN';
records[index] = record;
await fs.writeFile(localDnsFile, JSON.stringify(records, null, 2));
state.localDnsRecords = records;
broadcast({ type: 'update-local-dns' });
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('OK');
} else {
res.writeHead(400);
res.end('Invalid index');
}
} catch (err) {
logError('Admin', `Failed to update local DNS record: ${err.message}`);
res.writeHead(500);
res.end(err.message);
}
});
return true;
}
if (method === 'POST' && urlPath === '/api/delete-local-dns') {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', async () => {
try {
const { index } = JSON.parse(body);
let records = state.localDnsRecords || [];
if (index >= 0 && index < records.length) {
records.splice(index, 1);
await fs.writeFile(localDnsFile, JSON.stringify(records, null, 2));
state.localDnsRecords = records;
broadcast({ type: 'update-local-dns' });
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('OK');
} else {
res.writeHead(400);
res.end('Invalid index');
}
} catch (err) {
logError('Admin', `Failed to delete local DNS record: ${err.message}`);
res.writeHead(500);
res.end(err.message);
}
});
return true;
}
if (method === 'POST' && urlPath === '/api/update-version-preference') {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', async () => {
try {
const { domain, version } = JSON.parse(body);
if (version !== 'p2p' && version !== 'public') {
res.writeHead(400);
res.end('Invalid version, must be "p2p" or "public"');
return;
}
state.versionPreferences.set(domain, version);
await saveSelectorCache();
broadcast({ type: 'update-local-dns' });
logInfo('Admin', `Updated version preference for ${domain} to ${version}`);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('OK');
} catch (err) {
logError('Admin', `Failed to update version preference: ${err.message}`);
res.writeHead(500);
res.end(err.message);
}
});
return true;
}
return false;
}
module.exports = { handleLocalDnsRoutes };