Commit Graph
57 Commits
Author SHA1 Message Date
hikmettanandClaude Fable 5.1 17c781a9ba feat: gercek sirket-ihale uyum skoru — sahte %98 rozeti gitti
Karttaki "%N Uyumlu" rozeti sabit yazili degerlerdi (liveTenderService
98/95/91/99), sunucudan null geliyordu. Artik profil terimleri ile ihale
metni arasinda agirlikli sozcuk eslesmesi (src/services/uyum.js):
- terimler: urunler (1.0), ihale konulari (1.0), sektorler (0.6),
  sertifikalar (0.3); "ve/alimi/malzeme" gibi durma kelimeleri sayilmaz
- alanlar: baslik (1.0), teklif cetveli kalemleri (1.1 — en guclu sinyal),
  OKAS/CPV (0.7), ilan metni (0.6), sektor (0.5)
- doygunluk egrisi: 1 guclu eslesme ≈ 55, 2 ≈ 75, 3+ ≈ 90
- profilde terim yoksa skor null → sahte yuzde gosterilmez
- her skorun nedeni var: kartta ipucu, detayda "‘eldiven’ · teklif cetveli"
Olculen: "VETERINER ILAC VE SARF MALZEME" basligi hicbir urunu icermiyor ama
kalemlerinde ELDIVEN + HEMOGRAM KITI var → %86; kirtasiye → 0.

- Siralama: "Sirketime En Uyumlu"; hizli filtre 'uyumlu' (≥60)
- Profil yetkinlikleri artik kalici (localStorage + sunucu 'firma_profil');
  onceden sayfa yenilenince siliniyordu
- Web sitesi taramasi gercek uca bagli (/api/company/scan: site metni +
  model → sektorler/urunler/sertifikalar/ihaleKonulari); basarisizsa eski
  alan-adi tahmini yedek. Onceden 1,5 sn setTimeout ile taklitti.

Sozluksel yol bilincli: dis servis yok, 8 bin ihalede aninda, deterministik,
aciklanabilir. Embedding katmani istenirse ustune eklenebilir.
(Fikir: kaanvp/kobi-ihale-eslestirme'nin esik + aciklama deseni; kod alinmadi.)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 21:51:28 +03:00
hikmettanandClaude Fable 5.1 1fce9df06f feat: OKAS kod aramasi, toplu ihale dokumani yukleme, kazanan istihbarati
Uc ozellik, 27+1 depo taramasinda gorulen fikirlerden; kod kopyalanmadi
(ilgili depolar lisanssiz), uygulamalar sifirdan yazildi.

1) OKAS koduyla arama (fikir: firatbadur/ihaletakip-api'nin kullandigi
   IhtiyacKalemleri/GetAll ucu). EKAP'in kendi OKAS katalogunda ad ile
   (TR/EN) arama: "ortoped" → 33141700 Ortopedik malzemeler, 33183000
   Ortopedik destek cihazlari. Secilen kodla EKAP'ta ihale aramasi
   (okasBransKodList). Olculen: 33141700 altinda 335 ihale, basliklari
   "VETERINER ILAC VE SARF MALZEME" — basliktan gorunmeyen turden.
   - ekap-v2.mjs: okasAra(), searchByOkas(); sonuc normalize ortak
   - GET /api/okas/ara?q= (1 sa onbellek); /api/urun-ara?okas=kod,kod
   - Urun Arama modalinda "OKAS koduyla" modu (katalog onerisi + cok kod)
   - Sol menude CPV bolumunun ustunde OKAS kutusu: kod secilince modal
     o kodla acilip hemen arar

2) Toplu ihale dokumani yukleme (fikir: barkem-tayyar/ihale'nin izlenen
   klasor + extractIkn deseni). Belge Kasasi'na coklu dosya secimi: her
   dosyanin IKN'si METNINDEN bulunur ("Ihale Kayit Numarasi (IKN):
   2026/1166496"), kullaniciya sorulmaz; ihaleye baglanir ve teklif
   cetveli kalemleri otomatik havuza yazilir. Onlarca sartnameyi tek
   seferde atarak kalem havuzunu hizla doldurmak icin.
   - kalem-cikar.mjs#iknCikar + istemci esi (PDF tarayicida okunuyor)
   - Yukleme ucu: otoIkn → ofis metninden IKN; donuste ikn alani
   - Dosya basina sonuc satiri: IKN / kalem sayisi / "IKN bulunamadi"
   Dogrulandi: ikn parametresiz docx → 2026/1588948 bulundu, 2 kalem.

3) Kazanan istihbarati (fikir: gulmezeren2-byte/ihalent, MIT). Rakip
   Radari'nda firma basina kirim orani (1 − sozlesme/yaklasik maliyet,
   ortalama), ortalama gecerli teklif sayisi (rekabet derinligi) ve tek
   teklifle kazanma orani. Veri yoksa null — 0 ile karismasin; bedel >
   maliyet×1,5 olan bozuk satirlar orana katilmiyor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 20:16:53 +03:00
hikmettanandClaude Opus 5 9c142fa0f8 feat: EKAP imzalama anahtari rotasyona girince kendini toparlasin
Anahtar sabit kodluydu (EKAP_R8FACT_KEY ile gecilebiliyordu). EKAP anahtari
donemsel degistiriyor; degistigi anda ihale havuzu tamamen karariyor ve birinin
elle yeni anahtari bulup env'e yazmasi gerekiyordu.

Artik kimlik dogrulama hatasindan sonra anahtar portalin kendi paketinden
kesfediliyor. Zincir canli EKAP'ta dogrulandi:
  /ekap/search      → <script src="main.<hash>.js">
  main.<hash>.js    → webpack chunk haritasi; 2076 ozel olarak "common" adiyla
                      yayimlaniyor: y=>(2076===y?"common":y)+"."+{...}
  common.<hash>.js  → r8fact:"<32 baytlik anahtar>"
