689 Commits

Author SHA1 Message Date
db646d10f4 feat(canonical-sublexicon): zayıf kova tuning (yaglar/panjur/direksiyon/debriyaj)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
+7 yaprak (migration 0027): yag-sistemi (yağlama/pompa/çubuk), panjur kaput/
tavan/şasi/cam/kanat, direksiyon simit/dişli/hidrolik/elektronik + debriyaj
serbest-bırakma. En düşük kovaları (Yağlar %10, Panjur %17, Direksiyon %38)
yükseltir. 150 yaprak toplam.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 16:48:42 +03:00
2d5e2d04a9 feat(canonical): 2-level serving — taksonomi endpoint + araç sub-gruplama
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
GET /categories/canonical/taxonomy: kova→alt-yaprak + populasyon sayısı (B2C API
kontratı). getCanonicalTree (vehicle + catalog) artık subLeaves geçip her kovayı
alt-gruplara böler (materialize + on-the-fly sub fallback). jant-tire slug fix
(jant-lastik üst-kova ile çakışıyordu). Kategori sub-kapsama: Filtre %88, Fren/
Aydınlatma %74, Motor/Süsp/Elektrik %60-66. Eşleşmeyen → Genel/SP manuel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 16:40:39 +03:00
9dd3b2c858 feat(canonical): 2. seviye alt-kategori mapping (otoparcasan-türevi)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Kendi kanonik yapımıza tam geçiş — B2C API için alt-kategori. migration 0026:
canonical_categories'e 143 alt-yaprak (parent_id=üst kova) + categories/
category_canonical_map'e canonical_subcategory_id. canonical-sublexicon.ts:
bucket başına satılabilir parça yaprakları + Türkçe varyant (kaliyer/hidroliği)
+ Bağlantı&Donanım catch-all. Backfill artık isim→kova→alt-yaprak deterministik
materialize ediyor (inheritance dahil). Eşleşmeyen = null → SP manuel. Testler +.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 16:32:25 +03:00
458603fb98 fix(canonical-backfill): model/yıl seçici düğümlere parent-inheritance verme
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Opel model-öncelikli: "X 2004-2014" model düğümü parent'ından (MOTOR VE DEBRIYAJ)
Debriyaj miras alıyor, sonra jenerik çocukları (BOYALAR/MÜTEFERRİK/SIZDIRMAZ) de
ondan Debriyaj miras alıyordu → yanlış eşleme zinciri. Inheritance artık yıl-
aralıklı + ^[a-z]{1,3}\d{2} model düğümlerine miras VERMEZ (null kalır, cascade
kırılır). Kod/plaka yaprakları (X1318_/20-201A) bu desenlere uymaz → Renault/
Hyundai etkilenmez. Sadece Opel 58 + Nissan 1 düğüm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 15:18:18 +03:00
5900b8d399 feat(canonical-lexicon): MAN kalan otobüs platform prefixleri (SG/SL/SR/SUE/UEL/RH/NM…) + model adları
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:46:09 +03:00
a10654f210 feat(canonical-lexicon): MAN EL/GS/LE/MAZ/MFTPL platform kodlarını da yapısal yap
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:30:54 +03:00
86a08dbe12 feat(canonical-lexicon): MAN deep-tune — otobüs/koçbuilder model adları yapısal
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
MAN neredeyse tamamen model-öncelikli (LION'S CITY, SKYLINER/CITYLINER/TOURLINER,
NL/ND/NG/MAN platform kodları, AUFBAU koçbuilder). classifyNode MAN_MODEL_CODE:
^(nd|ng|nl|ngt|man) \d + distinctive model kelimeleri → structural. Truck-kod
^\d{4,5} KULLANILMADI (2800 emex mapped bozardı). Ayrıca LPG-model yanlış-pozitif
(NL 205 M11 LPG→Yakıt) düzelir. elektr→elektrik, aggregate→motor. 27/27.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:16:38 +03:00
c88b6b45c5 feat(canonical-lexicon): Kollar/Ön Takım→süspansiyon; conta→motor strong
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
emex "Kollar"(salıncak kolları)/"Ön Takım"(aks) → süspansiyon (kollar/takim
weak). "conta" motor strong'a alındı ki "conta takımı"→motor kalsın (takim ile
tie'ı kazansın). 26/26 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:49:01 +03:00
e42345f35b feat(canonical-lexicon): emex servis sayfalarını (Servis Aralıkları/Ek Onarımlar) junk yap
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:45:28 +03:00
ee7925c960 feat(canonical-lexicon): Faz 3 emex yüksek-frekans terimleri
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
emex'in en sık eşleşmeyen adları (100-250x, tüm araçlarda paylaşılan):
stabilizer/absorber→suspansiyon; esanjor/kondansator/ventilator/kompresor/
sogutucu/uflec→sogutma; hali/halilar→ic-doseme; kilit/yagmurluk→dis-kaporta;
stepne→jant-lastik. Jenerik (BOYALAR/İZOLATÖRLER/Montaj) bırakıldı. 25/25.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:22:06 +03:00
50a92c78cc feat(canonical-lexicon): son cila — koltuğu/takoz/stabilizatör edge-case'leri
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
koltugu (koltuk k→ğ eki)→ic-doseme, stabilizator→suspansiyon strong, takoz/
takozu→suspansiyon weak. Küçük markaların (Smart/Mini) kalan gerçek boşluklarını
kapatır. Kalan eşleşmeyenler artık junk/generic/veri-kalitesi (pratik maksimum).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:36:57 +03:00
db4b85da8e feat(canonical-lexicon): küçük/düşük markalar için maksimum kapsama terimleri
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Tüm sub-90 markaların global eşleşmeyen adlarından net olanlar (çoğu İngilizce,
markalar arası): diagnostic/entertainment/bilgisayar/elektrigi→elektrik,
heater/ventilation→sogutma, braking→fren, sunroof/exterior/locking→dis-kaporta,
restraint/occupant/konfor→ic-doseme, mekanik→motor, yapistirici→yaglar. 24/24.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:29:57 +03:00
44987e7e7e feat(canonical-lexicon): Ford + BMW fix'leri
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
BMW (~%81): elektronik terimleri → elektrik (audio/navigasyon/elektronik/
iletisim/multimedya/cruise), "ışık"→aydınlatma; servis/doküman sayfaları →
junk (teknik doküman/servis ve onarım/value parts). Ford (~%74): trans
varyantları speed/spd→sanziman, motor varyantları diesel/dohc→motor, gövde→
panjur. 23/23 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:07:09 +03:00
1fcbfdf5d1 feat(canonical-lexicon): MAN kamyon model kodları yapısal + parça terimleri
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
MAN %32 = Opel gibi metrik yanılsaması (kamyon model tanımlayıcıları
denominatörde). classifyNode: ^\d{1,2}[./]\d ("12.155","10/11.150") + ^\d{2}t
("18T HOCL-SD") → structural (^\d{4,} KULLANILMADI — 362 mapped satırı bozardı).
Parça terimleri: salter/verici→elektrik, tahrik→sanziman (ticari araç + genel).
21/21 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:00:03 +03:00
347d9f2ddb feat(canonical-lexicon): Mercedes uzun-kuyruk parça adları
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Mercedes fragmented long-tail'inin eşlenebilir kısmı (çoğu İngilizce, diğer
markalara da yarar): propeller→sanziman, radiator→sogutma, roof/wall/underpan→
panjur, door/window/kapak→dis-kaporta, timing→triger, carburetor→yakit,
pedal→fren, ortu→ic-doseme. Doküman: preliminary remarks/genel bakış tablosu→
junk. Genuinely jenerik (AYAR/GENEL PARÇALAR/DONANIM/CONTROL) Diğer'de kalır. 20/20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 11:31:48 +03:00
f1f36d8cf1 fix(canonical-lexicon): MB katalog-kodu regex'ini çıplak-koda sabitle
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
^[cd]\d{3,} fazla genişti — "D765300(22H), LS 7 F, MB SERVO DİREKSİYON" gibi
parça bilgisi taşıyan düğümleri yapısal yapıp Direksiyon eşleşmesini bozuyordu
(Mercedes ham kapsama %79→%49). $ ile sabitle → sadece çıplak kod (C442,
C105011). Açıklamalı düğümler keyword'le eşleşmeye devam eder. 19/19 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 11:08:47 +03:00
25cf0fa670 feat(canonical-lexicon): Mercedes C/D katalog kodları yapısal + doküman sayfaları çöp
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Mercedes'in düşük kapsaması büyük ölçüde metrik yanılsaması: 175 dağınık kök,
C/D katalog-kodu düğümleri (C371211, C442, D308802(811),308,COWL) yapısal ama
classifyNode kaçırıyordu → denominatörde. Ekle: ^[cd]\d{3,} → structural;
"SA LİSTESİ"/"…resim açıklama sayfası" → junk. 19/19 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:52:42 +03:00
82793a8257 feat(canonical): Opel model-öncelikli — yazım varyantları + serving yapısal-atla
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Opel gerçek parça-kat kapsaması zaten %85; %38 model-kodu köklerin (yapısal)
denominatörde olmasından. Lexicon: sanzuman/disli→sanziman, suspensiyon/dingil→
suspansiyon (Opel yazımları). Serving: groupCategoriesByCanonical yapısal/çöp
düğümleri "Diğer"e sokmadan atlar (Opel model kökleri Birleşik'te görünmez).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:25:46 +03:00
aba1bfc126 feat(canonical-backfill): parent-inheritance (kod-yaprak → parent kovası)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Kendi adı eşleşmeyen kategori (Renault P5 X1318_ plaka kodları, figür
illustration'ları) parent'ının kanonik kovasını miras alır ("Alternatör"→
Elektrik). Materyalizasyondan sonra birkaç seviye kaskad. Re-scrape gerektirmez.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:11:22 +03:00
c70b9b61b9 fix(pl24): Hyundai/Kia illustration açıklamasını kod-yaprağa ekle
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Upstream doğrulandı: illustration satırında td[0]=figür kodu ("20-201A .1"),
td[1]=açıklama ("SUB ENGINE ASSY") — parser ilk td'yi (kod) alıp açıklamayı
düşürüyordu. Artık ilk td figür-koduysa (\d\d-\d\d\d[A-Z]?) açıklamalı sonraki
td eklenir → "20-201A .1 SUB ENGINE ASSY", canonical eşleşir (engine→motor).
Diğer markalar etkilenmez (yalnız kod-only ilk td'de tetiklenir). 10/10 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:02:01 +03:00
6bbc32d9c6 debug(pl24): TEMP Hyundai illustration rows + td dump 2026-07-05 09:55:30 +03:00
05951f9853 debug(pl24): TEMP Hyundai/Kia raw HTML row log (katalogJob Faz 2 upstream doğrulama) 2026-07-05 09:51:46 +03:00
4b6642a78a feat(canonical-lexicon): Faz 2 — İngilizce kategori adları
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
pl24 Asyalı markaları (Hyundai/Kia/Nissan/Toyota) ve çevrilmemiş emex/pcat
yaprakları İngilizce kategori adı taşıyor (ENGINE/BODY/BRAKE/TRANSMISSION…) →
%0 kapsama. Sistem kelimeleri strong (engine→motor, brake→fren, transmission→
sanziman, electrical→elektrik…), konum kelimeleri weak (body/chassis/floor/axle)
ki "BODY ELECTRICAL"→elektrik olsun. 17/17 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 09:30:34 +03:00
b547140eb1 feat(canonical): Jant ve Lastik + Aksesuar kovaları + pcat kararları
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
migration 0025: 2 yeni kanonik kova (jant-lastik #19, aksesuar #20).
lexicon: jant/lastik/tekerlek/göbek→jant-lastik, aksesuar→aksesuar (catch-all,
en düşük öncelik). Kullanıcı kararları: Hava Sistemi→Soğutma (hava weak),
Bakım parçaları→Filtreler (bakim weak). "hava yastığı"→direksiyon için yastigi
strong'a alındı. 16/16 test. Yorumlar 18→20 kova güncellendi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 09:24:29 +03:00
5abdc6f628 feat(canonical-lexicon): Faz 1 pcat kök eşlemeleri
pcat paylaşımlı taksonomisinin eşleşmeyen köklerinden net olanları ekledim:
jenerator→elektrik, yaglama→yaglar, karo/cerceve→panjur-panel, aktarim→
sanziman, aks→suspansiyon(weak). sasi weak (süspansiyon+şasi net kalsın).
15/15 test. Kova-kararı gerektirenler (jant-lastik, aksesuar) katalogJob.md'de.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 09:18:58 +03:00
b96ca78942 feat(web): birleşik görünüm varsayılan + "Orijinal Katalog" seçeneği
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Araç sayfasında browse varsayılanı "canonical" (Birleşik) oldu; toggle
"Birleşik | Orijinal Katalog" — Orijinal Katalog seçilince ham OEM ağacı
gösterilir. Kullanıcı isteği: birleşik katalog default, orijinal opsiyonel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 09:09:37 +03:00
643e1d923c fix(canonical-lexicon): kova adının kendisini anahtar kelime yap
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Kök tam da kanonik başlıkla anılınca ("Süspansiyon, şasi", "Elektrik",
"Aydınlatma") lexicon'da o kelime yoktu → "Diğer"e düşüyordu. suspansiyon/
elektrik/aydinlatma kendi kovalarına strong olarak eklendi. Serving on-the-fly
fallback aynı lexicon'u kullandığı için taze araçlarda backfill'siz düzelir.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 09:00:33 +03:00
07d4bf7e69 feat(canonical-lexicon): 3-harfli kök stem çekimlerini ekle
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
"yag/mil/far/aku" gibi 3-harfli kökler ≥4 prefix kuralı yüzünden çekimli
hallerine (yagi/yaglar, mili, farlar/fari, akusu) eşleşmiyordu — "HPS yağı",
"Kardan Mili", "araba aküsü" "Diğer"e düşüyordu. Çekimleri açıkça ekledim +
sanziman'a "kardan". Global düşürmek riskli (sis→sistemi). 13/13 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 18:42:06 +03:00
cc1d22a7d4 feat(catalog): kök-seviye canonical fallback + araç sayfası "Birleşik" toggle
Backend: groupCategoriesByCanonical'a requireParts opsiyonu; her iki serving
metodu artık parça-içeren yaprak yoksa (taze/drill-edilmemiş araç) kök
kategorileri kovalayarak boş dönmüyor.

Frontend: araç sayfasına "Katalog | Birleşik" toggle'ı; Birleşik seçilince
CanonicalView aracın kataloğunu 18 kanonik başlık altında gösterir, her kova
CategoryGrid ile normal drill'e bağlanır. canonical_view_opened event'i.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 18:28:07 +03:00
af2b35e6e2 feat(catalog): pl24 catalog-vehicle canonical gezinme variantı
GET /api/catalog/vehicles/:id/canonical → catalog_vehicle_id akışının canonical
karşılığı; pl24 markaları (VW/Opel/Renault) emex/pcat ile aynı 18 başlıktan
gezilir. Parça sayısı category_id ile join'lenir (pl24 parçaları çoğu zaman
vehicle_id taşır, catalog_vehicle_id değil). Gruplama mantığı iki servisin
paylaştığı canonical-grouping.ts helper'ına çıkarıldı (categories.service da
bunu kullanacak şekilde refactor edildi).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 18:08:59 +03:00
3c26f00a19 feat(categories): canonical taksonomiyle birleşik gezinme endpoint'i
GET /categories/canonical → 18 kanonik üst kova (browse landing).
GET /categories/canonical/:vehicleId → aracın parça-içeren leaf kategorilerini
18 kova altında gruplar; VW ve Opel gibi kökten farklı OEM ağaçları tek/aynı
başlık setinden gezilir. Kovalama materyalize canonical_category_id'yi
kullanır, son backfill'den sonra decode edilmiş kategoriler için ortak
canonical-lexicon ile on-the-fly fallback yapar; kovasız olanlar "Diğer"e düşer.
foldName() NORMALIZE_SQL'in JS karşılığı olarak eklendi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:58:17 +03:00
cd66947303 feat(canonical-backfill): reset/prune modu (global re-run için)
reset:true → non-manual keyword katmanını sıfırdan kurar: kapsamdaki
materyalize id'leri NULL'lar + stale non-manual map satırlarını siler,
sonra yeniden eşler. Manuel satırlar korunur. Sadece GLOBAL run'da (marka
scope'suz) geçerli — map isim-düzeyi/marka-bağımsız olduğu için marka-scope'lu
silme başka markaların paylaşılan eşlemelerini bozardı. Böylece lexicon/
sınıflandırıcı değişince eski eşlemeler dangling kalmaz.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:36:35 +03:00
c3d0dbaa0b fix(canonical-backfill): ">>" marker'ını yapısal sayma
Gerçek VW parça adları geçerlilik-tarihi marker'ı taşıyor ("pencere camlari
d >> - 14.07.2019"); bare ">>" onları yanlışlıkla yapısal işaretliyordu (VW
kapsamı düşüyordu). Gerçek VIN düğümlerini zaten VIN_LIKE yakalıyor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:29:29 +03:00
8b88ccd238 feat(canonical-backfill): ağırlıklı skorlama + genişletilmiş lexicon
- Belirsizlik skorlaması: strong head-noun ×3 / weak ×1; kazanan skor
  marjıyla belirlenir, ≤1 marj = ambiguous (eski eşit-sayı tie-break'i
  fazla ambiguous işaretliyordu). confidence marja göre (0.5–0.95).
- Lexicon genişletildi: egzoz/egz/konvertor/katalitik/susturucu (yakıt),
  karoser (panjur-panel), bcm/ecu (elektrik) + strong/weak ayrımı.
- classifyNode sıkılaştırıldı: VIN markerları (wvwzzz, >>), bare motor/
  sipariş kodları (z16dt[lfu]) artık yapısal.
- map.confidence artık per-eşleşme marjdan yazılıyor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:24:42 +03:00
754f1af61c fix(canonical-backfill): brand/source filtresini inArray ile kur
`= ANY($1)` postgres-js'te parametreyi Postgres array olarak bağlamıyor
("op ANY/ALL (array) requires array on right side") → job'lar fail'liyordu.
inArray() IN-listesine açar, sürücü-güvenli.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:12:49 +03:00
0ae7c3ceb6 fix(worker): canonical-backfill Worker'ını worker.ts'e bağla
Önceki commit'te (7b5e678) src/worker.ts git add kapsamı dışında kaldı
(apps/api/src/jobs/ eklendi ama worker.ts src/ altında). Queue/processor
deploy oldu ama tüketici Worker yoktu → enqueue edilen job'lar 'waiting'de
takıldı. Bu commit Worker instance'ını ekler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:02:37 +03:00
7b5e678c55 feat(catalog): kanonik taksonomi şeması + backfill job'ı
Kaynak/marka-bağımsız tek parça taksonomisi. Ham OEM ağaçları (categories.parent_id
+ pl24/emex/pcat aynaları) dokunulmadan kalır; kanonik omurga onlara dik.

- migration 0024: canonical_categories (18 üst kova seed) + category_canonical_map
  (isim-düzeyi eşleme) + categories.canonical_category_id kolonu
- canonical-lexicon.ts: iki-aşamalı eşleme (classifyNode: parça/yapısal/çöp;
  mapToCanonical: 18-kova keyword). VW+Opel pilotunda kanıtlandı — Opel ağacının
  %87'si model-seçici düğüm, bunlar kovaya girmez.
- canonical-backfill BullMQ job'ı (on-demand): distinct foldlanmış adları
  sınıflandırır, category_canonical_map'e upsert eder (is_manual korumalı),
  categories.canonical_category_id'yi join ile materyalize eder. brands/sources/
  dryRun parametreli.
- 8 unit test (classifyNode/mapToCanonical)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 16:54:24 +03:00
8f6894eecb feat(api): api-key admin uçları — PATCH güncelleme, Redis kota usage, global liste
Süper Panel'den tam anahtar yönetimi için:
- GET /internal/admin/api-keys artık userId/email'siz TÜM anahtarları
  kullanıcı e-postasıyla birlikte döner
- GET /internal/admin/api-keys/usage — günlük decode kotasının anlık
  Redis durumu (limit/used/remaining), ids ile filtrelenebilir
- PATCH /internal/admin/api-keys/:id — name, dailyDecodeLimit(null=varsayılan),
  domains (yalnız publishable), rateLimitMax(null=plugin varsayılanı),
  expiresInDays(null=süresiz)
- PublicApiQuotaService internal-admin'e provider olarak eklendi

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 06:20:05 +03:00
cd30278ad9 feat(pcat): JWT capture proxy-outage resilience + pool cap
JWT sitelerinin (parts-catalogs v3 widget'ını gömen partner mağazalar) sağlık
incelemesinden çıkan iki iyileştirme:

1) Direct-capture fallback (PCAT_CAPTURE_ALLOW_DIRECT, default açık): proxied
   capture tünel/connect hatasıyla ölürse (Floxy bakiye 402 / tünel down,
   DataImpulse portları ölü — 2026-06-11 ve 2026-07-03'te yaşandı) aynı siteyi
   sunucu IP'sinden DIRECT yeniden dener. Güvenli: TWS- token IP-bound DEĞİL +
   capture düşük frekans (≤MAX_POOL_SIZE refresh/~8.5dk). Doğrulandı: Floxy down
   iken direct 1.4s'de gerçek TWS- token yakaladı → warm pool artık tam proxy
   kesintisinde bile açlığa girmez.

2) Sert havuz üst-sınırı (enforcePoolCap): refresh yolundaki slot-muhasebe
   yarışı havuzu MAX_POOL_SIZE=5'in üstüne şişiriyordu (prod'da 32-39 gözlendi
   → gereksiz capture yükü + partner-site ban riski). Her push'tan sonra en eski
   slot(lar) çıkarılıp cap uygulanıyor, timer'ları temizleniyor.

