fix(email): match IMAP card to SMTP styling + auto-detect mailbox folders

- IncomingMailConfigCard rebuilt to mirror the outgoing SMTP card: Card
  padding=md, icon inputs (Server/User/Lock), password eye toggle, stacked
  full-width fields, full-width primary Save button
- Folder is now a dropdown auto-populated by a 'Detect' button instead of a
  free-text path: backend emailIntakeService.listFolders() lists IMAP
  mailboxes (POST /admin/email/incoming-config/folders, accepts current form
  creds, masked password falls back to stored); UI auto-selects the inbox
  (special-use) folder
- en/de strings added
This commit is contained in:
Luca
2026-06-12 01:11:31 +02:00
parent 7e0098edcd
commit abb23f01c7
6 changed files with 203 additions and 27 deletions
+36 -1
View File
@@ -48,6 +48,41 @@ async function saveAttachment(att) {
return filePath;
}
/**
* List the mailbox folders on the IMAP server so the UI can offer a
* dropdown instead of a free-text path. Uses the saved config; an
* `override` ({ host, port, secure, user, pass }) lets the admin detect
* folders BEFORE saving. A masked/blank override password falls back to
* the stored one. Returns [{ path, name, specialUse }] (specialUse like
* '\\Inbox' lets the caller auto-select the inbox).
*/
async function listFolders(override) {
let cfg;
if (override && override.host && override.user) {
cfg = {
host: override.host,
port: override.port || 993,
secure: override.secure !== false && override.secure !== 0,
auth: { user: override.user, pass: override.pass || '' },
};
if (!cfg.auth.pass || cfg.auth.pass === '********') {
const stored = await getImapConfig();
cfg.auth.pass = stored?.auth?.pass || '';
}
} else {
cfg = await getImapConfig();
}
if (!cfg) return [];
const client = new ImapFlow({ host: cfg.host, port: cfg.port, secure: cfg.secure, auth: cfg.auth, logger: false });
await client.connect();
try {
const list = await client.list();
return (list || []).map((m) => ({ path: m.path, name: m.name, specialUse: m.specialUse || null }));
} finally {
await client.logout().catch(() => {});
}
}
/** Poll the mailbox once. Safe to call repeatedly; self-skips when busy/off. */
async function pollOnce() {
if (polling) return { skipped: 'busy' };
@@ -120,4 +155,4 @@ function startIncomingMailPoller() {
logger.info?.('Incoming-mail poller started (every 60s when enabled)');
}
module.exports = { pollOnce, startIncomingMailPoller, _internal: { getImapConfig, isEnabled, saveAttachment } };
module.exports = { pollOnce, startIncomingMailPoller, listFolders, _internal: { getImapConfig, isEnabled, saveAttachment } };