Kesfedilen deger su anki sabit anahtarla birebir ayni cikti — zincir dogru.

- Kesif YALNIZCA yedek: normalde sabit/env anahtar kullanilir, kesif ancak
  401/403/5xx sonrasi ve istek basina BIR KEZ denenir (gercek sunucu hatasinda
  her denemede portalden uc dosya indirmeyelim)
- 5xx de tetikliyor: EKAP gecersiz anahtari bazen 401, bazen 500 ile bildiriyor
  (imza cozulemeyince sifre cozme katmani patliyor); yalnizca 401'e bakmak
  rotasyonun yarisini kacirirdi
- Basari 1 sa, basarisizlik 5 dk onbellekleniyor
- Anahtar 32 bayt degilse kullanilmiyor — sema degismis demektir, korlemesine
  denemek yerine mevcut anahtarla devam edilir
- SPA fallback tuzagi: bilinmeyen yol HTTP 200 + index.html donduruyor; JS
  beklenen isteklerde HTML gelirse hata veriliyor (sessiz basarisizlik olmasin)

Sahte rotasyon testi: bozuk anahtarla EKAP 500 dondu → kesif devreye girdi →
dogru anahtar bulundu → tekrar denendi → arama basarili (860 ms).

Bulgu kaynagi: saidsurucu/ihale-mcp taramasinda gorulen imza semasi ve
firatbadur/ihaletakip-api'nin belgeledigi rotasyon davranisi. Ilgili depo
lisanssiz oldugu icin kod kopyalanmadi, uygulama sifirdan yazildi.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 20:04:11 +03:00
hikmettanandClaude Opus 5 ff797f5a91 feat: urun arama — urun adini ihale DOKUMANLARININ icinde aratir
saidsurucu/ihale-mcp deposu incelenirken EKAP arama API'sinin doküman ici
arama parametreleri oldugu goruldu: teklifCetvelindeAra, teknikSartnamedeAra,
idariSartnamedeAra, sozlesmeTasarisindaAra. EKAP dokumani kendi tarafinda
indeksliyor — yani teklif cetvelinde arama icin dokumani indirmeye (ve
CAPTCHA'ya) gerek yok.

Kullanicinin sikayetini olcen kanit:
  ELDIVEN         basliкta  1.135  → teklif cetvelinde 29.057
  ORTOPEDIK VIDA  baslikta      0  → teklif cetvelinde      3
  HEMOGRAM        baslikta    283  → teklif cetvelinde  2.529
"ORTOPEDIK VIDA" hicbir ihale BASLIGINDA gecmiyor; gectigi ihalelerin adi
"VETERINER ILAC ... SARF MALZEME". Basliga bakan firma bunlari bulamiyordu.

- ekap-v2.mjs: searchBody artik opts.kapsam ile daraltilabiliyor;
  searchInDocuments() yalnizca teklif cetvelinde (istege bagli teknik
  sartnamede) arar ve sonucu normalize eder
- GET /api/urun-ara?q=&skip=&take=&teknik= — 5 dk bellek onbellegi ile
  (EKAP'a uzak cagri, ayni sorgu kaynagi yormasin)
- Header'da "URUN ARA" dugmesi + UrunAramaModal: sayfalama, "sadece teklif
  verilebilir" suzgeci, havuzda acik olan ihalede detayi acma kisayolu

SINIR: EKAP hangi kalemin eslestigini dondurmuyor, yalnizca eslesen ihaleleri.
Kalem dokumu icin dokuman yuklemeye dayali ortak havuz yolu gecerli kaliyor;
arayuzde bu acikca yaziliyor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:55:36 +03:00
hikmettanandClaude Opus 5 8826ff8e68 feat: ortak kalem havuzu — teklif cetveli kalemleri kartta gorunur ve aranabilir
Firmalar ihalenin sadece basligina bakarak kendi urunlerinin icinde olup
olmadigini goremiyordu ("148 KALEM KIRTASIYE MALZEME ALIMI"). Artik bir
kullanici ihale dokumanini yukledinde teklif cetveli kalemleri ayristirilip
IKN'ye baglaniyor ve o ihale HERKESIN kartinda kalemleriyle gorunuyor.

NEDEN YUKLEMEYE DAYALI: kalem listesi EKAP'in programatik yuzeyinde yok.
- Arama/detay API'si (ihtiyacKalemiOkasList) yalnizca OKAS sinifi veriyor:
  "KALP AKCIGER POMPASI CIHAZI ALIMI" icin tek satir "Tibbi cihazlar"
- Ilan metni yalnizca sayiyi veriyor: "148 KALEM ... Ayrintili bilgiye EKAP'ta
  yer alan ihale dokumani icinde bulunan idari sartnameden ulasilabilir"
- Dokuman sayfasi CAPTCHA ile korunuyor (ctl00_btnCaptchaProtect, guvenlik
  resmi). Gercek tarayiciyla (Playwright) da acildi, donen tek metin: "Dogrula"

OTOMATIK, AI'SIZ: kalem cikarimi yukleme aninda kendiliginden calisir,
kullanicidan tiklama beklemez ve model cagirmaz (saf regex). AI analizi ayri
dugmede, kullanici isterse.

- server/kalem-cikar.mjs: teklif cetveli ayristirici. " | " tablolarindan
  (docx/xlsx/zip) sutun basligini cozerek; ayracsiz PDF metninde sezgisel
  satir kalibiyla. Miktar+birim tek hucrede olabiliyor ("25000 TEST").
  Birim listesinde 'test' var — tibbi kitlerde kalem miktari boyle yaziliyor.
- server/kalem-store.mjs: kalem.sqlite, IKN bazli, Turkce duyarli arama.
  Bos ayristirma mevcut iyi veriyi silmez.
- Uclar: POST /api/kalem/cikar (tarayicidan gelen PDF metni), GET /api/kalem,
  GET /api/kalem/ara, GET /api/health/kalem
- /api/tenders payloadu kalemSayisi + ilk kalemleri tasiyor
- Kartta kalem rozeti ve kalem seridi; aramada eslesen kalemler one alinir
- Ust arama kutusu kalem havuzunda da ariyor (350ms debounce, sunucu tarafi)
- Detay modalinda tam kalem listesi + kalem ici arama

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 11:51:39 +03:00
hikmettanandClaude Fable 5.1 b41bc338af fix: analiz icin dosya yuklenemiyordu — nginx varsayilan 1 MB govde siniri 413 veriyordu (arayuz 10 MB vaat ediyor); 20 MB + 300 sn zaman asimi, 413/504 icin anlasilir hata mesaji, 10 MB on kontrol
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 13:48:22 +03:00
hikmettanandClaude Fable 5.1 920a147126 fix: ust serit tiklaninca donuyordu — Kanban havuzdaki tum ihaleleri (8 bin+ kart) ciziyordu; artik yalnizca takipteki/kolona tasinan ihaleler, sekme gecisi transition ile aninda vurgulaniyor
- KanbanBoard: sunucu her ilana varsayilan 'incelenecek' verdigi icin bos pano 8.372 kart basiyordu; yalnizca takipte veya kolona tasinmis ihaleler kaliyor
- TopNavigation: useTransition + spinner; agir sekmelerde tiklama karsiliksiz kalmiyor
- sunucu: 'yeni liste eskisinden kucukse alma' kurali havuzu 30 Agustos zirvesinde dondurmustu (gunlerce yeni ilan yok); eksiksiz inen liste her zaman devrede
- sunucu: isYeni/bugun eklenen UTC-TR gun farkindan hep 0'di; Europe/Istanbul gunune gore ve her yanitta tazeleniyor (eklenmeZamani da donmuyor)
- ust kartlar: 'bugun eklenenler' tum ilanlari sayiyordu ('once' metnine bakiyordu), 'son giristen beri' sabit 14'tu; sayimlar liste filtresiyle ayni olcut (ihaleTarih.js), arsiv/sonuclanan filtreleri 'sadece acik' kapisina takilmiyor

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 13:02:40 +03:00
hikmettan 3eb0f91823 feat: derin analiz & yol haritasi + ihale dosyasi hazirlama; docx/xlsx/zip okunuyor
Doküman panelindeki "Tum Evraki Derin Analiz Et" dugmesi EKAP ilan metnini ve
ihaleye yuklenmis butun belgeleri TEK metinde birlestirip gonderiyor; model
sartnameyi ilanla ve eklerle capraz okuyabiliyor.

Derin analiz (mode='derin', ayri sistem istemi + 8000 token):
- Teklifi gecersiz kilabilecek noktalar (kirmizi uyari, en ustte)
- Gozden kacan ince detaylar (Kritik/Yuksek/Orta + madde no + neden onemli)
- Alinacak kalemler: aranan evsaflar, miktar, sartnamede geciyorsa fiyat
  (modele "TAHMIN URETME" talimati verildi, yoksa null)
- Belge kontrol listesi, kritik tarihler, maliyet kalemleri
- Teklife kadar yol haritasi (faz / kim / ne zaman / cikti)
- Idareye sorulacak belirsizlikler
Standart hizli ozet modu aynen duruyor.

Ihale Dosyasi Hazirla (rapor sonunda, acilir bolum):
- Eksik firma bilgileri kullaniciya sorulur, profilden gelenler sorulmaz,
  girilenler tarayicida saklanir
- Kalem fiyatlandirma tablosu, canli toplam + yaziyla karsiligi
- Teklif Mektubu, Birim Fiyat Teklif Cetveli, Evrak Kontrol Listesi
- Belgeler yazdirilabilir HTML: jsPDF'in gomulu fontlari WinAnsi, s/g/i/I
  tasimiyor (bkz. services/pdf.js ASCII basliklari) — resmi teklif mektubunda
  bozuk Turkce kabul edilemez

docx/xlsx/zip metin cikarimi (server/office-text.mjs):
- ihaleab projesindeki officeText.ts'ten tasindi, harici bagimlilik yok
- Sunucuda: tarayicida cozucu yok, baytlar zaten BELGE_DIR'de
- Tablolar " | " ile korunuyor, ic ice zip aciliyor, ~$ gecici dosyalari atlanir
- Yeni uc: GET /api/me/documents/text
- Yukleme kabul listesi .doc/.rar cikarildi (karsiligi yok), .xlsx eklendi

fix: yuklenen belgelerdeki "AI analiz" dugmesi bozuktu — extractText nesne
donduruyor ({text,pageCount,usedPages}), kod onu duz metin sanip gonderiyordu;
sunucuya "[object Object]" gidiyor ve analiz reddediliyordu. Uzunluk kontrolu
de nesne uzerinde undefined verdigi icin hatayi yutuyordu.

fix: analiz dokuman panelinden baslatildiginda modal kendi isAnalyzing'ini
bilmiyordu, kullanici 1-2 dakika bos modal goruyordu — ortak aiAnalyzing sinyali
2026-08-31 14:53:46 +03:00
hikmettan be79f9366a docs: seans devir notu — canlidaki havuz sorunu, bekleyen deploylar, bilinen aciklar 2026-08-31 00:34:14 +03:00
hikmettan ac77146f73 feat: ilan ve ihale tarihi etiketli gosteriliyor + 'sadece acik ihaleler' filtresi gercekten calisiyor (varsayilan acik) 2026-08-30 17:58:26 +03:00
hikmettan 14f24e00f4 fix: CPV kapisi havuzu blokluyordu — 3000 ilan icin AI beklenirken kullanici 120 ilan goruyordu; havuz aninda yayinlaniyor, AI arka planda tamamliyor 2026-08-30 17:45:35 +03:00
hikmettan 7701ce43e3 feat: ihale listesi sayfalama yerine sonsuz kaydirma (10 bin kayitta 1000+ sayfa dugmesi de kalkti) 2026-08-30 16:57:01 +03:00
hikmettan 40fea290e4 perf: DT detaylari 10 paralel + adaptif fren (0.36 -> 3.2 kayit/sn) & feat: EKAP'tan inen dokumani ihaleye yukleme 2026-08-30 14:07:29 +03:00
hikmettan c5662d2e60 CPV/OKAS ham kod sınıflandırması: yayınlanmadan önce tamamlanan bir gate + sidebar filtresi
- server/cpv-classify.mjs (yeni): existing (kalem kodu) → keyword (66 kural, 45 OKAS_BOLUM kodunun tamamı kapsanıyor) → AI (analyzeSpec, sadece sözlükte gerçekten var olan kod kabul edilir) hibrit sınıflandırma
- server/index.js: cache.tenders'a eklenmeden ÖNCE her ihale için CPV sınıflandırması await'leniyor (mevcut "sektör" alanının aksine, sonradan zenginleştirilmiyor) — hem ilk yükleme hem arka plan tamamlama yolunda
- İKN bazlı kalıcı SQLite önbellek (history-store.mjs) — aynı ilan bir daha asla yeniden sınıflandırılmıyor
- /api/meta/filtreler artık cpvBolumler döndürüyor
- Frontend: Sektörler'in hemen altına CPV Kodu filtresi (arama + çoklu seçim), ihale kartında CPV rozeti, landing page'de yeni özellik: "AB uyumlu CPV sınıflandırması — Türkiye'de ilk"
2026-08-30 14:00:56 +03:00
hikmettan f98568da8e feat: DT detaylari tam cekiliyor — BransKodList=OKAS ile siniflandirma, ilan belgeleri, en yeni kayitlar oncelikli detay kuyrugu, butce 30->250 2026-08-30 13:20:28 +03:00
hikmettan d9ca8a5b52 fix: kurum adi basligi eziyordu (gida ihalesi 'Hastanesi' yuzunden Saglik) — parcalar oncelik sirasiyla taraniyor 2026-08-30 02:05:01 +03:00
hikmettan 80bb4bc6c4 fix: doküman sınıflandırması ilanın tamamını tarayıp yanlış sonuç üretiyordu — yalnızca işin konusu taranıyor, eski kayıtlar geçersiz 2026-08-30 01:52:31 +03:00
hikmettan 495118ef9e feat: doküman metninden sektör zenginleştirme — başlığı sinyalsiz ilanlar ilan metninden sınıflandırılıyor (kalıcı önbellekli) 2026-08-30 01:39:46 +03:00
hikmettan a6a421d745 feat: OKAS/CPV kodları sınıflandırmaya katıldı + kelime sınırı hatası (bin'ASI' → aşı) düzeltildi 2026-08-30 01:35:08 +03:00
hikmettan 5c67429caa fix: Türkçe büyük harf (İ/I) regexleri bozuyordu — sınıflandırılmayan ihale %51'den %23'e düştü 2026-08-30 01:15:15 +03:00
hikmettan 62d1b8844e fix: DT ve arşiv kayıtları sektör alanına ihale türü yazıyordu — sektör filtresi bu kayıtları bulamıyordu 2026-08-30 00:28:32 +03:00
hikmettan fd78b6e7d9 feat: 30 günlük havuz referansı (kalıcı erime) + 25 sektör, çoklu sektör seçimi ve sunucudan gelen tek kaynaklı liste 2026-08-30 00:24:33 +03:00
hikmettan 8ced4d73d5 fix: havuz uyarısı tek ölçüm yerine 7 günlük ortalamaya kıyaslasın (yavaş erime de yakalansın) 2026-08-29 23:22:16 +03:00
hikmettan eb20a05478 feat: havuz izleme — ölçüm geçmişi, ani düşüş/eksik yükleme uyarısı, /api/health/havuz 2026-08-29 22:25:55 +03:00
hikmettan 65997055fe feat: abonelik iptal ekranı (ayrılma nedeni + açıklama) & gerçek abonelik rozeti (yönetici erişimi aboneliği gizlemesin) 2026-08-29 21:47:58 +03:00
hikmettan 5d502c8ae4 fix: Planlar sekmesi erişilemiyordu (gizli taşma) + isim menüsündeki ölü bağlantılar ve uydurma '7 gün kalan' etiketi 2026-08-29 17:39:31 +03:00
hikmettan 351884ac02 fix: 12 bin ihalelik havuz localStorage kotasını aşıp uygulamayı beyaz ekranla çökertiyordu 2026-08-29 14:43:20 +03:00
hikmettan 4b4f8a6951 chore: /api/billing/plans fiyat kimliği + webhook tanılaması 2026-08-29 12:36:23 +03:00
hikmettan 8bb3d6ee48 feat: yıllık abonelik (varsayılan) — Stripe yıllık fiyatlar + periyot checkout'a geçiyor 2026-08-29 11:58:46 +03:00
hikmettan f04401031c feat: sunucu tarafı hesap altyapısı — Stripe webhook, kotalar, gerçek API anahtarı ve belge deposu
Denetlenen 5 teknik borç kalemi de kapatıldı:

1) Stripe webhook + gerçek yetkilendirme (ÖNCEDEN YOKTU)
   - POST /api/billing/webhook — SDK'sız HMAC-SHA256 imza doğrulaması,
     tekrar saldırısına karşı ±300sn zaman penceresi, timingSafeEqual
   - checkout.session.completed / subscription.created|updated|deleted işlenir
   - Ödeme → kullanıcının planı sunucuda güncellenir (önce ödeme hiçbir şeyi
     değiştirmiyordu)

2) Plan kotaları GERÇEKTEN uygulanıyor (önce sadece tanımlıydı)
   - server/entitlements.mjs: plan hakları + kotaKontrol()
   - ekip, belge adedi/boyutu, API anahtarı kotaları sunucuda zorlanır
   - Kota aşımında 402 + anlaşılır Türkçe mesaj; arayüzde KotaUyarisi bandı
   - /api/me/plan: plan, haklar ve gerçek kullanım

