docs: federation management design spec
This commit is contained in:
@@ -0,0 +1,116 @@
|
|||||||
|
# Federation Management Design
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Allow users to manage their ActivityPub federation: accept/reject incoming remote follow requests, remove accepted remote followers, and unfollow remote actors they're following. Surface this in three places via a shared component set.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Hexagonal layers respected throughout:
|
||||||
|
- **Application layer**: new use cases in `federation_management.rs` — all business routing lives here
|
||||||
|
- **Presentation layer**: handlers call use cases only, no direct port access
|
||||||
|
- **Frontend**: four components under `components/federation/`, used in three locations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backend
|
||||||
|
|
||||||
|
### New use cases — `crates/application/src/use_cases/federation_management.rs`
|
||||||
|
|
||||||
|
Six functions, each taking `&dyn FederationActionPort` and `&UserId`. Return domain types (`Vec<RemoteActor>` or `()`).
|
||||||
|
|
||||||
|
```
|
||||||
|
list_pending_requests(federation, user_id) → Result<Vec<RemoteActor>, DomainError>
|
||||||
|
accept_follow_request(federation, user_id, actor_url: &str) → Result<(), DomainError>
|
||||||
|
reject_follow_request(federation, user_id, actor_url: &str) → Result<(), DomainError>
|
||||||
|
list_remote_followers(federation, user_id) → Result<Vec<RemoteActor>, DomainError>
|
||||||
|
remove_remote_follower(federation, user_id, actor_url: &str) → Result<(), DomainError>
|
||||||
|
list_remote_following(federation, user_id) → Result<Vec<RemoteActor>, DomainError>
|
||||||
|
```
|
||||||
|
|
||||||
|
Unfollow remote reuses the existing `unfollow_actor` use case in `social.rs` (already routes `@handle` to `federation.unfollow_remote`).
|
||||||
|
|
||||||
|
### New HTTP endpoints — `crates/presentation/src/handlers/federation_management.rs`
|
||||||
|
|
||||||
|
All routes require authentication (`AuthUser` extractor). Actor URLs go in the JSON request body to avoid percent-encoding issues.
|
||||||
|
|
||||||
|
| Method | Path | Body | Action |
|
||||||
|
|--------|------|------|--------|
|
||||||
|
| `GET` | `/federation/me/followers/pending` | — | List pending follow requests |
|
||||||
|
| `POST` | `/federation/me/followers/accept` | `{ actor_url: String }` | Accept a follow request |
|
||||||
|
| `DELETE` | `/federation/me/followers` | `{ actor_url: String }` | Remove/reject a follower |
|
||||||
|
| `GET` | `/federation/me/followers` | — | List accepted remote followers |
|
||||||
|
| `GET` | `/federation/me/following` | — | List remote actors being followed |
|
||||||
|
| `DELETE` | `/federation/me/following` | `{ handle: String }` | Unfollow a remote actor (delegates to `unfollow_actor`) |
|
||||||
|
|
||||||
|
Handlers are thin: extract auth, call use case, return JSON. No logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
### API client additions — `thoughts-frontend/lib/api.ts`
|
||||||
|
|
||||||
|
Six new functions mirroring the six endpoints. All take `token: string`.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
getPendingFollowRequests(token)
|
||||||
|
acceptFollowRequest(actorUrl: string, token)
|
||||||
|
rejectFollowRequest(actorUrl: string, token)
|
||||||
|
getRemoteFollowers(token)
|
||||||
|
removeRemoteFollower(actorUrl: string, token)
|
||||||
|
getRemoteFollowing(token)
|
||||||
|
// unfollowRemote reuses existing unfollowUser or a new call to DELETE /federation/me/following
|
||||||
|
```
|
||||||
|
|
||||||
|
Response schema: `RemoteActorSchema` (already defined — handle, display_name, avatar_url, url).
|
||||||
|
|
||||||
|
### Components — `thoughts-frontend/components/federation/`
|
||||||
|
|
||||||
|
**`pending-requests.tsx`**
|
||||||
|
- Client component
|
||||||
|
- Fetches `getPendingFollowRequests` on mount
|
||||||
|
- Renders list of remote actors with Accept and Reject buttons
|
||||||
|
- On action: optimistic removal from list, then API call
|
||||||
|
- Prop: `compact?: boolean` — when true, renders as a flat list without card chrome (for notifications embed)
|
||||||
|
|
||||||
|
**`remote-followers.tsx`**
|
||||||
|
- Client component
|
||||||
|
- Fetches `getRemoteFollowers` on mount
|
||||||
|
- Renders list of accepted remote followers with a Remove button
|
||||||
|
- On remove: optimistic removal, then API call
|
||||||
|
|
||||||
|
**`remote-following.tsx`**
|
||||||
|
- Client component
|
||||||
|
- Fetches `getRemoteFollowing` on mount
|
||||||
|
- Renders list of remote actors being followed with an Unfollow button
|
||||||
|
- On unfollow: optimistic removal, then API call
|
||||||
|
|
||||||
|
**`federation-panel.tsx`**
|
||||||
|
- Composes the three above inside a shadcn `Tabs` component
|
||||||
|
- Tabs: "Requests", "Followers", "Following"
|
||||||
|
- Shows a numeric badge on "Requests" tab when pending count > 0
|
||||||
|
- No data fetching of its own — delegates entirely to sub-components
|
||||||
|
|
||||||
|
### Usage locations
|
||||||
|
|
||||||
|
**`app/settings/federation/page.tsx`** (new)
|
||||||
|
- Server component shell, renders `<FederationPanel />`
|
||||||
|
- Add "Federation" link to the settings sidebar alongside "Profile" and "API Keys"
|
||||||
|
|
||||||
|
**`app/users/[username]/page.tsx`** (modify)
|
||||||
|
- Add a "Federation" tab to the profile tabs row
|
||||||
|
- Render `<FederationPanel />` as its content
|
||||||
|
- Tab is only visible when `isOwnProfile === true`
|
||||||
|
|
||||||
|
**Notifications page** (modify)
|
||||||
|
- Render `<PendingRequests compact />` as a card section above the notification feed
|
||||||
|
- Only shown when the user is authenticated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Local follow request management (local follows are auto-accepted)
|
||||||
|
- Blocking remote actors (separate feature, already partially implemented)
|
||||||
|
- Notification count badge in the nav for pending requests (can be added later)
|
||||||
Reference in New Issue
Block a user