spamreport is using the new storage. see #675

This commit is contained in:
mic 2026-03-22 22:29:31 +01:00
parent b30da27c84
commit 55240868dd
3 changed files with 106 additions and 26 deletions

View file

@ -16,11 +16,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { taStorage } from './mzta-storage.js';
export const taSpamReport = { export const taSpamReport = {
logger: console, logger: console,
do_debug: false,
_data_prefix: 'mzta-spam-report-', _data_prefix: 'mzta-spam-report-',
_processing_prefix: 'mzta-spam-processing-', _processing_prefix: 'mzta-spam-processing-',
_max_reports: 100, _max_reports: 100,
_storage: null,
_getStorage() {
if (!this._storage) this._storage = new taStorage(this.do_debug);
return this._storage;
},
async setProcessing(data_id) { async setProcessing(data_id) {
const key = this._processing_prefix + data_id; const key = this._processing_prefix + data_id;
@ -34,8 +43,7 @@ export const taSpamReport = {
}, },
async saveReportData(data, data_id) { async saveReportData(data, data_id) {
const key = this._data_prefix + data_id; await this._getStorage().writeSpam(data_id, data, true);
await browser.storage.session.set({ [key]: data });
await browser.storage.session.remove(this._processing_prefix + data_id); await browser.storage.session.remove(this._processing_prefix + data_id);
}, },
@ -51,47 +59,53 @@ export const taSpamReport = {
}, },
async loadReportData(data_id) { async loadReportData(data_id) {
const key = this._data_prefix + data_id; let record = await this._getStorage().getRecord(data_id);
let output = await browser.storage.session.get(key); if (!record || !this._getStorage().hasField(record, 'spam')) return null;
return output[key] || null; let spam = record.spam;
return {
headerMessageId: data_id,
spamValue: spam.spamValue,
explanation: spam.explanation,
report_date: new Date(spam.ts),
subject: spam.subject,
from: spam.from,
message_date: spam.message_date,
moved: spam.moved,
SpamThreshold: spam.SpamThreshold,
};
}, },
async removeReportData(data_id) { async removeReportData(data_id) {
const key = this._data_prefix + data_id; await this._getStorage().deleteSpamField(data_id);
await browser.storage.session.remove(key);
await browser.storage.session.remove(this._processing_prefix + data_id); await browser.storage.session.remove(this._processing_prefix + data_id);
}, },
async getAllReportData() { async getAllReportData() {
let allData = await browser.storage.session.get(null); return await this._getStorage().getAllSpamRecords();
let reportData = {};
for (const [key, value] of Object.entries(allData)) {
if (key.startsWith(this._data_prefix)) {
reportData[key.replace(this._data_prefix, '')] = value;
}
}
return reportData;
}, },
async clearReportData() { async clearReportData() {
let allData = await browser.storage.session.get(null); let storage = this._getStorage();
let keysToDelete = Object.keys(allData).filter(key => key.startsWith(this._data_prefix) || key.startsWith(this._processing_prefix)); let allSpam = await storage.getAllSpamRecords();
for (let messageId of Object.keys(allSpam)) {
await storage.deleteSpamField(messageId);
}
let allSession = await browser.storage.session.get(null);
let keysToDelete = Object.keys(allSession).filter(k => k.startsWith(this._processing_prefix));
for (let key of keysToDelete) { for (let key of keysToDelete) {
await browser.storage.session.remove(key); await browser.storage.session.remove(key);
} }
}, },
async truncReportData() { async truncReportData() {
let data = await this.getAllReportData(); let data = await this._getStorage().getAllSpamRecords();
let sortedData = this.sortReportsByDate(data); let sortedData = this.sortReportsByDate(data);
let keys = Object.keys(sortedData); let keys = Object.keys(sortedData);
if (keys.length > this._max_reports) { if (keys.length > this._max_reports) {
let storage = this._getStorage();
for (let i = this._max_reports; i < keys.length; i++) { for (let i = this._max_reports; i < keys.length; i++) {
await browser.storage.session.remove(this._data_prefix + keys[i]); await storage.deleteSpamField(keys[i]);
} }
} }
}, },

View file

@ -72,11 +72,11 @@ export class taStorage {
/** /**
* Write the spam field for a given Message-ID. * Write the spam field for a given Message-ID.
* @param {string} messageId - The Message-ID header string. * @param {string} messageId - The Message-ID header string.
* @param {number} score - Spam score (float 0-1). * @param {object} report_data - The full spam report object with fields:
* @param {string} reason - Textual motivation for the score. * spamValue, explanation, subject, from, message_date, moved, SpamThreshold.
* @param {boolean} [force=true] - If true, overwrite existing spam data. * @param {boolean} [force=true] - If true, overwrite existing spam data.
*/ */
async writeSpam(messageId, score, reason, force = true) { async writeSpam(messageId, report_data, force = true) {
try { try {
let key = this._buildKey(messageId); let key = this._buildKey(messageId);
let record = await this.getRecord(messageId) || { v: taStorage.SCHEMA_VERSION }; let record = await this.getRecord(messageId) || { v: taStorage.SCHEMA_VERSION };
@ -84,7 +84,18 @@ export class taStorage {
return; return;
} }
let now = Date.now(); let now = Date.now();
record.spam = { score: score, reason: reason, ts: now }; record.spam = {
spamValue: report_data.spamValue,
explanation: report_data.explanation,
subject: report_data.subject,
from: report_data.from,
message_date: report_data.message_date instanceof Date
? report_data.message_date.toISOString()
: report_data.message_date,
moved: report_data.moved,
SpamThreshold: report_data.SpamThreshold,
ts: now,
};
record.ts = now; record.ts = now;
await messenger.storage.local.set({ [key]: record }); await messenger.storage.local.set({ [key]: record });
} catch (e) { } catch (e) {
@ -92,6 +103,60 @@ export class taStorage {
} }
} }
/**
* Get all records that contain a spam field.
* @returns {Promise<object>} Map of messageId -> spam data object (legacy shape).
*/
async getAllSpamRecords() {
try {
let all = await messenger.storage.local.get(null);
let result = {};
for (let [key, record] of Object.entries(all)) {
if (!key.startsWith(taStorage.STORAGE_KEY_PREFIX)) continue;
if (!this.hasField(record, 'spam')) continue;
let messageId = key.slice(taStorage.STORAGE_KEY_PREFIX.length);
let spam = record.spam;
result[messageId] = {
headerMessageId: messageId,
spamValue: spam.spamValue,
explanation: spam.explanation,
report_date: new Date(spam.ts),
subject: spam.subject,
from: spam.from,
message_date: spam.message_date,
moved: spam.moved,
SpamThreshold: spam.SpamThreshold,
};
}
return result;
} catch (e) {
this.taLog.error('getAllSpamRecords error: ' + e);
return {};
}
}
/**
* Delete only the spam field from a record.
* Deletes the entire record if no other data fields remain.
* @param {string} messageId - The Message-ID header string.
*/
async deleteSpamField(messageId) {
try {
let key = this._buildKey(messageId);
let record = await this.getRecord(messageId);
if (!record || !('spam' in record)) return;
delete record.spam;
const remainingFields = Object.keys(record).filter(k => k !== 'v' && k !== 'ts');
if (remainingFields.length === 0) {
await messenger.storage.local.remove(key);
} else {
await messenger.storage.local.set({ [key]: record });
}
} catch (e) {
this.taLog.error('deleteSpamField error: ' + e);
}
}
/** /**
* Write the summary field for a given Message-ID. * Write the summary field for a given Message-ID.
* @param {string} messageId - The Message-ID header string. * @param {string} messageId - The Message-ID header string.

View file

@ -1043,6 +1043,7 @@ const newEmailListener = (folder, messagesList) => {
let messages = getMessages(messagesList); let messages = getMessages(messagesList);
taSpamReport.logger = taLog; taSpamReport.logger = taLog;
taSpamReport.do_debug = prefs_init.do_debug;
let add_tags_auto_enabled = prefs_init.add_tags && prefs_init.add_tags_auto; let add_tags_auto_enabled = prefs_init.add_tags && prefs_init.add_tags_auto;