Commit Graph

379 Commits

Author SHA1 Message Date
Sven
1f4f55d4fc fix(patches): add internal URL rewrite to webmention livefetch
The livefetch patch was fetching postUrl (the public HTTPS URL) which
fails behind the nginx jail. Now rewrites to INTERNAL_FETCH_URL so the
live page fetch goes through nginx on the internal network. Without
this, likes/reposts/bookmarks fall back to stored content which has no
microformat links, resulting in 0 webmentions sent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 17:23:12 +01:00
Sven
c1197608ef fix(patches): make livefetch handle both original and retry-patched code
The retry and livefetch patches both target the same upstream code block.
If retry runs first (current postinstall order), it transforms the code
into a variant that livefetch couldn't match — silently losing the
"always fetch live page" behavior. Now livefetch detects both the
original upstream code and the retry-patched variant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 12:05:44 +01:00
Sven
af683d8bb4 chore(patches): restore webmention-sender-retry patch and add to postinstall
Retry behavior is already covered by the livefetch patch, but keeping
this patch ensures the skip-on-failure guard applies even if livefetch
is removed or the upstream code changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 11:57:52 +01:00
Sven
c9c958e9f5 chore: point blogroll dependency to main branch
The bookmark-import branch has been merged to main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 10:38:34 +01:00
Sven
f275fdef67 fix(webmention-sender): drop internal URL rewriting, fetch public URL directly
The public URL (blog.giersig.eu) is reachable from inside the jail, so
the INTERNAL_FETCH_URL rewriting approach was unnecessary and caused 400/502
errors because 10.100.0.10 does not serve the static blog pages.

Simplify the livefetch patch to fetch postUrl directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 10:28:49 +01:00
Sven
b522662199 fix(webmention-sender): send Host header when fetching via internal URL
When rewriting the public URL to INTERNAL_FETCH_URL for jailed setups,
the Host header becomes the internal IP (e.g. 10.100.0.10) instead of
the public hostname (blog.giersig.eu). nginx uses the Host header for
virtual host routing and returns 400 without the correct value.

Fix: extract the host from the original public postUrl and set it as the
Host header on the internal fetch, so nginx routes to the correct vhost.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 10:02:12 +01:00
Sven
fd045d9c0e fix(webmention-sender): fall back to siteUrl for public base URL rewriting
The livefetch patch used `process.env.PUBLICATION_URL || process.env.SITE_URL`
to determine the public base URL for rewriting to INTERNAL_FETCH_URL. Neither
env var is set — the publication URL comes from indiekit.config.mjs which has
a hardcoded fallback. Without a publicBase, URL rewriting never fired, the
public HTTPS URL was fetched directly, failed inside the FreeBSD jail, and fell
back to stored content.

For interaction posts (repost/bookmark/reply/like), stored content is just the
body text — the target URL (repostOf, bookmarkOf, inReplyTo, likeOf) is only
rendered in the live page via reply-context.njk. So 0 webmentions were sent.

Fix: add `|| siteUrl` as fallback. siteUrl is already in scope (derived from
`publication.me`) and is the correct value when env vars are absent.

Also bump reset-stale migration to v7 so interaction posts incorrectly marked
with 0 results (e.g. 342a5 repost) are reset and retried on next startup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 09:49:23 +01:00
Sven
059f36bf06 fix(webmention-sender): bump reset-stale migration to v6
Posts processed before the livefetch patch was applied were re-processed
without live page fetching, leaving them with empty results again.
Bumping the migration ID triggers another reset on next startup so the
poller retries them with livefetch active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 08:48:47 +01:00
Sven
2bfaa27702 fix(webmention-sender): show 'no external links' message for empty detail rows
When a post was processed but had no discoverable external links, the
expanded detail row rendered completely blank — result.details was
truthy ({}) so the 'noDetails' fallback never fired, but all three
arrays were empty so no tables rendered either.

