update the website code files, fix dns check for DKIM

This commit is contained in:
nahakubuilde
2025-06-08 22:51:07 +01:00
parent a7e41ad231
commit a0dfe8a535
24 changed files with 2747 additions and 1630 deletions

View File

@@ -38,7 +38,7 @@
<i class="bi bi-plus-circle me-2"></i>
Create DKIM
</button>
<button class="btn btn-outline-info" onclick="checkAllDNS()">
<button class="btn btn-outline-info" data-action="check-all-dns">
<i class="bi bi-arrow-clockwise me-2"></i>
Check All DNS
</button>
@@ -81,10 +81,10 @@
</div>
{% for item in dkim_data %}
<div class="card mb-4" id="domain-{{ item.domain.id }}">
<div class="card mb-4" id="domain-{{ item.domain.domain_name.replace('.', '-') }}" data-is-active="{{ item.dkim_key.is_active|tojson }}">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<div class="flex-grow-1 card-header-clickable" style="cursor: pointer;" data-bs-toggle="collapse" data-bs-target="#collapse-{{ item.domain.id }}" aria-expanded="false" aria-controls="collapse-{{ item.domain.id }}">
<div class="flex-grow-1 card-header-clickable" style="cursor: pointer;" data-bs-toggle="collapse" data-bs-target="#collapse-{{ item.domain.domain_name.replace('.', '-') }}" aria-expanded="false" aria-controls="collapse-{{ item.domain.domain_name.replace('.', '-') }}">
<h5 class="mb-0">
<i class="bi bi-server me-2"></i>
{{ item.domain.domain_name }}
@@ -96,7 +96,11 @@
</h5>
</div>
<div class="btn-group btn-group-sm me-2">
<button class="btn btn-outline-primary" onclick="event.stopPropagation(); checkDomainDNS('{{ item.domain.domain_name }}', '{{ item.dkim_key.selector }}')">
<button class="btn btn-outline-primary"
data-action="check-dns"
data-domain="{{ item.domain.domain_name }}"
data-selector="{{ item.dkim_key.selector }}"
onclick="event.stopPropagation();">
<i class="bi bi-search me-1"></i>
Check DNS
</button>
@@ -109,12 +113,12 @@
</a>
<form method="post" action="{{ url_for('email.toggle_dkim', dkim_id=item.dkim_key.id) }}" class="d-inline">
{% if item.dkim_key.is_active %}
<button type="submit" class="btn btn-outline-warning" onclick="event.stopPropagation();">
<button type="submit" class="btn btn-outline-warning" onclick="event.stopPropagation();" title="Disable DKIM">
<i class="bi bi-pause-circle me-1"></i>
Disable
</button>
{% else %}
<button type="submit" class="btn btn-outline-success" onclick="event.stopPropagation();">
<button type="submit" class="btn btn-outline-success" onclick="event.stopPropagation();" title="Enable DKIM">
<i class="bi bi-play-circle me-1"></i>
Enable
</button>
@@ -135,12 +139,12 @@
Regenerate
</button>
</div>
<div class="card-header-clickable" style="cursor: pointer;" data-bs-toggle="collapse" data-bs-target="#collapse-{{ item.domain.id }}" aria-expanded="false" aria-controls="collapse-{{ item.domain.id }}">
<i class="bi bi-chevron-down" id="chevron-{{ item.domain.id }}"></i>
<div class="card-header-clickable" style="cursor: pointer;" data-bs-toggle="collapse" data-bs-target="#collapse-{{ item.domain.domain_name.replace('.', '-') }}" aria-expanded="false" aria-controls="collapse-{{ item.domain.domain_name.replace('.', '-') }}">
<i class="bi bi-chevron-down" id="chevron-{{ item.domain.domain_name.replace('.', '-') }}"></i>
</div>
</div>
</div>
<div class="collapse" id="collapse-{{ item.domain.id }}">
<div class="collapse" id="collapse-{{ item.domain.domain_name.replace('.', '-') }}">
<div class="card-body">
<div class="row">
<!-- DKIM DNS Record -->
@@ -150,7 +154,7 @@
DKIM DNS Record
<span class="dns-status" id="dkim-status-{{ item.domain.domain_name.replace('.', '-') }}">
<span class="status-indicator status-warning"></span>
<small class="text-muted">Not checked</small>
<small class="text-muted">Active (DNS not checked)</small>
</span>
</h6>
<div class="mb-2">
@@ -327,6 +331,56 @@
{% block extra_js %}
<script>
// Show toast notification
function showToast(message, type = 'info') {
const toastContainer = document.getElementById('toastContainer');
if (!toastContainer) {
// Create toast container if it doesn't exist
const container = document.createElement('div');
container.id = 'toastContainer';
container.style.cssText = 'position: fixed; top: 20px; right: 20px; z-index: 1050;';
document.body.appendChild(container);
}
const toast = document.createElement('div');
toast.className = `toast align-items-center border-0 bg-${type}`;
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
const toastContent = document.createElement('div');
toastContent.className = 'd-flex';
const toastBody = document.createElement('div');
toastBody.className = 'toast-body text-white';
toastBody.textContent = message;
const closeButton = document.createElement('button');
closeButton.type = 'button';
closeButton.className = 'btn-close btn-close-white me-2 m-auto';
closeButton.setAttribute('data-bs-dismiss', 'toast');
closeButton.setAttribute('aria-label', 'Close');
toastContent.appendChild(toastBody);
toastContent.appendChild(closeButton);
toast.appendChild(toastContent);
document.getElementById('toastContainer').appendChild(toast);
const bsToast = new bootstrap.Toast(toast, {
animation: true,
autohide: true,
delay: 5000
});
bsToast.show();
// Remove toast after it's hidden
toast.addEventListener('hidden.bs.toast', function() {
toast.remove();
});
}
// Check DNS records for a domain
async function checkDomainDNS(domain, selector) {
const dkimStatus = document.getElementById(`dkim-status-${domain.replace('.', '-')}`);
const spfStatus = document.getElementById(`spf-status-${domain.replace('.', '-')}`);
@@ -337,30 +391,43 @@
try {
// Check DKIM DNS
const dkimResponse = await fetch('{{ url_for("email.check_dkim_dns") }}', {
const dkimResponse = await fetch("{{ url_for('email.check_dkim_dns') }}", {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `domain=${encodeURIComponent(domain)}&selector=${encodeURIComponent(selector)}`
body: new URLSearchParams({
domain: domain,
selector: selector
})
});
const dkimResult = await dkimResponse.json();
// Check SPF DNS
const spfResponse = await fetch('{{ url_for("email.check_spf_dns") }}', {
const spfResponse = await fetch("{{ url_for('email.check_spf_dns') }}", {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `domain=${encodeURIComponent(domain)}`
body: new URLSearchParams({
domain: domain
})
});
const spfResult = await spfResponse.json();
// Update DKIM status
if (dkimResult.success) {
dkimStatus.innerHTML = '<span class="status-indicator status-success"></span><small class="text-success">✓ Configured</small>';
// Get DKIM key status from data attribute
const domainCard = document.getElementById(`domain-${domain.replace('.', '-')}`);
const isActive = domainCard && domainCard.dataset.isActive === 'true';
// Update DKIM status based on active state and DNS visibility
if (isActive) {
if (dkimResult.success) {
dkimStatus.innerHTML = '<span class="status-indicator status-success"></span><small class="text-success">✓ Active & Configured</small>';
} else {
dkimStatus.innerHTML = '<span class="status-indicator" style="background-color: #fd7e14;"></span><small class="text-warning">Active but DNS not found</small>';
}
} else {
dkimStatus.innerHTML = '<span class="status-indicator status-danger"></span><small class="text-danger">✗ Not found</small>';
dkimStatus.innerHTML = '<span class="status-indicator" style="background-color: #6c757d;"></span><small class="text-muted">Disabled</small>';
}
// Update SPF status
@@ -380,7 +447,30 @@
}
}
// Show DNS check results in modal
function showDNSResults(domain, dkimResult, spfResult) {
// Clean up record strings by removing extra quotes and normalizing whitespace
function cleanRecordDisplay(record) {
if (!record) return '';
return record
.replace(/^["']|["']$/g, '') // Remove outer quotes
.replace(/\\n/g, '') // Remove newlines
.replace(/\s+/g, ' ') // Normalize whitespace
.trim(); // Remove leading/trailing space
}
const dkimRecordsHtml = dkimResult.records ?
dkimResult.records.map(record =>
`<div class="record-value" style="word-break: break-all; font-family: monospace; background: #f8f9fa; padding: 8px; border-radius: 4px;">
${cleanRecordDisplay(record)}
</div>`
).join('') : '';
const spfRecordHtml = spfResult.spf_record ?
`<div class="record-value mt-2" style="word-break: break-all; font-family: monospace; background: #f8f9fa; padding: 8px; border-radius: 4px;">
${cleanRecordDisplay(spfResult.spf_record)}
</div>` : '';
const resultsHtml = `
<h6>DNS Check Results for ${domain}</h6>
@@ -389,7 +479,12 @@
<div class="alert ${dkimResult.success ? 'alert-success' : 'alert-danger'}">
<strong>Status:</strong> ${dkimResult.success ? 'Found' : 'Not Found'}<br>
<strong>Message:</strong> ${dkimResult.message}
${dkimResult.records ? `<br><strong>Records:</strong> ${dkimResult.records.join(', ')}` : ''}
${dkimResult.records ? `
<br><strong>Records:</strong>
<div class="records-container mt-2">
${dkimRecordsHtml}
</div>
` : ''}
</div>
</div>
@@ -398,68 +493,10 @@
<div class="alert ${spfResult.success ? 'alert-success' : 'alert-danger'}">
<strong>Status:</strong> ${spfResult.success ? 'Found' : 'Not Found'}<br>
<strong>Message:</strong> ${spfResult.message}
${spfResult.spf_record ? `<br><strong>Current SPF:</strong> <code>${spfResult.spf_record}</code>` : ''}
</div>
</div>
`;
document.getElementById('dnsResults').innerHTML = resultsHtml;
new bootstrap.Modal(document.getElementById('dnsResultModal')).show();
}
function showAllDNSResults(results) {
let tableRows = '';
results.forEach(result => {
const dkimIcon = result.dkim.success ? '<i class="bi bi-check-circle-fill text-success"></i>' : '<i class="bi bi-x-circle-fill text-danger"></i>';
const spfIcon = result.spf.success ? '<i class="bi bi-check-circle-fill text-success"></i>' : '<i class="bi bi-x-circle-fill text-danger"></i>';
tableRows += `
<tr>
<td><strong>${result.domain}</strong></td>
<td class="text-center">
${dkimIcon}
<small class="d-block">${result.dkim.success ? 'Configured' : 'Not Found'}</small>
</td>
<td class="text-center">
${spfIcon}
<small class="d-block">${result.spf.success ? 'Found' : 'Not Found'}</small>
</td>
<td>
<small class="text-muted">
DKIM: ${result.dkim.message}<br>
SPF: ${result.spf.message}
</small>
</td>
</tr>
`;
});
const resultsHtml = `
<h6>DNS Check Results for All Domains</h6>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Domain</th>
<th class="text-center">DKIM Status</th>
<th class="text-center">SPF Status</th>
<th>Details</th>
</tr>
</thead>
<tbody>
${tableRows}
</tbody>
</table>
</div>
<div class="mt-3">
<div class="alert alert-info">
<small>
<i class="bi bi-info-circle me-1"></i>
<strong>DKIM:</strong> Verifies email signatures for authenticity<br>
<i class="bi bi-info-circle me-1"></i>
<strong>SPF:</strong> Authorizes servers that can send email for your domain
</small>
${spfResult.spf_record ? `
<br><strong>Current SPF:</strong>
${spfRecordHtml}
` : ''}
</div>
</div>
`;
@@ -468,6 +505,7 @@
new bootstrap.Modal(document.getElementById('dnsResultModal')).show();
}
// Check all domains' DNS records
async function checkAllDNS() {
const domains = document.querySelectorAll('[id^="domain-"]');
const results = [];
@@ -544,100 +582,88 @@
showAllDNSResults(results);
}
// AJAX DKIM regeneration function
// Simple DKIM regeneration function with page reload
async function regenerateDKIM(domainId, domainName) {
const confirmed = await showConfirmation(
`Regenerate DKIM key for ${domainName}? This will require updating DNS records.`,
'Regenerate DKIM Key',
'Regenerate',
'btn-warning'
);
// Show combined DNS check results
function showAllDNSResults(results) {
let tableRows = '';
if (!confirmed) {
return;
}
const button = event.target.closest('button');
const originalContent = button.innerHTML;
// Show loading state
button.innerHTML = '<i class="bi bi-arrow-clockwise spinner-border spinner-border-sm me-1"></i>Regenerating...';
button.disabled = true;
try {
const response = await fetch(`{{ url_for('email.regenerate_dkim', domain_id=0) }}`.replace('0', domainId), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({})
});
results.forEach(result => {
const dkimIcon = result.dkim.success ? '<i class="bi bi-check-circle-fill text-success"></i>' : '<i class="bi bi-x-circle-fill text-danger"></i>';
const spfIcon = result.spf.success ? '<i class="bi bi-check-circle-fill text-success"></i>' : '<i class="bi bi-x-circle-fill text-danger"></i>';
const result = await response.json();
if (result.success) {
showToast('DKIM key regenerated successfully! Reloading page...', 'success');
// Reload the page after a short delay to show the success message
setTimeout(() => {
window.location.reload();
}, 1500);
} else {
showToast(result.message || 'Error regenerating DKIM key', 'danger');
// Restore button on error
button.innerHTML = originalContent;
button.disabled = false;
}
} catch (error) {
console.error('DKIM regeneration error:', error);
showToast('Error regenerating DKIM key', 'danger');
// Restore button on error
button.innerHTML = originalContent;
button.disabled = false;
}
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
// Show temporary success message
const button = event.target.closest('button');
const originalText = button.innerHTML;
button.innerHTML = '<i class="bi bi-check me-1"></i>Copied!';
button.classList.add('btn-success');
button.classList.remove('btn-outline-secondary');
setTimeout(() => {
button.innerHTML = originalText;
button.classList.remove('btn-success');
button.classList.add('btn-outline-secondary');
}, 2000);
}).catch(err => {
console.error('Failed to copy: ', err);
tableRows += `
<tr>
<td><strong>${result.domain}</strong></td>
<td class="text-center">
${dkimIcon}
<small class="d-block">${result.dkim.success ? 'Configured' : 'Not Found'}</small>
</td>
<td class="text-center">
${spfIcon}
<small class="d-block">${result.spf.success ? 'Found' : 'Not Found'}</small>
</td>
<td>
<small class="text-muted">
DKIM: ${result.dkim.message}<br>
SPF: ${result.spf.message}
</small>
</td>
</tr>
`;
});
const resultsHtml = `
<h6>DNS Check Results for All Domains</h6>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Domain</th>
<th class="text-center">DKIM Status</th>
<th class="text-center">SPF Status</th>
<th>Details</th>
</tr>
</thead>
<tbody>
${tableRows}
</tbody>
</table>
</div>
<div class="mt-3">
<div class="alert alert-info">
<small>
<i class="bi bi-info-circle me-1"></i>
<strong>DKIM:</strong> Verifies email signatures for authenticity<br>
<i class="bi bi-info-circle me-1"></i>
<strong>SPF:</strong> Authorizes servers that can send email for your domain
</small>
</div>
</div>
`;
document.getElementById('dnsResults').innerHTML = resultsHtml;
new bootstrap.Modal(document.getElementById('dnsResultModal')).show();
}
// Handle form submissions with custom confirmation dialogs
async function handleFormSubmit(event, message) {
event.preventDefault(); // Prevent default form submission
const confirmed = await showConfirmation(
message,
'Confirm Action',
'Confirm',
'btn-danger'
);
if (confirmed) {
// Submit the form if confirmed
event.target.submit();
}
return false; // Always return false to prevent default submission
}
// Handle collapsible cards
// Initialize event handlers
document.addEventListener('DOMContentLoaded', function() {
// Add click handler for DNS check buttons
document.querySelectorAll('[data-action="check-dns"]').forEach(button => {
button.addEventListener('click', function(event) {
event.stopPropagation();
const domain = this.dataset.domain;
const selector = this.dataset.selector;
checkDomainDNS(domain, selector);
});
});
// Add click handler for check all DNS button
const checkAllButton = document.querySelector('[data-action="check-all-dns"]');
if (checkAllButton) {
checkAllButton.addEventListener('click', function() {
checkAllDNS();
});
}
// Add click handlers for card headers - only for clickable areas
document.querySelectorAll('.card-header-clickable[data-bs-toggle="collapse"]').forEach(function(element) {
element.addEventListener('click', function() {
@@ -658,43 +684,78 @@
}
});
});
});
document.getElementById('createDKIMForm').addEventListener('submit', async function(event) {
event.preventDefault();
const domain = document.getElementById('dkimDomain').value;
const selector = document.getElementById('dkimSelector').value.trim();
const errorDiv = document.getElementById('createDKIMError');
const successDiv = document.getElementById('createDKIMSuccess');
errorDiv.classList.add('d-none');
successDiv.classList.add('d-none');
if (!domain) {
errorDiv.textContent = 'Please select a domain.';
errorDiv.classList.remove('d-none');
return;
}
try {
const response = await fetch("{{ url_for('email.create_dkim') }}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ domain, selector })
// Add submit handler for DKIM toggle forms
document.querySelectorAll('form[action*="toggle_dkim"]').forEach(form => {
form.addEventListener('submit', async function(event) {
event.preventDefault();
event.stopPropagation();
const formData = new FormData(this);
const response = await fetch(this.action, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
if (response.ok) {
const result = await response.json();
if (result.success) {
// Get the domain card
const domainCard = this.closest('.card');
if (domainCard) {
// Update the data-is-active attribute
domainCard.dataset.isActive = result.is_active.toString();
// Update the badge
const badge = domainCard.querySelector('.badge');
if (badge) {
if (result.is_active) {
badge.className = 'badge bg-success ms-2';
badge.textContent = 'Active';
} else {
badge.className = 'badge bg-secondary ms-2';
badge.textContent = 'Inactive';
}
}
// Update the button
const button = this.querySelector('button');
if (button) {
if (result.is_active) {
button.className = 'btn btn-outline-warning';
button.innerHTML = '<i class="bi bi-pause-circle me-1"></i>Disable';
button.title = 'Disable DKIM';
} else {
button.className = 'btn btn-outline-success';
button.innerHTML = '<i class="bi bi-play-circle me-1"></i>Enable';
button.title = 'Enable DKIM';
}
}
// Update the status indicator
const dkimStatus = domainCard.querySelector('.dns-status');
if (dkimStatus) {
if (result.is_active) {
dkimStatus.innerHTML = '<span class="status-indicator status-warning"></span><small class="text-muted">Active (DNS not checked)</small>';
} else {
dkimStatus.innerHTML = '<span class="status-indicator" style="background-color: #6c757d;"></span><small class="text-muted">Disabled</small>';
}
}
// Show success message
showToast(result.message, 'success');
}
} else {
showToast(result.message, 'error');
}
} else {
showToast('Error toggling DKIM status', 'error');
}
});
const result = await response.json();
if (result.success) {
successDiv.textContent = result.message || 'DKIM key created.';
successDiv.classList.remove('d-none');
setTimeout(() => { window.location.reload(); }, 1200);
} else {
errorDiv.textContent = result.message || 'Failed to create DKIM key.';
errorDiv.classList.remove('d-none');
}
} catch (err) {
errorDiv.textContent = 'Error creating DKIM key.';
errorDiv.classList.remove('d-none');
}
});
});
</script>
{% endblock %}