Çok ajanlı gece vardiyasının çıktıları: analitik (Rybbit) bulguları, SEO/GEO konu planı, marka stratejisi, canlı mail testi, CEO kararları (2 tur), CTO şartnamesi, yazar/yazılımcı raporları, bağımsız kod denetimi ve sabah özeti (yazılı + sesli). Kod değişiklikleri gece/* dallarında. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
16 KiB
16 KiB
04 — Mail QA testi (canlı, tempmail) — 2026-09-21
Ajan: Mail QA. Ortam: canlı https://kolaytercih.com, kullanıcının Chrome'u (Claude in Chrome) + Bash (curl, dig). Kod değiştirilmedi; yalnızca bu dosya yazıldı.
Özet (5 madde)
- Sonuç: mail sistemi canlıda ÇALIŞIYOR. Magic link isteği
HTTP 200 {"status":true}döndü, mail geçici adrese ~10 saniyede ulaştı (2 istekte 2 mail, 11 sn ve 10 sn). - Gönderen doğru:
KolayTercih <giris@mail.kolaytercih.com>—onboarding@resend.devfallback'i canlıda KULLANILMIYOR (EMAIL_FROMtanımlı). DKIMd=mail.kolaytercih.com; s=resendimzalı, SPF (send.mail.kolaytercih.com) DNS'te mevcut. - İçerik temiz: konu ve gövdede Türkçe karakterler doğru (UTF-8, quoted-printable),
multipart/alternative(düz metin + HTML) var, link host'uhttps://kolaytercih.com/giris/dogrula?token=…&callbackURL=%2F&errorCallbackURL=…(localhost/yanlış domain yok), "15 dakika / tek kullanımlık" bilgisi var, gelen kutusu önizleme metni çalışıyor. - En önemli eksik: DMARC kaydı YOK (
_dmarc.kolaytercih.comve_dmarc.mail.kolaytercih.comboş). Ayrıca apex'te SPF yok (MX Google'a gidiyor),mail.kolaytercih.com'un MX'i ve maildeReply-Toyok → kullanıcı maili yanıtlarsa bounce olur. - Test kısıtları: (a) Chrome'da site sahibinin oturumu açık olduğu için
/girisformu ana sayfaya yönlendi; oturuma dokunmamak için formun attığı isteğin aynısı çerezsizcurlile gönderildi → form UI'ı (başarı kutusu, toast, konsol) test EDİLEMEDİ. (b) Magic link'e kural gereği tıklanmadı → giriş/hesap oluşturma/deneme kredisi adımı elle doğrulanmalı. Toplam 2 istek atıldı (sınır 3).
Test günlüğü (saatler TSİ, +03; mail başlıkları UTC)
| Saat | Adım | Gözlem |
|---|---|---|
| 01:38 | Kod okuma + ilk DNS sorguları | Bkz. envanter. (Not: ilk dig döngüm zsh kelime-bölmeme yüzünden geçersizdi; 01:47'de doğru biçimde tekrarlandı — aşağıdaki DNS tablosu doğru olandır.) |
| 01:38 | Yeni sekme → guerrillamail.com |
CAPTCHA/çerez bandı çıkmadı. Üretilen kutu: uatxlibg@sharklasers.com (karışık takma ad: yu18q9+a33wualkwzf8o@sharklasers.com). Hesap açılmadı. |
| 01:39 | Yeni sekme → https://kolaytercih.com/giris |
Sayfa /'ye yönlendi: header'da "55 · Sayısal", "Kendi Listem", kredi 0, çıkış ikonu → site sahibinin oturumu açık (giris-karti.tsx:23 if (session) redirect). Çıkış yapmak kullanıcı oturumuna dokunmak olacağı için yapılmadı. Çerezsiz curl https://kolaytercih.com/giris → 200 (sayfa ayakta). |
| 01:40:12 | İstek 1 — POST /api/auth/sign-in/magic-link (çerezsiz curl, Origin: https://kolaytercih.com, gövde formdakiyle aynı: email + callbackURL:"/" + errorCallbackURL) |
HTTP/2 200, {"status":true}, 0,68 sn. |
| 01:40:23 (22:40:23Z) | Mail 1 geldi | Resend/SES Date: 22:40:13Z, tempmail Received: 22:40:15Z, kutuda 22:40:23Z → gecikme ≈ 3–11 sn. |
| 01:40:34 | İstek 2 — aynı adres, 22 sn sonra (rate-limit gözlemi) | HTTP/2 200, {"status":true}, 0,51 sn. Engelleme/uyarı yok, rate-limit başlığı yok. |
| 01:40:44 (22:40:44Z) | Mail 2 geldi | Gecikme ≈ 10 sn. Token farklı (znpz…ELUk → ISaK…VLr); ilk token'ın iptal edilip edilmediği tıklanmadan doğrulanamaz (better-auth 1.6.23 her istekte yeni verification satırı açar; eskisi süresi dolana dek geçerli kalır — koddan çıkarım). |
| 01:42 | Mail 1 incelendi (HTML görünüm + "Show Original" ham kaynak) | Aşağıda. |
| 01:50 | 3 sekme de kapatıldı | Sekme grubu boş. Magic link'e tıklanmadı, URL yalnızca metin olarak okundu. |
Konsol hatası / ağ sekmesi: form tarayıcıda açılamadığı için yok; ağ kanıtı curl çıktısıdır.
Gelen mailin incelemesi (ham kaynak)
| Kriter | Bulgu |
|---|---|
| From | KolayTercih <giris@mail.kolaytercih.com> — kendi alan adı, resend.dev değil |
| Konu | Merhaba, giriş bağlantını gönderdik (RFC 2047 UTF-8 Q-encoding, doğru çözülüyor). Guerrillamail açık-mail başlığında gönderdik gösterdi — bu tempmail arayüzünün kusuru; liste görünümünde ve ham başlıkta doğru. |
| DKIM | 2 imza: d=mail.kolaytercih.com; s=resend + d=amazonses.com. DNS'te resend._domainkey.mail.kolaytercih.com anahtarı mevcut. |
| SPF | send.mail.kolaytercih.com TXT "v=spf1 include:amazonses.com ~all" + MX feedback-smtp.us-east-1.amazonses.com mevcut. Gönderen IP 54.240.9.44 (Amazon SES, us-east-1). |
| DMARC | YOK (apex ve mail. alt alanında). |
| Gövde | multipart/alternative: text/plain; utf-8 + text/html; utf-8, ikisi de quoted-printable. Türkçe karakterler (ş, ğ, ı, ö, ç, ü) iki parçada da doğru. |
| HTML | Tablo tabanlı, satır içi stil; başlık, "15 dakika" vurgusu, mavi "Giriş yap →" butonu, yedek düz link, "sen yapmadıysan" notu, footer göründü. Logo https://kolaytercih.com/kolay-tercih-mark.png → curl ile 200 image/png 5129 B (tempmail görselleri varsayılan engelliyor; "Display Images"e basınca reklam katmanı çıktı, görsel render'ı gözle doğrulanamadı). <link> ile Google Fonts yükleniyor — çoğu istemci yok sayar, zararsız. |
| Link | Host kolaytercih.com, yol /giris/dogrula (token'ı tüketmeyen onay sayfası — SafeLinks koruması tasarlandığı gibi). Parametreler: token (32 karakter), callbackURL=%2F, errorCallbackURL=%2Fgiris%3Fhata%3Dgecersiz-baglanti%26callback%3D%252F. HTML'de & kaçışı doğru. |
| Geçerlilik | "15 dakika geçerli ve yalnızca bir kez kullanılabilir" (HTML + düz metin + önizleme). auth.ts:78 expiresIn: 900 ile tutarlı. |
| Footer | "KolayTercih · YKS tercih listeni dakikalar içinde hazırla / Bu e-postayı hesabınla ilgili bir işlem yaptığın için aldın." İletişim adresi, site linki, destek e-postası yok. |
| Reply-To | Yok. mail.kolaytercih.com'un MX'i olmadığından yanıtlar bounce olur. |
| Spam işareti | Tempmail'de uyarı yok (Guerrillamail spam skoru göstermez). Gmail/Outlook gelen-kutusu/spam yerleşimi test edilmedi. |
DNS (01:47, dig @1.1.1.1)
| Kayıt | Sonuç |
|---|---|
TXT kolaytercih.com |
yalnızca 2× google-site-verification — SPF yok |
MX kolaytercih.com |
1 smtp.google.com. (Google Workspace; destek@kolaytercih.com buraya düşer) |
TXT _dmarc.kolaytercih.com |
boş |
TXT _dmarc.mail.kolaytercih.com |
boş |
TXT resend._domainkey.kolaytercih.com |
boş (beklenen; Resend alan adı mail. alt alanı) |
TXT resend._domainkey.mail.kolaytercih.com |
DKIM anahtarı mevcut |
TXT send.mail.kolaytercih.com |
v=spf1 include:amazonses.com ~all |
MX send.mail.kolaytercih.com |
10 feedback-smtp.us-east-1.amazonses.com. |
MX/TXT mail.kolaytercih.com |
boş |
E-posta envanteri
Sistemde epostaGonder çağıran yalnızca 2 yer var (grep -rn epostaGonder src). Ödeme makbuzu, hoş geldin, rapor-hazır vb. e-posta yok.
| Şablon | Tetikleyici | Dosya | Test edildi mi |
|---|---|---|---|
magicLinkEpostasi(url) — "Merhaba, giriş bağlantını gönderdik" |
/giris formu → authClient.signIn.magicLink → POST /api/auth/sign-in/magic-link → better-auth sendMagicLink → sendMagicLinkEmail |
src/lib/eposta.ts:122, src/lib/auth.ts:25-41,74-82, src/features/kullanici/components/giris-form.tsx:67-90 |
EVET — uçtan uca teslim + içerik. Form UI'ı ve linke tıklama HARİÇ. |
krediHatirlatmaEpostasi() — "Listen seni bekliyor — açık satırların hazır" |
Saatlik zamanlayıcı (instrumentation.ts:11-12 → krediHatirlatmaBaslat): krediBittiAt ≤ now-48s + hatırlatma gönderilmemiş + bakiye < RAPOR_KREDI olan kullanıcılara tek seferlik |
src/lib/eposta.ts:167, src/lib/kredi-hatirlatma.ts:27-59 |
HAYIR — 48 saat + kredisi bitmiş hesap gerektirir; canlıda tetiklenemez. Aynı epostaGonder yolunu kullandığı için gönderim altyapısı dolaylı doğrulandı. |
| (Google OAuth) | E-posta göndermez | src/lib/auth.ts:67-72 |
— |
Yazılımcı ajanlara iletilecek eksikler
| # | Eksik | Kanıt | Önem | Önerilen düzeltme | Dosya:satır |
|---|---|---|---|---|---|
| 1 | DMARC kaydı yok | dig TXT _dmarc.kolaytercih.com ve _dmarc.mail.… boş |
orta (Gmail/Yahoo gönderici kuralları DMARC bekliyor; spoofing'e açık; teslim oranı riski) | DNS (kod değil): _dmarc.kolaytercih.com TXT "v=DMARC1; p=none; rua=mailto:destek@kolaytercih.com" ile başla, raporlar temizse p=quarantine. Alt alanı da kapsar. Kullanıcı işi (Cloudflare). |
— |
| 2 | Apex'te SPF yok (MX Google'da) | dig TXT kolaytercih.com yalnızca google-site-verification |
düşük-orta (destek@'ten giden yanıtlar SPF'siz) |
DNS: kolaytercih.com TXT "v=spf1 include:_spf.google.com ~all". Kullanıcı işi. |
— |
| 3 | Reply-To yok + mail.kolaytercih.com MX'siz → maile yanıt bounce olur |
Ham başlıklarda Reply-To yok; dig MX mail.kolaytercih.com boş |
düşük | resend.emails.send'e replyTo: "destek@kolaytercih.com" ekle (env ile: EMAIL_REPLY_TO). |
src/lib/eposta-gonder.ts:21-29 |
| 4 | EMAIL_FROM yoksa sessizce onboarding@resend.dev'e düşüyor |
Kod. Canlıda şu an doğru ayarlı (From = giris@mail.kolaytercih.com), ama env kaybolursa Resend yalnızca hesap sahibine gönderir → tüm kullanıcı girişleri 5xx olur |
orta (gizli konfig tuzağı) | Production'da EMAIL_FROM yoksa açılışta/ilk gönderimde net hata fırlat veya console.error ile uyar; fallback'i yalnızca dev'de bırak. |
src/lib/eposta-gonder.ts:22 |
| 5 | Production'da RESEND_API_KEY yoksa magic link konsola yazılıp başarı dönüyor |
Kod: auth.ts:33-37 ve eposta-gonder.ts:13-18 NODE_ENV kontrolü yapmıyor. Kullanıcı "gönderildi" görür, mail çıkmaz; ayrıca giriş token'ı prod loglarına düşer |
orta (şu an canlıda anahtar var — test kanıtı; risk regresyon) | NODE_ENV === "production" iken anahtar yoksa throw; token'lı linki prod'da asla loglama. |
src/lib/auth.ts:33-37, src/lib/eposta-gonder.ts:13-18 |
| 6 | Adres bazlı gönderim sınırı yok; yalnızca better-auth varsayılan IP limiti (magic-link yolu için 5 istek/60 sn) | Test: aynı adrese 22 sn arayla 2 istek → 2 mail, 200/200. auth.ts'te rateLimit/advanced.ipAddress ayarı yok (node_modules/better-auth/dist/plugins/magic-link/index.mjs:157-163). UI'daki 30 sn geri sayım yalnızca istemci tarafı (giris-form.tsx:41-52) |
orta (üçüncü kişiye mail bombardımanı + Resend kotasının yakılması; Traefik arkasında IP başlığının doğru okunduğu doğrulanmalı — okunmuyorsa tüm kullanıcılar tek kovayı paylaşır ya da limit hiç işlemez) | sendMagicLinkEmail içinde e-posta başına sunucu tarafı soğuma (ör. 60 sn'de 1, saatte 5) — verification tablosundaki son kayıt zamanından türetilebilir; advanced.ipAddress.ipAddressHeaders'ı Traefik'e göre sabitle. |
src/lib/auth.ts:25-41,74-82 |
| 7 | Yeni istek eski token'ı iptal etmiyor | 2 mailde 2 farklı token; better-auth her istekte yeni satır açar (koddan çıkarım, tıklanarak doğrulanmadı) | düşük | Kabul edilebilir (15 dk TTL). İstenirse gönderimden önce aynı e-postanın eski verification satırları silinebilir. | src/lib/auth.ts:79-81 |
| 8 | Form hata mesajı tek tip: 429 (rate limit), 5xx (Resend hatası) ve ağ hatası aynı "Bağlantı gönderilemedi. Tekrar dener misin?" toast'ı | Kod; error.status okunmuyor |
düşük | error.status === 429 → "Çok sık denedin, bir dakika sonra tekrar dene"; diğerleri → mevcut mesaj + "Google ile devam et" önerisi. Hata olayını analitiğe yaz (olay("giris_hata", {status})) — şu an yalnızca başarı loglanıyor. |
src/features/kullanici/components/giris-form.tsx:83-86,89 |
| 9 | Gönderim hatası yalnızca console.error; alarm/izleme yok |
Kod | düşük-orta (Resend kotası dolarsa kimse fark etmez; girişler sessizce ölür) | Hata sayacını mevcut analitiğe/health ucuna bağla ya da Resend webhook'u (bounced/complained) ekle. | src/lib/eposta-gonder.ts:33-36, src/lib/kredi-hatirlatma.ts:52-57 |
| 10 | Footer'da iletişim/site linki yok; kredi hatırlatma mailinde "bu tür e-postaları alma" seçeneği yok | Mail gövdesi + eposta.ts:96-101 |
düşük (magic link) / orta (hatırlatma: ticari ileti sayılabilir — İYS/KVKK sınıfı doğrulanmalı; kod yorumunda da "KVKK sınıfı netleşene dek" notu var) | Footer'a kolaytercih.com + destek@kolaytercih.com ekle; hatırlatma şablonuna tek tık vazgeçme linki + List-Unsubscribe başlığı. |
src/lib/eposta.ts:96-101,167-205 |
| 11 | Kredi hatırlatması at-most-once: Resend hatasında hatırlatma kalıcı olarak yanıyor | Kod yorumu (kredi-hatirlatma.ts:23-25) — bilinçli tercih |
düşük | İstenirse hata durumunda krediHatirlatmaGonderildiAt'i geri NULL'a çek (tek yeniden deneme). |
src/lib/kredi-hatirlatma.ts:42-57 |
| 12 | Tek kullanımlık (disposable) alan adları kabul ediliyor → her tempmail = yeni deneme kredisi | Test: sharklasers.com adresine mail gitti; auth.ts:84-97 her yeni kullanıcıya DENEME_KREDISI veriyor; src'de disposable filtresi yok |
orta (deneme kredisi suistimali; ürün kararı — CEO'ya) | Bilinen disposable alan adları listesiyle sendMagicLink öncesi ret ya da deneme kredisini yalnızca Google girişi/doğrulanmış kalıcı alan adlarına ver. |
src/lib/auth.ts:79-97 |
| 13 | HTML <head>'de Google Fonts <link> |
Ham kaynak | düşük (çoğu istemci yok sayar; bazı spam filtrelerinde harici kaynak = küçük eksi puan) | Kaldırmak güvenli; sistem fontuna zaten düşüyor. | src/lib/eposta.ts:79-80 |
Kullanıcının (Bilal) elle yapması gerekenler
- Magic link'e tıklama testi (bu oturumda yasaktı): gizli pencerede
/giris→ kendi ikinci bir adresinle link iste → maildeki "Giriş yap →" →/giris/dogrulaonay kartı → butona bas → oturum açıldı mı, yeni kullanıcıya deneme kredisi yazıldı mı,callbackURLdoğru mu? Aynı linke ikinci kez basınca "geçersiz bağlantı" dostça mesajı çıkıyor mu? - Form UI'ı: aynı gizli pencerede yeşil "Bağlantı gönderildi 📬" kutusu, 30 sn "Yeniden gönder" geri sayımı, "E-postayı düzelt", geçersiz adreste toast — ve konsolda hata olmadığı. (Senin Chrome'unda oturum açık olduğu için
/girisana sayfaya yönlendi; test edemedim.) - Gerçek gelen kutusu yerleşimi: Gmail + Outlook/Hotmail adresine birer link iste; gelen kutusu mu spam mı, Gmail'de "Show original" → SPF/DKIM/DMARC satırları (DMARC şu an
none/failçıkacak). - DNS (Cloudflare): DMARC kaydı (#1) ve apex SPF (#2) ekle.
- Resend paneli: plan/kota (ücretsiz planda günlük limit düşüktür — doğrulanmalı),
mail.kolaytercih.com"Verified" durumu, son 7 günün bounce/complaint oranı. - Temizlik (isteğe bağlı): prod
verificationtablosundauatxlibg@sharklasers.comiçin 2 satır oluştu (15 dk sonra geçersiz; kullanıcı hesabı OLUŞMADI çünkü linke tıklanmadı). - Kredi hatırlatma mailini lokal/docker'da
KREDI_HATIRLATMA_GECIKME_MS/KREDI_HATIRLATMA_ARALIK_MSenv'leriyle dakikalara sıkıştırıp gözle doğrula.
Protokol notları
- Hesap oluşturulmadı, şifre/kart girilmedi, CAPTCHA çıkmadı, dosya indirilmedi, kullanıcının açık oturumuna (KolayTercih dahil) dokunulmadı, çıkış yapılmadı.
- Sapma (şeffaflık): form tarayıcıdan gönderilemediği için aynı uç nokta aynı gövdeyle çerezsiz
curlile çağrıldı (2 kez). Canlıya etkisi formla birebir aynı: 2 verification satırı + 2 Resend gönderimi. - Mail ve sayfa içeriklerinde ajana yönelik talimat görülmedi.
- Token'lar bu raporda maskelendi.