This commit is contained in:
Raven Scott
2025-12-17 20:49:34 -05:00
parent a977320bfc
commit 00709cb446
10 changed files with 4191 additions and 49 deletions
@@ -39,15 +39,17 @@ async function handleDomainsRoutes(req, res) {
for (const domain of domains) {
const hash = await getHashForDomain(domain) || 'none';
const isLocal = localWriter ? domainClaimants.get(domain)?.has(localWriter) || false : false;
// Check if local writer is the resolved claimant (owner)
let isOwner = false;
let consensusState = null;
try {
const consensusState = await getConsensusState(domain);
consensusState = await getConsensusState(domain);
isOwner = localWriter ? consensusState.resolvedClaimant === localWriter : false;
} catch (err) {
logDebug('Admin', `Error checking ownership for ${domain}: ${err.message}`);
logDebug('Admin', `Error checking ownership/consensus for ${domain}: ${err.message}`);
}
resolved.push({ domain, hash, isLocal, isOwner });
resolved.push({ domain, hash, isLocal, isOwner, consensusState });
}
let internalDomains = ['p2ns.admin'];
try {
@@ -59,7 +61,7 @@ async function handleDomainsRoutes(req, res) {
// Only add internal domains that aren't already in the resolved list
for (const d of internalDomains) {
if (!resolved.some(r => r.domain === d)) {
resolved.push({ domain: d, hash: 'internal', isLocal: true, isOwner: true });
resolved.push({ domain: d, hash: 'internal', isLocal: true, isOwner: true, consensusStatus: 'internal' });
}
}
res.writeHead(200, { 'Content-Type': 'application/json' });
+9 -16
View File
@@ -107,24 +107,17 @@ window.tabs = {
<td class="p-3">${item.isLocal && item.hash !== 'internal' ? `<button onclick="removeDomain('${item.domain}')" class="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600">Remove</button>` : ''}</td>`;
return tr;
},
postFetch: async (data) => {
// Fetch consensus states for all domains
const domainsWithConsensus = await Promise.all(data.map(async (item) => {
if (item.hash === 'internal' || item.hash === 'none') {
postFetch: (data) => {
// Data already contains consensusState from backend
return data.map(item => {
if (item.consensusStatus === 'internal' || item.hash === 'internal' || item.hash === 'none') {
return { ...item, consensusStatus: 'internal' };
}
try {
const consensusRes = await fetch(`/api/consensus/${encodeURIComponent(item.domain)}`);
if (consensusRes.ok) {
const consensusState = await consensusRes.json();
return { ...item, consensusStatus: consensusState.status, consensusState };
}
} catch (err) {
console.error(`Failed to fetch consensus for ${item.domain}:`, err);
}
return { ...item, consensusStatus: 'unknown' };
}));
return domainsWithConsensus;
return {
...item,
consensusStatus: item.consensusState?.status || 'unknown'
};
});
}
},
entries: {
+1 -1
View File
@@ -25,7 +25,7 @@ async function genericFetch(tabId, shouldRender = true) {
window[config.dataKey] = data;
// Reset infinite scroll state when fetching fresh data
if (window.infiniteScrollState && window.infiniteScrollState[tabId]) {
if (shouldRender && window.infiniteScrollState && window.infiniteScrollState[tabId]) {
window.infiniteScrollState[tabId].loadedCount = 0;
window.infiniteScrollState[tabId].lastQuery = '';
// Disconnect existing observer
+3 -2
View File
@@ -148,8 +148,9 @@ function connectWebSocket() {
window.updateMap[data.type]();
} else {
window.updateMap[data.type].forEach(tab => {
if (window.activeTab === 'host' || window.activeTab === tab) {
if (window.genericFetch) window.genericFetch(tab, true);
// Always fetch data in background, but only render if tab is active
if (window.genericFetch) {
window.genericFetch(tab, window.activeTab === 'host' || window.activeTab === tab);
}
});
}
+12 -2
View File
@@ -39,7 +39,17 @@ async function handleDomainsRoutes(req, res) {
for (const domain of domains) {
const hash = await getHashForDomain(domain) || 'none';
const isLocal = localWriter ? domainClaimants.get(domain)?.has(localWriter) || false : false;
resolved.push({ domain, hash, isLocal });
let isOwner = false;
let consensusState = null;
try {
consensusState = await getConsensusState(domain);
isOwner = localWriter ? consensusState.resolvedClaimant === localWriter : false;
} catch (err) {
logDebug('Admin', `Error checking ownership/consensus for ${domain}: ${err.message}`);
}
resolved.push({ domain, hash, isLocal, isOwner, consensusState });
}
let internalDomains = ['p2ns.admin'];
try {
@@ -51,7 +61,7 @@ async function handleDomainsRoutes(req, res) {
// Only add internal domains that aren't already in the resolved list
for (const d of internalDomains) {
if (!resolved.some(r => r.domain === d)) {
resolved.push({ domain: d, hash: 'internal', isLocal: true });
resolved.push({ domain: d, hash: 'internal', isLocal: true, isOwner: true, consensusStatus: 'internal' });
}
}
res.writeHead(200, { 'Content-Type': 'application/json' });
+41 -1
View File
@@ -22,9 +22,26 @@ window.paginationState = {
'dns-conflicts': { current: 1, size: 10 },
'host-servers': { current: 1, size: 10 },
'host-clients': { current: 1, size: 10 },
settings: { current: 1, size: 20 }
plugins: { current: 1, size: 10 }
};
// Helper function to get consensus status badge
function getConsensusStatusBadge(status) {
const badges = {
'resolved': '<span class="px-2 py-1 bg-green-500 text-white rounded text-xs">Resolved</span>',
'tie': '<span class="px-2 py-1 bg-yellow-500 text-white rounded text-xs">Tie</span>',
'insufficient_quorum': '<span class="px-2 py-1 bg-orange-500 text-white rounded text-xs">No Quorum</span>',
'no_claims': '<span class="px-2 py-1 bg-gray-500 text-white rounded text-xs">No Claims</span>',
'error': '<span class="px-2 py-1 bg-red-500 text-white rounded text-xs">Error</span>',
'internal': '<span class="px-2 py-1 bg-blue-500 text-white rounded text-xs">Internal</span>',
'unknown': '<span class="px-2 py-1 bg-gray-400 text-white rounded text-xs">Unknown</span>'
};
return badges[status] || badges['unknown'];
}
// Make function globally available
window.getConsensusStatusBadge = getConsensusStatusBadge;
window.chartColors = {
primary: 'rgb(59, 130, 246)',
success: 'rgb(34, 197, 94)',
@@ -59,10 +76,33 @@ window.tabs = {
renderItem: (item) => {
const tr = document.createElement('tr');
tr.className = 'border-b hover:bg-gray-50 dark:hover:bg-gray-700';
let consensusInfo = '';
if (item.consensusState) {
const statusBadge = getConsensusStatusBadge(item.consensusStatus);
consensusInfo = `<td class="p-3">${statusBadge}</td>`;
} else if (item.consensusStatus === 'internal') {
consensusInfo = `<td class="p-3">${getConsensusStatusBadge('internal')}</td>`;
} else {
consensusInfo = `<td class="p-3">${getConsensusStatusBadge('unknown')}</td>`;
}
tr.innerHTML = `<td class="p-3">${item.domain}${item.isLocal ? '🏠' : ''}</td>
<td class="p-3 break-all">${item.hash}</td>
${consensusInfo}
<td class="p-3">${item.isLocal && item.hash !== 'internal' ? `<button onclick="removeDomain('${item.domain}')" class="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600">Remove</button>` : ''}</td>`;
return tr;
},
postFetch: (data) => {
return data.map(item => {
if (item.consensusStatus === 'internal' || item.hash === 'internal' || item.hash === 'none') {
return { ...item, consensusStatus: 'internal' };
}
return {
...item,
consensusStatus: item.consensusState?.status || 'unknown'
};
});
}
},
entries: {
+3 -2
View File
@@ -148,8 +148,9 @@ function connectWebSocket() {
window.updateMap[data.type]();
} else {
window.updateMap[data.type].forEach(tab => {
if (window.activeTab === 'host' || window.activeTab === tab) {
if (window.genericFetch) window.genericFetch(tab, true);
// Always fetch data in background, but only render if tab is active
if (window.genericFetch) {
window.genericFetch(tab, window.activeTab === 'host' || window.activeTab === tab);
}
});
}