@bobfrankston/rmfmail 1.2.186 → 1.2.188
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/app.bundle.js +18 -0
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +4 -2
- package/client/app.js.map +1 -1
- package/client/app.ts +4 -2
- package/client/components/message-viewer.js +22 -0
- package/client/components/message-viewer.js.map +1 -1
- package/client/components/message-viewer.ts +18 -0
- package/client/package.json +1 -1
- package/client/styles/layout.css +20 -5
- package/package.json +1 -1
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-store/db.d.ts +6 -2
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +46 -16
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +53 -24
- package/packages/mailx-store/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-57292 → node_modules.npmglobalize-stash-33816}/.package-lock.json +0 -0
|
@@ -1759,8 +1759,12 @@ export class MailxDB {
|
|
|
1759
1759
|
* During the additive migration we ALSO have to handle the legacy
|
|
1760
1760
|
* case where the row's folder_id matches but no message_folders row
|
|
1761
1761
|
* was migrated for it (race during initial population, etc.) — we
|
|
1762
|
-
* fall back to the messages.folder_id check to clean up consistently.
|
|
1763
|
-
|
|
1762
|
+
* fall back to the messages.folder_id check to clean up consistently.
|
|
1763
|
+
*
|
|
1764
|
+
* `reason`/`source` override the audit text — deleteMessage routes user
|
|
1765
|
+
* deletes through here, and "reconcile: server missing this UID" would
|
|
1766
|
+
* be a lie in that audit trail. */
|
|
1767
|
+
dropFolderMembership(accountId: string, folderId: number, uid: number, folderPath?: string, reason?: string, source?: string): boolean {
|
|
1764
1768
|
// First find the messages row this membership pointed at.
|
|
1765
1769
|
const mf = this.db.prepare(
|
|
1766
1770
|
"SELECT message_row_id FROM message_folders WHERE folder_id = ? AND uid = ?"
|
|
@@ -1809,8 +1813,8 @@ export class MailxDB {
|
|
|
1809
1813
|
this.audit({
|
|
1810
1814
|
kind: "delete-membership",
|
|
1811
1815
|
accountId, folderId, uid,
|
|
1812
|
-
reason: `reconcile dropped folder membership; ${remaining.cnt} other location(s) remain`,
|
|
1813
|
-
source: `dropFolderMembership (${folderPath || ""})`,
|
|
1816
|
+
reason: reason || `reconcile dropped folder membership; ${remaining.cnt} other location(s) remain`,
|
|
1817
|
+
source: source || `dropFolderMembership (${folderPath || ""})`,
|
|
1814
1818
|
});
|
|
1815
1819
|
return true;
|
|
1816
1820
|
}
|
|
@@ -1827,8 +1831,8 @@ export class MailxDB {
|
|
|
1827
1831
|
accountId, folderId, uid,
|
|
1828
1832
|
messageId: env.message_id || undefined,
|
|
1829
1833
|
subject: env.subject || undefined,
|
|
1830
|
-
reason: "reconcile: server missing this UID after grace, no other folder memberships",
|
|
1831
|
-
source: `dropFolderMembership (${folderPath || ""})`,
|
|
1834
|
+
reason: reason || "reconcile: server missing this UID after grace, no other folder memberships",
|
|
1835
|
+
source: source || `dropFolderMembership (${folderPath || ""})`,
|
|
1832
1836
|
});
|
|
1833
1837
|
console.log(` [reconcile-delete] ${accountId} ${folderPath || folderId}/${uid} msgid=${env.message_id || "?"} (no other memberships) — body ${env.body_path || "(none)"}`);
|
|
1834
1838
|
}
|
|
@@ -2505,13 +2509,19 @@ export class MailxDB {
|
|
|
2505
2509
|
* hitting the message in `_Spam` (correct) and hitting nothing
|
|
2506
2510
|
* because the legacy messages.folder_id still points at INBOX. */
|
|
2507
2511
|
getMessageByUid(accountId: string, uid: number, folderId?: number): MessageEnvelope {
|
|
2512
|
+
// Alias mf.folder_id over m.folder_id too — the envelope must carry
|
|
2513
|
+
// the identity of the membership that MATCHED, not the legacy primary
|
|
2514
|
+
// location, or a caller doing delete/move/flag with the returned
|
|
2515
|
+
// folderId operates on a different folder than the one it looked up
|
|
2516
|
+
// (the two drift: local moves keep the original uid, re-binds update
|
|
2517
|
+
// one table but not the other).
|
|
2508
2518
|
const sql = folderId != null
|
|
2509
|
-
? `SELECT m.*, mf.uid AS uid
|
|
2519
|
+
? `SELECT m.*, mf.uid AS uid, mf.folder_id AS folder_id
|
|
2510
2520
|
FROM messages m
|
|
2511
2521
|
JOIN message_folders mf ON mf.message_row_id = m.id
|
|
2512
2522
|
WHERE m.account_id = ? AND mf.uid = ? AND mf.folder_id = ?
|
|
2513
2523
|
LIMIT 1`
|
|
2514
|
-
: `SELECT m.*, mf.uid AS uid
|
|
2524
|
+
: `SELECT m.*, mf.uid AS uid, mf.folder_id AS folder_id
|
|
2515
2525
|
FROM messages m
|
|
2516
2526
|
JOIN message_folders mf ON mf.message_row_id = m.id
|
|
2517
2527
|
WHERE m.account_id = ? AND mf.uid = ?
|
|
@@ -3114,22 +3124,39 @@ export class MailxDB {
|
|
|
3114
3124
|
* Reason is propagated into the audit_log so the DB carries an
|
|
3115
3125
|
* authoritative trail of every removal. */
|
|
3116
3126
|
deleteMessage(accountId: string, folderId: number | null, uid: number, reason?: string, source?: string): void {
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
:
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3127
|
+
if (folderId != null) {
|
|
3128
|
+
// Resolve through message_folders — the SAME identity that
|
|
3129
|
+
// getMessageByUid and every list query use. The legacy
|
|
3130
|
+
// messages.folder_id/uid columns can drift from the membership
|
|
3131
|
+
// rows (local moves keep the original uid, server re-binds update
|
|
3132
|
+
// one table but not the other), and a delete keyed only on the
|
|
3133
|
+
// legacy columns silently matches ZERO rows on a drifted message:
|
|
3134
|
+
// the membership keeps it visible in the UI and it becomes
|
|
3135
|
+
// undeletable forever (Eleanor 2026-07-29: ElkinWs uid 125482 in
|
|
3136
|
+
// Trash "refuses to delete" — every attempt no-op'd here while
|
|
3137
|
+
// the sync action reported success). dropFolderMembership drops
|
|
3138
|
+
// the membership, orphan-cleans the messages row, and falls back
|
|
3139
|
+
// to the legacy columns for pre-migration rows.
|
|
3140
|
+
const cleaned = this.dropFolderMembership(
|
|
3141
|
+
accountId, folderId, uid, undefined,
|
|
3142
|
+
reason || "deleteMessage (no reason)",
|
|
3143
|
+
source || "db.deleteMessage",
|
|
3144
|
+
);
|
|
3145
|
+
if (!cleaned) {
|
|
3146
|
+
// A delete that deletes nothing is the "refuses to delete"
|
|
3147
|
+
// symptom — never let it pass silently.
|
|
3148
|
+
console.error(` [delete-miss] ${accountId} folder=${folderId} uid=${uid}: no membership or legacy row matched — nothing deleted (source=${source || "db.deleteMessage"})`);
|
|
3149
|
+
}
|
|
3150
|
+
this.recalcFolderCounts(folderId);
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3153
|
+
// No folderId (legacy callers): fall back to the messages-table key.
|
|
3154
|
+
const msg = this.db.prepare(
|
|
3155
|
+
"SELECT id, folder_id, message_id, subject FROM messages WHERE account_id = ? AND uid = ?"
|
|
3156
|
+
).get(accountId, uid) as any;
|
|
3157
|
+
const r = this.db.prepare(
|
|
3158
|
+
"DELETE FROM messages WHERE account_id = ? AND uid = ?"
|
|
3159
|
+
).run(accountId, uid);
|
|
3133
3160
|
if ((r as any).changes && msg) {
|
|
3134
3161
|
this.audit({
|
|
3135
3162
|
kind: "delete-msg",
|
|
@@ -3141,6 +3168,8 @@ export class MailxDB {
|
|
|
3141
3168
|
reason: reason || "deleteMessage (no reason)",
|
|
3142
3169
|
source: source || "db.deleteMessage",
|
|
3143
3170
|
});
|
|
3171
|
+
} else if (!(r as any).changes) {
|
|
3172
|
+
console.error(` [delete-miss] ${accountId} uid=${uid} (no folderId): no messages row matched — nothing deleted (source=${source || "db.deleteMessage"})`);
|
|
3144
3173
|
}
|
|
3145
3174
|
// Refresh folder counts
|
|
3146
3175
|
if (msg) this.recalcFolderCounts(msg.folder_id);
|