/** * Otomatik e-posta bildirimi abonelikleri (SQLite). * Kullanıcı kendi filtre kriterlerini kaydeder; zamanlayıcı yeni eşleşen * ihaleleri bulup özet e-posta gönderir. */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; let DatabaseSync = null; try { ({ DatabaseSync } = await import('node:sqlite')); } catch { console.warn('[notify-store] node:sqlite yok — otomatik bildirim devre dışı'); } const __dirname = path.dirname(fileURLToPath(import.meta.url)); const DATA_DIR = path.join(__dirname, 'data'); const DB_PATH = path.join(DATA_DIR, 'notify.sqlite'); const SCHEMA = ` CREATE TABLE IF NOT EXISTS abonelikler ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL, ad TEXT, sehir TEXT, sektor TEXT, alim_turu TEXT, anahtar TEXT, kurumlar TEXT, -- JSON: favori kurum listesi konular TEXT, -- JSON: konu başlıkları (kullanıcı seçer) aktif INTEGER NOT NULL DEFAULT 1, sonGonderim TEXT, sonGorulenler TEXT, -- JSON: son gönderimde bildirilen ihale id'leri olusturma TEXT, UNIQUE(email, sehir, sektor, alim_turu, anahtar) ); CREATE INDEX IF NOT EXISTS idx_abone_email ON abonelikler(email); `; export function available() { return Boolean(DatabaseSync); } function open({ create = true } = {}) { if (!DatabaseSync) return null; if (!fs.existsSync(DB_PATH)) { if (!create) return null; fs.mkdirSync(DATA_DIR, { recursive: true }); } const db = new DatabaseSync(DB_PATH); db.exec(SCHEMA); try { const k = db.prepare('PRAGMA table_info(abonelikler)').all().map((c) => c.name); if (!k.includes('konular')) db.exec('ALTER TABLE abonelikler ADD COLUMN konular TEXT'); } catch (e) { console.warn('[notify-store] şema geçişi:', e.message); } return db; } const bosaNull = (v) => (v == null || v === '' ? '' : String(v).trim()); export function subscribe(email, kriter = {}, ad = null) { const db = open(); if (!db) throw new Error('Bildirim deposu kullanılamıyor'); try { db.prepare(` INSERT INTO abonelikler (email, ad, sehir, sektor, alim_turu, anahtar, kurumlar, konular, aktif, olusturma) VALUES (?,?,?,?,?,?,?,?,1,?) ON CONFLICT(email, sehir, sektor, alim_turu, anahtar) DO UPDATE SET aktif=1, kurumlar=excluded.kurumlar, konular=excluded.konular, ad=excluded.ad `).run( email, ad, bosaNull(kriter.sehir), bosaNull(kriter.sektor), bosaNull(kriter.alimTuru), bosaNull(kriter.anahtar), JSON.stringify(kriter.kurumlar || []), JSON.stringify(kriter.konular || []), new Date().toISOString() ); return list(email); } finally { db.close(); } } export function list(email) { const db = open({ create: false }); if (!db) return []; try { const rows = email ? db.prepare('SELECT * FROM abonelikler WHERE email=? ORDER BY id DESC').all(email) : db.prepare('SELECT * FROM abonelikler WHERE aktif=1 ORDER BY id').all(); return rows.map((r) => ({ id: r.id, email: r.email, ad: r.ad, sehir: r.sehir || null, sektor: r.sektor || null, alimTuru: r.alim_turu || null, anahtar: r.anahtar || null, kurumlar: (() => { try { return JSON.parse(r.kurumlar || '[]'); } catch { return []; } })(), konular: (() => { try { return JSON.parse(r.konular || '[]'); } catch { return []; } })(), aktif: Boolean(r.aktif), sonGonderim: r.sonGonderim, sonGorulenler: (() => { try { return JSON.parse(r.sonGorulenler || '[]'); } catch { return []; } })(), })); } finally { db.close(); } } export function remove(id, email) { const db = open({ create: false }); if (!db) return false; try { const r = db.prepare('DELETE FROM abonelikler WHERE id=? AND email=?').run(id, email); return r.changes > 0; } finally { db.close(); } } export function markSent(id, ihaleIdleri) { const db = open({ create: false }); if (!db) return; try { db.prepare('UPDATE abonelikler SET sonGonderim=?, sonGorulenler=? WHERE id=?') .run(new Date().toISOString(), JSON.stringify(ihaleIdleri.slice(0, 400)), id); } finally { db.close(); } }