import "server-only"; import type { EpostaIcerik } from "./eposta"; /** * Ortak e-posta göndericisi (Resend). Şablonlar eposta.ts'te, gönderim * burada — magic link (auth.ts) ve kredi hatırlatması aynı yoldan çıkar. * * Env okumalarının tamamı ÇAĞRI ANINDA yapılır, modül seviyesinde değil: * `next build` NODE_ENV=production + env'siz koşar; modül yüklenirken hata * fırlatmak build'i kırar. */ /** Maile yanıt verilirse düşeceği adres — mail.kolaytercih.com'un MX'i yok. */ const VARSAYILAN_YANIT_ADRESI = "destek@kolaytercih.com"; /** Prod'da konsol fallback'ini bilinçli açan kaçış bayrağı (lokal docker testi). */ function kacisBayragi(): boolean { return process.env.EPOSTA_KONSOL_FALLBACK === "1"; } /** * Konsol modu: gönderim yerine içerik terminale yazılır. Yalnızca Resend * anahtarı yokken ve (dev'de ya da EPOSTA_KONSOL_FALLBACK=1 ile) geçerlidir. * Production'da anahtar yoksa konsol moduna DÜŞÜLMEZ — giriş token'ı taşıyan * link prod loglarına yazılmasın, kullanıcı da "gönderildi" sanmasın diye * epostaGonder net hata fırlatır. */ export function epostaKonsolModu(): boolean { return ( !process.env.RESEND_API_KEY && (process.env.NODE_ENV !== "production" || kacisBayragi()) ); } export async function epostaGonder( to: string, icerik: EpostaIcerik, ): Promise { if (epostaKonsolModu()) { console.log( `\n[eposta] (konsol modu) ${to} adresine gönderilecekti — ${icerik.subject}\n${icerik.text}\n`, ); return; } const anahtar = process.env.RESEND_API_KEY; if (!anahtar) { throw new Error("[eposta] RESEND_API_KEY tanımlı değil"); } // EMAIL_FROM yoksa resend.dev adresi yalnızca Resend hesap sahibine teslim // eder — prod'da sessizce buna düşmek yerine yapılandırma hatasını göster. const gonderen = process.env.EMAIL_FROM; if (!gonderen && process.env.NODE_ENV === "production" && !kacisBayragi()) { throw new Error("[eposta] EMAIL_FROM tanımlı değil"); } const { Resend } = await import("resend"); const resend = new Resend(anahtar); const { error } = await resend.emails.send({ from: gonderen || "KolayTercih ", to, // `||`: CI'da secret tanımsızsa env boş string gelir; boş Reply-To ile // Resend'e gitmek yerine varsayılana düş. replyTo: process.env.EMAIL_REPLY_TO || VARSAYILAN_YANIT_ADRESI, subject: icerik.subject, html: icerik.html, // Düz metin alternatifi spam puanını düşürür ve HTML'i engelleyen // istemcilerde içeriğin okunabilir kalmasını sağlar. text: icerik.text, }); // Resend SDK hata fırlatmaz, { data, error } döndürür. Kontrol etmezsek // doğrulanmamış domain/kota hatası sessizce yutulur — fırlatarak çağıran // tarafın (better-auth, hatırlatma taraması) haberdar olmasını sağlıyoruz. if (error) { console.error(`[eposta] Gönderilemedi (${to}):`, error); throw new Error(`Resend: ${error.name} — ${error.message}`); } }