İki ayrı neden vardı:
1) window.open sunucu yanıtından SONRA çağrılıyordu; Safari, PWA ve sıkı ayarlı
Chrome bunu açılır-pencere sayıp sessizce engelliyordu — düğme "çalışmıyor"
görünüyordu. Sekme artık tıklamanın hemen içinde boş açılır, adres gelince
yönlendirilir. Engellenirse gerçek bir bağlantı (a href) gösterilir; bağlantı
tıklaması hiçbir tarayıcıda engellenmez.
2) ilan.gov.tr bazı kayıtlarda İKN'yi başta/sonda boşluk ve kesme boşluğu (nbsp)
ile veriyor (" 2026/1622878"). Bu 50 kayıtta panel "EKAP formatında değil"
deyip düğmeyi hiç göstermiyordu. İKN artık alınırken boşluksuz saklanıyor;
panel ve doküman araması da savunma amaçlı temizliyor.
Ayrıca: "EKAP'ta 0 doküman kayıtlı" yanıltıcı metni kaldırıldı (EKAP arama
kaydı sayıyı vermiyor), EKAP'ın önce güvenlik doğrulaması istediği açıklandı.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- server/ted-ceviri.mjs: TED'in kendi Türkçe alanı varsa onu kullanır; yoksa
OpenRouter ile toplu çevirir (deepseek-v4-flash → gemini 3.7 flash → glm),
sonucu server/data/ted.sqlite'ta ilan numarasına göre saklar
- İstek en fazla 12 sn bekler, kalan çeviriler arka planda sürer;
yanıt ceviriBekleniyor bayrağı taşır, ön yüz sessizce yeniden çeker
- Kartta Türkçe başlık + küçük özgün başlık, Türkçe açıklama (3 satır),
ülke kodları Türkçe ad (TUR → Türkiye, AUT → Avusturya…)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Panelde seçilen modeller tıkanınca (limit/429) şartname analizi ve teklif asistanı
boş kalıyordu; analyzeSpec zincire yoksa 'gemini'yi sona ekler. Panel ve kılavuz notu.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Günlük Bülten ve İhale Takvimi sabit "10.08.2026" yerine bugünden hesaplar;
takvim 7 günü gerçek ihale tarihleriyle sayar
- Ekip/üye/firma varsayılanları oturumdaki kullanıcıdan; sahte "Ekip Notları"
ve sabit "%94 Uyum" rozeti kaldırıldı (gerçek uyum skoru varsa gösterilir)
- Kılavuz düğmesi document.body'ye portal (ekran dışına düşüyordu)
- Detay modalı havuzdaki güncel kaydı okur: Takip Et / Görev aç etiketleri güncellenir
- Mobil: başlıkta ikincil düğmeler md altında gizli, arama formu küçülebilir
- PWA sayfası: sahte önbellek sayıları yerine Cache API ve bellek sayımı
- Sözleşme sorgula pazar payı bir ondalık (hep %0 görünüyordu)
- Rakip Radar "SMS" vaadi düzeltildi; sektör=CPV etiketi tekrarı gizlendi;
Stripe kurulum uyarısı yalnız yöneticiye
- Açılışta 7 gereksiz PUT /api/me/data: ilk yükleme yazımı atlanır
- server: teklif taslağı çökmesi (kalemOku nesne döndürür), denetim yanıtından
model adı kaldırıldı, gereklilikler satır bazlı; derin analiz 12k token +
kesik JSON onarımı
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sonuc ayristirici yaklasik maliyeti TUM ihale icin okuyor
((?<!Kısımlarının )Yaklaşık Maliyeti), sozlesme bedeli ise kisim basina
satir. 11 kisimlik ilac ihalesinin ilk kismi 1,6 milyon tahmine karsi 25 bine
verilmisse rakip radarinda "%98 kirim" cikiyordu — aritmetik kurgu.
Kirim artik yalnizca tek sonuc satiri olan (kisimsiz) ihalelerde
hesaplaniyor. Yanlis bir oran, hic oran olmamasindan kotudur.
Test: A (kisimsiz) → %20; B ve C (ayni IKN, iki kisim) → null.
Bulgu kaynagi: erenulutas0/MOBIT-DURT README "olcerek cozulmus uc tuzak".
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>
- 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"
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
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.
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ı)
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.
- 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
- 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ı
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
- 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ı)
- 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)