Full codebase including API server (32 modules + routes), dashboard frontend, DashCA certificate distribution, installer script, and deployment skills.
274 lines
10 KiB
JavaScript
274 lines
10 KiB
JavaScript
// ========== DNS MANAGEMENT ==========
|
|
(function () {
|
|
// Restart DNS service via backend proxy (backend handles auth automatically)
|
|
async function restartDnsService(dnsId) {
|
|
const response = await secureFetch(`/api/v1/dns/restart/${dnsId}`, { method: 'POST' });
|
|
const result = await response.json();
|
|
if (!result.success) {
|
|
throw new Error(result.error || 'Restart failed');
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Event delegation for DNS restart buttons (dynamic cards)
|
|
document.querySelector('.top')?.addEventListener('click', async (e) => {
|
|
const restartBtn = e.target.closest('[id$="-restart"]');
|
|
if (!restartBtn) return;
|
|
const dnsId = restartBtn.id.replace('-restart', '');
|
|
if (!SITE.dnsServers[dnsId]) return;
|
|
if (!confirm(`Restart ${dnsId.toUpperCase()} service?`)) return;
|
|
try {
|
|
await withButton(restartBtn, '...', () => restartDnsService(dnsId));
|
|
setTimeout(window.refreshAll, DC.DELAYS.RELOAD);
|
|
} catch (e) {
|
|
showNotification('Restart failed: ' + e.message, 'error');
|
|
}
|
|
});
|
|
|
|
// DNS Update buttons
|
|
async function updateDnsServer(dnsId, serverIp) {
|
|
const btn = document.getElementById(`${dnsId}-update`);
|
|
const originalText = btn?.textContent || '⬆️';
|
|
|
|
try {
|
|
// First check for updates
|
|
btn.textContent = '🔍';
|
|
btn.disabled = true;
|
|
btn.title = 'Checking for updates...';
|
|
|
|
const checkResponse = await fetch(`/api/v1/dns/check-update?server=${encodeURIComponent(serverIp)}`);
|
|
const checkResult = await checkResponse.json();
|
|
|
|
if (!checkResult.success) {
|
|
throw new Error(checkResult.error || 'Failed to check for updates');
|
|
}
|
|
|
|
if (!checkResult.updateAvailable) {
|
|
btn.textContent = '✅';
|
|
btn.title = `Already on latest version (${checkResult.currentVersion})`;
|
|
showNotification(`${dnsId.toUpperCase()} is already up to date! Current version: ${checkResult.currentVersion}`, 'info');
|
|
setTimeout(() => {
|
|
btn.textContent = originalText;
|
|
btn.disabled = false;
|
|
btn.title = 'Update DNS server';
|
|
}, 3000);
|
|
return;
|
|
}
|
|
|
|
// Update available - confirm with user
|
|
const confirmUpdate = confirm(
|
|
`Update available for ${dnsId.toUpperCase()}!\n\n` +
|
|
`Current: ${checkResult.currentVersion}\n` +
|
|
`New: ${checkResult.updateVersion}\n\n` +
|
|
(checkResult.updateTitle ? `${checkResult.updateTitle}\n\n` : '') +
|
|
`The DNS server will restart during the update.\nProceed?`
|
|
);
|
|
|
|
if (!confirmUpdate) {
|
|
btn.textContent = originalText;
|
|
btn.disabled = false;
|
|
btn.title = 'Update DNS server';
|
|
return;
|
|
}
|
|
|
|
// Perform the update
|
|
btn.textContent = '🔄';
|
|
btn.title = 'Updating...';
|
|
|
|
const updateResponse = await secureFetch(`/api/v1/dns/update?server=${encodeURIComponent(serverIp)}`, {
|
|
method: 'POST'
|
|
});
|
|
const updateResult = await updateResponse.json();
|
|
|
|
if (!updateResult.success) {
|
|
throw new Error(updateResult.error || 'Update failed');
|
|
}
|
|
|
|
if (updateResult.manualUpdateRequired) {
|
|
// Technitium v14+ doesn't support auto-install via API
|
|
btn.textContent = '⬆️';
|
|
btn.title = `Update available: ${updateResult.newVersion}`;
|
|
const downloadInfo = updateResult.downloadLink
|
|
? `\nDownload: ${updateResult.downloadLink}`
|
|
: '';
|
|
const instructionsInfo = updateResult.instructionsLink
|
|
? `\nInstructions: ${updateResult.instructionsLink}`
|
|
: '';
|
|
showNotification(`${dnsId.toUpperCase()} update requires manual installation. Current: ${updateResult.previousVersion} → ${updateResult.newVersion}. Please update manually on the host machine.`, 'warning', 8000);
|
|
btn.disabled = false;
|
|
return;
|
|
}
|
|
|
|
btn.textContent = '✅';
|
|
btn.title = 'Updated successfully!';
|
|
showNotification(`${dnsId.toUpperCase()} updated successfully! ${updateResult.previousVersion} → ${updateResult.newVersion}. Server is restarting...`, 'success');
|
|
|
|
// Refresh after delay for server restart
|
|
setTimeout(() => {
|
|
btn.textContent = originalText;
|
|
btn.disabled = false;
|
|
btn.title = 'Update DNS server';
|
|
window.refreshAll();
|
|
}, 10000);
|
|
|
|
} catch (error) {
|
|
console.error('DNS update error:', error);
|
|
btn.textContent = '❌';
|
|
btn.title = 'Update failed';
|
|
showNotification(`Failed to update ${dnsId.toUpperCase()}: ${error.message}`, 'error');
|
|
setTimeout(() => {
|
|
btn.textContent = originalText;
|
|
btn.disabled = false;
|
|
btn.title = 'Update DNS server';
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
// Event delegation for DNS update buttons (dynamic cards)
|
|
document.querySelector('.top')?.addEventListener('click', (e) => {
|
|
const updateBtn = e.target.closest('[id$="-update"]');
|
|
if (!updateBtn) return;
|
|
const dnsId = updateBtn.id.replace('-update', '');
|
|
if (!SITE.dnsServers[dnsId]) return;
|
|
updateDnsServer(dnsId, SITE.dnsServers[dnsId]?.ip);
|
|
});
|
|
|
|
// ===== DNS SETTINGS MODAL =====
|
|
|
|
// Inject modal HTML
|
|
injectModal('dns-settings-modal', `
|
|
<div id="dns-settings-modal" class="weather-modal">
|
|
<div class="weather-modal-content" style="min-width: 420px; max-width: 520px;">
|
|
<h3 id="dns-settings-title">DNS Settings</h3>
|
|
|
|
<div style="display: grid; gap: 16px; margin-top: 16px;">
|
|
<div>
|
|
<label for="dns-edit-ip" class="form-label-accent-sm">Server IP</label>
|
|
<input type="text" id="dns-edit-ip" class="form-input-md" placeholder="192.168.1.1" />
|
|
</div>
|
|
<div>
|
|
<label for="dns-edit-port" class="form-label-accent-sm">Port</label>
|
|
<input type="number" id="dns-edit-port" class="form-input-md" placeholder="5380" />
|
|
</div>
|
|
<div>
|
|
<label for="dns-edit-name" class="form-label-accent-sm">Display Name (optional)</label>
|
|
<input type="text" id="dns-edit-name" class="form-input-md" placeholder="e.g. Primary DNS" />
|
|
</div>
|
|
<div class="form-hint-sm">Manage credentials via Tokens in the toolbar</div>
|
|
</div>
|
|
|
|
<div class="weather-modal-buttons" style="margin-top: 24px;">
|
|
<button id="dns-settings-cancel">Cancel</button>
|
|
<button id="dns-settings-delete" style="background: color-mix(in srgb, #e74c3c 20%, transparent); border-color: #e74c3c; color: #e74c3c;">Remove</button>
|
|
<button id="dns-settings-save" class="btn-accent">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>`);
|
|
|
|
let currentDnsId = null;
|
|
|
|
function openDnsSettings(dnsId) {
|
|
currentDnsId = dnsId;
|
|
const server = SITE.dnsServers[dnsId] || {};
|
|
const modal = document.getElementById('dns-settings-modal');
|
|
|
|
document.getElementById('dns-settings-title').textContent = `${(server.name || dnsId).toUpperCase()} Settings`;
|
|
document.getElementById('dns-edit-ip').value = server.ip || '';
|
|
document.getElementById('dns-edit-port').value = server.port || DC.DEFAULTS.DNS_PORT;
|
|
document.getElementById('dns-edit-name').value = server.name || '';
|
|
|
|
modal.classList.add('show');
|
|
}
|
|
|
|
async function saveDnsSettings() {
|
|
if (!currentDnsId) return;
|
|
const ip = document.getElementById('dns-edit-ip').value.trim();
|
|
const port = document.getElementById('dns-edit-port').value.trim() || DC.DEFAULTS.DNS_PORT;
|
|
const name = document.getElementById('dns-edit-name').value.trim();
|
|
|
|
if (!ip) {
|
|
showNotification('Server IP is required', 'warning');
|
|
return;
|
|
}
|
|
|
|
const update = { dnsServers: {} };
|
|
update.dnsServers[currentDnsId] = { ip, port: String(port) };
|
|
if (name) update.dnsServers[currentDnsId].name = name;
|
|
|
|
try {
|
|
const response = await secureFetch('/api/v1/config', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(update)
|
|
});
|
|
const result = await response.json();
|
|
if (result.success) {
|
|
SITE.dnsServers[currentDnsId] = update.dnsServers[currentDnsId];
|
|
showNotification(`${currentDnsId.toUpperCase()} settings saved`, 'success');
|
|
closeDnsSettings();
|
|
window.refreshAll();
|
|
} else {
|
|
showNotification(result.error || 'Failed to save settings', 'error');
|
|
}
|
|
} catch (err) {
|
|
showNotification('Failed to save: ' + err.message, 'error');
|
|
}
|
|
}
|
|
|
|
async function removeDnsServer() {
|
|
if (!currentDnsId) return;
|
|
if (!confirm(`Remove ${currentDnsId.toUpperCase()} from dashboard? This won't affect the actual DNS server.`)) return;
|
|
|
|
// Remove by setting to null in dnsServers
|
|
try {
|
|
const resp = await secureFetch('/api/v1/config');
|
|
const config = await resp.json();
|
|
if (config.dnsServers) {
|
|
delete config.dnsServers[currentDnsId];
|
|
}
|
|
const saveResp = await secureFetch('/api/v1/config', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ dnsServers: config.dnsServers || {} })
|
|
});
|
|
const result = await saveResp.json();
|
|
if (result.success) {
|
|
delete SITE.dnsServers[currentDnsId];
|
|
// Remove card from DOM
|
|
const card = document.querySelector(`.top [data-app="${currentDnsId}"]`);
|
|
if (card) card.remove();
|
|
showNotification(`${currentDnsId.toUpperCase()} removed from dashboard`, 'success');
|
|
closeDnsSettings();
|
|
} else {
|
|
showNotification(result.error || 'Failed to remove', 'error');
|
|
}
|
|
} catch (err) {
|
|
showNotification('Failed to remove: ' + err.message, 'error');
|
|
}
|
|
}
|
|
|
|
function closeDnsSettings() {
|
|
closeModal('dns-settings-modal');
|
|
currentDnsId = null;
|
|
}
|
|
|
|
// Event listeners for modal buttons
|
|
document.getElementById('dns-settings-cancel')?.addEventListener('click', closeDnsSettings);
|
|
document.getElementById('dns-settings-save')?.addEventListener('click', saveDnsSettings);
|
|
document.getElementById('dns-settings-delete')?.addEventListener('click', removeDnsServer);
|
|
document.getElementById('dns-settings-modal')?.addEventListener('click', (e) => {
|
|
if (e.target.id === 'dns-settings-modal') closeDnsSettings();
|
|
});
|
|
// Event delegation for DNS settings buttons (dynamic cards)
|
|
document.querySelector('.top')?.addEventListener('click', (e) => {
|
|
const settingsBtn = e.target.closest('[id$="-settings"]');
|
|
if (!settingsBtn) return;
|
|
const dnsId = settingsBtn.id.replace('-settings', '');
|
|
if (!SITE.dnsServers[dnsId]) return;
|
|
e.stopPropagation();
|
|
openDnsSettings(dnsId);
|
|
});
|
|
|
|
document.getElementById('refresh')?.addEventListener('click', window.refreshAll);
|
|
})();
|