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.
This commit is contained in:
2026-08-29 10:19:44 +03:00
parent b44b45c67e
commit f73c7bd6cb
6 changed files with 350 additions and 114 deletions
+14
View File
@@ -27,6 +27,7 @@ import { searchKik, kararMetni } from './kik.mjs';
import { documentUrl, ilanMetni, documentText, browserAvailable } from './documents.mjs';
import { subscribe, list as listSubs, remove as removeSub, available as notifyAvailable } from './notify-store.mjs';
import { bildirimleriIsle } from './notifier.mjs';
import { scanSite } from './site-scan.mjs';
// ilan.gov.tr sunucusu ara sertifikayı (GeoTrust TLS RSA CA G1) göndermiyor;
// zinciri tamamlamak için PEM'i yerelden ekliyoruz.
@@ -590,6 +591,19 @@ const server = http.createServer(async (req, res) => {
}
}
// --- Firma web sitesi tarama (gerçek fetch + AI) ---
if (url.pathname === '/api/company/scan' && req.method === 'POST') {
const user = userFromAuthHeader(req.headers['authorization']);
if (!user) return json(res, 401, { success: false, error: 'oturum yok' });
try {
const body = await readBody(req);
const out = await scanSite(body.url);
return json(res, 200, { success: true, ...out });
} catch (e) {
return json(res, 502, { success: false, error: e.message });
}
}
// --- Otomatik bildirim abonelikleri ---
if (url.pathname === '/api/notify/subscriptions') {
const user = userFromAuthHeader(req.headers['authorization']);