3) Kullanıcı verisi localStorage'dan sunucuya taşındı
   - server/user-store.mjs: kullanici_verisi tablosu (izinli anahtar listesi)
   - AppContext girişte sunucudan yükler, değişiklikleri sunucuya yazar;
     localStorage yalnızca çevrimdışı önbellek
   - Artık cihaz değişince veri kaybolmuyor

4) API anahtarları gerçek (önce Math.random ile tarayıcıda üretiliyordu)
   - Sunucuda crypto.randomBytes ile üretilir, YALNIZCA sha256 hash'i saklanır
   - Ham değer bir kez gösterilir; verifyApiKey ile doğrulanır, son kullanım
     izlenir, iptal anında geçerli olur

5) Belge deposu gerçek (önce boyut Math.random'dı, dosya hiç yüklenmiyordu)
   - Dosyalar kalıcı volume'e yazılır, boyut GERÇEK bayt
   - Auth'lu indirme (blob), silme diskten de siler, kota sunucuda uygulanır
2026-08-29 10:40:05 +03:00
hikmettan f73c7bd6cb feat: konu bazlı bildirim + gerçek site tarama
1) Bildirim artık KONU odaklı (il değil)
   - Kullanıcı hangi konularda ihale alabildiğini seçer; varsayılan tüm Türkiye
   - İl/alım türü/favori kurum 'daha da daralt' altında isteğe bağlı
   - notify-store: konular kolonu + otomatik şema geçişi
   - notifier: konu başlıkları ihale başlığı/sektör/branş/kalemlerde aranır