Adds a patch script for the template that shows "No external links
discovered in this post." in that case, and wires it into both
postinstall and serve scripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 08:33:29 +01:00
Sven
4a81b5afab fix: unfurl corrupted self-filter patch script, drop webmention-sender backend patch
The patch script was corrupted (nested snippets embedded inside template
literals). Reconstructed it cleanly with only the two original specs
(scheduler + conversations controller). The webmention-sender backend
patch is removed — self-Bluesky filtering belongs in the blog sidebar
widget, not the backend dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 08:04:27 +01:00
Sven
8b06894c01 patch: filter Bluesky self-interactions from webmention dashboard widget 2026-03-18 07:42:08 +01:00
Sven
c5c4d06184 fix: FreeBSD Jails self-linking config 2026-03-17 17:33:57 +01:00
Sven
8b35fc13fd fix(startup): route webmention poller through nginx via INTERNAL_FETCH_URL
Direct connections from the node jail to itself cause empty replies in
FreeBSD jails. Route through nginx (INTERNAL_FETCH_URL=http://10.100.0.10)
instead, which also provides correct Host and X-Forwarded-Proto headers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:45:12 +01:00
Sven
421a2944f8 docs: fix nginx proxy Host header to blog.giersig.eu
Internal requests arrive with Host: 10.100.0.10 which Indiekit doesn't
recognize, causing auth redirects. Hardcode the public hostname so
Indiekit's session/token handling works correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:34:45 +01:00
Sven
e98c704e78 docs: add server architecture, jail setup, and nginx config to README
Documents the two-jail FreeBSD setup (web + node), INTERNAL_FETCH_URL
mechanism, nginx port 80 internal listener config, and static file
paths at /usr/local/www/blog. Also documents the bluesky-syndicator
and micropub-fetch-internal-url patches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:21:58 +01:00
Sven
cc1edd81a8 fix(startup): use configurable bind host instead of hardcoded 127.0.0.1
FreeBSD jails often don't have loopback available. The webmention poller
and readiness check now use INDIEKIT_BIND_HOST (defaults to 127.0.0.1
for backward compat). Set INDIEKIT_BIND_HOST=10.100.0.20 in .env.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:09:57 +01:00
Sven
42db9d85b6 fix(patches): rewrite Bluesky syndicator fetch URLs to localhost for jailed setup
uploadMedia, uploadImageFromUrl, and fetchOpenGraphData all fetch from
the blog's public URL which is unreachable behind the nginx jail. Rewrite
own-domain URLs to http://localhost:PORT, same as micropub-fetch-internal-url.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:19:24 +01:00
Sven
3beac515cd feat: add indiekit-endpoint-readlater 2026-03-17 12:26:40 +01:00
Sven
4955863bb8 fix(startup): add empty body and timeout to webmention poller curl
The bodyless POST (no Content-Type/Content-Length) caused curl error 52
(empty reply from server). Adding `-d ""` sends proper headers and
`--max-time 300` prevents hangs on long-running webmention processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 12:18:20 +01:00
Sven
c1f9d64401 fix(patches): rewrite livefetch URL to localhost and add timeout
The webmention sender livefetch patch was fetching the public HTTPS URL
which hangs in the jailed setup (port 443 is on the nginx jail). Rewrite
to localhost like all other internal-fetch patches, and add a 15s
AbortController timeout. Bump reset-stale migration to v5 so posts
incorrectly marked as sent with 0/0/0 get retried.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 11:58:35 +01:00
Sven
d501d70e1d fix(patches): restore livefetch patch so webmentions use live page HTML
The webmention sender was using stored post content (just the body text)
instead of the live page, missing template-rendered links like
u-in-reply-to, u-like-of, u-bookmark-of. This caused reply/like/bookmark
posts to be marked as sent with 0 webmentions. Bump reset-stale migration
to v4 so affected posts are retried.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 09:56:35 +01:00
Sven
fc9afa9bff fix(rssapi): flatten feedTitle, sourceTitle and author in item response
The /rssapi transformation was missing feedTitle, sourceTitle and author
fields that the /news page expects. Map them from the nested blog object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 09:21:23 +01:00
Sven
cb7b525368 fix(patches): extend internal URL rewrite to auth, token, and media endpoints
Add localhost rewrite for three more self-referential fetches:

- indieauth.js: token exchange during login
- token.js: token introspection on every authenticated request
- media.js: file uploads via media endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:40:59 +01:00
Sven
a2e2764108 fix(patches): extend internal URL rewrite to microsub, activitypub, and @rmdes posts
The localhost rewrite only covered endpoint-syndicate and endpoint-share.
Add coverage for 4 more files that also self-fetch via the public URL:

- @rmdes/indiekit-endpoint-microsub reader.js (2 fetch calls)
- @rmdes/indiekit-endpoint-activitypub compose.js (2 fetch calls)
- @rmdes/indiekit-endpoint-posts utils.js and endpoint.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:35:50 +01:00
Sven
e3765948e5 fix(patches): silence false warnings for already-applied patches
- patch-ap-allow-private-address: detect allowPrivateAddress in source
- patch-endpoint-posts-ai-cleanup: detect any AI field key variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:32:24 +01:00
Sven
733c00b1b3 fix(patches): rewrite micropub self-fetch to localhost for jailed setup
Node can't reach its own public HTTPS URL (ECONNREFUSED 127.0.0.1:443)
because port 443 only exists on the nginx jail. Rewrite self-referential
fetch URLs to http://localhost:3000 in endpoint-posts, endpoint-syndicate,
and endpoint-share.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:24:46 +01:00
Sven
24a9fb8f6b fix(patches): restore PeerTube View patches and add fetch diagnostic
Re-add PeerTube View activity patches that were prematurely removed in
e52e98c5c — the upstream fork doesn't reliably include these fixes on
all server deployments, causing noisy "Unsupported activity type" errors.

Also add fetch diagnostic patch to surface the real cause of
"TypeError: fetch failed" when posting articles via the form controller.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:14:26 +01:00
Sven
117aa2f577 fix(patches): normalize nested tags before syndicating to fediverse
Tags like `on/art/music` are reduced to their last segment (`#music`)
in both buildPlainTags and buildFedifyTags so ActivityPub hashtags are
valid on Mastodon and other platforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 13:01:36 +01:00
Sven
e52e98c5c5 chore(patches): remove patches now fixed in forks
Fixes implemented upstream in svemagie/indiekit-endpoint-activitypub
and svemagie/indiekit-endpoint-microsub; postinstall patches no longer
needed:

- patch-microsub-feed-discovery (HTML <link> discovery in fetcher.js)
- patch-inbox-skip-view-activity-parse (PeerTube View buffering in federation-bridge.js)
- patch-ap-inbox-raw-body-digest (raw body preservation for HTTP Signature Digest)
- patch-ap-object-url-trailing-slash (trailing slash $in fix in federation-setup.js)
- patch-ap-signature-time-window (12h signatureTimeWindow in federation-setup.js)
- patch-inbox-ignore-view-activity (View no-op handler in inbox-listeners.js)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:19:18 +01:00
Sven
dac2411a08 test: delete patch-microsub-reader-ap-dispatch 2026-03-16 11:47:06 +01:00
Sven
437554cdc9 fix(patches): always strip legacy ai-text-level keys on form save
Hyphenated keys (ai-text-level, ai-code-level, etc.) from the upstream
beta.41 endpoint are now unconditionally deleted on form submission,
preventing them from coexisting with the camelCase equivalents in MongoDB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 00:36:55 +01:00
Sven
5b21fd3ea3 chore(patches): remove obsolete webmention-sender-content-scope patch
@rmdes/indiekit-endpoint-webmention-sender@1.0.8 ships `$.root()` natively.
The old buggy `const scope = contentRoot ?? $` no longer exists upstream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 00:04:03 +01:00
Sven
757c931408 fix(syndicate): register endpoint-syndicate plugin to enable /syndicate route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:55:25 +01:00
Sven
3ab6c4caa2 fix(patches): silence false-positive warnings for beta.41 native features
Four patch scripts were warning when they couldn't find their target snippets
in @indiekit/endpoint-posts beta.41, because beta.41 already ships those
features natively:

- patch-endpoint-posts-ai-cleanup: beta.41 form.js has native AI field
  cleanup — detect via "ai-text-level" presence and skip silently
- patch-endpoint-posts-search-tags: beta.41 posts.js/posts.njk have native
  filter/sort/search — detect via buildFilterQuery / posts-filter-row
- patch-endpoint-posts-uid-lookup: beta.41 utils.js uses direct MongoDB
  queries (getPostProperties) — skip silently
- patch-preset-eleventy-ai-frontmatter: add v5 block matching the new
  upstream structure (mpUrl + URL pathname normalization)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:36:55 +01:00
Sven
6c5b003bcf fix(i18n): extend posts locale patch to cover beta.41 and filter/purge/AI keys
- Add @indiekit/endpoint-posts (beta.41) as a second patch target
- Translate new filter.* keys (type, status, sort, search, clear)
- Translate purge.* keys (permanently delete flow)
- Translate form.pinned, form.ai-text-level, form.ai-code-level,
  form.ai-tools, form.ai-description
- Fix mp-syndicate-to label: "Syndikat zu" → "Syndizieren nach"
- Fix posts.posts.title: "Veröffentlichte Beiträge" → "Beiträge"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:28:56 +01:00
Sven
4d5b1eba16 fix(i18n): add patch script for /conversations endpoint German locale
Adds missing German translations for indiekit-endpoint-conversations:
- dashboard.activitypubTitle: "ActivityPub"
- dashboard.activitypubHint: ActivityPub auto-enable explanation
- source.activitypub: "ActivityPub"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:21:10 +01:00
Sven
066cc12402 fix(i18n): add patch script for /posts endpoint German locale
Fixes rough translations in de.json for indiekit-endpoint-posts:
- delete.cancel: "zurück zur Post" → "zurück zum Beitrag"
- post.syndicate: "Syndikatsposten" → "Beitrag syndizieren"
- status.unlisted: "Nicht gelistete" → "Nicht gelistet"
- form.summary.label: "Übersicht" → "Zusammenfassung"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:19:32 +01:00
Sven
a9575059a7 chore: allow Fedify to fetch own-site URLs resolving to private IPs
blog.giersig.eu resolves to 10.100.0.10 (private LAN) from the server,
causing Fedify's SSRF guard to block lookupObject() and WebFinger calls
for own posts when processing incoming ActivityPub activities.

Adds patch-ap-allow-private-address.mjs which sets allowPrivateAddress: true
on createFederation(), wired into both postinstall and serve scripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:16:12 +01:00
Sven
15fb4bb914 chore: application.locale:de 2026-03-15 22:46:23 +01:00
Sven
0898df1b66 chore: rss back out 2026-03-15 22:23:02 +01:00
Sven
98f059614e chore: rss back in 2026-03-15 22:15:11 +01:00
Sven
7104094ce0 chore: no rss reader for now 2026-03-15 19:46:37 +01:00
Sven
ff0a8759a8 chore: upd package-lock.json 2026-03-15 19:34:01 +01:00
Sven
0331271cb5 chore: update to upstream 2026-03-15 19:31:10 +01:00
Sven
abf06565d5 fix: extend Fedify signature time window to 12h for retried deliveries
Before the raw-body digest fix, every Mastodon inbox delivery was
rejected with a Digest mismatch. Mastodon queued those activities for
retry. After the digest fix, the retried deliveries arrive with their
original HTTP Signatures which are now > 1 hour old. Fedify's default
signatureTimeWindow: { hours: 1 } rejects them with "Date is too far
in the past", logged as "Failed to verify the request's HTTP Signatures."

Extending to 12 hours allows those retried deliveries to be accepted.
The signature still must be cryptographically valid — only the replay
window is relaxed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:35:55 +01:00
Sven
02919b6e37 fix: patch resolvePost to match post URLs with or without trailing slash
The Fedify object dispatcher constructs the post lookup URL from the
{+id} path variable (e.g. "replies/bd78a"), which has no trailing slash.
Posts in MongoDB store their URL with a trailing slash, so the exact
findOne() match was silently returning null → Fedify serving 404 →
mountains.social showing "Could not connect to the given address".

Fix uses $in to try both variants so the dispatcher works regardless
of whether the request URL has a trailing slash or not.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:17:27 +01:00
Sven
a42a6b6d45 fix: add AP inbox digest patch and AP URL lookup API for fediverse interaction
- patch-ap-inbox-raw-body-digest: preserve raw request bytes through the
  AP inbox buffer guard so Fedify's HTTP Signature Digest verification
  passes (JSON.stringify re-encoding broke SHA-256 digest check, causing
  Mastodon likes/replies/boosts to be silently rejected)
- patch-ap-url-lookup-api: add GET /activitypub/api/ap-url endpoint that
  maps a blog post URL to its Fedify-served AP object URL, enabling
  reliable content negotiation for authorize_interaction redirects
- wire both patches into postinstall and serve scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:00:13 +01:00
Sven
d2573146a7 feat: default ActivityPub syndication when force-syndicating from backend
When the backend Syndicate button is pressed on a post with no
mp-syndicate-to and no prior syndication URLs, fall back to targets
with checked:true (e.g. ActivityPub) instead of no-oping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 14:02:52 +01:00
Sven
4af7c49c6a feat: inline card template in /posts to show tags inside cards
Non-garden tags shown as small chips in the card body (above footer).
The 'garden' tag shown in the card footer on the right side of the
publish-state badge, using margin-left:auto.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 12:14:32 +01:00
Sven
65f94da71c fix: remove broken regex-escape snippet from micropub source-filter patch
The template-literal double-backslash escaping produced malformed JS in
the injected code, causing a SyntaxError at startup. Replace the
regex-escape helper with a direct String(searchParam) pass-through —
safe for an admin-only search interface.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 12:03:53 +01:00