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 q = url.searchParams.get('q')?.trim(); 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 || !q) return json({ error: 'Bad request' }, 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 uids = await client.search( { or: [{ subject: q }, { body: q }, { from: q }, { to: q }] }, { uid: true }, ); if (!uids || uids.length === 0) return json({ emails: [], total: 0 }); uids.sort((a, b) => b - a); const total = uids.length; const slice = uids.slice((page - 1) * perPage, page * perPage); const msgs: object[] = []; for await (const msg of client.fetch(slice, { uid: true, flags: true, envelope: true, bodyStructure: true, headers: ['references'], }, { uid: true })) { 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.sort((a, b) => (b as any).uid - (a as any).uid); 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' } }); }