2) Gerçek firma sitesi tarama (webScraperService mock'unun yerine)
   - server/site-scan.mjs: siteyi GERÇEKTEN çeker, ana sayfa + ürün/hizmet/
     hakkımızda iç sayfalarını tarar, HTML'i metne çevirir, AI ile firmanın
     tedarik edebildiği ihale konularını çıkarır ('uydurma' yasaklı prompt)
   - /api/company/scan (auth'lu)
   - ai.mjs: systemOverride desteği (farklı görevler için ayrı sistem promptu)
   - Arayüz: 'Sitenizi tarayıp konuları otomatik önerelim' → tıklayarak ekleme

Not: eski webScraperService.js sabit anahtar-kelime sözlüğüydü; domain adında
'tanriverdi' geçince tıbbi ürün listesi uyduruyordu.
2026-08-29 10:19:44 +03:00
hikmettan b44b45c67e chore: /api/notify/email/config gönderen adresini ve EMAIL_FROM'un ayarlı olup olmadığını raporlasın
EMAIL_FROM'un konteynere ulaşıp ulaşmadığını tahmin etmek yerine ölçmek için.
2026-08-29 09:58:14 +03:00
hikmettan c64889782f feat: yönetici için 'şimdi gönder' (POST /api/notify/run?zorla=1)
Bildirim turunu temel-alma adımını atlayarak çalıştırır; abonelik kriterine uyan
ihaleler hemen e-posta olarak gönderilir. Test ve talep üzerine özet için.
2026-08-29 03:22:03 +03:00
hikmettan f86339417a feat: otomatik e-posta bildirimi
Kullanıcı kriter kaydediyor; sunucu periyodik olarak eşleşen YENİ ihaleleri
bulup özet e-posta gönderiyor.

- server/notify-store.mjs: abonelik deposu (SQLite, kalıcı volume'de)
  kriterler: il · sektör · alım türü · anahtar kelime · favori kurumlar
- server/notifier.mjs: eşleştirme + gönderim motoru
  · aynı ihale iki kez bildirilmez (sonGorulenler takibi)
  · ilk abonelikte geçmiş bildirilmez, yalnızca temel alınır (spam önleme)
  · sonuçlanmış/arşiv kayıtları bildirime girmez
- /api/notify/subscriptions (GET/POST/DELETE) + /api/notify/run (admin, anlık test)
- scheduler: NOTIFY_INTERVAL_SAAT (varsayılan 6) ile periyodik tur
- NotifySubscriptions paneli: kriter ekle/sil, 'ekrandaki filtreleri kullan'
- tendersPayload ortak yardımcısına çıkarıldı (API ve bildirim aynı veriyi kullanır)
2026-08-29 03:19:47 +03:00
hikmettan 667b335ea7 fix: 10 dakikada bir havuz 120 kayda düşüyordu
Sorun: önbellek süresi dolunca getTenders cache.tenders'ı ilk partiyle (120 ilan)
DEĞİŞTİRİYOR, kalanı arka planda dolduruyordu. Yani her 10 dakikada bir kullanıcı
birkaç dakika boyunca eksik liste görüyordu (canlıda 8.258 havuza karşı 5.319
yüklü olarak yakalandı).

Çözüm: yenilemede mevcut dolu liste sunulmaya devam ediyor; yeni liste ayrı
kurulup TAMAMLANINCA atomik olarak devreye alınıyor. Yalnızca ilk açılışta
(elde hiç veri yokken) ilk parti anında gösteriliyor.

Doğrulama: zorla yenileme sırasında liste 3.795'te sabit kaldı (öncesinde 120).
2026-08-29 03:03:03 +03:00
hikmettan c701083642 fix: il adları kaynaklar arasında bölünüyordu (Ankara / ANKARA)
Sorun: ilan.gov.tr illeri 'Ankara' biçiminde, EKAP arşivi/DT 'ANKARA' biçiminde
veriyordu. İl filtresi birebir string karşılaştırdığı için kullanıcı 'Ankara'
seçtiğinde 161 'ANKARA' kaydı görünmüyordu. 81 ilin hepsi çift kayıtlıydı
(162 farklı il değeri).

- server/tr-text.mjs: Türkçe duyarlı titleCaseTr / normTr yardımcıları
- history-store + dt-store: il adı okunurken tek biçime indiriliyor
  (mevcut kayıtlar için de geçerli, yeniden tarama gerekmiyor)
- TenderList: il filtresi normalize karşılaştırma yapıyor (savunma katmanı)
2026-08-29 02:40:22 +03:00
hikmettan 03d5a04c91 perf: canlı ihale havuzunun tamamı yükleniyor
Sorun: /api/tenders yalnızca 1.060 kayıt döndürüyordu — üç sabit sınır yüzünden
(ilan.gov.tr 8 sayfa=160, DT 300, arşiv 600). Havuz ise 5.000+.

Çözüm:
- ilan.gov.tr sayfaları artık PARALEL çekiliyor (6 eşzamanlı), sıralı değil
- İlk yanıt için 6 sayfa yeter; kalan sayfalar ARKA PLANDA doldurulur, kullanıcı
  beklemez (ölçüm: ilk yanıt 0.05s, havuz arka planda 120 → 3.059 ilana çıktı)
- Sayfa sınırı 8 → 300 (6.000 ilan), DT 300 → 5.000, arşiv 600 → 5.000
- Hepsi env ile ayarlanabilir: ILAN_MAX_PAGES, ILAN_ILK_PARTI, ILAN_ESZAMANLI,
  DT_LIMIT, HISTORY_LIMIT
- totalCount artık arşivi de içeriyor; 'yüklü' sayısı 'havuz'u geçmiyor

Not: yanıt nginx tarafından gzip'leniyor (8.5x), tüm havuz ~600 KB — boyut
darboğaz değildi, sayfalama darboğazdı.
2026-08-29 02:30:49 +03:00
hikmettan f45afbfa3d fix: geçersiz/yer tutucu RESEND_API_KEY 'aktif' görünmesin
emailConfigured artık anahtarın 're_' ile başlamasını arıyor; böylece Coolify'da
boş/yer tutucu değer bırakıldığında arayüz yanlışlıkla 'E-posta aktif' demiyor.
2026-08-28 22:57:58 +03:00
hikmettan 1e614abc21 refactor(ai): glm-latest zincirden çıkarıldı (birincil ile aynı modele çözünüyordu) — 2. sıra Gemini Flash Latest; kayıtlı zincirler sürüm 3 geçişiyle taşınır 2026-08-28 22:50:00 +03:00
hikmettan 5282543471 feat: zamanlayıcı teşhis ucu + geniş arşiv dolumu
- /api/health/scheduler: DT/sonuç taraması durumu, son DT turu, boş bellek,
  Chromium varlığı — uzaktan teşhis için
- Dolum eşiği 2.000 → 25.000, pencere 180 → 365 gün (arşiv 6 aylık havuzun
  %7'sinde erken duruyordu)
2026-08-28 22:37:11 +03:00
hikmettan 1838de95f5 feat: ihale dokümanları + OKAS kalem verisi + doğrudan temin (Playwright) + kalıcı arşiv
1) İhale evrakları (yeni)
   - server/documents.mjs: EKAP doküman URL üretimi (imzalı), ilan metni +
     OKAS kalem çıkarımı, Playwright ile doküman metni (varsa)
   - /api/documents/url · /ilan · /text · /config uçları
   - TenderDocuments paneli: 'EKAP Dokümanlarını Aç' + 'İlan Metnini AI ile
     Analiz Et' (11K+ karakter gerçek ilan metni doğrudan AI'ya gider)
   - NOT: eski EKAP host'u bot isteklerine bağlantı kesiyor; bağlantı
     kullanıcının tarayıcısında açılır, sunucu tarafı metin için Chromium gerekir

2) Kalem verisi gerçek oldu
   - ekap-scan: her ihale için IhaleDetay'dan OKAS ihtiyaç kalemleri çekilir
   - history-store: okas_kalemler kolonu + otomatik şema geçişi
   - --kalem-doldur modu: mevcut kayıtlara IKN'den çözerek kalem ekler
   - scheduler: tarama sonrası kalem doldurmayı zincirler
   - PriceAnalysis: yöntem şeffaflığı notu (birim fiyatlar kamuya açık değil;
     kategori bazlı yaklaşık değer olduğu açıkça yazıyor)

3) Doğrudan temin (Playwright)
   - Dockerfile: node:22-slim + nginx + Playwright/Chromium
   - scheduler: DT worker periyodik (DT_INTERVAL_SAAT, varsayılan 4 saat)
   - Bellek koruması: boş bellek DT_MIN_BOS_MB altındaysa tarama atlanır ve
     uyarı loglanır; DT_ENABLED=0 ile tamamen kapatılabilir