6 JWT sitesinin hepsi canlı (direct 1.3-2s) — liste budanmadı. Birim testler +
canlı Floxy-down doğal deneyiyle doğrulandı.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:33:43 +03:00
f586b2f9c2 fix(api): widget şema görseli schemaPics[0].url okusun
schemaPics öğeleri {id,url,label} nesneleri; src'ye nesnenin kendisi
verilince kırık görsel ikonu çıkıyordu. + yüklenemeyen görsel gizlenir.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:09:38 +03:00
da2eaecd47 fix(api): public API parça cevabını beyaz-listeye indir + widget pozisyon alanı
- Kategori-detay parçalarında iç alanlar (source=upstream sağlayıcı adı,
  presel, ham price/currency, catalogVehicleId...) public API'den sızıyordu —
  decode kontratı disipliniyle beyaz-liste: id/name/description/oemCode/
  position/quantity/remark/unavailable/categoryId
- Widget parça tablosu positionNumber yerine gerçek alanı (position ??
  hotspotIndex) okuyor

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:05:21 +03:00
d46a463076 fix(api): widget Sorgula butonu MouseEvent'i candidate sanıyordu
onclick handler'ına doğrudan decode verilince click event'i candidate
parametresine düşüyor, sunucu da onu aday anahtarı sanıp 'araç seçimi
zaman aşımı' dönüyordu. Sarmalayıcı + typeof string guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:00:56 +03:00
e952687710 fix(api): widget loader.js global response-interceptor'a takılmasın
String dönüş {success,data} JSON'una sarılıyordu; Content-Type JS ama gövde
JSON olunca tarayıcı ERR_BLOCKED_BY_ORB ile script'i blokladı. @Res ile ham
send'e geçildi (shell zaten öyleydi).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 17:55:52 +03:00
ae61ac055e feat(api): gömülebilir katalog widget'ı (Faz 2 POC)
- GET /widget/loader.js: müşterinin yapıştırdığı tek script — iframe kurar,
  postMessage köprüsü (yükseklik + sase:partClick/sase:decoded olayları),
  parça tıklamasını data-part-click-url şablonuna ({code},{brand}) yönlendirir,
  window.saseWidget.onPartClick/onDecode callback'leri; CORP cross-origin
  (helmet same-origin varsayılanı müşteri sitesinde script'i bloklardı)
- GET /widget?key=sase_pk_...: iframe shell — vanilla JS katalog uygulaması
  (VIN formu, çok-aday seçimi, kategori grid + breadcrumb, parça tablosu,
  şema görseli, açık/koyu tema). Domain kilidi = anahtarın metadata.domains
  listesinden üretilen frame-ancestors CSP'si (tarayıcı zorlar); publishable
  olmayan anahtar 403; X-Frame-Options bu cevapta kaldırılır
- internal-admin: publishable:true + domains[] ile sase_pk_ anahtar üretimi
- api.sase.tr kısa yolu: /widget* → /api/widget*

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 17:50:29 +03:00
acadd84e4a fix(api): createApiKey expiresIn saniye cinsinden (ms değil)
30 günlük pilot anahtarı üretimi 'expiresIn is larger than the predefined
maximum value' hatasıyla düşüyordu — plugin expiresIn'i saniye bekliyor,
gün→ms çevriliyordu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 17:27:59 +03:00
773e9c35fa feat(api): public API v1 katalog endpoint'leri
- GET /v1/vehicles/:vehicleId/categories — kategori ağacı (rollup parça sayılı)
- GET /v1/vehicles/:vehicleId/categories/:categoryId — kategori detayı + parça
  listesi + şema görselleri (lazy drill/heal aynen çalışır)
- GET /v1/vehicles/:vehicleId/search?q= — araç kataloğunda serbest arama
Hepsi gezinme sayılır, decode kotasından düşmez; Swagger anotasyonlu.
Dashboard'daki kuralla aynı: decode edilmiş araçlar paylaşımlı, varlık
kontrolü 404 için.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 17:25:38 +03:00
06f247beb7 feat(api): public API docs (Swagger) + api.sase.tr kısa yolları
- Swagger/OpenAPI yalnız PublicApiModule'ü kapsar (iç dashboard yüzeyi spec'e
  girmez); UI /api/v1/docs, JSON /api/v1/docs-json; bearer auth + kota
  açıklamaları TR
