sdfsd
This commit is contained in:
@@ -59,7 +59,25 @@ const publicDir = config.publicDir.startsWith('/')
|
|||||||
|
|
||||||
console.log(`Serving frontend from: ${publicDir}`);
|
console.log(`Serving frontend from: ${publicDir}`);
|
||||||
|
|
||||||
app.use(express.static(publicDir));
|
// Avoid stale frontend JS while we are actively developing the MVP.
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (
|
||||||
|
req.path.endsWith('.js') ||
|
||||||
|
req.path.endsWith('.css') ||
|
||||||
|
req.path.endsWith('.html')
|
||||||
|
) {
|
||||||
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
||||||
|
res.setHeader('Pragma', 'no-cache');
|
||||||
|
res.setHeader('Expires', '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(express.static(publicDir, {
|
||||||
|
etag: false,
|
||||||
|
lastModified: false,
|
||||||
|
}));
|
||||||
|
|
||||||
app.get('*', (_req, res) => {
|
app.get('*', (_req, res) => {
|
||||||
res.sendFile(resolve(publicDir, 'index.html'));
|
res.sendFile(resolve(publicDir, 'index.html'));
|
||||||
|
|||||||
105
frontend/app.js
105
frontend/app.js
@@ -169,11 +169,106 @@ function modal(html) {
|
|||||||
|
|
||||||
function renderCreateMailboxModal() {
|
function renderCreateMailboxModal() {
|
||||||
const domain = state.selectedDomain || '';
|
const domain = state.selectedDomain || '';
|
||||||
const d = modal(`<h2>New mailbox</h2><form id="createForm" class="form-grid"><label>Email<input name="email" type="email" value="@${esc(domain)}" placeholder="@${esc(domain)}" required></label><label>Password<input name="password" type="password" value="" minlength="8" autocomplete="new-password" required></label><div><button>Create</button></div></form>`);
|
|
||||||
const input = d.querySelector('input[name="email"]');
|
const d = modal(`
|
||||||
input.focus();
|
<h2>New mailbox</h2>
|
||||||
input.setSelectionRange(0, 0);
|
|
||||||
d.querySelector('#createForm').onsubmit = guard(async e => { e.preventDefault(); const f = new FormData(e.target); await api('/api/mailboxes', { method:'POST', body: JSON.stringify({ email:f.get('email'), password:f.get('password') }) }); d.remove(); await loadDomains(false); render(); });
|
<div class="form-grid">
|
||||||
|
<label>
|
||||||
|
Email
|
||||||
|
<input
|
||||||
|
id="createEmail"
|
||||||
|
type="email"
|
||||||
|
value="@${esc(domain)}"
|
||||||
|
placeholder="@${esc(domain)}"
|
||||||
|
autocomplete="off"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Password
|
||||||
|
<input
|
||||||
|
id="createPassword"
|
||||||
|
type="password"
|
||||||
|
value=""
|
||||||
|
minlength="8"
|
||||||
|
autocomplete="new-password"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button type="button" id="createMailboxSubmit">Create</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const emailInput = d.querySelector('#createEmail');
|
||||||
|
const passwordInput = d.querySelector('#createPassword');
|
||||||
|
const submitButton = d.querySelector('#createMailboxSubmit');
|
||||||
|
|
||||||
|
emailInput.focus();
|
||||||
|
|
||||||
|
// Cursor before the @domain, so you can directly type "test".
|
||||||
|
try {
|
||||||
|
emailInput.setSelectionRange(0, 0);
|
||||||
|
} catch {
|
||||||
|
// Some browsers do not allow selection on email inputs.
|
||||||
|
}
|
||||||
|
|
||||||
|
const createMailbox = guard(async () => {
|
||||||
|
const email = String(emailInput.value || '').trim().toLowerCase();
|
||||||
|
const password = String(passwordInput.value || '');
|
||||||
|
|
||||||
|
if (!email || !email.includes('@')) {
|
||||||
|
throw new Error('Please enter a valid email address.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!email.endsWith(`@${domain}`)) {
|
||||||
|
throw new Error(`Mailbox must belong to ${domain}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < 8) {
|
||||||
|
throw new Error('Password must have at least 8 characters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
submitButton.disabled = true;
|
||||||
|
submitButton.textContent = 'Creating...';
|
||||||
|
|
||||||
|
await api('/api/mailboxes', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
d.remove();
|
||||||
|
|
||||||
|
state.message = `Mailbox created: ${email}`;
|
||||||
|
|
||||||
|
await loadDomains(false);
|
||||||
|
await loadMailboxes(true);
|
||||||
|
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
submitButton.onclick = createMailbox;
|
||||||
|
|
||||||
|
passwordInput.addEventListener('keydown', (event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
createMailbox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
emailInput.addEventListener('keydown', (event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
passwordInput.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDeleteModal(email) {
|
function renderDeleteModal(email) {
|
||||||
|
|||||||
Reference in New Issue
Block a user