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-07 10:48:03 +01:00
< button class = "btn btn-outline-info" onclick = "checkAllDNS()" >
< 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 %}
< div class = "card mb-4" id = "domain-{{ item.domain.id }}" >
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-07 14:43:00 +01:00
< 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 }}" >
< 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" >
< button class = "btn btn-outline-primary" onclick = "event.stopPropagation(); checkDomainDNS('{{ item.domain.domain_name }}', '{{ item.dkim_key.selector }}')" >
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-07 14:43:00 +01:00
< button type = "submit" class = "btn btn-outline-warning" onclick = "event.stopPropagation();" >
2025-06-07 11:57:21 +01:00
< i class = "bi bi-pause-circle me-1" > < / i >
Disable
< / button >
{% else %}
2025-06-07 14:43:00 +01:00
< button type = "submit" class = "btn btn-outline-success" onclick = "event.stopPropagation();" >
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-07 14:43:00 +01:00
< 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 }}" >
2025-06-07 11:57:21 +01:00
< i class = "bi bi-chevron-down" id = "chevron-{{ item.domain.id }}" > < / i >
< / div >
2025-06-07 10:48:03 +01:00
< / div >
< / div >
2025-06-07 11:57:21 +01:00
< div class = "collapse" id = "collapse-{{ item.domain.id }}" >
< 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 >
< small class = "text-muted" > Not checked< / small >
< / 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 >
< div class = "dns-record text-info" > {{ item.existing_spf }}< / div >
< / div >
{% endif %}
< div class = "mb-2" >
< strong > Recommended SPF:< / strong >
< div class = "dns-record text-success" > {{ item.recommended_spf }}< / div >
< / 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 >
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
const dkimResponse = await fetch('{{ url_for("email.check_dkim_dns") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `domain=${encodeURIComponent(domain)}& 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(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 > ';
} else {
dkimStatus.innerHTML = '< span class = "status-indicator status-danger" > < / span > < small class = "text-danger" > ✗ Not found< / small > ';
}
// Update SPF status
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 > ';
}
// 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 > ';
}
}
function showDNSResults(domain, dkimResult, spfResult) {
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}
${dkimResult.records ? `< br > < strong > Records:< / strong > ${dkimResult.records.join(', ')}` : ''}
< / 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}
${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();
}
2025-06-07 14:43:00 +01:00
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 >
< / div >
< / div >
`;
document.getElementById('dnsResults').innerHTML = resultsHtml;
new bootstrap.Modal(document.getElementById('dnsResultModal')).show();
}
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);
}
// 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'
);
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({})
});
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;
2025-06-07 10:48:03 +01:00
}
2025-06-07 14:43:00 +01:00
} catch (error) {
console.error('DKIM regeneration error:', error);
showToast('Error regenerating DKIM key', 'danger');
// Restore button on error
button.innerHTML = originalContent;
button.disabled = false;
2025-06-07 10:48:03 +01:00
}
}
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);
});
}
2025-06-07 11:57:21 +01:00
2025-06-07 14:43:00 +01:00
// 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
}
2025-06-07 11:57:21 +01:00
// Handle collapsible cards
document.addEventListener('DOMContentLoaded', function() {
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
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 })
});
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');
}
});
2025-06-07 10:48:03 +01:00
< / script >
{% endblock %}