156 lines
9.4 KiB
HTML
156 lines
9.4 KiB
HTML
{{define "title"}}Domains - Email Server Management{{end}}
|
|
{{define "page_title"}}Domain Management{{end}}
|
|
|
|
{{define "content"}}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2><i class="bi bi-globe me-2"></i>Domains</h2>
|
|
<a href="/pymta-manager/domains/add" class="btn btn-primary"><i class="bi bi-plus-circle me-2"></i>Add Domain</a>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header"><h5 class="mb-0"><i class="bi bi-list-ul me-2"></i>All Domains</h5></div>
|
|
<div class="card-body p-0">
|
|
{{if .rows}}
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-hover mb-0">
|
|
<thead><tr><th>Domain Name</th><th>Status</th><th>Ownership</th><th>Created</th><th>Senders</th><th>DKIM</th><th>Actions</th></tr></thead>
|
|
<tbody>
|
|
{{range .rows}}
|
|
{{$domain := .domain}}
|
|
<tr>
|
|
<td><div class="fw-bold">{{$domain.DomainName}}</div></td>
|
|
<td>
|
|
{{if $domain.IsActive}}<span class="badge bg-success"><i class="bi bi-check-circle me-1"></i>Active</span>
|
|
{{else}}<span class="badge bg-danger"><i class="bi bi-x-circle me-1"></i>Inactive</span>{{end}}
|
|
</td>
|
|
<td>
|
|
{{if $domain.IsVerified}}
|
|
<span class="badge bg-success" data-bs-toggle="tooltip" title="DNS ownership verified — this domain can send mail"><i class="bi bi-patch-check-fill me-1"></i>Verified</span>
|
|
{{else}}
|
|
<span class="badge bg-warning text-dark" data-bs-toggle="tooltip" title="Not yet verified — sending is blocked until the DNS TXT record is confirmed"><i class="bi bi-exclamation-triangle me-1"></i>Unverified</span>
|
|
<button type="button" class="btn btn-outline-warning btn-sm ms-1"
|
|
data-action="show-verify"
|
|
data-domain-id="{{$domain.ID}}"
|
|
data-domain-name="{{$domain.DomainName}}"
|
|
data-record-name="_pymta-verify.{{$domain.DomainName}}"
|
|
data-record-value="pymta-verify={{$domain.VerificationToken}}">
|
|
Verify
|
|
</button>
|
|
{{end}}
|
|
</td>
|
|
<td><small class="text-muted">{{strftime "%Y-%m-%d %H:%M" $domain.CreatedAt}}</small></td>
|
|
<td><span class="badge bg-info">{{.sender_count}} senders</span></td>
|
|
<td>
|
|
{{if .has_active_dkim}}
|
|
<span class="status-indicator status-warning"></span>
|
|
<i class="bi bi-shield-check" title="DKIM Active (DNS not checked)"></i>
|
|
{{else if .has_any_dkim}}
|
|
<span class="text-secondary"><i class="bi bi-shield" title="DKIM Disabled"></i></span>
|
|
{{else}}
|
|
<span class="text-danger"><i class="bi bi-shield-exclamation" title="No DKIM Key"></i></span>
|
|
{{end}}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
<a href="/pymta-manager/domains/{{$domain.ID}}/edit" class="btn btn-outline-primary" title="Edit Domain"><i class="bi bi-pencil"></i></a>
|
|
<form method="post" action="/pymta-manager/domains/{{$domain.ID}}/toggle" class="d-inline">
|
|
{{if $domain.IsActive}}
|
|
<button type="submit" class="btn btn-outline-warning" data-confirm="Are you sure you want to disable domain {{$domain.DomainName}}?" title="Disable Domain"><i class="bi bi-pause-circle"></i></button>
|
|
{{else}}
|
|
<button type="submit" class="btn btn-outline-success" data-confirm="Are you sure you want to enable domain {{$domain.DomainName}}?" title="Enable Domain"><i class="bi bi-play-circle"></i></button>
|
|
{{end}}
|
|
</form>
|
|
<form method="post" action="/pymta-manager/domains/{{$domain.ID}}/remove" class="d-inline">
|
|
<button type="submit" class="btn btn-outline-danger" data-confirm="WARNING: This will permanently delete domain {{$domain.DomainName}} and ALL associated data. This action cannot be undone. Continue?" title="Permanently Remove Domain"><i class="bi bi-trash"></i></button>
|
|
</form>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{else}}
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-globe text-muted" style="font-size: 4rem;"></i>
|
|
<h4 class="text-muted mt-3">No domains configured</h4>
|
|
<p class="text-muted">Get started by adding your first domain</p>
|
|
<a href="/pymta-manager/domains/add" class="btn btn-primary"><i class="bi bi-plus-circle me-2"></i>Add Your First Domain</a>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="verifyDomainModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="bi bi-patch-check me-2"></i>Verify domain ownership</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Add this DNS TXT record for <strong id="verifyDomainName"></strong>, then check:</p>
|
|
<div class="mb-2"><strong>Name:</strong><div class="dns-record" id="verifyRecordName" style="font-family: monospace; background: var(--bs-gray-100); color: #111; border-radius: 0.375rem; padding: 0.6rem; word-break: break-all;"></div></div>
|
|
<div class="mb-3"><strong>Value:</strong><div class="dns-record" id="verifyRecordValue" style="font-family: monospace; background: var(--bs-gray-100); color: #111; border-radius: 0.375rem; padding: 0.6rem; word-break: break-all;"></div></div>
|
|
<div id="verifyResult"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
<button type="button" class="btn btn-primary" id="verifyCheckNowBtn"><i class="bi bi-arrow-clockwise me-1"></i>Check Now</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.status-indicator { width: 8px; height: 8px; 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>
|
|
{{end}}
|
|
|
|
{{define "extra_js"}}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
let currentDomainID = null;
|
|
const modalEl = document.getElementById('verifyDomainModal');
|
|
const modal = new bootstrap.Modal(modalEl);
|
|
|
|
document.querySelectorAll('[data-action="show-verify"]').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
currentDomainID = this.dataset.domainId;
|
|
document.getElementById('verifyDomainName').textContent = this.dataset.domainName;
|
|
document.getElementById('verifyRecordName').textContent = this.dataset.recordName;
|
|
document.getElementById('verifyRecordValue').textContent = this.dataset.recordValue;
|
|
document.getElementById('verifyResult').innerHTML = '';
|
|
modal.show();
|
|
});
|
|
});
|
|
|
|
document.getElementById('verifyCheckNowBtn').addEventListener('click', async function() {
|
|
if (!currentDomainID) return;
|
|
const btn = this;
|
|
const original = btn.innerHTML;
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Checking...';
|
|
try {
|
|
const response = await fetch(`/pymta-manager/domains/${currentDomainID}/verify_check`, { method: 'POST' });
|
|
const result = await response.json();
|
|
const resultEl = document.getElementById('verifyResult');
|
|
resultEl.innerHTML = `<div class="alert ${result.success ? 'alert-success' : 'alert-warning'} mb-0 mt-2">${result.message}</div>`;
|
|
if (result.success) {
|
|
showToast(result.message, 'success');
|
|
setTimeout(() => location.reload(), 1200);
|
|
}
|
|
} catch (e) {
|
|
showToast('DNS check failed', 'danger');
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.innerHTML = original;
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{{end}}
|