4) Kalıcı arşiv
   - Coolify'da /app/server/data volume bağlandı; arşiv artık deploy'lar
     arası korunuyor

Ayrıca: TenderDetailModal'daki sabit '3-5 Firma' tahmini kaldırıldı, yerine
gerçek geçerli teklif sayısı gösteriliyor.
2026-08-28 22:31:40 +03:00
hikmettan e2512be9f7 feat: GLM 5.3 Flash eklendi ve birincil modele alındı
- server/ai.mjs: yeni 'glmflash' sağlayıcısı (z-ai/glm-5.3-flash), varsayılan
  zincirin başında
- AppContext: kataloğa eklendi; varsayılan zincir glmflash → glm → deepseek → gemini
- Kayıtlı ayarlar için sürüm geçişi (ZINCIR_SURUM): mevcut kullanıcıların
  zincirinin başına glmflash otomatik eklenir, kalan sıraları korunur
- Env ile geçilebilir: GLM_FLASH_OR_MODEL / GLM_FLASH_MODEL
2026-08-28 21:52:14 +03:00
hikmettan dd2bfa12e6 feat: OpenRouter entegrasyonu — tek anahtarla tüm modeller, latest alias'lar
- server/ai.mjs: OpenRouter öncelikli yönlendirme (routeFor). OPENROUTER_API_KEY
  tanımlıysa zincirdeki tüm modeller OpenRouter üzerinden çağrılır; doğrudan
  sağlayıcı anahtarı varsa o ezer
