diff --git a/backend/src/server.ts b/backend/src/server.ts
index 905cbe6..a5f9e52 100644
--- a/backend/src/server.ts
+++ b/backend/src/server.ts
@@ -59,7 +59,25 @@ const publicDir = config.publicDir.startsWith('/')
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) => {
res.sendFile(resolve(publicDir, 'index.html'));
diff --git a/frontend/app.js b/frontend/app.js
index 00fc37c..7732ab0 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -169,11 +169,106 @@ function modal(html) {
function renderCreateMailboxModal() {
const domain = state.selectedDomain || '';
- const d = modal(`
New mailbox
`);
- const input = d.querySelector('input[name="email"]');
- input.focus();
- 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(); });
+
+ const d = modal(`
+ New mailbox
+
+
+ `);
+
+ 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) {