export default { // ===================================================== // EMAIL HANDLER // ===================================================== async email(message, env, ctx) { try { const id = Date.now().toString(); const from = message.from; const to = message.to; const subject = message.headers.get("subject") || "(No subject)"; const date = new Date().toISOString(); // ----- Đọc toàn bộ raw MIME email ----- const rawResponse = new Response(message.raw); const buffer = await rawResponse.arrayBuffer(); const raw = new TextDecoder("utf-8").decode(buffer); // ===== LẤY PHẦN NỘI DUNG (CONTENT) & DECODE ===== let body = ""; // Ưu tiên phần text/plain const plainPart = raw.match( /Content-Type:\s*text\/plain[\s\S]*?\r?\n\r?\n([\s\S]*?)(\r?\n--|$)/i ); if (plainPart) { const partFull = plainPart[0]; // cả headers + body let content = plainPart[1].trim(); // chỉ body // xem encoding là gì const encMatch = partFull.match(/Content-Transfer-Encoding:\s*([^\r\n]+)/i); const encoding = encMatch ? encMatch[1].toLowerCase().trim() : ""; if (encoding === "quoted-printable") { body = decodeQuotedPrintableUtf8(content); } else if (encoding === "base64") { body = decodeBase64Utf8(content); } else { body = content; } } else { // Nếu không có text/plain, thử text/html const htmlPart = raw.match( /Content-Type:\s*text\/html[\s\S]*?\r?\n\r?\n([\s\S]*?)(\r?\n--|$)/i ); if (htmlPart) { const partFull = htmlPart[0]; let content = htmlPart[1].trim(); const encMatch = partFull.match(/Content-Transfer-Encoding:\s*([^\r\n]+)/i); const encoding = encMatch ? encMatch[1].toLowerCase().trim() : ""; if (encoding === "quoted-printable") { body = decodeQuotedPrintableUtf8(content); } else if (encoding === "base64") { body = decodeBase64Utf8(content); } else { body = content; } } else { // Bất đắc dĩ: không parse được thì để nguyên raw body = raw; } } const emailData = JSON.stringify({ id, from, to, subject, body, date }); // Lưu email vào KV await env.EMAILS.put(id, emailData); console.log("📩 Email saved:", id); } catch (err) { console.error("❌ Email handler error:", err); } }, // ===================================================== // HTTP HANDLER (UI) // ===================================================== async fetch(request, env) { const url = new URL(request.url); const params = url.searchParams; // ===================================================== // DELETE EMAIL // ===================================================== if (params.get("delete")) { const id = params.get("delete"); await env.EMAILS.delete(id); return new Response("Deleted", { status: 302, headers: { "Location": "/" } }); } // ===================================================== // VIEW SINGLE EMAIL // ===================================================== if (params.get("view")) { const id = params.get("view"); const data = await env.EMAILS.get(id); if (!data) return new Response("Email not found", { status: 404 }); const email = JSON.parse(data); return new Response(` ${escapeHTML(email.subject)} ← Back

${escapeHTML(email.subject)}

From: ${escapeHTML(email.from)}
To: ${escapeHTML(email.to)}
Date: ${escapeHTML(email.date)}
${escapeHTML(email.body)}
`, { headers: { "Content-Type": "text/html; charset=utf-8" } }); } // ===================================================== // LIST ALL EMAILS // ===================================================== const list = await env.EMAILS.list(); let emails = []; for (const key of list.keys) { const data = await env.EMAILS.get(key.name); if (data) emails.push(JSON.parse(data)); } // Sort email mới nhất lên đầu emails.sort((a, b) => Number(b.id) - Number(a.id)); const html = ` Email Inbox

Email Inbox

${emails.map(e => `
Delete
${escapeHTML(e.subject)}
From: ${escapeHTML(e.from)} • ${escapeHTML(e.date)}
`).join("")} `; return new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } }); } }; // ===================================================== // HTML ESCAPE (CHỐNG XSS) // ===================================================== function escapeHTML(str) { str = String(str || ""); return str .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } // ===================================================== // QUOTED-PRINTABLE UTF-8 DECODER // ===================================================== function decodeQuotedPrintableUtf8(input) { if (!input) return ""; // Bỏ soft line breaks: "=\r\n" hoặc "=\n" let cleaned = input.replace(/=\r?\n/g, ""); const bytes = []; for (let i = 0; i < cleaned.length; i++) { const ch = cleaned[i]; if (ch === "=") { const hex = cleaned.slice(i + 1, i + 3); if (/^[0-9A-Fa-f]{2}$/.test(hex)) { bytes.push(parseInt(hex, 16)); i += 2; } else { bytes.push("=".charCodeAt(0)); } } else { bytes.push(ch.charCodeAt(0)); } } return new TextDecoder("utf-8").decode(new Uint8Array(bytes)); } // ===================================================== // BASE64 UTF-8 DECODER // ===================================================== function decodeBase64Utf8(input) { if (!input) return ""; const cleaned = input.replace(/\s+/g, ""); const binary = atob(cleaned); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return new TextDecoder("utf-8").decode(bytes); }