Files
GoNetKit/web/dns.html

290 lines
12 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{template "base.html" .}}
{{define "title"}}DNS Tools{{end}}
{{define "content"}}
<div class="container mx-auto px-4 py-8 max-w-6xl">
<div class="bg-gray-800 rounded-xl p-8 border border-gray-700 mb-8">
<a href="/dns" class="inline-block">
<h1 class="text-2xl md:text-3xl font-bold text-gray-100 hover:text-blue-400 transition-colors cursor-pointer mb-4">
🌐 DNS Tools
</h1>
</a>
<form class="flex flex-wrap gap-3 items-end mb-6" id="dnsForm" onsubmit="return false;">
<div class="flex-1 min-w-64">
<input type="text" id="dnsInput" placeholder="Enter domain or IP" required
class="w-full px-4 py-3 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 placeholder-gray-400 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
</div>
<div>
<select id="dnsType" class="px-4 py-3 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
<option value="A">A</option>
<option value="AAAA">AAAA</option>
<option value="MX">MX</option>
<option value="TXT">TXT</option>
<option value="NS">NS</option>
<option value="CNAME">CNAME</option>
<option value="PTR">PTR</option>
<option value="SOA">SOA</option>
<option value="SPF">SPF</option>
<option value="DKIM">DKIM</option>
<option value="DMARC">DMARC</option>
<option value="WHOIS">WHOIS</option>
</select>
</div>
<div class="min-w-48">
<input type="text" id="dnsServer" placeholder="Custom DNS server (optional)"
class="w-full px-4 py-3 bg-gray-900 border border-gray-600 rounded-lg text-gray-100 placeholder-gray-400 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none">
</div>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-6 py-3 rounded-lg transition-colors flex items-center space-x-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
<span>Query</span>
</button>
</form>
<div class="flex gap-3">
<button onclick="saveResults('csv')" class="bg-green-600 hover:bg-green-700 text-white font-medium px-4 py-2 rounded-lg transition-colors flex items-center space-x-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<span>Save as CSV</span>
</button>
<button onclick="saveResults('txt')" class="bg-green-600 hover:bg-green-700 text-white font-medium px-4 py-2 rounded-lg transition-colors flex items-center space-x-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<span>Save as TXT</span>
</button>
</div>
</div>
<div class="space-y-4" id="dnsResults"></div>
</div>
<style>
.popup-notification {
background-color: rgb(31 41 55);
border: 1px solid rgb(75 85 99);
color: rgb(243 244 246);
padding: 1rem;
border-radius: 0.5rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
max-width: 24rem;
position: relative;
transform: translateX(100%);
opacity: 0;
transition: all 0.3s ease;
}
.popup-notification.show {
transform: translateX(0);
opacity: 1;
}
.popup-notification.error {
border-color: rgb(239 68 68);
background-color: rgba(127 29 29, 0.5);
}
.popup-notification.success {
border-color: rgb(34 197 94);
background-color: rgba(20 83 45, 0.5);
}
.popup-notification.warning {
border-color: rgb(234 179 8);
background-color: rgba(133 77 14, 0.5);
}
.popup-notification .close-btn {
position: absolute;
top: 0.5rem;
right: 0.5rem;
color: rgb(156 163 175);
width: 1.5rem;
height: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.125rem;
line-height: 1;
cursor: pointer;
border: none;
background: none;
}
.popup-notification .close-btn:hover {
color: rgb(229 231 235);
}
</style>
<!-- Popup notification container -->
<div id="popup-container" class="fixed top-4 right-4 z-50 space-y-2"></div>
{{end}}
{{define "scripts"}}
<script>
let results = [];
// Popup notification system
function showPopup(message, type = 'error', duration = 5000) {
const container = document.getElementById('popup-container');
const popup = document.createElement('div');
popup.className = `popup-notification ${type}`;
popup.innerHTML = `
<button class="close-btn" onclick="this.parentElement.remove()">&times;</button>
<div class="flex items-start gap-2">
<div class="flex-shrink-0 mt-0.5">
${type === 'error' ? '❌' : type === 'success' ? '✅' : type === 'warning' ? '⚠️' : ''}
</div>
<div class="flex-1">${message}</div>
</div>
`;
container.appendChild(popup);
// Trigger animation
setTimeout(() => popup.classList.add('show'), 10);
// Auto-remove after duration
if (duration > 0) {
setTimeout(() => {
popup.classList.remove('show');
setTimeout(() => popup.remove(), 300);
}, duration);
}
}
document.getElementById('dnsForm').addEventListener('submit', function() {
const query = document.getElementById('dnsInput').value.trim();
const type = document.getElementById('dnsType').value;
const server = document.getElementById('dnsServer').value.trim();
if (!query) {
showPopup('Please enter a domain or IP address to query', 'warning');
return;
}
let url = `/api/dns?query=${encodeURIComponent(query)}&type=${encodeURIComponent(type)}`;
if (server) {
url += `&server=${encodeURIComponent(server)}`;
}
// Add selector field for DKIM queries
if (type === 'DKIM' && !query.includes(':')) {
const selector = prompt('Enter DKIM selector (e.g., "selector1", "default"):');
if (selector) {
url = `/api/dns?query=${encodeURIComponent(query + ':' + selector)}&type=${encodeURIComponent(type)}`;
if (server) {
url += `&server=${encodeURIComponent(server)}`;
}
} else {
showPopup('DKIM selector is required for DKIM queries', 'warning');
return;
}
}
// Show loading state
const submitBtn = document.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.innerHTML = `
<svg class="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="m4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Querying...
`;
submitBtn.disabled = true;
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`Server error: ${response.status}`);
}
return response.text();
})
.then(data => {
const timestamp = new Date().toLocaleString();
const result = {
timestamp: timestamp,
query: query,
type: type,
server: server || 'Default',
result: data
};
results.push(result);
const resultDiv = document.createElement('div');
resultDiv.className = 'bg-gray-800 border border-gray-700 rounded-lg p-6 mb-4';
resultDiv.innerHTML = `
<h3 class="text-blue-400 font-semibold mb-2">${type} query for ${query}</h3>
<p class="text-gray-400 text-sm mb-3">Time: ${timestamp} | Server: ${server || 'Default'}</p>
<pre class="bg-gray-900 text-gray-100 p-3 rounded-lg overflow-x-auto text-sm border border-gray-700">${data}</pre>
`;
document.getElementById('dnsResults').insertBefore(resultDiv, document.getElementById('dnsResults').firstChild);
showPopup(`Successfully queried ${type} record for ${query}`, 'success', 3000);
})
.catch(error => {
console.error('DNS Query Error:', error);
const resultDiv = document.createElement('div');
resultDiv.className = 'bg-gray-800 border border-red-500/50 rounded-lg p-6 mb-4';
resultDiv.innerHTML = `
<h3 class="text-red-400 font-semibold mb-2">❌ Error querying ${query}</h3>
<pre class="bg-gray-900 text-red-300 p-3 rounded-lg text-sm border border-gray-700">Error: ${error.message}</pre>
`;
document.getElementById('dnsResults').insertBefore(resultDiv, document.getElementById('dnsResults').firstChild);
showPopup(`Failed to query ${query}: ${error.message}`, 'error');
})
.finally(() => {
// Restore button
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
});
});
function saveResults(format) {
if (results.length === 0) {
showPopup('No results to save. Please run some DNS queries first.', 'warning');
return;
}
let content = '';
let filename = '';
try {
if (format === 'csv') {
content = 'Timestamp,Query,Type,Server,Result\n';
results.forEach(r => {
const escapedResult = '"' + r.result.replace(/"/g, '""') + '"';
content += `"${r.timestamp}","${r.query}","${r.type}","${r.server}",${escapedResult}\n`;
});
filename = 'dns-results.csv';
} else if (format === 'txt') {
results.forEach(r => {
content += `=== ${r.type} query for ${r.query} ===\n`;
content += `Time: ${r.timestamp}\n`;
content += `Server: ${r.server}\n`;
content += `Result:\n${r.result}\n\n`;
});
filename = 'dns-results.txt';
}
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
showPopup(`Successfully exported ${results.length} results as ${format.toUpperCase()}`, 'success', 3000);
} catch (error) {
showPopup(`Failed to export results: ${error.message}`, 'error');
}
}
</script>
{{end}}