Commit Graph

28 Commits

Author SHA1 Message Date
Ricardo
944917b3f0 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 00:22:47 +01:00
Ricardo
af4d9f7ddf i18n: add translations for all 14 supported locales
Add de, es, es-419, fr, hi, id, it, nl, pl, pt, pt-BR, sr, sv,
zh-Hans-CN locale files (300 keys each).
2026-03-21 12:46:13 +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
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
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
55baa7cef5 feat: replace explore deck layout with full-width tabbed design
Replace the cramped deck/column layout on the explore page with a
tabbed interface. Three tab types: Search (always first), Instance
(pinned with local/federated badge), and Hashtag (aggregated across
all pinned instances).

- New ap_explore_tabs collection replaces ap_decks (clean start)
- Tab CRUD API: add, remove, reorder with CSRF/SSRF validation
- Per-tab infinite scroll with IntersectionObserver + AbortController
- Hashtag tabs query up to 10 instances in parallel, merge by date,
  deduplicate by URL
- WAI-ARIA tabs pattern with arrow key navigation
- LRU cache (5 tabs) for tab content
- Extract shared explore-utils.js (validators + status mapping)
- Remove all old deck code (JS, CSS, controllers, locale strings)
2026-02-28 16:30:48 +01:00
Ricardo
145e329d2f feat: add TweetDeck-style deck layout for explore view
Users can favorite instances (with local or federated scope) as persistent
columns in a multi-column deck view. Each column streams its own public
timeline with independent infinite scroll. Includes two-tab explore UI
(Search + Decks), deck CRUD API with CSRF/SSRF protection, 8-deck limit,
responsive CSS Grid layout, and scope badges.
2026-02-27 11:24:53 +01:00
Ricardo
cee0050be8 feat: add FediDB-powered autocomplete for explore and reader lookup
- Add FediDB API client (lib/fedidb.js) with MongoDB caching (24h TTL)
  for instance search, timeline support checks, and popular accounts
- Explore page: instance input now shows autocomplete suggestions from
  FediDB with software type, MAU count, and timeline support indicator
  (checkmark/cross) via background pre-check
- Reader page: @handle lookup input now shows popular fediverse accounts
  from FediDB with avatar, name, handle, and follower count
- Three new API endpoints: /api/instances, /api/instance-check,
  /api/popular-accounts
- Alpine.js components for both autocomplete UIs with keyboard navigation
2026-02-27 09:26:45 +01:00
Ricardo
a4f72a588d feat: enhance ActivityPub reader with mentions, hashtags, infinite scroll, explore, and tag following
- Fix mentions/hashtags bug: separate Fedify Mention and Hashtag types into
  distinct mentions[] and category[] arrays with proper @ and # rendering
- Add hashtag timeline filtering at /admin/reader/tag with regex-safe queries
- Replace prev/next pagination with AlpineJS infinite scroll (IntersectionObserver)
  with no-JS fallback pagination preserved
- Add public instance timeline explorer at /admin/reader/explore with SSRF
  prevention and XSS sanitization via Mastodon-compatible API
- Add hashtag following with ap_followed_tags collection, inbox listener
  integration for non-followed accounts, and followed tags sidebar display
- Include one-time migration script for legacy timeline data
2026-02-26 18:15:21 +01:00
Ricardo
23fc8f4614 feat: rewrite moderation UI with filter mode, fix sparse index bug
Moderation page rewritten as single Alpine.js component with inline DOM
updates instead of location.reload(). Added hide/warn filter mode toggle
— warn mode shows muted items behind content warning instead of hiding.

Expanded keyword matching to check content, titles, and summaries.
Fixed MongoDB E11000 duplicate key error by dropping non-sparse indexes
on startup and recreating with sparse:true. Storage layer no longer
stores null url/keyword fields.
2026-02-23 23:11:28 +01:00
Ricardo
743cb6b85b feat: notification tabs, my-profile page, clickable timestamps, quick-reply
- Notification view: tab navigation (Replies, Likes, Boosts, Follows, All)
  with count badges; defaults to Replies tab; type filter in storage layer
  with compound index for efficient queries
- My Profile admin page: profile header with avatar/stats/bio, tabbed
  activity view (Posts, Replies, Likes, Boosts) pulling from posts,
  ap_activities, and ap_interactions collections
