export const prerender = false; import type { APIRoute } from 'astro'; import { ImapFlow } from 'imapflow'; import { getAccount } from '../../../lib/mail-accounts'; function hasAttachmentParts(node: any): boolean { if (!node) return false; if (node.disposition === 'attachment') return true; if (node.disposition === 'inline' && node.type && !node.type.startsWith('text/') && !node.type.startsWith('message/')) return true; if (!node.childNodes && node.type && !node.type.startsWith('text/') && !node.type.startsWith('multipart/') && !node.type.startsWith('message/') && node.type !== 'application/pgp-signature') return true; if (node.childNodes) return node.childNodes.some(hasAttachmentParts); return false; } function parseReferences(buf: Buffer | undefined): string[] { if (!buf) return []; const raw = buf.toString(); const match = raw.match(/References:\s*([\s\S]*?)(?:\r?\n(?!\s)|$)/i); if (!match) return []; return match[1].trim().split(/\s+/).filter(Boolean); } export const GET: APIRoute = async ({ url }) => { const id = url.searchParams.get('account'); const folder = url.searchParams.get('folder') || 'INBOX'; const page = parseInt(url.searchParams.get('page') || '1'); const perPage = 30; const acct = id ? getAccount(id) : undefined; if (!acct) return json({ error: 'Unknown account' }, 400); const client = new ImapFlow({ host: acct.imap.host, port: acct.imap.port, secure: true, auth: { user: acct.imap.user, pass: acct.imap.password }, logger: false, }); try { await client.connect(); const lock = await client.getMailboxLock(folder); try { const total = client.mailbox?.exists ?? 0; if (total === 0) return json({ emails: [], total: 0 }); if ((page - 1) * perPage >= total) return json({ emails: [], total }); const start = Math.max(1, total - (page * perPage) + 1); const end = total - ((page - 1) * perPage); const msgs: object[] = []; for await (const msg of client.fetch(`${start}:${end}`, { uid: true, flags: true, envelope: true, bodyStructure: true, headers: ['references'], })) { const from = msg.envelope?.from?.[0]; const to = msg.envelope?.to?.[0]; msgs.push({ uid: msg.uid, seq: msg.seq, seen: msg.flags.has('\\Seen'), flagged: msg.flags.has('\\Flagged'), subject: msg.envelope?.subject || '(no subject)', from: from ? { name: from.name || '', address: from.address || from.name || '', } : null, to: to ? { name: to.name || '', address: to.address || to.name || '', } : null, date: msg.envelope?.date?.toISOString() || null, messageId: msg.envelope?.messageId || null, inReplyTo: msg.envelope?.inReplyTo || null, references: parseReferences(msg.headers), hasAttachments: hasAttachmentParts(msg.bodyStructure), }); } msgs.reverse(); return json({ emails: msgs, total }); } finally { lock.release(); } } catch (e: any) { return json({ error: e.message }, 500); } finally { await client.logout().catch(() => {}); } }; function json(data: object, status = 200) { return new Response(JSON.stringify(data), { status, headers: { 'Content-Type': 'application/json' } }); }