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.375 rem ;
padding : 0.75 rem ;
border : 1 px solid var ( - - bs - border - color ) ;
word-break : break-all ;
}
. status-indicator {
width : 12 px ;
height : 12 px ;
border-radius : 50 % ;
display : inline-block ;
margin-right : 0.5 rem ;
}
. 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 %}