- Reader: default tab changed from All to Notes
- Timeline cards: timestamps now link to post detail view
- Notification cards: Reply and View Thread buttons on reply/mention types
2026-02-23 15:55:44 +01:00
Ricardo
5c5e53bf3d feat: public profile page for actor URL
Replace the browser redirect on /activitypub/users/:handle with a
standalone HTML profile page showing avatar, bio, profile fields,
stats (posts/following/followers/joined), follow-me prompt with
copy button, pinned posts, and recent posts. Supports light/dark
mode via prefers-color-scheme. ActivityPub clients still get JSON-LD
from Fedify before this route is reached.
2026-02-22 12:36:07 +01:00
Ricardo
cf284e8633 feat: add fediverse URL/handle lookup input to reader
Adds a search box at the top of the reader page where users can paste
any fediverse URL or @user@domain handle. Uses Fedify's lookupObject()
which natively resolves URLs, handles, and acct: URIs, then redirects
to the internal post detail or remote profile view.
2026-02-21 21:33:08 +01:00
Ricardo
d20dea2dc8 feat: notification management — clear, mark read, dismiss, TTL retention
- Add "Mark all read" and "Clear all" toolbar buttons on notifications page
- Add per-notification dismiss (×) button
- Remove auto-mark-all-as-read on page load (explicit action only)
- Add 30-day TTL index on createdAt for automatic notification cleanup
- New config option: notificationRetentionDays (default 30)
2026-02-21 20:00:05 +01:00
Ricardo
5ff3197493 feat: add internal AP link resolution and OpenGraph card unfurling (v1.1.14)
Reader now resolves ActivityPub links internally instead of navigating
to external instances. Actor links open the profile view, post links
open a new post detail view with thread context (parent chain + replies).

External links in post content get rich preview cards (title, description,
image, favicon) fetched via unfurl.js at ingest time with fire-and-forget
async processing and concurrency limiting.

New files: post-detail controller, og-unfurl module, lookup-cache,
link preview template/CSS, client-side link interception JS.
Includes SSRF protection for OG fetching and GoToSocial URL support.
2026-02-21 18:32:12 +01:00
Ricardo
4e514235c2 feat: ActivityPub reader — timeline, notifications, compose, moderation
Add a dedicated fediverse reader view with:
- Timeline view showing posts from followed accounts with threading,
  content warnings, boosts, and media display
- Compose form with dual-path posting (quick AP reply + Micropub blog post)
- Native AP interactions (like, boost, reply, follow/unfollow)
- Notifications view for likes, boosts, follows, mentions, replies
- Moderation tools (mute/block actors, keyword filters)
- Remote actor profile pages with follow state
- Automatic timeline cleanup with configurable retention
- CSRF protection, XSS prevention, input validation throughout

Removes Microsub bridge dependency — AP content now lives in its own
MongoDB collections (ap_timeline, ap_notifications, ap_interactions,
ap_muted, ap_blocked).

Bumps version to 1.1.0.
2026-02-21 12:13:10 +01:00
Ricardo
81a28ef086 feat: add actor type selector and profile links to admin UI
- Actor type radio buttons (Person/Service/Organization) in Profile page,
  stored in ap_profile and read by federation-setup actor dispatcher
- Profile links (attachments) section with add/remove for rel="me"
  verification links, rendered as PropertyValue on the ActivityPub actor
- New locale strings for all new UI elements
2026-02-21 10:07:03 +01:00
Ricardo
4827a98614 feat: Fedify feature completeness — collections, dispatchers, delivery hardening
Implement all missing Fedify features for full ActivityPub compliance:

