feat(unsubscribe): RFC 8058 one-click unsubscribe on feed deletion
Capture each sender's List-Unsubscribe one-click URL during ingestion (stored per sender in feed metadata, mirroring the iconDomain pattern) and fire one-click POSTs via ctx.waitUntil when a feed is deleted, so newsletters stop mailing the now-dead address. Tracked with a new unsubscribes_sent counter surfaced on the status page and /api/stats. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
eb12f21894
commit
3ad0188bc0
14 files changed
+558
-4
No files matched your search
@@ -12,6 +12,7 @@ import {
|
||||
cacheFaviconForDomain,
|
||||
extractEmailDomain,
|
||||
} from "../utils/favicon-fetcher";
|
||||
import { parseOneClickUnsubscribe } from "../utils/unsubscribe";
|
||||
import { logger } from "./logger";
|
||||
import { FEED_MAX_BYTES } from "../config/constants";
|
||||
|
||||
@@ -223,6 +224,19 @@ export async function storeEmail(
|
||||
feedMetadata.iconDomain = iconDomain;
|
||||
}
|
||||
|
||||
// Capture the sender's RFC 8058 one-click unsubscribe link so we can stop the
|
||||
// newsletter when the feed is deleted. Keyed by sender: each newsletter on the
|
||||
// feed keeps its own entry, and a repeat send overwrites with the latest URL.
|
||||
const unsubUrl = parseOneClickUnsubscribe(input.headers ?? {});
|
||||
if (unsubUrl) {
|
||||
const senderKey =
|
||||
input.senders[0] || extractEmailDomain(input.from) || input.from;
|
||||
feedMetadata.unsubscribe = {
|
||||
...(feedMetadata.unsubscribe ?? {}),
|
||||
[senderKey]: unsubUrl,
|
||||
};
|
||||
}
|
||||
|
||||
let totalSize = feedMetadata.emails.reduce(
|
||||
(sum, e) => sum + (e.size ?? 0),
|
||||
0,
|
||||
|
||||
Reference in new issue
Block a user