- api.sase.tr host'unda /v1/* → /api/v1/* ve /docs* → /api/v1/docs* rewrite —
  müşteriye dokümante edilen taban https://api.sase.tr/v1
- Controller'lara ApiOperation/ApiBody/ApiOkResponse anotasyonları (decode
  tek-eşleşme + çok-aday şemaları, OEM xref)
- Coolify: api.sase.tr dev app'in api servisine ikinci domain olarak eklendi
  (docker_compose_domains)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:40:17 +03:00
7a4fe51d09 feat(api): public API POC — API-key auth + /api/v1 decode & OEM xref
Kapalı-beta public API temeli (rapor: api-widget-analizi-2026-07-03):
- better-auth apiKey plugin (defaultPrefix sase_, enableMetadata, anahtar
  başına 120 istek/dk); enableSessionForAPIKeys KAPALI — anahtar dashboard
  oturumu yerine geçmez, cookie AuthGuard davranışı değişmez
- apikeys tablosu (migration 0023, plugin şemasının birebir karşılığı)
- ApiKeyGuard: Authorization Bearer / x-api-key → verifyApiKey → kullanıcı
  yüklenir (status kontrolü), request.user + request.apiKey doldurulur
- /api/v1/vin/decode: mevcut slim kontrat + aday akışı; günlük başarılı-decode
  kotası (Redis, TR günü, aynı VIN idempotent; limit: key metadata
  dailyDecodeLimit → PUBLIC_API_DAILY_DECODE_LIMIT → 100) ve
  X-Decode-Quota-* header'ları
