feat: havuz izleme — ölçüm geçmişi, ani düşüş/eksik yükleme uyarısı, /api/health/havuz
This commit is contained in:
+53
-1
@@ -24,7 +24,7 @@ import { billingConfigured, webhookConfigured, planCatalog, createCheckoutSessio
|
||||
import * as store from './user-store.mjs';
|
||||
import { planOf, haklar, kullanim, kotaKontrol, ozellikVar } from './entitlements.mjs';
|
||||
import { searchTed } from './ted.mjs';
|
||||
import { readHistory, historyStats } from './history-store.mjs';
|
||||
import { readHistory, historyStats, havuzOlc, havuzGecmisi } from './history-store.mjs';
|
||||
import { startScheduler, schedulerStatus } from './scheduler.mjs';
|
||||
import { searchKik, kararMetni } from './kik.mjs';
|
||||
import { documentUrl, ilanMetni, documentText, browserAvailable } from './documents.mjs';
|
||||
@@ -324,6 +324,7 @@ async function arkaPlandaTamamla(numFound, ilkListe) {
|
||||
cache.todayCount = yeni.filter((t) => t.isYeni).length;
|
||||
}
|
||||
console.log(`[ihaleist-api] havuz hazır: ${cache.tenders.length}/${numFound} ilan`);
|
||||
havuzKaydet(numFound, cache.tenders.length);
|
||||
} catch (e) {
|
||||
console.warn('[ihaleist-api] arka plan doldurma hatası:', e.message);
|
||||
} finally {
|
||||
@@ -331,6 +332,40 @@ async function arkaPlandaTamamla(numFound, ilkListe) {
|
||||
}
|
||||
}
|
||||
|
||||
// Havuz büyüklüğünü kaydeder; ani düşüş / eksik yükleme varsa yöneticiyi uyarır.
|
||||
let sonHavuzUyarisi = 0;
|
||||
function dtSayisiGuvenli() {
|
||||
try { return readDtTenders(1)?.totalCount ?? null; } catch { return null; }
|
||||
}
|
||||
function havuzKaydet(kaynakToplam, yuklenen) {
|
||||
try {
|
||||
const ist = historyStats?.() || {};
|
||||
const sonuc = havuzOlc({
|
||||
kaynakToplam,
|
||||
yuklenen,
|
||||
dt: dtSayisiGuvenli(),
|
||||
arsiv: ist.toplam ?? null,
|
||||
});
|
||||
if (!sonuc?.uyari) return;
|
||||
console.warn(`[havuz-izleme] ${sonuc.uyari}`);
|
||||
// Aynı uyarıyı sürekli göndermemek için 6 saatte bir e-posta.
|
||||
if (Date.now() - sonHavuzUyarisi < 6 * 3600e3) return;
|
||||
sonHavuzUyarisi = Date.now();
|
||||
const alicilar = adminEmails();
|
||||
if (!alicilar.length || !emailConfigured()) return;
|
||||
sendEmail({
|
||||
to: alicilar,
|
||||
subject: sonuc.tur === 'eksik_yukleme'
|
||||
? 'İhaleist: havuz eksik yüklendi'
|
||||
: 'İhaleist: canlı ihale havuzu ani düştü',
|
||||
text: `${sonuc.uyari}\n\nÖnceki ölçüm: ${sonuc.onceki ?? '-'}\nŞimdi: ${sonuc.simdi ?? '-'}\n\n` +
|
||||
`Ayrıntı: ${process.env.APP_URL || 'https://ihaleist.seymata.com'}/api/health/havuz`,
|
||||
}).catch((e) => console.warn('[havuz-izleme] uyarı e-postası gönderilemedi:', e.message));
|
||||
} catch (e) {
|
||||
console.warn('[havuz-izleme] ölçüm alınamadı:', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function getTenders(force = false) {
|
||||
const fresh = cache.tenders && Date.now() - cache.fetchedAt < CACHE_TTL_MS;
|
||||
if (fresh && !force) return cache;
|
||||
@@ -930,6 +965,23 @@ const server = http.createServer(async (req, res) => {
|
||||
}
|
||||
|
||||
if (url.pathname === '/api/health') return json(res, 200, { ok: true });
|
||||
if (url.pathname === '/api/health/havuz') {
|
||||
const gecmis = havuzGecmisi(Number(url.searchParams.get('limit')) || 60);
|
||||
const son = gecmis[0] || null;
|
||||
// Son 7 günün en yükseği — bugünkü sayı buna göre kıyaslanır.
|
||||
const haftaBas = new Date(Date.now() - 7 * 864e5).toISOString();
|
||||
const hafta = gecmis.filter((r) => r.zaman >= haftaBas);
|
||||
const zirve = hafta.reduce((m, r) => Math.max(m, r.kaynak_toplam || 0), 0);
|
||||
return json(res, 200, {
|
||||
success: true,
|
||||
son,
|
||||
haftalikZirve: zirve || null,
|
||||
zirveyeOran: son?.kaynak_toplam && zirve ? Number((son.kaynak_toplam / zirve).toFixed(3)) : null,
|
||||
olcumSayisi: gecmis.length,
|
||||
gecmis,
|
||||
});
|
||||
}
|
||||
|
||||
if (url.pathname === '/api/health/scheduler') {
|
||||
return json(res, 200, { success: true, tarayiciVar: await browserAvailable(), ...schedulerStatus() });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user