- Tüm modeller OpenRouter floating 'latest' alias'larına çevrildi (sabit sürüm
  yerine otomatik güncel kalır):
    ~z-ai/glm-latest · ~deepseek/deepseek-v4-flash-latest
    ~google/gemini-flash-latest · ~x-ai/grok-latest
    openai/gpt-chat-latest · ~anthropic/claude-opus-latest
- /api/ai/config artık her model için kullanılan yolu (openrouter|direct) döner
- AI Model Ayarları: canlı durum bandı (aktif modeller + yol) ve OpenRouter
  kurulum yönergesi; eski 'alias' alanı gerçek OpenRouter kimliğiyle değişti
- Analiz modali: tek OPENROUTER_API_KEY önerisi
2026-08-28 13:37:40 +03:00
hikmettan 7a396ddeb6 fix: sabit 'Gemini 3.6' etiketleri kaldırıldı — AI zinciri admin panelden seçiliyor 2026-08-28 10:50:54 +03:00
hikmettan daf5c2c117 fix: geçmiş arşiv kayıtları ana ekranı çökertiyordu
- SummaryCards/TenderList/DailyBulletin/ResultSearch/TenderCalendar:
  eklenmeZamani, baslik, kurum, ikn, tarih alanlarına güvenli erişim
  (arşiv kayıtlarında bu alanlar bulunmuyordu → TypeError, boş ekran)
