From c64889782f4c5b0bbed9d02f2debb8f764ba3b6a Mon Sep 17 00:00:00 2001 From: ht Date: Sat, 29 Aug 2026 03:22:03 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20y=C3=B6netici=20i=C3=A7in=20'=C5=9Fimdi?= =?UTF-8?q?=20g=C3=B6nder'=20(POST=20/api/notify/run=3Fzorla=3D1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- server/index.js | 2 +- server/notifier.mjs | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/server/index.js b/server/index.js index f4555b7..411b2b1 100644 --- a/server/index.js +++ b/server/index.js @@ -617,7 +617,7 @@ const server = http.createServer(async (req, res) => { const user = userFromAuthHeader(req.headers['authorization']); if (!user?.isAdmin) return json(res, 403, { success: false, error: 'yönetici yetkisi gerekli' }); try { - const sonuc = await bildirimleriIsle(() => tendersPayload()); + const sonuc = await bildirimleriIsle(() => tendersPayload(), { zorla: url.searchParams.get('zorla') === '1' }); return json(res, 200, { success: true, ...sonuc }); } catch (e) { return json(res, 502, { success: false, error: e.message }); diff --git a/server/notifier.mjs b/server/notifier.mjs index 27fab71..429ae88 100644 --- a/server/notifier.mjs +++ b/server/notifier.mjs @@ -41,7 +41,7 @@ function bildirilebilir(t) { * Tüm aktif abonelikleri işler. * @param {Function} tenderGetir () => Promise<{tenders: []}> */ -export async function bildirimleriIsle(tenderGetir) { +export async function bildirimleriIsle(tenderGetir, { zorla = false } = {}) { if (process.env.NOTIFY_ENABLED === '0') return { atlandi: 'kapalı' }; if (!available()) return { atlandi: 'depo yok' }; if (!emailConfigured()) return { atlandi: 'e-posta yapılandırılmamış' }; @@ -63,11 +63,13 @@ export async function bildirimleriIsle(tenderGetir) { const yeniler = eslesen.filter((t) => !gorulen.has(t.id)); // İlk kurulumda geçmişi spam'lemeyelim: sadece işaretle, gönderme. - if (!a.sonGonderim) { + // zorla=true ise (yönetici "şimdi gönder" dediğinde) bu adım atlanır. + if (!a.sonGonderim && !zorla) { markSent(a.id, eslesen.map((t) => t.id)); continue; } - if (!yeniler.length) continue; + const gonderilecek = zorla && !yeniler.length ? eslesen.slice(0, maxAdet) : yeniler; + if (!gonderilecek.length) continue; const kriterMetni = [ a.sehir && `İl: ${a.sehir}`, @@ -77,20 +79,20 @@ export async function bildirimleriIsle(tenderGetir) { a.kurumlar?.length && `${a.kurumlar.length} favori kurum`, ].filter(Boolean).join(' · ') || 'Tüm ihaleler'; - const html = tenderDigestHtml({ name: a.ad || a.email, tenders: yeniler.slice(0, maxAdet) }) + const html = tenderDigestHtml({ name: a.ad || a.email, tenders: gonderilecek.slice(0, maxAdet) }) + `

- Kriteriniz: ${kriterMetni} — toplam ${yeniler.length} yeni ihale. + Kriteriniz: ${kriterMetni} — toplam ${gonderilecek.length} ihale. Bu bildirimi uygulamadaki Mobil PWA → E-posta Bildirimi bölümünden kapatabilirsiniz.

`; await sendEmail({ to: a.email, - subject: `İhaleist — ${yeniler.length} yeni ihale (${kriterMetni})`, + subject: `İhaleist — ${gonderilecek.length} ihale (${kriterMetni})`, html, }); markSent(a.id, eslesen.map((t) => t.id)); gonderilen++; - console.log(`[notifier] ${a.email} → ${yeniler.length} yeni ihale gönderildi (${kriterMetni})`); + console.log(`[notifier] ${a.email} → ${gonderilecek.length} ihale gönderildi (${kriterMetni})`); } catch (e) { hata++; console.warn(`[notifier] abonelik ${a.id} gönderilemedi: ${e.message}`);