Commit Graph

139 Commits

Author SHA1 Message Date
Ricardo
fee1706d38 fix: direct follow workaround for tags.pub identity/v1 context rejection
tags.pub's activitypub-bot (activitystrea.ms parser) rejects any activity
body containing the https://w3id.org/identity/v1 JSON-LD context with
400 Invalid request body. Fedify 2.0 adds this context via LD Signatures
(RsaSignature2017) on all outbound activities.

Workaround: lib/direct-follow.js sends Follow/Undo(Follow) with a minimal
body (no LD Sig, no proof) using draft-cavage HTTP Signatures, scoped only
to tags.pub via DIRECT_FOLLOW_HOSTS set.

Also removes [federation-diag] inbox POST logging (no longer needed).

Upstream: https://github.com/social-web-foundation/tags.pub/issues/10
2026-03-22 20:00:46 +01:00
Ricardo
4298a6a307 fix: remove RSA Multikey from assertionMethod to fix tags.pub signature verification
The Fedify 2.0 migration added assertionMethods = keyPairs.map(k => k.multikey),
which places the RSA Multikey (id: #main-key) into assertionMethod alongside the
Ed25519 Multikey (id: #key-2).

This creates a keyId collision: the RSA CryptographicKey in publicKey and the RSA
Multikey in assertionMethod both use #main-key. Servers that traverse JSON-LD
properties alphabetically (assertionMethod before publicKey) find the Multikey
first — which lacks publicKeyPem — and return "public key not found".

Fix: filter assertionMethods to only Ed25519 keys (Object Integrity Proofs).
RSA keys already have their correct representation in publicKey (HTTP Signatures).
This matches Mastodon's behavior and is semantically correct per the two key systems.
2026-03-22 15:31:29 +01:00
Ricardo
cb6c1b5bbc fix: serve AP JSON for actor URLs without explicit text/html Accept header
Fedify's acceptsJsonLd() returns false for Accept: */* or no Accept header
because it only checks for explicit application/activity+json in the list.
Remote servers fetching actor URLs for HTTP Signature verification (e.g.
tags.pub) often omit Accept or use */*, getting HTML back instead of the
actor JSON and causing "public key not found" failures.

Add middleware to upgrade ambiguous Accept headers to application/activity+json
for GET requests to /users/:id paths. Explicit text/html requests (browsers)
are unaffected.

Also fix followActor() storing inbox: "" for actors where Fedify uses
remoteActor.inboxId?.href (not remoteActor.inbox?.id?.href). The inbox URL
is stored correctly now for all actor types.
2026-03-22 15:31:29 +01:00
Ricardo
65e37c92d7 chore: bump to 3.8.1 2026-03-22 11:26:30 +01:00
Ricardo
a84c6f1abd feat: tags.pub global hashtag discovery integration (v3.8.0)
- Add setGlobalFollow/removeGlobalFollow/getFollowedTagsWithState to
  followed-tags storage; unfollowTag now preserves global follow state
- Add followTagGloballyController/unfollowTagGloballyController that
  send AP Follow/Undo via Fedify to tags.pub actor URLs
- Register POST /admin/reader/follow-tag-global and unfollow-tag-global
  routes with plugin reference for Fedify access
- Tag timeline controller passes isGloballyFollowed + error query param
- Tag timeline template adds global follow/unfollow buttons with globe
  indicator and inline error display
- Wire GET /api/v1/followed_tags to return real data with globalFollow state
- Add i18n keys: followGlobally, unfollowGlobally, globallyFollowing,
  globalFollowError
2026-03-22 11:26:30 +01:00
svemagie
97a902bda1 feat: merge upstream v3.7.1–v3.7.5 into svemagie/main
All five 3.7.x releases published 2026-03-21 in one pass.

Changes from upstream:

- lib/lookup-helpers.js: lookupWithSecurity → async with signed→unsigned
  fallback (handles servers like tags.pub that return 400 on signed GETs)

- lib/mastodon/helpers/account-cache.js: add reverse lookup map
  (hashId → actorUrl) populated by cacheAccountStats(); export
  getActorUrlFromId() for follow/unfollow resolution

- lib/mastodon/helpers/enrich-accounts.js: NEW — enrichAccountStats()
  enriches embedded account objects in serialized statuses with real
  follower/following/post counts; Phanpy never calls /accounts/:id so
  counts were always 0 without this

- lib/mastodon/routes/timelines.js: call enrichAccountStats() after
  serialising home, public, and hashtag timelines

- lib/mastodon/routes/statuses.js: processStatusContent() linkifies bare
  URLs and converts @user@domain mentions to <a> links; extractMentions()
  builds mention list; date lookup now tries both .000Z and bare Z suffixes

- lib/mastodon/routes/stubs.js: /api/v1/domain_blocks now returns real
  blocked-server hostnames from ap_blocked_servers instead of []

- lib/mastodon/routes/accounts.js: /accounts/relationships computes
  domain_blocking using ap_blocked_servers; resolveActorUrl() falls back
  to getActorUrlFromId() cache for timeline-author resolution

- lib/controllers/federation-mgmt.js: fetch blocked servers, blocked
  accounts, and muted accounts in parallel; pass to template

- views/activitypub-federation-mgmt.njk: add Moderation section showing
  blocked servers, blocked accounts, and muted accounts

- package.json: bump version 3.6.8 → 3.7.5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 20:22:04 +01:00
Ricardo
fb11a517c0 chore: bump version to 3.6.8 2026-03-21 12:46:13 +01:00
Ricardo
3787be4c69 feat: cache remote account stats for embedded status accounts
Phanpy never calls /accounts/:id for timeline authors — it uses the
embedded account object from the status response. These had 0 counts
because the timeline author data doesn't include follower stats.

Fix: in-memory LRU cache (500 entries, 1h TTL) stores account stats
from remote resolutions. serializeAccount() reads from cache when
the actor has 0 counts, enriching embedded accounts with real data.

Cache is populated by resolveRemoteAccount() (lookup, search, and
/accounts/:id calls). Once a profile has been viewed once, all
subsequent status embeds for that author show real counts.
2026-03-21 12:38:27 +01:00
Ricardo
f9b8baec42 fix: route ordering + remote resolution for account profiles
Two bugs causing profile counts to show 0 in Phanpy:

1. Route ordering: /accounts/relationships and /accounts/familiar_followers
   were defined AFTER /accounts/:id. Express matched "relationships" as
   the :id parameter, returning 404. Moved them before the :id catch-all.

2. /accounts/:id only used local data (followers/following/timeline) which
   has no follower counts. Now tries remote actor resolution via Fedify
   to get real counts from AP collection totalItems.
2026-03-21 12:18:38 +01:00
Ricardo
bc72bf1e02 feat: populate remote profile counts, fields, and join date
Extract followers/following/statuses counts from AP collection
totalItems, profile fields from actor attachments, and published
date from the actor document. Previously showed 0/0/0 and today's
date for all remote profiles.
2026-03-21 12:06:49 +01:00
Ricardo
9f1287073b feat: resolve remote profiles via WebFinger in Mastodon API
Account lookup (/api/v1/accounts/lookup) and search (/api/v2/search)
now resolve remote actors via Fedify's ctx.lookupObject() when not
found locally. Previously only checked ap_followers — missed accounts
we follow, timeline authors, and any remote actor.

Lookup chain: local profile → followers → following → timeline authors
→ remote WebFinger+actor fetch (Fedify)

Search uses remote resolution when resolve=true and query contains @.
2026-03-21 11:49:12 +01:00
Ricardo
01edd6e92e fix: improve timeline content for own posts (4 issues)
1. Empty content on bookmarks/likes/reposts: synthesize content from
   the interaction target URL (bookmark-of, like-of, repost-of) when
   the post has no body text

2. Hashtags not extracted: parse #hashtag patterns from content text
   and merge with explicit categories. Applies to both backfill
   (startup) and POST /api/v1/statuses (runtime)

3. Hashtag links rewritten: /categories/tag/ links (site-internal)
   are rewritten to /tags/tag (Mastodon convention) in the HTML
   content stored in ap_timeline

4. Relative media URLs resolved: photo/video/audio URLs like
   media/photos/... are resolved to absolute URLs using the site URL
2026-03-21 10:34:11 +01:00
Ricardo
2a4ac75c77 fix: use HTML+JS redirect for native app OAuth callbacks
Android Chrome Custom Tabs block 302 redirects to custom URI schemes
(fedilab://, moshidon-android-auth://) for security. The server sends
the redirect correctly but the WebView silently ignores it — "nothing
happens" when the user taps Authorize.

Fix: detect non-HTTP redirect URIs and render an HTML page with both
a JavaScript window.location redirect and a meta refresh fallback.
Client-side navigation to custom schemes is allowed by WebViews.

HTTP(S) redirect URIs (Phanpy, Elk) still use standard 302.
2026-03-21 09:42:31 +01:00
Ricardo
41c43be4cb fix: rename variable to avoid 'published' redeclaration (SyntaxError) 2026-03-20 20:36:51 +01:00
Ricardo
c0d4b77b94 fix: sort Mastodon API timeline by published date instead of ObjectId
The Mastodon API timeline sorted by MongoDB _id (insertion order), not
by published date. This caused chronological jumps — backfilled or
syndicated posts got ObjectIds at import time, interleaving them
incorrectly with federation-received posts.

Changes:
- Pagination cursors now use published date (encoded as ms-since-epoch)
  instead of ObjectId. Mastodon clients pass these as opaque max_id/
  min_id/since_id values and they sort correctly.
- Status and notification IDs are now encodeCursor(published) so the
  cursor round-trips through client pagination.
- Status lookups (GET/DELETE /statuses/:id, context, interactions) use
  findTimelineItemById() which tries published-based lookup first, then
  falls back to ObjectId for backwards compatibility.
- Link pagination headers emit published-based cursors.

This matches the native reader's sort (storage/timeline.js) which has
always sorted by published: -1.
2026-03-20 18:05:45 +01:00
Ricardo
a8947b205f fix: omit null fields instead of setting them in OAuth token documents
MongoDB sparse indexes skip documents where the indexed field is ABSENT,
but still enforce uniqueness on explicit null values. The auth code insert
set accessToken:null and the client_credentials insert set code:null,
causing E11000 duplicate key errors on the second authorization attempt.

Fix: omit accessToken/code entirely from inserts where they don't apply.
The field gets added later during token exchange ($set in updateOne).
2026-03-20 17:25:25 +01:00
Ricardo
f55cfbfcd2 fix: use existing default-avatar.svg instead of missing placeholder-avatar.png
The fallback avatar URL pointed to /placeholder-avatar.png which doesn't
exist (404). Changed to /images/default-avatar.svg which exists in the
Eleventy theme and is served by the nginx image caching location with
CORS headers — fixing cross-origin errors in Phanpy/Elk.
2026-03-20 15:30:50 +01:00
Ricardo
d42b2fce9a chore: bump version to 3.5.7 2026-03-20 14:03:19 +01:00
Ricardo
5fc4d3a6f5 fix: add sparse:true to accessToken index (duplicate key on OAuth authorize)
The accessToken_1 unique index on ap_oauth_tokens lacked sparse:true.
During OAuth2 authorization, POST /oauth/authorize inserts a document
with accessToken:null (auth code phase — token not yet issued). MongoDB
unique indexes include null values by default, so only one such document
could exist. Every subsequent authorization attempt failed with E11000
duplicate key error.

Adding sparse:true skips null values in the index, allowing multiple
auth code documents to coexist while still enforcing uniqueness among
actual access tokens. This matches the code index pattern (line 1423)
which already uses sparse:true.

Note: existing deployments must drop the stale index before restart:
  mongosh $MONGODB_URL --eval 'db.ap_oauth_tokens.dropIndex("accessToken_1")'
  mongosh $MONGODB_URL --eval 'db.ap_oauth_tokens.deleteMany({accessToken:null})'

Confab-Link: http://localhost:8080/sessions/0b241cd6-aff2-4fec-853c-2b5a61e61946
2026-03-20 13:29:25 +01:00
Ricardo
2c0cfffd54 feat: add Mastodon Client API layer for Phanpy/Elk compatibility
Implement the Mastodon Client REST API (/api/v1/*, /api/v2/*) and OAuth2
server within the ActivityPub plugin, enabling Mastodon-compatible clients
to connect to the Fedify-based server.

Core features:
- OAuth2 with PKCE (S256) — app registration, authorization, token exchange
- Instance info + nodeinfo for client discovery
- Account lookup, verification, relationships, follow/unfollow/mute/block
- Home/public/hashtag timelines with cursor-based pagination
- Status viewing, creation, deletion, thread context
- Favourite, boost, bookmark interactions with AP federation
- Notifications with type filtering and pagination
- Search across accounts, statuses, and hashtags
- Markers for read position tracking
- Bookmarks and favourites collection lists
- 25+ stub endpoints preventing client errors on unimplemented features

Architecture:
- 24 new files under lib/mastodon/ (entities, helpers, middleware, routes)
- Virtual endpoint at "/" via Indiekit.addEndpoint() for domain-root access
- CORS + JSON error handling for browser-based clients
- Six-layer mute/block filtering reusing existing moderation infrastructure

BREAKING CHANGE: bumps to v3.0.0 — adds new MongoDB collections
(ap_oauth_apps, ap_oauth_tokens, ap_markers) and new route registrations

Confab-Link: http://localhost:8080/sessions/5360e3f5-b3cc-4bf3-8c31-5448e2b23947
2026-03-18 12:50:52 +01:00
Ricardo
2ca491f28b fix: wire content-warning property for CW text
Updated jf2-to-as2 and compose controller to use the renamed
"content-warning" property instead of overloading "summary" for
CW text. This pairs with the endpoint-posts fix that renamed the
CW form input to prevent collision with the summary field.

Confab-Link: http://localhost:8080/sessions/1dcdf030-8015-4d23-89da-b43fd69c7138
2026-03-18 00:24:35 +01:00
Ricardo
b4daa806bd chore: bump version to 2.15.3
Confab-Link: http://localhost:8080/sessions/af5f8b45-6b8d-442d-8f25-78c326190709
2026-03-17 17:13:57 +01:00
Ricardo
e066222422 chore: bump version to 2.15.2
Confab-Link: http://localhost:8080/sessions/af5f8b45-6b8d-442d-8f25-78c326190709
2026-03-17 15:09:31 +01:00
Ricardo
a23c955b94 chore: bump version to 2.15.1
Confab-Link: http://localhost:8080/sessions/af5f8b45-6b8d-442d-8f25-78c326190709
2026-03-17 13:17:59 +01:00
Ricardo
0d6668e741 chore: bump version to 2.15.0
Confab-Link: http://localhost:8080/sessions/af5f8b45-6b8d-442d-8f25-78c326190709
2026-03-17 11:13:11 +01:00
Ricardo
297b5b9464 chore: bump version to 2.14.0
Confab-Link: http://localhost:8080/sessions/af5f8b45-6b8d-442d-8f25-78c326190709
2026-03-17 09:18:38 +01:00
Ricardo
9a61145d97 feat: FEP-8fcf/fe34 compliance, custom emoji, manual follow approval (v2.13.0)
- FEP-8fcf: add syncCollection to Undo(Announce) sendActivity
- FEP-fe34: centralized lookupWithSecurity() helper with crossOrigin: "ignore" on all 23 lookupObject call sites
- Custom emoji: replaceCustomEmoji() renders :shortcode: as inline <img> in content and actor display names
- Manual follow approval: profile toggle, ap_pending_follows collection, approve/reject controllers with federation, pending tab on followers page, follow_request notification type
- Coverage audit updated to v2.12.x (overall ~70% → ~82%)

Confab-Link: http://localhost:8080/sessions/1f1e729b-0087-499e-a991-f36f46211fe4
2026-03-17 08:21:36 +01:00
Ricardo
6707450079 fix: federation-mgmt extends ap-reader.njk layout for proper CSS loading (v2.12.1)
Confab-Link: http://localhost:8080/sessions/c2335791-4b8c-44a6-b1b7-8d0fa8d7f647
2026-03-15 17:27:27 +01:00
Ricardo
19aa83ab8d feat: federation management page with collection stats, post actions, object lookup (v2.12.0)
Confab-Link: http://localhost:8080/sessions/c2335791-4b8c-44a6-b1b7-8d0fa8d7f647
2026-03-15 16:32:14 +01:00
Ricardo
6238e7d4e5 feat: visibility/CW compose controls, @mention support (v2.11.0)
Add visibility and content warning controls to the reply compose form.
Add @user@domain mention parsing, WebFinger resolution, Mention tags,
inbox delivery, and content linkification for outbound posts.

Confab-Link: http://localhost:8080/sessions/cc343b15-8d10-43cd-a48f-ca912eb79b83
2026-03-14 21:28:24 +01:00
Ricardo
1dc42ad5e5 feat: outbound Delete, visibility addressing, CW/sensitive, polls, Flag reports (v2.10.0)
- Outbound Delete: broadcastDelete() + POST /admin/federation/delete route
- Visibility: unlisted + followers-only addressing via defaultVisibility config
- Content Warning: outbound sensitive flag + summary as CW text
- Polls: inbound Question/poll parsing with progress bar rendering
- Flag: inbound report handler with ap_reports collection + Reports tab
- Includes DM support files from v2.9.x (messages controller, storage, templates)
- Includes coverage audit and high-impact gaps implementation plan

Confab-Link: http://localhost:8080/sessions/cc343b15-8d10-43cd-a48f-ca912eb79b83
2026-03-14 08:51:44 +01:00
Ricardo
a266b6d9ba fix: render compose reply context with card content styling (v2.8.2)
The reply context on the compose page rendered raw Mastodon HTML in a
bare <blockquote>, causing hashtag links to display as block elements
(each on its own line with # separated from the tag name). Wrapping
the content in a <div class="ap-card__content"> applies the same
inline hashtag/mention/invisible-span CSS rules used in the timeline.

Confab-Link: http://localhost:8080/sessions/cc343b15-8d10-43cd-a48f-ca912eb79b83
2026-03-13 14:37:40 +01:00
Ricardo
bf386e0c41 chore: phase 2 convention alignment — onerror/onclick removal, CSS stacking avatar fallback (v2.8.1)
- Replace inline onerror handlers with CSS stacking + event delegation for avatar fallback
- Replace inline onclick with event delegation for profile link removal
- Replace hardcoded border values with design tokens in reader-links.css
- Add data-avatar-fallback pattern: fallback initials always visible, img layered on top

Confab-Link: http://localhost:8080/sessions/bb4a6ec4-b711-48cd-b3d7-942ec2a9851d
2026-03-13 12:32:14 +01:00
Ricardo
1c2fb321bc feat: image rendering, link preview CSS, lightbox swipe, URL linkification (v2.8.0)
- Gallery photos: 220px → 280px height, 180px on mobile (≤480px)
- Link preview cards: full CSS for horizontal card layout (text left, image right)
- Lightbox: touch/swipe support for mobile (50px threshold)
- URL linkification: bare URLs in content auto-wrapped in <a> tags before AP delivery

Confab-Link: http://localhost:8080/sessions/c5b1471e-b046-44d9-b94f-ab5e68fae7cc
2026-03-06 10:42:39 +01:00
Ricardo
2083741535 fix: use human-readable URLs for reply-to links (v2.7.1)
Reply links were using the AP internal object ID (e.g.
/ap/users/{id}/statuses/{id}) which returns 404 on Mastodon for
browsers. Now uses the human-readable URL (/@username/{id}) for
replyTo params in item cards and notification cards.

- Store url field on reply/mention notifications (inbox-listeners)
- Prefer item.url over item.uid for compose replyTo links
- Falls back to uid for existing notifications without url field

Confab-Link: http://localhost:8080/sessions/d116ad5b-ef8a-424e-9ebe-76c06bef1df6
2026-03-05 08:24:08 +01:00
Ricardo
7611dba40f feat: remove quick reply, streamline blog reply (v2.7.0)
Remove the quick-reply code path entirely — all replies now go through
Micropub as blog posts. Quick replies created orphan URLs that served
raw JSON-LD to browsers and caused unreadable links in conversations.

- Delete quick-reply controller (note-object.js) and route
- Remove ap_notes collection registration
- Simplify compose form: no mode toggle, no character counter
- Remove quick-reply CSS and locale strings

Confab-Link: http://localhost:8080/sessions/d116ad5b-ef8a-424e-9ebe-76c06bef1df6
2026-03-04 17:33:02 +01:00
Ricardo
ec41fec366 fix: dark mode color comfort — use adaptive tokens, add dark overrides
Replace undefined --color-accent/--color-neutral-lighter with Indiekit tokens.
Swap --color-primary to --color-primary-on-background for all text/links.
Swap --color-red45 to --color-error in danger contexts (auto dark adapt).
Add @media (prefers-color-scheme: dark) block: softer action button colors
(red80, green90), pastel post-type borders, toned-down notification glow,
softened post detail highlight, light-tinted card shadows.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 20:51:14 +01:00
Ricardo
9332421890 feat: visual polish, focus-point cropping, blurhash placeholders (Release 8)
Card styling: softer 8px radius, subtle box-shadow elevation, hover enhancement.
Action buttons: borderless with color-coded hover states via color-mix().
Typography: tighter line-height (4/3), larger avatars (44px), gallery images (220px).
Focus-point cropping: convert Mastodon focus.x/y to CSS object-position.
Blurhash placeholders: decode DC component to background-color on images.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 19:26:38 +01:00
Ricardo
b9fc98f40c feat: content enhancements — URL shortening, hashtag collapse, bot badge, edit indicator (Release 7)
Shorten long URLs in post content (30 char display limit with tooltip).
Collapse hashtag-heavy paragraphs into expandable <details> toggle.
Show BOT badge for Service/Application actors. Show pencil icon for
edited posts with hover tooltip showing edit timestamp.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 16:40:01 +01:00
Ricardo
fca1738bd3 feat: skeleton loaders replace loading text (Release 6)
Animated card-shaped placeholders with shimmer effect shown during
content loading instead of plain "Loading..." text. Applied to reader,
tag timeline, and explore tabs (both first-load and load-more states).

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 15:48:59 +01:00
Ricardo
2d2dcaec7d feat: interaction counts on timeline cards (Release 5)
Extract reply/boost/like counts from AP Collections (getReplies,
getLikes, getShares) and Mastodon API (replies_count, reblogs_count,
favourites_count). Display counts next to interaction buttons with
optimistic updates on like/boost actions.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 14:30:40 +01:00
Ricardo
c243b70629 feat: enriched media model with ALT badges (Release 3+4)
Change photo storage from bare URL strings to objects with url, alt,
width, height (AP) plus blurhash and focus (Mastodon API). Templates
handle both old string and new object format for backward compat.

Add ALT text badges on gallery images — click to expand the full
alt text in an overlay. Renders in both reader and explore views.

Also pass alt text through to lightbox and quote embed photos.

Bump version to 2.5.3.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:46:58 +01:00
Ricardo
e34d9c124d feat: relative timestamps in reader (Release 2)
Add Alpine.js directive x-relative-time that converts absolute dates
to human-friendly relative strings: just now, 5m, 3h, 2d, Mar 3.
Updates every 60s for posts less than 24h old. Server-rendered absolute
time stays as no-JS fallback and hover tooltip.

Applied to item cards, quote embeds, and notification cards.

Bump version to 2.5.2.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:34:01 +01:00
Ricardo
02d449d03c feat: render custom emoji in reader (Release 1)
Extract custom emoji from ActivityPub objects (Fedify Emoji tags) and
Mastodon API (status.emojis, account.emojis). Replace :shortcode:
patterns with <img> tags in the unified processing pipeline.

Emoji rendering applies to post content, author display names, boost
attribution, and quote embed authors. Uses the shared postProcessItems()
pipeline so both reader and explore views get emoji automatically.

Bump version to 2.5.1.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:13:28 +01:00
Ricardo
af2f899073 refactor: unify reader and explore processing pipeline (Release 0)
Extract shared item-processing.js module with postProcessItems(),
applyModerationFilters(), buildInteractionMap(), applyTabFilter(),
renderItemCards(), and loadModerationData(). All controllers (reader,
api-timeline, explore, hashtag-explore, tag-timeline) now flow through
the same pipeline.

Unify Alpine.js infinite scroll into single parameterized
apInfiniteScroll component configured via data attributes, replacing
the separate apExploreScroll component.

Also adds fetchAndStoreQuote() for quote enrichment and on-demand
quote fetching in post-detail controller.

Bump version to 2.5.0.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 12:48:40 +01:00
Ricardo
9fa3412875 fix: chain dropIndex before createIndex on ap_muted to prevent race condition
The non-async init() fired dropIndex and createIndex concurrently,
causing MongoDB to abort the index build (IndexBuildAborted error 276).
Chain createIndex via .then() so it runs after the drop completes.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 11:40:43 +01:00
Ricardo
76571b00ac chore: bump version to 2.4.0
Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 10:54:26 +01:00
Ricardo
68aadb6ff2 chore: bump version to 2.3.0
Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-02 10:33:20 +01:00
Ricardo
abf1b94bd6 feat: migrate Fedify KV store and plugin cache from MongoDB to Redis
Replace unbounded ap_kv MongoDB collection (169K docs, 49MB) with Redis:
- Fedify KV store uses @fedify/redis RedisKvStore (native TTL support)
- Plugin cache (fedidb, batch-refollow state, migration flags) uses new
  redis-cache.js utility with indiekit: key prefix
- All controllers updated to remove kvCollection parameter passing
- Addresses OOM kills caused by ap_kv growing ~14K entries/day
2026-03-01 16:26:17 +01:00
Ricardo
5c2fd09f8f fix: normalize category to array in my-profile controller
When a post has a single category, Indiekit stores it as a string
(e.g. "Fraude") rather than an array. Nunjucks iterates strings
character by character, producing hashtag pills like #F #r #a #u #d #e.
2026-02-28 18:21:32 +01:00