mirror of
https://github.com/juherr/kill-the-news.git
synced 2026-06-20 22:03:48 +00:00
44fcbfc4f6
Senders on a subdomain that hosts no favicon (e.g. mail.example.com) left feeds blank because both the direct /favicon.ico and the DuckDuckGo lookup were tried only against the full subdomain. Resolution now walks up to the apex via Domain.parents() and caches the result under the original sender domain. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8.8 KiB
8.8 KiB
Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Keep the ## [Unreleased] section up to date as part of every change (the
same rule as the rest of the docs). At release time npm run release X.Y.Z
promotes this section to ## [X.Y.Z] and the Release workflow publishes it
verbatim as the GitHub Release notes — so what you write here is what ships.
Unreleased
Added
- The admin dashboard now shows each feed's email count on its Emails button
and a "Last email …" freshness line under the feed title, in both the list
and table views. Both values are projected into
feeds:list, so the dashboard stays a single KV read; they backfill on a feed's next email or save.
Fixed
- Per-feed favicons now resolve for senders on a subdomain that hosts no icon of
its own (e.g.
mail.example.com): the lookup walks up to the apex domain (example.com) and uses its favicon, caching it under the original sender domain. Previously both the direct/favicon.icoand the DuckDuckGo lookup were tried only against the full subdomain, leaving such feeds blank. - Subscription-confirmation detection now flags code-based signup verifications
(OTP) that have no link to click — e.g. "Your verification code is 371404",
whose only link is a
mailto:support address. These cleared the keyword threshold but were dropped because the detector required an http(s) candidate link. A code path now raises the flag/badge/banner when a verification keyword sits next to an OTP-style code; the code itself is never extracted or surfaced. - Subscription-confirmation detection now recognizes localized "subscribe" CTAs.
The weak link-signal vocabulary was English-only (
subscrib), so a genuine double opt-in whose confirm button reads "Je m'inscris…" over an opaque tracking redirect scored 0 on every link and was missed. The weak vocab is now multilingual (FR/DE/ES) to match the confirmation keywords. - Per-feed favicons no longer fail for senders whose DuckDuckGo icon is a
hi-res PNG: the maximum accepted favicon size is raised from 100 KB to 256 KB,
so legitimate large icons (~107 KB and up) are cached instead of rejected.
A domain that was already negatively cached only re-fetches once that entry's
TTL expires (and something — a new email or a favicon request — retriggers
the fetch); delete its
icon:<domain>KV key to force an immediate refresh. - Admin dashboard table view: long feed titles no longer overflow into the Feed ID column — the title/description cell now shrinks so its text ellipsises.
- RSS and Atom feeds now advertise the WebSub hub inside the feed body
(
<atom:link rel="hub">), not just in the HTTPLinkheader. Readers like FreshRSS discover the hub from the XML, so they can now subscribe and receive an instant push when a new email arrives instead of waiting up to the cachemax-age(30 min) to refresh. - Subscription-confirmation detection now recognises a confirm email whose CTA button carries the subscribe/subscription hint only in its visible text (e.g. "Yes, subscribe me to this mailing list.") over an opaque tracking-redirect href — previously the link scored zero and the email was missed.
- Sender favicons now recover from a transient miss: a failed favicon lookup is cached negatively for 6 hours instead of a full week, so a domain whose icon was momentarily unavailable (e.g. not yet indexed upstream) is retried on the next email instead of staying blank for days.
- Feed entry HTML now escapes bare ampersands in attribute URLs (e.g. query
strings like
?a=1&b=2), clearing the W3C feed validator's "Named entity expected. Got none." warning and improving interoperability with stricter feed readers.
0.3.1 - 2026-05-25
Fixed
- Feed self link (RSS/Atom/JSON) is derived from the configured domain instead
of the request host — it no longer leaks the
workers.devhost when a feed is reached directly, and now matches the alternate link.
0.3.0 - 2026-05-25
Added
- Native feed detection — incoming newsletters are inspected for a
self-advertised Atom/RSS/JSON feed (
rel=alternatelinks in the email HTML); discovered feeds are stored per sender on the Feed aggregate and surfaced as chips on the feed detail page, a dashboard pill, and (read-only) on the RESTFeedschema, with a dismissable notice. - Subscription confirmation surfacing — confirmation emails ("click to confirm your subscription") are detected at ingestion and flagged on the feed; the admin UI surfaces the confirmation link, a badge, a dashboard pill, and an inline banner (all dismissable), tightened against false positives via a weak-signal heuristic.
- JSON Feed 1.1 output (
/json/:feedId). - OPML export of all feeds (
/admin/opml). - Conditional GET (ETag / Last-Modified / 304) on the feed routes.
- Per-feed Subscribe chips for RSS/Atom/JSON with copy / open / validate actions, reused across dashboard and feed detail page.
- Email detail page links to its public entry page; land on the feed's emails page right after creation.
- Optional per-feed "sender in title" toggle.
- Running version shown in the admin/status footer,
/health, and/api/v1/stats.
Changed
- Read/write identity decoupling (privacy) — the public read id (
FeedId, used in/rss/:feedId) is fully decoupled from the inbound email address (MailboxId,noun.noun.NN); a feed's read URL never reveals its inbound alias and vice-versa (reading/rss/<noun.noun.NN>404s). - Sender display name, site URL and parsing now owned by the
EmailAddressvalue object (DDD cleanup). - Release version is derived from the git tag; CI guards against tagging the wrong commit.
0.2.1 - 2026-05-24
Added
- Optional
FALLBACK_FORWARD_ADDRESS: forward non-feed mail to a verified address so a domain catch-all can point at kill-the-news without swallowing personal mail (forwarded mail is counted in the stats dashboard).
Changed
- Feed, entry, and attachment responses send
X-Robots-Tag: noindex; a new/robots.txtdisallows/rss,/atom,/entries,/files, and/admin— private feeds and emails stay out of search engines. - Relative links/images in email bodies are absolutized against the sender's site; lazy-loaded images are promoted so they don't render blank.
- Feed
<title>is plain text (HTML stripped, entities decoded). - Sender-site derivation moved onto the
EmailAddressvalue object (siteBaseUrl).
Fixed
- XML-illegal control characters are stripped from generated feeds (valid astral characters such as emoji preserved).
0.2.0 - 2026-05-24
Added
- Versioned REST API (
/api/v1/feeds*) with an OpenAPI 3.1 spec (/api/openapi.json) and rendered reference docs via Scalar (/api/docs). /api/v1/statsas the canonical public stats endpoint (JSON + CORS).- Optional R2 attachment storage with a config toggle, storage metrics, download
links on the email/admin views, and inline
cid:image rendering. - Project favicon (
/favicon.svg,/favicon.ico) and per-feed favicon derived from the last sender's domain (/favicon/:feedId). - RFC 8058 one-click unsubscribe dispatched when a feed is deleted.
Changed
- Large internal refactor toward a clean domain-driven architecture; redesigned landing/status page.
Removed
- The deprecated
/api/statsendpoint (use/api/v1/stats).
0.1.0 - 2026-05-22
Added
- Atom feed format (
/atom/:feedId) alongside RSS 2.0. - WebSub push notifications advertised via
Linkheader for real-time delivery instead of polling. - HTML email processing — bodies sanitized via
linkedom+escape-html(XSS prevention, MSO style stripping, plain-text fallback). - Email attachments as RSS enclosures, stored in R2 and served at
/files/:attachmentId/:filename. - Sender blocklist with 4-level priority matching and a quick-add dropdown.
EMAIL_DOMAINenv var to separate web domain and email domain.- Authelia / reverse-proxy auth via trusted headers (
Remote-User,X-Forwarded-User). - Demo environment auto-deployed to
demo.kill-the.newswith a nightly KV reset. - Admin UI redesign (Inter font, orange theme), client scripts compiled via
esbuild, templates on
hono/jsx.