- history-store: arşiv kayıtlarına arayüzün beklediği varsayılan alanlar
2026-08-28 10:46:54 +03:00
hikmettan d606d55e41 feat: geçmiş arşiv dolumu + arşiv sayacı
- scheduler: arşiv eşiğin altındaysa geniş geçmiş dolumu (180 gün/1500 kayıt),
  doluysa artımlı günlük tarama; ilk tur 90 sn sonra
- LiveStatsBar: 'Sonuç Arşivi' sayacı (kazanan firma kayıt sayısı)
- KİK: HTML entity çözümü tamamlandı
2026-08-28 10:42:33 +03:00
hikmettan 168052c267 feat: geçmiş veri motoru + gerçek AI + KİK kararları + TED + kırmızı bayrak + planlar
Roadmap'in 6 maddesi de gerçek çalışır halde:

1) Geçmiş veri motoru (EKAP v2)
   - server/ekap-v2.mjs: AES-256-CBC imzalı EKAP v2 istemcisi (zero-dep),
     ihale arama + sonuç ilanı parse (kazanan firma, sözleşme bedeli,
     yaklaşık maliyet, teklif sayısı), TR firma adı normalize
   - server/history-store.mjs: SQLite deposu (IKN+kısım anahtarlı, upsert)
   - server/ekap-scan.mjs: tarih aralıklı artımlı tarayıcı (6 paralel)
   - server/scheduler.mjs: sunucu içi periyodik tarama (SCAN_* env ile)
   - /api/tenders artık geçmiş sonuçları da birleştiriyor → kazanan firma,
     rakip ve kırmızı bayrak analizleri gerçek veriyle doluyor

2) Gerçek AI şartname analizi
   - server/ai.mjs: 6 sağlayıcılı fallback zinciri (GLM/DeepSeek/Gemini/
     Grok/GPT/Claude), JSON şemalı çıktı, uydurma yasak promptu
   - pdfjs-dist ile tarayıcıda PDF metin çıkarma (dinamik import)
   - AiSpecAnalyzerModal: sahte sonuç kaldırıldı, gerçek dosya + dürüst hata

3) KİK kurul kararları
   - server/kik.mjs: EKAP v2 JSON API (Sec-1..6 imzalı), uyuşmazlık/
     düzenleyici/mahkeme; karar tam metni (ayrı AES anahtarıyla URL)
   - KikDecisions sekmesi: arama + takipteki idarelerden hızlı öneri

4) Fiyatlandırma + ödeme
   - server/billing.mjs: Stripe Checkout (zero-dep, form-encoded)
   - Pricing sekmesi: 3 plan, aylık/yıllık, yapılandırılmadıysa dürüst uyarı

5) TED (AB ihaleleri)
   - server/ted.mjs: anonim TED v3 API, 3 mod (açık/tüm TR/AB'de kazanan TR)
   - rate-limit korumalı (429 HTML gövdesi ele alınır), çok dilli alan çözümü

6) Kırmızı bayrak & şeffaflık
   - analytics.js: redFlags (tek teklif, düşük kırım, tahmin üstü bedel,
     aşırı düşük teklif, tedarikçi yoğunlaşması) + marketConcentration (HHI)
   - RedFlags sekmesi: seviyeli uyarılar + yöntem şeffaflığı notu
