Enhance Traefik configuration for www.kolaytercih.com
All checks were successful
Deploy / deploy (push) Successful in 7m18s
All checks were successful
Deploy / deploy (push) Successful in 7m18s
Added a new router and middleware for handling www to apex domain redirection, ensuring that Let's Encrypt certificates are obtained for both domains. Updated the docker-compose.yml file to include necessary rules and middleware for proper redirection and TLS handling. Also, updated the CEO agent description to remove redundant details and clarified the QA engineer's responsibilities regarding testing critical flows and error reporting. Adjusted the CTO agent's section for consistency in expert guidance and clarified the designer's tools to include 'Skill' for better design resource management.
This commit is contained in:
103
docs/gece-vardiyasi/2026-09-21/04-mail-testi.md
Normal file
103
docs/gece-vardiyasi/2026-09-21/04-mail-testi.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# 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)
|
||||
|
||||
1. **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).
|
||||
2. **Gönderen doğru:** `KolayTercih <giris@mail.kolaytercih.com>` — `onboarding@resend.dev` fallback'i canlıda KULLANILMIYOR (`EMAIL_FROM` tanımlı). DKIM `d=mail.kolaytercih.com; s=resend` imzalı, SPF (`send.mail.kolaytercih.com`) DNS'te mevcut.
|
||||
3. **İç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'u `https://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.
|
||||
4. **En önemli eksik: DMARC kaydı YOK** (`_dmarc.kolaytercih.com` ve `_dmarc.mail.kolaytercih.com` boş). Ayrıca apex'te SPF yok (MX Google'a gidiyor), `mail.kolaytercih.com`'un MX'i ve mailde `Reply-To` yok → kullanıcı maili yanıtlarsa bounce olur.
|
||||
5. **Test kısıtları:** (a) Chrome'da site sahibinin oturumu açık olduğu için `/giris` formu ana sayfaya yönlendi; oturuma dokunmamak için formun attığı isteğin aynısı çerezsiz `curl` ile 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
|
||||
|
||||
1. **Magic link'e tıklama testi (bu oturumda yasaktı):** gizli pencerede `/giris` → kendi ikinci bir adresinle link iste → maildeki "Giriş yap →" → `/giris/dogrula` onay kartı → butona bas → oturum açıldı mı, yeni kullanıcıya deneme kredisi yazıldı mı, `callbackURL` doğru mu? Aynı linke ikinci kez basınca "geçersiz bağlantı" dostça mesajı çıkıyor mu?
|
||||
2. **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 `/giris` ana sayfaya yönlendi; test edemedim.)
|
||||
3. **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).
|
||||
4. **DNS (Cloudflare):** DMARC kaydı (#1) ve apex SPF (#2) ekle.
|
||||
5. **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ı.
|
||||
6. **Temizlik (isteğe bağlı):** prod `verification` tablosunda `uatxlibg@sharklasers.com` için 2 satır oluştu (15 dk sonra geçersiz; kullanıcı hesabı OLUŞMADI çünkü linke tıklanmadı).
|
||||
7. Kredi hatırlatma mailini lokal/docker'da `KREDI_HATIRLATMA_GECIKME_MS` / `KREDI_HATIRLATMA_ARALIK_MS` env'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 `curl` ile ç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.
|
||||
Reference in New Issue
Block a user