2025-06-07 10:48:03 +01:00
{% extends "base.html" %}
{% block title %}DKIM Keys - Email Server{% endblock %}
{% block extra_css %}
< style >
.dns-record {
font-family: 'Courier New', monospace;
2025-06-07 11:57:21 +01:00
color: black;
2025-06-07 10:48:03 +01:00
background-color: var(--bs-gray-100);
border-radius: 0.375rem;
padding: 0.75rem;
border: 1px solid var(--bs-border-color);
word-break: break-all;
}
.status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 0.5rem;
}
.status-success { background-color: #28a745; }
.status-warning { background-color: #ffc107; }
.status-danger { background-color: #dc3545; }
< / style >
{% endblock %}
{% block content %}
< div class = "container-fluid" >
< div class = "d-flex justify-content-between align-items-center mb-4" >
< h2 >
< i class = "bi bi-shield-check me-2" > < / i >
DKIM Key Management
< / h2 >
< div class = "btn-group" >
2025-06-07 14:43:00 +01:00
< button class = "btn btn-outline-primary me-2" data-bs-toggle = "modal" data-bs-target = "#createDKIMModal" >
< i class = "bi bi-plus-circle me-2" > < / i >
Create DKIM
< / button >
2025-06-08 22:51:07 +01:00
< button class = "btn btn-outline-info" data-action = "check-all-dns" >
2025-06-07 10:48:03 +01:00
< i class = "bi bi-arrow-clockwise me-2" > < / i >
Check All DNS
< / button >
< / div >
< / div >
2025-06-07 14:43:00 +01:00
<!-- Create DKIM Modal -->
< div class = "modal fade" id = "createDKIMModal" tabindex = "-1" aria-labelledby = "createDKIMModalLabel" aria-hidden = "true" >
< div class = "modal-dialog" >
< div class = "modal-content" >
< form id = "createDKIMForm" >
< div class = "modal-header" >
< h5 class = "modal-title" id = "createDKIMModalLabel" > Create New DKIM Key< / h5 >
< button type = "button" class = "btn-close" data-bs-dismiss = "modal" aria-label = "Close" > < / button >
< / div >
< div class = "modal-body" >
< div class = "mb-3" >
< label for = "dkimDomain" class = "form-label" > Domain< / label >
< select class = "form-select" id = "dkimDomain" name = "domain" required >
< option value = "" disabled selected > Select domain< / option >
{% for item in dkim_data %}
< option value = "{{ item.domain.domain_name }}" > {{ item.domain.domain_name }}< / option >
{% endfor %}
< / select >
< / div >
< div class = "mb-3" >
< label for = "dkimSelector" class = "form-label" > Selector (optional)< / label >
< input type = "text" class = "form-control" id = "dkimSelector" name = "selector" maxlength = "32" placeholder = "Leave blank for random selector" >
< / div >
< div id = "createDKIMError" class = "alert alert-danger d-none" > < / div >
< div id = "createDKIMSuccess" class = "alert alert-success d-none" > < / div >
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-secondary" data-bs-dismiss = "modal" > Cancel< / button >
< button type = "submit" class = "btn btn-primary" > Create< / button >
< / div >
< / form >
< / div >
< / div >
< / div >
2025-06-07 10:48:03 +01:00
{% for item in dkim_data %}
2025-06-08 22:51:07 +01:00
< div class = "card mb-4" id = "domain-{{ item.domain.domain_name.replace('.', '-') }}" data-is-active = "{{ item.dkim_key.is_active|tojson }}" >
2025-06-07 14:43:00 +01:00
< div class = "card-header" >
2025-06-07 10:48:03 +01:00
< div class = "d-flex justify-content-between align-items-center" >
2025-06-08 22:51:07 +01:00
< 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('.', '-') }}" >
2025-06-07 14:43:00 +01:00
< h5 class = "mb-0" >
< i class = "bi bi-server me-2" > < / i >
{{ item.domain.domain_name }}
{% if item.dkim_key.is_active %}
< span class = "badge bg-success ms-2" > Active< / span >
{% else %}
< span class = "badge bg-secondary ms-2" > Inactive< / span >
{% endif %}
< / h5 >
< / div >
< div class = "btn-group btn-group-sm me-2" >
2025-06-08 22:51:07 +01:00
< 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();">
2025-06-07 10:48:03 +01:00
< i class = "bi bi-search me-1" > < / i >
Check DNS
< / button >
2025-06-07 11:57:21 +01:00
< div class = "btn-group btn-group-sm" role = "group" >
< a href = "{{ url_for('email.edit_dkim', dkim_id=item.dkim_key.id) }}"
2025-06-07 14:43:00 +01:00
class="btn btn-outline-info"
onclick="event.stopPropagation();">
2025-06-07 11:57:21 +01:00
< i class = "bi bi-pencil me-1" > < / i >
Edit
< / 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 %}
2025-06-08 22:51:07 +01:00
< button type = "submit" class = "btn btn-outline-warning" onclick = "event.stopPropagation();" title = "Disable DKIM" >
2025-06-07 11:57:21 +01:00
< i class = "bi bi-pause-circle me-1" > < / i >
Disable
< / button >
{% else %}
2025-06-08 22:51:07 +01:00
< button type = "submit" class = "btn btn-outline-success" onclick = "event.stopPropagation();" title = "Enable DKIM" >
2025-06-07 11:57:21 +01:00
< i class = "bi bi-play-circle me-1" > < / i >
Enable
< / button >
{% endif %}
< / form >
2025-06-07 14:43:00 +01:00
< form method = "post" action = "{{ url_for('email.remove_dkim', dkim_id=item.dkim_key.id) }}" class = "d-inline" onsubmit = "return handleFormSubmit(event, 'Are you sure you want to permanently remove the DKIM key for {{ item.domain.domain_name }}? This action cannot be undone and you will lose the ability to sign emails until you regenerate a new key.')" >
2025-06-07 11:57:21 +01:00
< button type = "submit"
class="btn btn-outline-danger"
2025-06-07 14:43:00 +01:00
onclick="event.stopPropagation();">
2025-06-07 11:57:21 +01:00
< i class = "bi bi-trash me-1" > < / i >
Remove
< / button >
< / form >
< / div >
2025-06-07 14:43:00 +01:00
< button class = "btn btn-outline-warning"
onclick="event.stopPropagation(); regenerateDKIM({{ item.domain.id }}, '{{ item.domain.domain_name }}')">
< i class = "bi bi-arrow-clockwise me-1" > < / i >
Regenerate
< / button >
2025-06-07 10:48:03 +01:00
< / div >
2025-06-08 22:51:07 +01:00
< 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 >
2025-06-07 11:57:21 +01:00
< / div >
2025-06-07 10:48:03 +01:00
< / div >
< / div >
2025-06-08 22:51:07 +01:00
< div class = "collapse" id = "collapse-{{ item.domain.domain_name.replace('.', '-') }}" >
2025-06-07 11:57:21 +01:00
< div class = "card-body" >
2025-06-07 10:48:03 +01:00
< div class = "row" >
<!-- DKIM DNS Record -->
< div class = "col-lg-6 mb-3" >
< h6 >
< i class = "bi bi-key me-2" > < / i >
DKIM DNS Record
2025-06-07 14:43:00 +01:00
< span class = "dns-status" id = "dkim-status-{{ item.domain.domain_name.replace('.', '-') }}" >
2025-06-07 10:48:03 +01:00
< span class = "status-indicator status-warning" > < / span >
2025-06-08 22:51:07 +01:00
< small class = "text-muted" > Active (DNS not checked)< / small >
2025-06-07 10:48:03 +01:00
< / span >
< / h6 >
< div class = "mb-2" >
< strong > Name:< / strong >
< div class = "dns-record" > {{ item.dns_record.name }}< / div >
< / div >
< div class = "mb-2" >
< strong > Type:< / strong > TXT
< / div >
< div class = "mb-2" >
< strong > Value:< / strong >
< div class = "dns-record" > {{ item.dns_record.value }}< / div >
< / div >
< button class = "btn btn-outline-secondary btn-sm" onclick = "copyToClipboard('{{ item.dns_record.value }}')" >
< i class = "bi bi-clipboard me-1" > < / i >
Copy Value
< / button >
< / div >
<!-- SPF DNS Record -->
< div class = "col-lg-6 mb-3" >
< h6 >
< i class = "bi bi-shield-lock me-2" > < / i >
SPF DNS Record
2025-06-07 14:43:00 +01:00
< span class = "dns-status" id = "spf-status-{{ item.domain.domain_name.replace('.', '-') }}" >
2025-06-07 10:48:03 +01:00
< span class = "status-indicator status-warning" > < / span >
< small class = "text-muted" > Not checked< / small >
< / span >
< / h6 >
< div class = "mb-2" >
< strong > Name:< / strong >
< div class = "dns-record" > {{ item.domain.domain_name }}< / div >
< / div >
< div class = "mb-2" >
< strong > Type:< / strong > TXT
< / div >
{% if item.existing_spf %}
< div class = "mb-2" >
< strong > Current SPF:< / strong >
2025-06-08 11:13:43 +01:00
< div class = "dns-record" > {{ item.existing_spf }}< / div >
2025-06-07 10:48:03 +01:00
< / div >
{% endif %}
< div class = "mb-2" >
< strong > Recommended SPF:< / strong >
2025-06-08 11:13:43 +01:00
< div class = "dns-record" > {{ item.recommended_spf }}< / div >
2025-06-07 10:48:03 +01:00
< / div >
< button class = "btn btn-outline-secondary btn-sm" onclick = "copyToClipboard('{{ item.recommended_spf }}')" >
< i class = "bi bi-clipboard me-1" > < / i >
Copy SPF
< / button >
< / div >
< / div >
<!-- Key Information -->
< div class = "row" >
< div class = "col-12" >
< h6 > < i class = "bi bi-info-circle me-2" > < / i > Key Information< / h6 >
< div class = "row" >
< div class = "col-md-3" >
< strong > Selector:< / strong > < br >
< code > {{ item.dkim_key.selector }}< / code >
< / div >
< div class = "col-md-3" >
< strong > Created:< / strong > < br >
{{ item.dkim_key.created_at.strftime('%Y-%m-%d %H:%M') }}
< / div >
< div class = "col-md-3" >
< strong > Server IP:< / strong > < br >
< code > {{ item.public_ip }}< / code >
< / div >
< div class = "col-md-3" >
< strong > Status:< / strong > < br >
{% if item.dkim_key.is_active %}
< span class = "text-success" > Active< / span >
{% else %}
< span class = "text-secondary" > Inactive< / span >
{% endif %}
< / div >
< / div >
< / div >
< / div >
2025-06-07 11:57:21 +01:00
< / div >
2025-06-07 10:48:03 +01:00
< / div >
< / div >
{% endfor %}
2025-06-07 14:43:00 +01:00
<!-- Old DKIM Keys Section -->
{% if old_dkim_data %}
< div class = "card mb-4" >
< div class = "card-header" >
< h4 class = "mb-0" >
< i class = "bi bi-archive me-2" > < / i >
Old DKIM Keys
< span class = "badge bg-secondary ms-2" > {{ old_dkim_data|length }}< / span >
< / h4 >
< / div >
< div class = "card-body" >
< p class = "text-muted mb-3" > These keys have been replaced or disabled. They are kept for reference and can be permanently removed.< / p >
{% for item in old_dkim_data %}
< div class = "card mb-3 border-secondary" >
< div class = "card-header bg-dark" >
< div class = "d-flex justify-content-between align-items-center" >
< div >
< h6 class = "mb-0" >
< i class = "bi bi-server me-2" > < / i >
{{ item.domain.domain_name }}
< span class = "badge bg-secondary ms-2" > {{ item.status_text }}< / span >
< / h6 >
< small class = "text-muted" >
Selector: < code > {{ item.dkim_key.selector }}< / code > |
Created: {{ item.dkim_key.created_at.strftime('%Y-%m-%d %H:%M') }}
{% if item.dkim_key.replaced_at %}
| Replaced: {{ item.dkim_key.replaced_at.strftime('%Y-%m-%d %H:%M') }}
{% endif %}
< / small >
< / div >
< div class = "btn-group btn-group-sm" >
< form method = "post" action = "{{ url_for('email.toggle_dkim', dkim_id=item.dkim_key.id) }}" class = "d-inline" >
< button type = "submit" class = "btn btn-outline-success btn-sm" >
< i class = "bi bi-play-circle me-1" > < / i >
Reactivate
< / button >
< / form >
< form method = "post" action = "{{ url_for('email.remove_dkim', dkim_id=item.dkim_key.id) }}" class = "d-inline" onsubmit = "return handleFormSubmit(event, 'Are you sure you want to permanently remove this old DKIM key? This action cannot be undone.')" >
< button type = "submit"
class="btn btn-outline-danger btn-sm">
< i class = "bi bi-trash me-1" > < / i >
Remove
< / button >
< / form >
< / div >
< / div >
< / div >
< / div >
{% endfor %}
< / div >
< / div >
{% endif %}
2025-06-07 10:48:03 +01:00
{% if not dkim_data %}
< div class = "card" >
< div class = "card-body text-center py-5" >
< i class = "bi bi-shield-x text-muted" style = "font-size: 4rem;" > < / i >
< h4 class = "text-muted mt-3" > No DKIM Keys Found< / h4 >
< p class = "text-muted" > Add domains first to automatically generate DKIM keys< / p >
< a href = "{{ url_for('email.add_domain') }}" class = "btn btn-primary" >
< i class = "bi bi-plus-circle me-2" > < / i >
Add Domain
< / a >
< / div >
< / div >
{% endif %}
< / div >
<!-- DNS Check Results Modal -->
< div class = "modal fade" id = "dnsResultModal" tabindex = "-1" >
< div class = "modal-dialog modal-lg" >
< div class = "modal-content" >
< div class = "modal-header" >
< h5 class = "modal-title" > DNS Check Results< / h5 >
< button type = "button" class = "btn-close" data-bs-dismiss = "modal" > < / button >
< / div >
< div class = "modal-body" id = "dnsResults" >
<!-- Results will be populated here -->
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-secondary" data-bs-dismiss = "modal" > Close< / button >
< / div >
< / div >
< / div >
< / div >
{% endblock %}
{% block extra_js %}
< script >
2025-06-10 01:50:35 +01:00
// Clipboard copy function for all copy buttons
function copyToClipboard(text) {
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function() {
showToast('Copied to clipboard!', 'success');
}, function(err) {
showToast('Failed to copy: ' + err, 'danger');
});
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
showToast('Copied to clipboard!', 'success');
} catch (err) {
showToast('Failed to copy: ' + err, 'danger');
}
document.body.removeChild(textarea);
}
}
2025-06-08 22:51:07 +01:00
// 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
2025-06-07 10:48:03 +01:00
async function checkDomainDNS(domain, selector) {
const dkimStatus = document.getElementById(`dkim-status-${domain.replace('.', '-')}`);
const spfStatus = document.getElementById(`spf-status-${domain.replace('.', '-')}`);
// Show loading state
dkimStatus.innerHTML = '< span class = "status-indicator status-warning" > < / span > < small class = "text-muted" > Checking...< / small > ';
spfStatus.innerHTML = '< span class = "status-indicator status-warning" > < / span > < small class = "text-muted" > Checking...< / small > ';
try {
// Check DKIM DNS
2025-06-08 22:51:07 +01:00
const dkimResponse = await fetch("{{ url_for('email.check_dkim_dns') }}", {
2025-06-07 10:48:03 +01:00
method: 'POST',
headers: {
2025-06-08 22:51:07 +01:00
'Content-Type': 'application/x-www-form-urlencoded'
2025-06-07 10:48:03 +01:00
},
2025-06-08 22:51:07 +01:00
body: new URLSearchParams({
domain: domain,
selector: selector
})
2025-06-07 10:48:03 +01:00
});
const dkimResult = await dkimResponse.json();
// Check SPF DNS
2025-06-08 22:51:07 +01:00
const spfResponse = await fetch("{{ url_for('email.check_spf_dns') }}", {
2025-06-07 10:48:03 +01:00
method: 'POST',
headers: {
2025-06-08 22:51:07 +01:00
'Content-Type': 'application/x-www-form-urlencoded'
2025-06-07 10:48:03 +01:00
},
2025-06-08 22:51:07 +01:00
body: new URLSearchParams({
domain: domain
})
2025-06-07 10:48:03 +01:00
});
const spfResult = await spfResponse.json();
2025-06-08 22:51:07 +01:00
// 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 > ';
}
2025-06-07 10:48:03 +01:00
} else {
2025-06-08 22:51:07 +01:00
dkimStatus.innerHTML = '< span class = "status-indicator" style = "background-color: #6c757d;" > < / span > < small class = "text-muted" > Disabled< / small > ';
2025-06-07 10:48:03 +01:00
}
// Update SPF status
if (spfResult.success) {
spfStatus.innerHTML = '< span class = "status-indicator status-success" > < / span > < small class = "text-success" > ✓ Found< / small > ';
2025-06-10 01:50:35 +01:00
// Show additional SPF check message if available
if (typeof spfResult.spf_valid_for_server !== 'undefined') {
if (spfResult.spf_valid_for_server) {
spfStatus.innerHTML += '< br > < span class = "text-success" > < i class = "bi bi-check-circle me-1" > < / i > SPF is valid for this server< / span > ';
} else {
spfStatus.innerHTML += '< br > < span class = "text-warning" > < i class = "bi bi-exclamation-triangle me-1" > < / i > SPF missing server IP< / span > ';
}
}
2025-06-07 10:48:03 +01:00
} else {
spfStatus.innerHTML = '< span class = "status-indicator status-danger" > < / span > < small class = "text-danger" > ✗ Not found< / small > ';
}
// Show detailed results in modal
showDNSResults(domain, dkimResult, spfResult);
} catch (error) {
console.error('DNS check error:', error);
dkimStatus.innerHTML = '< span class = "status-indicator status-danger" > < / span > < small class = "text-danger" > Error< / small > ';
spfStatus.innerHTML = '< span class = "status-indicator status-danger" > < / span > < small class = "text-danger" > Error< / small > ';
}
}
2025-06-08 22:51:07 +01:00
// Show DNS check results in modal
2025-06-07 10:48:03 +01:00
function showDNSResults(domain, dkimResult, spfResult) {
2025-06-08 22:51:07 +01:00
// 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 > ` : '';
2025-06-07 10:48:03 +01:00
const resultsHtml = `
< h6 > DNS Check Results for ${domain}< / h6 >
< div class = "mb-3" >
< h6 class = "text-primary" > DKIM Record< / h6 >
< div class = "alert ${dkimResult.success ? 'alert-success' : 'alert-danger'}" >
< strong > Status:< / strong > ${dkimResult.success ? 'Found' : 'Not Found'}< br >
< strong > Message:< / strong > ${dkimResult.message}
2025-06-08 22:51:07 +01:00
${dkimResult.records ? `
< br > < strong > Records:< / strong >
< div class = "records-container mt-2" >
${dkimRecordsHtml}
< / div >
` : ''}
2025-06-07 10:48:03 +01:00
< / div >
< / div >
< div class = "mb-3" >
< h6 class = "text-primary" > SPF Record< / h6 >
< div class = "alert ${spfResult.success ? 'alert-success' : 'alert-danger'}" >
< strong > Status:< / strong > ${spfResult.success ? 'Found' : 'Not Found'}< br >
< strong > Message:< / strong > ${spfResult.message}
2025-06-08 22:51:07 +01:00
${spfResult.spf_record ? `
< br > < strong > Current SPF:< / strong >
${spfRecordHtml}
` : ''}
2025-06-07 14:43:00 +01:00
< / div >
< / div >
`;
document.getElementById('dnsResults').innerHTML = resultsHtml;
new bootstrap.Modal(document.getElementById('dnsResultModal')).show();
}
2025-06-08 22:51:07 +01:00
// Check all domains' DNS records
2025-06-07 10:48:03 +01:00
async function checkAllDNS() {
const domains = document.querySelectorAll('[id^="domain-"]');
2025-06-07 14:43:00 +01:00
const results = [];
// Show a progress indicator
showToast('Checking DNS records for all domains...', 'info');
2025-06-07 10:48:03 +01:00
for (const domainCard of domains) {
2025-06-07 14:43:00 +01:00
try {
const domainId = domainCard.id.split('-')[1];
// Extract domain name from the card header
const domainHeaderText = domainCard.querySelector('h5').textContent.trim();
const domainName = domainHeaderText.split('\n')[0].trim().replace(/^\s*\S+\s+/, ''); // Remove icon
const selectorElement = domainCard.querySelector('code');
if (selectorElement) {
const selector = selectorElement.textContent;
// Check DKIM DNS
const dkimResponse = await fetch('{{ url_for("email.check_dkim_dns") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `domain=${encodeURIComponent(domainName)}& selector=${encodeURIComponent(selector)}`
});
const dkimResult = await dkimResponse.json();
// Check SPF DNS
const spfResponse = await fetch('{{ url_for("email.check_spf_dns") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `domain=${encodeURIComponent(domainName)}`
});
const spfResult = await spfResponse.json();
results.push({
domain: domainName,
dkim: dkimResult,
spf: spfResult
});
// Update individual status indicators
const dkimStatus = document.getElementById(`dkim-status-${domainName.replace('.', '-')}`);
const spfStatus = document.getElementById(`spf-status-${domainName.replace('.', '-')}`);
if (dkimStatus) {
if (dkimResult.success) {
dkimStatus.innerHTML = '< span class = "status-indicator status-success" > < / span > < small class = "text-success" > ✓ Configured< / small > ';
} else {
dkimStatus.innerHTML = '< span class = "status-indicator status-danger" > < / span > < small class = "text-danger" > ✗ Not found< / small > ';
}
}
if (spfStatus) {
if (spfResult.success) {
spfStatus.innerHTML = '< span class = "status-indicator status-success" > < / span > < small class = "text-success" > ✓ Found< / small > ';
} else {
spfStatus.innerHTML = '< span class = "status-indicator status-danger" > < / span > < small class = "text-danger" > ✗ Not found< / small > ';
}
}
// Small delay between checks to avoid overwhelming the DNS server
await new Promise(resolve => setTimeout(resolve, 300));
}
} catch (error) {
console.error('Error checking DNS for domain:', error);
}
}
// Show combined results in modal
showAllDNSResults(results);
}
2025-06-08 22:51:07 +01:00
// Show combined DNS check results
function showAllDNSResults(results) {
let tableRows = '';
2025-06-07 14:43:00 +01:00
2025-06-08 22:51:07 +01:00
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 > ';
2025-06-07 10:48:03 +01:00
2025-06-08 22:51:07 +01:00
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 >
`;
2025-06-07 10:48:03 +01:00
});
2025-06-07 14:43:00 +01:00
2025-06-08 22:51:07 +01:00
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 >
`;
2025-06-07 14:43:00 +01:00
2025-06-08 22:51:07 +01:00
document.getElementById('dnsResults').innerHTML = resultsHtml;
new bootstrap.Modal(document.getElementById('dnsResultModal')).show();
2025-06-07 14:43:00 +01:00
}
2025-06-08 22:51:07 +01:00
// Initialize event handlers
2025-06-07 11:57:21 +01:00
document.addEventListener('DOMContentLoaded', function() {
2025-06-08 22:51:07 +01:00
// 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();
});
}
2025-06-07 14:43:00 +01:00
// Add click handlers for card headers - only for clickable areas
document.querySelectorAll('.card-header-clickable[data-bs-toggle="collapse"]').forEach(function(element) {
2025-06-07 11:57:21 +01:00
element.addEventListener('click', function() {
const targetId = this.getAttribute('data-bs-target');
const chevronId = targetId.replace('#collapse-', '#chevron-');
const chevron = document.querySelector(chevronId);
// Toggle chevron direction
if (chevron) {
setTimeout(() => {
const collapseElement = document.querySelector(targetId);
if (collapseElement & & collapseElement.classList.contains('show')) {
chevron.className = 'bi bi-chevron-up';
} else {
chevron.className = 'bi bi-chevron-down';
}
}, 100);
}
});
});
2025-06-07 14:43:00 +01:00
2025-06-08 22:51:07 +01:00
// 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');
}
2025-06-07 14:43:00 +01:00
});
2025-06-08 22:51:07 +01:00
});
2025-06-07 14:43:00 +01:00
});
2025-06-07 10:48:03 +01:00
< / script >
{% endblock %}