2026-08-28 10:37:50 +03:00
hikmettan 3e129c2988 feat: tüm mock veriyi kaldır + Resend e-posta entegrasyonu
- mockTenders.js silindi; referans listeleri (sektör/il/kurum) constants.js'e taşındı
- AppContext: tenders/liveStats/competitorWatchlist/vaultDocuments/notifications/
  savedFilters/apiKeys/scrapedCapabilities → gerçek/boş; sahte Math.random online
  sayacı kaldırıldı; competitor & belge kasası artık localStorage
- LiveStatsBar: gerçek alanlar (canlı havuz/yüklü/bugün/kalem/takibimdeki)
- AnalyticsDashboard: KPI'lar (katılım/kazanma oranı/kazanılan bedel) + aylık hacim
  + sektörel dağılım gerçek companyStats/monthlyStats/sectorStats'tan; ciro hedefi
  kullanıcı tarafından belirlenir; veri yoksa dürüst boş durum
- ContractSearch & Rakip Radar: gerçek firmStats
- analytics.js: companyStats/monthlyStats/parseDateTR eklendi
- Resend: server/email.mjs + /api/notify/email/config + /api/notify/email/test
  (auth'lu, kullanıcının kendi adresine); PwaSettings gerçek e-posta test kartı
2026-08-28 08:49:10 +03:00
hikmettan 7521f798f6 fix: Rakip Radar widget'ını gerçek firmStats'a bağla (son mock kaldırıldı)
RightSidebar teaser widget COMPETITOR_DATA mock yerine canlı firmStats(tenders)
kullanıyor; veri yokken dürüst boş durum gösteriyor.
2026-08-28 08:17:00 +03:00
hikmettan 0ab74143c0 feat: demo özelliklerini gerçekleştir — canlı veri + localStorage
- analytics.js: firmStats/itemStats/provinceDensity (gerçek hesaplama)
- Rakip Radar: canlı veriden kazanan firma analizi (mock 3 firma kaldırıldı)
- Fiyat Analizi: gerçek kalem min/medyan/ort/maks + CSV export
- TurkeyHeatMap: 81 il gerçek interaktif yoğunluk haritası (tıkla→filtrele)
- Favori kurumlar: kart yıldızı + hızlı filtre (localStorage)
- Ekip yönetimi: gerçek CRUD (RightSidebar) + Kanban ataması ekipten
- Bildirimler: gerçek Notification API + izin + test + sessiz saatler
- PDF: jsPDF ile gerçek birim fiyat teklif cetveli (kaşe/imza alanı)
2026-08-28 01:58:46 +03:00
hikmettan 512d529af2 feat(legal): Gizlilik Politikası + Kullanım Şartları statik sayfaları (KVKK) + login linkleri 2026-08-28 01:37:14 +03:00
hikmettan 13f3e9d366 feat(auth): Google ile Giriş + landing/login + çalışan çıkış + admin rolü
- server/auth.mjs: ID token'ı oauth2.googleapis.com/tokeninfo ile doğrula (ek bağımlılık yok), HMAC imzalı app token, ADMIN_EMAILS ile admin
- POST /api/auth/google, GET /api/auth/google/config, GET /api/auth/me
- LoginPage: GIS butonlu landing/giriş; giriş yapılmadan uygulama açılmaz
- Header çıkış butonu artık çalışıyor; kullanıcı adı/avatar Google'dan
- AI Model Ayarları sekmesi sadece admin'e görünür
- Env: GOOGLE_CLIENT_ID + ADMIN_EMAILS (Coolify'dan)
2026-08-28 00:43:08 +03:00
hikmettan 1b28ebb603 fix(deploy): backend'i sabit PORT=3001'e zorla (Coolify PORT=80 nginx ile çakışıyordu) 2026-08-27 10:46:59 +03:00
hikmettan cc0b6b6017 feat(admin): AI model yönetimi paneli (fallback zinciri seçimi)
- 6 model kataloğu: GLM 5.3, DeepSeek V4 Flash, Gemini 3.7 Flash, Grok 4.6, GPT-5.6 Sol, Claude Opus 5
- Varsayılan fallback zinciri: GLM (birincil) → DeepSeek → Gemini
- Admin panelden sıralama, birincil seçme, ekleme/çıkarma; localStorage'da saklanır
- AI analizi aktif modeli kullanır
2026-08-27 09:44:34 +03:00
hikmettan 9ea46eb4c2 deploy: .dockerignore ekle (node_modules, dist, data imaja girmesin) 2026-08-26 21:29:53 +03:00
hikmettan 4f1ba88e94 deploy: canlı veri backend'ini container'a ekle (nginx /api → Node :3001)
- Dockerfile: nginx + Node tek container; backend arka planda, nginx ön planda
- nginx.conf: /api → 127.0.0.1:3001 proxy
- dt-store: node:sqlite guard (yoksa DT devre dışı, açık ihaleler yine canlı)
- Açık ihale canlı verisi sıfır npm bağımlılığıyla deploy'da çalışır
2026-08-26 21:28:25 +03:00
hikmettan 89cfd03d92 feat: canlı veri — ilan.gov.tr açık ihale + EKAP doğrudan temin (22/d)
- server/index.js: ilan.gov.tr proxy (açık ihale + kira/satış), TLS zincir düzeltmesi
- server/dt-worker.mjs: EKAP doğrudan temin toplayıcı (Playwright, imzasız oturum içi fetch)
- server/dt-store.mjs + il-plaka.mjs: DT verisini tender modeline çevirme
- src/services/tenderApi.js + AppContext: mock veri yerine canlı API
- Doğrudan Temin sekmesi 256 gerçek 22/d kaydıyla dolu
2026-08-26 20:45:02 +03:00