Files
mailgoserver/internal/webui/templates/domains.html
T
2026-08-12 12:56:22 +01:00

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}}