- /api/v1/oem/:code: P çapraz-referans (kotasız)
- internal/admin/api-keys: x-internal-token ile anahtar üret/listele/kapat
  (düz metin yalnız create cevabında)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:15:17 +03:00
98c21fa30e feat(web): Google Ads conversion tracking (gtag + Enhanced Conversions)
Add lib/google-ads.ts (lazy gtag init, gclid capture, Enhanced Conversions,
trackGoogleAdsConversion). Fire sign-up conversion on email registration and
purchase conversion on Stripe success. Gated on VITE_GOOGLE_ADS_* env; no-op
when unset. Mirrors the Meta Pixel/CAPI pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 18:11:44 +03:00
8735eece13 Merge branch 'feat/trial-7-day' into dev
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Shorten the free trial from 30 to 7 days: backend source of truth
(subscriptions.service TRIAL_DAYS), in-app banner rescale to the 7-day
window, tr/en copy (17 keys each), hardcoded CTAs, and the trial-progress
test. Existing trials keep their stored endDate — new sign-ups only.

Clean 3-way merge with the discount-code billing feature: trial and promo
changes touched disjoint regions of subscription/index.tsx and the message
catalogs. Post-merge verified — web+api typecheck clean, web tests 92/93
(the 1 red is the pre-existing landing index.test.tsx, unrelated to trial).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:26:02 +03:00