forked from snxraven/p2ns
ix: Prevent claim overwriting and improve domain removal safety
Fix critical bugs in domain claim management to ensure data integrity: - Domain addition: Check for existing claims before adding to prevent duplicates - Update user's own claim if it exists instead of creating duplicates - Skip update if hash and SSL flag unchanged (idempotent) - Never touch other users' claims when updating own claim - Domain removal: Add strict validation to prevent removing other users' claims - Only perform full cleanup (atomicDomainCleanup) when user is confirmed resolved claimant - Require consensus status 'resolved' AND resolvedClaimant matches localWriter - Improved logging to show which removal path is taken - Enhanced removeOwnClaimAndVotes with explicit vote key parsing - Hash preferences cleanup: Fix module path resolution error - Correct require path from admin-backend/cache.js (../../core/core) - Add lazy require pattern with error handling to prevent crashes - Ensure cleanup runs after consensus updates and domain removals
This commit is contained in:
@@ -20,7 +20,8 @@ async function handleDomainsRoutes(req, res) {
|
||||
|
||||
if (method === 'GET' && urlPath === '/api/resolved-domains') {
|
||||
try {
|
||||
const allEntries = await getAllEntries();
|
||||
// Use fresh data (bypass cache) to ensure latest consensus state
|
||||
const allEntries = await getAllEntries(state.dnsPass, false);
|
||||
const domainClaimants = new Map();
|
||||
for (const entry of allEntries) {
|
||||
if (entry.key.startsWith('claim:')) {
|
||||
@@ -101,7 +102,23 @@ async function handleDomainsRoutes(req, res) {
|
||||
// Extract SSL flag - handle both boolean true and string "true"
|
||||
const ssl = data.ssl === true || data.ssl === 'true' || data.ssl === 1;
|
||||
await addDomain(validation.domain, validation.hash, ssl);
|
||||
|
||||
// Invalidate cache to ensure fresh consensus check
|
||||
invalidateEntriesCache();
|
||||
|
||||
// Recalculate consensus for the new domain
|
||||
await doAutoVotes();
|
||||
|
||||
// Small delay to allow consensus to settle
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
|
||||
// Trigger consensus recalculation request to notify peers
|
||||
if (state.sendConsensusRequest) {
|
||||
setTimeout(() => {
|
||||
state.sendConsensusRequest(validation.domain);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
let domains = [];
|
||||
if (await fs.access(domainsFile).then(() => true).catch(() => false)) {
|
||||
const parsed = JSON.parse(await fs.readFile(domainsFile, 'utf8'));
|
||||
@@ -123,7 +140,11 @@ async function handleDomainsRoutes(req, res) {
|
||||
// Note: IP assignment now happens automatically after consensus resolution
|
||||
// See assignAllIPs() function for automatic IP assignment logic
|
||||
logDebug('Admin', `Domain ${validation.domain} added - IP will be assigned automatically after consensus resolution`);
|
||||
|
||||
// Broadcast multiple update types to ensure frontend refreshes
|
||||
broadcast({ type: 'update-database' });
|
||||
broadcast({ type: 'update-local-dns' }); // Also update local DNS tab to show conflicts
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('OK');
|
||||
} catch (err) {
|
||||
@@ -171,18 +192,40 @@ async function handleDomainsRoutes(req, res) {
|
||||
|
||||
// Check if peer is the resolved claimant
|
||||
const consensusState = await getConsensusState(domain);
|
||||
const isResolvedClaimant = consensusState.resolvedClaimant === localWriter;
|
||||
|
||||
// Only do full cleanup if:
|
||||
// 1. Consensus is resolved
|
||||
// 2. User is the resolved claimant
|
||||
// 3. Resolved claimant is not null/undefined
|
||||
const isResolvedClaimant = consensusState.status === 'resolved' &&
|
||||
consensusState.resolvedClaimant &&
|
||||
consensusState.resolvedClaimant === localWriter;
|
||||
|
||||
if (isResolvedClaimant) {
|
||||
// Full removal: user is the resolved claimant
|
||||
logInfo('Admin', `User is resolved claimant for ${domain}, performing full cleanup`);
|
||||
await atomicDomainCleanup(domain);
|
||||
} else {
|
||||
// Partial removal: user has claim but isn't the resolved claimant
|
||||
// This includes cases where:
|
||||
// - Consensus is not resolved
|
||||
// - User is not the resolved claimant (conflict scenario)
|
||||
// - Resolved claimant is null/undefined
|
||||
logInfo('Admin', `User is NOT resolved claimant for ${domain} (status=${consensusState.status}, resolvedClaimant=${consensusState.resolvedClaimant}), removing only own claim and votes`);
|
||||
await removeOwnClaimAndVotes(domain, localWriter);
|
||||
}
|
||||
if (state.sendRemovalRequest) {
|
||||
state.sendRemovalRequest(domain);
|
||||
}
|
||||
|
||||
// Cleanup hash preferences if domain was removed
|
||||
try {
|
||||
const { cleanupHashPreferences } = require('../cache');
|
||||
await cleanupHashPreferences();
|
||||
} catch (err) {
|
||||
logWarn('Admin', `Failed to cleanup hash preferences after domain removal: ${err.message}`);
|
||||
}
|
||||
|
||||
trackRequest('/api/remove-domain', true);
|
||||
broadcast({ type: 'update-database' });
|
||||
broadcast({ type: 'update-holesail-clients' });
|
||||
|
||||
Reference in New Issue
Block a user