162 lines
6.3 KiB
HTML
162 lines
6.3 KiB
HTML
|
|
{% extends "base.html" %}
|
||
|
|
{% block title %}SMTP{% endblock %}
|
||
|
|
{% block content %}
|
||
|
|
<h1>[*] SMTP / Mail</h1>
|
||
|
|
|
||
|
|
<div class="toolbar">
|
||
|
|
<button class="btn" onclick="loadSmtpStatus()">Status</button>
|
||
|
|
<button class="btn" onclick="checkDNS()">Check DNS</button>
|
||
|
|
<button class="btn" onclick="flushQueue()">Flush Queue</button>
|
||
|
|
<button class="btn btn-warn" onclick="restartSmtp()">Restart Postfix</button>
|
||
|
|
<button class="btn" onclick="showTab('status')">Status</button>
|
||
|
|
<button class="btn" onclick="showTab('send')">Send Email</button>
|
||
|
|
<button class="btn" onclick="showTab('mass')">Mass Email (BCC)</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- TAB: Status -->
|
||
|
|
<div id="tab-status">
|
||
|
|
<div class="grid grid-2">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-title">SMTP Status</div>
|
||
|
|
<div class="output" id="smtp-output"><span class="info">Loading...</span></div>
|
||
|
|
</div>
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-title">DNS Records (SPF/DKIM/DMARC)</div>
|
||
|
|
<div class="output" id="dns-output"><span class="info">Click "Check DNS"</span></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-title">Quick Test</div>
|
||
|
|
<input type="email" id="test-email" placeholder="recipient@example.com" style="width:300px">
|
||
|
|
<button class="btn" onclick="sendTest()">Send Test</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- TAB: Send Email -->
|
||
|
|
<div id="tab-send" style="display:none">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-title">Compose Email</div>
|
||
|
|
<label>From</label>
|
||
|
|
<input type="text" id="send-from" value="noreply@seteclabs.io" style="width:100%">
|
||
|
|
<label>To</label>
|
||
|
|
<input type="text" id="send-to" placeholder="recipient@example.com" style="width:100%">
|
||
|
|
<label>Subject</label>
|
||
|
|
<input type="text" id="send-subject" placeholder="Email subject" style="width:100%">
|
||
|
|
<label>Body</label>
|
||
|
|
<textarea id="send-body" rows="10" style="width:100%" placeholder="Type your message here..."></textarea>
|
||
|
|
<br><br>
|
||
|
|
<button class="btn" onclick="sendEmail()">Send Email</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- TAB: Mass Email (BCC) -->
|
||
|
|
<div id="tab-mass" style="display:none">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-title">Mass Email (BCC Mode)</div>
|
||
|
|
<p style="color:#888;font-size:12px;margin-bottom:10px">
|
||
|
|
Each recipient gets their own individual email. No one can see other recipients' addresses.
|
||
|
|
</p>
|
||
|
|
<label>From</label>
|
||
|
|
<input type="text" id="mass-from" value="noreply@seteclabs.io" style="width:100%">
|
||
|
|
<label>Recipients (comma or newline separated)</label>
|
||
|
|
<textarea id="mass-to" rows="5" style="width:100%" placeholder="joe@example.com, sam@example.com, bob@example.com
|
||
|
|
or one per line:
|
||
|
|
joe@example.com
|
||
|
|
sam@example.com
|
||
|
|
bob@example.com"></textarea>
|
||
|
|
<label>Subject</label>
|
||
|
|
<input type="text" id="mass-subject" placeholder="Email subject" style="width:100%">
|
||
|
|
<label>Body</label>
|
||
|
|
<textarea id="mass-body" rows="10" style="width:100%" placeholder="Type your message here..."></textarea>
|
||
|
|
<br><br>
|
||
|
|
<div class="toolbar">
|
||
|
|
<button class="btn" onclick="previewMass()">Preview (count recipients)</button>
|
||
|
|
<button class="btn btn-warn" onclick="sendMass()">Send to All</button>
|
||
|
|
</div>
|
||
|
|
<div id="mass-preview" style="margin-top:10px;font-size:12px;color:#888"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-title">Output</div>
|
||
|
|
<div class="output" id="output"><span class="info">Ready.</span></div>
|
||
|
|
</div>
|
||
|
|
{% endblock %}
|
||
|
|
|
||
|
|
{% block scripts %}
|
||
|
|
<script>
|
||
|
|
function showTab(tab) {
|
||
|
|
document.getElementById('tab-status').style.display = tab === 'status' ? '' : 'none';
|
||
|
|
document.getElementById('tab-send').style.display = tab === 'send' ? '' : 'none';
|
||
|
|
document.getElementById('tab-mass').style.display = tab === 'mass' ? '' : 'none';
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Status ──
|
||
|
|
async function loadSmtpStatus() {
|
||
|
|
const res = await apiGet('/api/smtp/status');
|
||
|
|
showResult(res, 'smtp-output');
|
||
|
|
}
|
||
|
|
async function checkDNS() {
|
||
|
|
const res = await apiGet('/api/smtp/dns-check');
|
||
|
|
showResult(res, 'dns-output');
|
||
|
|
}
|
||
|
|
async function flushQueue() {
|
||
|
|
const res = await apiPost('/api/smtp/flush');
|
||
|
|
showResult(res);
|
||
|
|
}
|
||
|
|
async function restartSmtp() {
|
||
|
|
const res = await apiPost('/api/smtp/restart');
|
||
|
|
showResult(res);
|
||
|
|
}
|
||
|
|
async function sendTest() {
|
||
|
|
const to = document.getElementById('test-email').value;
|
||
|
|
if (!to) { alert('Enter email address'); return; }
|
||
|
|
const res = await apiPost('/api/smtp/send-test', {to});
|
||
|
|
showResult(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Send Email ──
|
||
|
|
async function sendEmail() {
|
||
|
|
const from = document.getElementById('send-from').value.trim();
|
||
|
|
const to = document.getElementById('send-to').value.trim();
|
||
|
|
const subject = document.getElementById('send-subject').value.trim();
|
||
|
|
const body = document.getElementById('send-body').value;
|
||
|
|
if (!to || !subject) { alert('Fill in To and Subject'); return; }
|
||
|
|
const res = await apiPost('/api/smtp/send', {from, to, subject, body});
|
||
|
|
showResult(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Mass Email ──
|
||
|
|
function parseRecipients() {
|
||
|
|
const raw = document.getElementById('mass-to').value;
|
||
|
|
return raw.split(/[,\n]+/).map(e => e.trim()).filter(e => e && e.includes('@'));
|
||
|
|
}
|
||
|
|
|
||
|
|
function previewMass() {
|
||
|
|
const recipients = parseRecipients();
|
||
|
|
const el = document.getElementById('mass-preview');
|
||
|
|
el.innerHTML = `<span style="color:#00ff41">${recipients.length} recipients found:</span><br>` +
|
||
|
|
recipients.map(r => ` - ${escHtml(r)}`).join('<br>');
|
||
|
|
}
|
||
|
|
|
||
|
|
async function sendMass() {
|
||
|
|
const recipients = parseRecipients();
|
||
|
|
if (!recipients.length) { alert('Enter at least one recipient'); return; }
|
||
|
|
const from = document.getElementById('mass-from').value.trim();
|
||
|
|
const subject = document.getElementById('mass-subject').value.trim();
|
||
|
|
const body = document.getElementById('mass-body').value;
|
||
|
|
if (!subject) { alert('Enter a subject'); return; }
|
||
|
|
if (!confirm(`Send email to ${recipients.length} recipients individually (BCC mode)?`)) return;
|
||
|
|
|
||
|
|
const out = document.getElementById('output');
|
||
|
|
out.innerHTML = `<span class="info">Sending to ${recipients.length} recipients...</span>\n`;
|
||
|
|
|
||
|
|
const res = await apiPost('/api/smtp/send-mass', {from, recipients, subject, body});
|
||
|
|
showResult(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
loadSmtpStatus();
|
||
|
|
</script>
|
||
|
|
{% endblock %}
|