Add audited administrator resolution of uncertain email deliveries
This commit is contained in:
+29
-1
@@ -40,6 +40,7 @@ CREATE TABLE IF NOT EXISTS abonelikler (
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_abone_email ON abonelikler(email);
|
||||
CREATE TABLE IF NOT EXISTS notification_outbox(id TEXT PRIMARY KEY,subscription_id INTEGER NOT NULL,payload TEXT NOT NULL,tender_ids TEXT NOT NULL,state TEXT NOT NULL,first_attempt INTEGER,lease TEXT,lease_until INTEGER,error TEXT,provider_id TEXT);
|
||||
CREATE TABLE IF NOT EXISTS notification_outbox_reviews(id TEXT PRIMARY KEY,actor TEXT NOT NULL,action TEXT NOT NULL,note TEXT NOT NULL,provider_id TEXT,reviewed_at TEXT NOT NULL);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS outbox_active ON notification_outbox(subscription_id) WHERE state IN ('pending','sending','uncertain');
|
||||
`;
|
||||
|
||||
@@ -166,7 +167,7 @@ export function remove(id, email) {
|
||||
db.exec('BEGIN IMMEDIATE');
|
||||
try {
|
||||
const r=db.prepare('DELETE FROM abonelikler WHERE id=? AND email=?').run(id,email);
|
||||
if(r.changes)db.prepare("UPDATE notification_outbox SET state='cancelled' WHERE subscription_id=? AND state!='sent'").run(id);
|
||||
if(r.changes)db.prepare("UPDATE notification_outbox SET state='cancelled' WHERE subscription_id=? AND state IN ('pending','sending','uncertain')").run(id);
|
||||
db.exec('COMMIT');return r.changes>0;
|
||||
}catch(error){db.exec('ROLLBACK');throw error;}
|
||||
} finally {
|
||||
@@ -235,3 +236,30 @@ export function failDelivery(delivery,error) {
|
||||
try{return db.prepare("UPDATE notification_outbox SET state='pending',error=? WHERE id=? AND lease=? AND state='sending'").run(String(error?.message || error),delivery.id,delivery.lease).changes>0;}
|
||||
finally{db.close();}
|
||||
}
|
||||
|
||||
export function deliveryQueue() {
|
||||
const db=open();
|
||||
try{return db.prepare("SELECT o.*,r.actor,r.action,r.note,r.reviewed_at FROM notification_outbox o LEFT JOIN notification_outbox_reviews r ON r.id=o.id WHERE o.state IN ('pending','sending','uncertain','sent_manual','skipped') ORDER BY o.first_attempt DESC LIMIT 50").all().map(row=>{
|
||||
const payload=JSON.parse(row.payload);
|
||||
return {id:row.id,subscriptionId:row.subscription_id,to:payload.to,subject:payload.subject,state:row.state,error:row.error,
|
||||
firstAttempt:row.first_attempt,providerId:row.provider_id,review:row.action?{actor:row.actor,action:row.action,note:row.note,at:row.reviewed_at}:null};
|
||||
});}finally{db.close();}
|
||||
}
|
||||
export function resolveDelivery(id,actor,input) {
|
||||
if(typeof id!=='string'||!id||!actor||!['confirmed_sent','skip'].includes(input?.action)||typeof input.note!=='string'||input.note.trim().length<20||input.note.length>2000)throw new Error('Karar ve en az 20 karakterlik kontrol açıklaması gerekli.');
|
||||
if(input.action==='confirmed_sent'&&(input.confirmed!==true||typeof input.providerId!=='string'||!input.providerId.trim()||input.providerId.length>200))throw new Error('Sağlayıcı gönderim kimliği ve kontrol onayı gerekli.');
|
||||
const db=open();
|
||||
try {
|
||||
db.exec('BEGIN IMMEDIATE');
|
||||
const row=db.prepare("SELECT * FROM notification_outbox WHERE id=? AND state='uncertain'").get(id);
|
||||
if(!row)throw Object.assign(new Error('Kayıt artık inceleme beklemiyor. Listeyi yenileyin.'),{status:409});
|
||||
const subscription=db.prepare('SELECT sonGorulenler FROM abonelikler WHERE id=?').get(row.subscription_id);
|
||||
if(!subscription)throw new Error('Abonelik artık yok.');
|
||||
const seen=[...new Set([...JSON.parse(subscription.sonGorulenler||'[]'),...JSON.parse(row.tender_ids)])];
|
||||
const now=new Date().toISOString(),sent=input.action==='confirmed_sent';
|
||||
db.prepare('UPDATE abonelikler SET sonGorulenler=?,sonGonderim=CASE WHEN ? THEN ? ELSE sonGonderim END WHERE id=?').run(JSON.stringify(seen),sent?1:0,now,row.subscription_id);
|
||||
db.prepare('INSERT INTO notification_outbox_reviews VALUES (?,?,?,?,?,?)').run(id,actor,input.action,input.note.trim(),sent?input.providerId.trim():null,now);
|
||||
db.prepare('UPDATE notification_outbox SET state=?,provider_id=?,error=NULL WHERE id=?').run(sent?'sent_manual':'skipped',sent?input.providerId.trim():null,id);
|
||||
db.exec('COMMIT');return {id,state:sent?'sent_manual':'skipped'};
|
||||
}catch(error){try{db.exec('ROLLBACK');}catch{}throw error;}finally{db.close();}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user