Give topic and institution subscription sets independent identities
This commit is contained in:
+51
-14
@@ -1,3 +1,4 @@
|
||||
import {createHash} from 'node:crypto';
|
||||
/**
|
||||
* Otomatik e-posta bildirimi abonelikleri (SQLite).
|
||||
* Kullanıcı kendi filtre kriterlerini kaydeder; zamanlayıcı yeni eşleşen
|
||||
@@ -34,7 +35,8 @@ CREATE TABLE IF NOT EXISTS abonelikler (
|
||||
sonGonderim TEXT,
|
||||
sonGorulenler TEXT, -- JSON: son gönderimde bildirilen ihale id'leri
|
||||
olusturma TEXT,
|
||||
UNIQUE(email, sehir, sektor, alim_turu, anahtar)
|
||||
criteria_key TEXT NOT NULL,
|
||||
UNIQUE(email, criteria_key)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_abone_email ON abonelikler(email);
|
||||
`;
|
||||
@@ -50,14 +52,48 @@ function open({ create = true } = {}) {
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true, mode:0o700 });
|
||||
}
|
||||
const db = new DatabaseSync(DB_PATH);
|
||||
fs.chmodSync(DB_PATH,0o600);
|
||||
db.exec('PRAGMA busy_timeout=5000');
|
||||
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;
|
||||
fs.chmodSync(DB_PATH,0o600);
|
||||
db.exec('PRAGMA busy_timeout=5000');
|
||||
db.exec('BEGIN IMMEDIATE');
|
||||
try {
|
||||
db.exec(SCHEMA);
|
||||
const columns=db.prepare('PRAGMA table_info(abonelikler)').all().map(column=>column.name);
|
||||
if(!columns.includes('konular'))db.exec('ALTER TABLE abonelikler ADD COLUMN konular TEXT');
|
||||
if(!columns.includes('criteria_key')) {
|
||||
const rows=db.prepare('SELECT * FROM abonelikler ORDER BY id').all();
|
||||
const sequence=db.prepare("SELECT seq FROM sqlite_sequence WHERE name='abonelikler'").get()?.seq || 0;
|
||||
db.exec('ALTER TABLE abonelikler RENAME TO abonelikler_legacy');
|
||||
db.exec(SCHEMA);
|
||||
const names=['id','email','ad','sehir','sektor','alim_turu','anahtar','kurumlar','konular','aktif','sonGonderim','sonGorulenler','olusturma','criteria_key'];
|
||||
const insert=db.prepare(`INSERT INTO abonelikler (${names.join(',')}) VALUES (${names.map(()=>'?').join(',')})`);
|
||||
const used=new Set();
|
||||
for(const row of rows) {
|
||||
const key=subscriptionKey({sehir:row.sehir,sektor:row.sektor,alimTuru:row.alim_turu,anahtar:row.anahtar,
|
||||
konular:JSON.parse(row.konular || '[]'),kurumlar:JSON.parse(row.kurumlar || '[]')});
|
||||
const identity=JSON.stringify([row.email,key]);
|
||||
// Preserve legacy duplicates and their delivery history; do not discard records.
|
||||
row.criteria_key=used.has(identity)?key+':legacy:'+row.id:key;used.add(identity);
|
||||
insert.run(...names.map(name=>row[name] ?? null));
|
||||
}
|
||||
db.exec('DROP TABLE abonelikler_legacy');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_abone_email ON abonelikler(email)');
|
||||
const current=db.prepare("SELECT seq FROM sqlite_sequence WHERE name='abonelikler'").get();
|
||||
if(current)db.prepare("UPDATE sqlite_sequence SET seq=? WHERE name='abonelikler'").run(Math.max(sequence,current.seq));
|
||||
else db.prepare('INSERT INTO sqlite_sequence(name,seq) VALUES (?,?)').run('abonelikler',sequence);
|
||||
}
|
||||
db.exec('COMMIT');
|
||||
}catch(error){db.exec('ROLLBACK');throw error;}
|
||||
return db;
|
||||
}catch(error){db.close();throw error;}
|
||||
}
|
||||
|
||||
export function subscriptionKey(criteria) {
|
||||
const normal=value=>String(value || '').trim().toLocaleLowerCase('tr-TR');
|
||||
const set=values=>{if(!Array.isArray(values))throw new Error('Abonelik konu/kurum listesi geçersiz.');return [...new Set(values.map(normal))].sort();};
|
||||
return createHash('sha256').update(JSON.stringify([
|
||||
normal(criteria.sehir),normal(criteria.sektor),normal(criteria.alimTuru),normal(criteria.anahtar),set(criteria.konular || []),set(criteria.kurumlar || [])
|
||||
])).digest('hex');
|
||||
}
|
||||
|
||||
const bosaNull = (v) => (v == null || v === '' ? '' : String(v).trim());
|
||||
@@ -70,9 +106,9 @@ export function subscribe(email, kriter = {}, ad = null) {
|
||||
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
|
||||
INSERT INTO abonelikler (email, ad, sehir, sektor, alim_turu, anahtar, kurumlar, konular, aktif, olusturma, criteria_key)
|
||||
VALUES (?,?,?,?,?,?,?,?,1,?,?)
|
||||
ON CONFLICT(email, criteria_key) DO UPDATE SET
|
||||
aktif=1, kurumlar=excluded.kurumlar, konular=excluded.konular, ad=excluded.ad
|
||||
`).run(
|
||||
email,
|
||||
@@ -81,9 +117,10 @@ export function subscribe(email, kriter = {}, ad = null) {
|
||||
bosaNull(kriter.sektor),
|
||||
bosaNull(kriter.alimTuru),
|
||||
bosaNull(kriter.anahtar),
|
||||
JSON.stringify(kriter.kurumlar || []),
|
||||
JSON.stringify(kriter.konular || []),
|
||||
new Date().toISOString()
|
||||
JSON.stringify((kriter.kurumlar || []).map(value=>value.trim())),
|
||||
JSON.stringify((kriter.konular || []).map(value=>value.trim())),
|
||||
new Date().toISOString(),
|
||||
subscriptionKey(kriter)
|
||||
);
|
||||
return list(email);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user