- Liked, Featured, Featured Tags collection dispatchers with admin UIs
- Object dispatcher for Note/Article dereferencing at AP URIs
- Instance actor (Application type) for domain-level federation
- Handle aliases (.mapAlias) for profile URL and /@handle resolution
- Configurable actor type (Person/Service/Organization/Group)
- Dynamic NodeInfo version from @indiekit/indiekit package.json
- Context data propagation (handle + publication URL)
- ParallelMessageQueue wrapping RedisMessageQueue (5 workers)
- Collection sync (FEP-8fcf) and ordering keys on sendActivity
- Permanent failure handler stub (deferred to Fedify 2.0)
- Profile attachments (PropertyValue) and alsoKnownAs support
- Strip invalid "type":"as:Endpoints" from actor JSON (Fedify #576)
- Fix .mapAlias() return type ({identifier} not bare string)
- Remove .authorize() predicate (causes 401 loops without auth doc loader)
- Narrow content negotiation router to /nodeinfo/ only

22/22 compliance tests pass (Grade A+). Version 1.0.26.
2026-02-20 22:57:41 +01:00
Ricardo
84122cc470 feat: batch re-follow system for imported AP accounts
After Mastodon migration, imported accounts exist only locally — no
Follow activities were sent. This adds a gradual background processor
that sends Follow activities to all source:"import" accounts so remote
servers start delivering Create activities to our inbox.

- New batch engine (lib/batch-refollow.js) processes 10 accounts per
  batch with 3s between follows and 30s between batches
- Accept(Follow) inbox listener transitions source to "federation"
  and cleans up tracking fields
- Admin API: pause, resume, and status JSON endpoints
- Dashboard progress bar with Alpine.js polling (10s interval)
- Following list badges for refollow:sent and refollow:failed states
- Restart recovery resets stale refollow:pending back to import
- 3 retries with 1-hour cooldown before permanent failure
2026-02-20 08:10:45 +01:00
Ricardo
eaf0f1d126 feat: migrate to Fedify for ActivityPub federation (v0.2.0)
Replace hand-rolled federation code with Fedify's battle-tested
implementation. This gives us proper HTTP Signatures, WebFinger,
NodeInfo, typed inbox listeners, and collection dispatchers out
of the box.

New modules:
- lib/federation-setup.js — Fedify Federation configuration
- lib/federation-bridge.js — Express↔Fedify middleware bridge
- lib/inbox-listeners.js — typed inbox handlers (Follow, Undo, etc.)
- lib/kv-store.js — MongoDB-backed KvStore adapter
- lib/controllers/profile.js — admin profile management
- views/activitypub-profile.njk — profile editing form

Removed hand-rolled modules:
- lib/actor.js, lib/federation.js, lib/inbox.js
- lib/keys.js, lib/webfinger.js

Key changes:
- Actor, inbox, outbox, followers, following all delegate to Fedify
- Syndication uses ctx.sendActivity() instead of manual delivery
- Profile managed via admin UI, stored in ap_profile collection
- Legacy PKCS#8 keys imported via Web Crypto API
- Custom bridge preserves Express mount path (req.originalUrl)
2026-02-19 11:59:23 +01:00
Ricardo
d159e79998 fix: switch CSV import to JSON fetch to bypass body size limit
The app-level Express urlencoded parser (100KB limit) runs before
route-level middleware, so overriding the limit on the route doesn't
help. Solution: POST the CSV as JSON via fetch() to a dedicated
/admin/migrate/import endpoint with its own express.json({ limit: '5mb' }).

- Import button now shows "Importing..." while working
- Results appear inline without page reload
- Failed handles shown in a collapsible details element
- Import button disabled until a file is selected
- Alias form remains a regular POST (small payload, no issue)
2026-02-19 10:22:34 +01:00
Ricardo
de00d3a16c fix: payload too large error and add migration logging
- Add express.urlencoded({ limit: '5mb' }) to migration POST route
  to handle large CSV files (default 100KB was too small)
- Add per-handle progress logging to console for monitoring imports
- Log failed handles with reasons (WebFinger failure, no AP link, etc.)
- Show failed handles in the UI result notification
- Use error notification type when all imports fail
2026-02-19 10:14:23 +01:00
Ricardo
fec4b1f242 fix: use client-side FileReader for CSV upload
Multipart form uploads fail because Indiekit has no multipart parsing
middleware. Instead, read the CSV file client-side with FileReader and
submit the text content as a hidden form field. Shows file name and
line count after selection for user confidence.
2026-02-19 09:50:43 +01:00
Ricardo
334f71d601 fix: improve migration page UX
- Show current alias value on the page (persists across GET/POST)
- Pre-fill alias input with current value
- Add fieldset legend and per-item hints to import checkboxes
- Add intro paragraph explaining the migration flow
- Rewrite copy to be clearer and more reassuring
- Note irreversibility of step 3 explicitly
2026-02-19 09:27:35 +01:00
Ricardo
43549c6334 fix: restructure locale keys to use proper nesting for migrate section
The i18n system resolves dots as nested path separators, but migrate
keys were flat strings with dots in the key name. Restructure migrate
as a nested object with a title sub-key.
2026-02-18 23:50:02 +01:00
Ricardo
da625592fd feat: ActivityPub federation endpoint for Indiekit
Implements full ActivityPub federation as an Indiekit plugin:
- Actor document (Person) with RSA key pair for HTTP Signatures
- WebFinger discovery (acct:rick@rmendes.net)
- Inbox: handles Follow, Undo, Like, Announce, Create, Delete, Move
- Outbox: serves published posts as ActivityStreams 2.0
- Content negotiation: AS2 JSON for AP clients, passthrough for browsers
- JF2-to-AS2 converter for all Indiekit post types
- Syndicator integration (pre-ticked checkbox for delivery to followers)
- Mastodon migration: alias config, CSV import for followers/following
- Admin UI: dashboard, followers, following, activity log, migration page
- Data retention: configurable TTL on activities, optional raw JSON storage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:13:51 +01:00