@commonpub/server 2.3.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/federation/hubMirroring.d.ts +100 -0
- package/dist/federation/hubMirroring.d.ts.map +1 -0
- package/dist/federation/hubMirroring.js +352 -0
- package/dist/federation/hubMirroring.js.map +1 -0
- package/dist/federation/inboxHandlers.d.ts.map +1 -1
- package/dist/federation/inboxHandlers.js +60 -1
- package/dist/federation/inboxHandlers.js.map +1 -1
- package/dist/federation/index.d.ts +1 -0
- package/dist/federation/index.d.ts.map +1 -1
- package/dist/federation/index.js +1 -0
- package/dist/federation/index.js.map +1 -1
- package/dist/hub/hub.d.ts +5 -107
- package/dist/hub/hub.d.ts.map +1 -1
- package/dist/hub/hub.js +54 -833
- package/dist/hub/hub.js.map +1 -1
- package/dist/hub/index.d.ts +4 -1
- package/dist/hub/index.d.ts.map +1 -1
- package/dist/hub/index.js +8 -1
- package/dist/hub/index.js.map +1 -1
- package/dist/hub/members.d.ts +26 -0
- package/dist/hub/members.d.ts.map +1 -0
- package/dist/hub/members.js +188 -0
- package/dist/hub/members.js.map +1 -0
- package/dist/hub/moderation.d.ts +29 -0
- package/dist/hub/moderation.d.ts.map +1 -0
- package/dist/hub/moderation.js +203 -0
- package/dist/hub/moderation.js.map +1 -0
- package/dist/hub/posts.d.ts +55 -0
- package/dist/hub/posts.d.ts.map +1 -0
- package/dist/hub/posts.js +473 -0
- package/dist/hub/posts.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { DB, FederatedHubListItem, FederatedHubPostItem } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* List all federated hubs (accepted + not hidden).
|
|
4
|
+
* Used by listHubs() when includeFederated is true.
|
|
5
|
+
*/
|
|
6
|
+
export declare function listFederatedHubs(db: DB, filters?: {
|
|
7
|
+
search?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
offset?: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
items: FederatedHubListItem[];
|
|
12
|
+
total: number;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Get a single federated hub by its local ID.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getFederatedHub(db: DB, hubId: string): Promise<FederatedHubListItem | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Get a federated hub by its AP actor URI.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Get a federated hub by its AP actor URI.
|
|
23
|
+
* Only returns hubs with status='accepted' (follow handshake completed).
|
|
24
|
+
*/
|
|
25
|
+
export declare function getFederatedHubByActorUri(db: DB, actorUri: string): Promise<FederatedHubListItem | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Follow a remote hub Group actor to start mirroring.
|
|
28
|
+
* Returns the created federated hub entry.
|
|
29
|
+
*/
|
|
30
|
+
export declare function followRemoteHub(db: DB, actorUri: string, metadata: {
|
|
31
|
+
originDomain: string;
|
|
32
|
+
remoteSlug: string;
|
|
33
|
+
name: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
iconUrl?: string;
|
|
36
|
+
bannerUrl?: string;
|
|
37
|
+
hubType?: string;
|
|
38
|
+
remoteMemberCount?: number;
|
|
39
|
+
remotePostCount?: number;
|
|
40
|
+
url?: string;
|
|
41
|
+
rules?: string;
|
|
42
|
+
categories?: string[];
|
|
43
|
+
}, followActivityUri?: string): Promise<{
|
|
44
|
+
id: string;
|
|
45
|
+
created: boolean;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Accept a hub follow — called when we receive Accept(Follow) from the remote Group actor.
|
|
49
|
+
*/
|
|
50
|
+
export declare function acceptHubFollow(db: DB, actorUri: string): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Unfollow/cancel a hub mirror.
|
|
53
|
+
*/
|
|
54
|
+
export declare function unfollowRemoteHub(db: DB, actorUri: string): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Queue an outbound Follow activity from the local instance actor to a remote Group actor.
|
|
57
|
+
* This is the AP handshake that initiates hub mirroring.
|
|
58
|
+
*/
|
|
59
|
+
export declare function sendHubFollow(db: DB, remoteGroupActorUri: string, domain: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Ingest a post from a federated hub (received via Announce from Group actor).
|
|
62
|
+
*/
|
|
63
|
+
export declare function ingestFederatedHubPost(db: DB, federatedHubId: string, post: {
|
|
64
|
+
objectUri: string;
|
|
65
|
+
actorUri: string;
|
|
66
|
+
content: string;
|
|
67
|
+
postType?: string;
|
|
68
|
+
isPinned?: boolean;
|
|
69
|
+
remoteLikeCount?: number;
|
|
70
|
+
remoteReplyCount?: number;
|
|
71
|
+
publishedAt?: Date;
|
|
72
|
+
}): Promise<{
|
|
73
|
+
id: string;
|
|
74
|
+
created: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* List posts for a federated hub.
|
|
78
|
+
*/
|
|
79
|
+
export declare function listFederatedHubPosts(db: DB, federatedHubId: string, opts?: {
|
|
80
|
+
limit?: number;
|
|
81
|
+
offset?: number;
|
|
82
|
+
}): Promise<{
|
|
83
|
+
items: FederatedHubPostItem[];
|
|
84
|
+
total: number;
|
|
85
|
+
}>;
|
|
86
|
+
/**
|
|
87
|
+
* Delete a federated hub post (on receiving Delete activity).
|
|
88
|
+
* Validates actorUri matches the post author for defense in depth.
|
|
89
|
+
*/
|
|
90
|
+
export declare function deleteFederatedHubPost(db: DB, objectUri: string, actorUri?: string): Promise<boolean>;
|
|
91
|
+
/**
|
|
92
|
+
* Like a federated hub post (increment local counter).
|
|
93
|
+
* The caller is responsible for queuing the outbound Like activity.
|
|
94
|
+
*/
|
|
95
|
+
export declare function likeFederatedHubPost(db: DB, postId: string): Promise<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Unlike a federated hub post (decrement local counter).
|
|
98
|
+
*/
|
|
99
|
+
export declare function unlikeFederatedHubPost(db: DB, postId: string): Promise<boolean>;
|
|
100
|
+
//# sourceMappingURL=hubMirroring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubMirroring.d.ts","sourceRoot":"","sources":["../../src/federation/hubMirroring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { eq, and, desc, isNull, sql, ilike } from 'drizzle-orm';
|
|
2
|
+
import { federatedHubs, federatedHubPosts, remoteActors, activities, followRelationships, } from '@commonpub/schema';
|
|
3
|
+
import { buildFollowActivity } from '@commonpub/protocol';
|
|
4
|
+
import { normalizePagination, escapeLike } from '../query.js';
|
|
5
|
+
// --- Federated Hub CRUD ---
|
|
6
|
+
/**
|
|
7
|
+
* List all federated hubs (accepted + not hidden).
|
|
8
|
+
* Used by listHubs() when includeFederated is true.
|
|
9
|
+
*/
|
|
10
|
+
export async function listFederatedHubs(db, filters = {}) {
|
|
11
|
+
const conditions = [
|
|
12
|
+
eq(federatedHubs.status, 'accepted'),
|
|
13
|
+
eq(federatedHubs.isHidden, false),
|
|
14
|
+
];
|
|
15
|
+
if (filters.search) {
|
|
16
|
+
conditions.push(ilike(federatedHubs.name, `%${escapeLike(filters.search)}%`));
|
|
17
|
+
}
|
|
18
|
+
const where = and(...conditions);
|
|
19
|
+
const { limit, offset } = normalizePagination(filters);
|
|
20
|
+
const [rows, countResult] = await Promise.all([
|
|
21
|
+
db
|
|
22
|
+
.select({ hub: federatedHubs })
|
|
23
|
+
.from(federatedHubs)
|
|
24
|
+
.where(where)
|
|
25
|
+
.orderBy(desc(federatedHubs.receivedAt))
|
|
26
|
+
.limit(limit)
|
|
27
|
+
.offset(offset),
|
|
28
|
+
db
|
|
29
|
+
.select({ count: sql `count(*)::int` })
|
|
30
|
+
.from(federatedHubs)
|
|
31
|
+
.where(where),
|
|
32
|
+
]);
|
|
33
|
+
const total = countResult[0]?.count ?? 0;
|
|
34
|
+
const items = rows.map((row) => ({
|
|
35
|
+
id: row.hub.id,
|
|
36
|
+
name: row.hub.name,
|
|
37
|
+
slug: row.hub.remoteSlug,
|
|
38
|
+
description: row.hub.description,
|
|
39
|
+
hubType: row.hub.hubType,
|
|
40
|
+
iconUrl: row.hub.iconUrl,
|
|
41
|
+
bannerUrl: row.hub.bannerUrl,
|
|
42
|
+
memberCount: row.hub.remoteMemberCount,
|
|
43
|
+
postCount: row.hub.localPostCount,
|
|
44
|
+
originDomain: row.hub.originDomain,
|
|
45
|
+
url: row.hub.url,
|
|
46
|
+
actorUri: row.hub.actorUri,
|
|
47
|
+
receivedAt: row.hub.receivedAt,
|
|
48
|
+
source: 'federated',
|
|
49
|
+
}));
|
|
50
|
+
return { items, total };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a single federated hub by its local ID.
|
|
54
|
+
*/
|
|
55
|
+
export async function getFederatedHub(db, hubId) {
|
|
56
|
+
const [row] = await db
|
|
57
|
+
.select({ hub: federatedHubs })
|
|
58
|
+
.from(federatedHubs)
|
|
59
|
+
.where(and(eq(federatedHubs.id, hubId), eq(federatedHubs.isHidden, false)))
|
|
60
|
+
.limit(1);
|
|
61
|
+
if (!row)
|
|
62
|
+
return null;
|
|
63
|
+
return {
|
|
64
|
+
id: row.hub.id,
|
|
65
|
+
name: row.hub.name,
|
|
66
|
+
slug: row.hub.remoteSlug,
|
|
67
|
+
description: row.hub.description,
|
|
68
|
+
hubType: row.hub.hubType,
|
|
69
|
+
iconUrl: row.hub.iconUrl,
|
|
70
|
+
bannerUrl: row.hub.bannerUrl,
|
|
71
|
+
memberCount: row.hub.remoteMemberCount,
|
|
72
|
+
postCount: row.hub.localPostCount,
|
|
73
|
+
originDomain: row.hub.originDomain,
|
|
74
|
+
url: row.hub.url,
|
|
75
|
+
actorUri: row.hub.actorUri,
|
|
76
|
+
receivedAt: row.hub.receivedAt,
|
|
77
|
+
source: 'federated',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get a federated hub by its AP actor URI.
|
|
82
|
+
*/
|
|
83
|
+
/**
|
|
84
|
+
* Get a federated hub by its AP actor URI.
|
|
85
|
+
* Only returns hubs with status='accepted' (follow handshake completed).
|
|
86
|
+
*/
|
|
87
|
+
export async function getFederatedHubByActorUri(db, actorUri) {
|
|
88
|
+
const [row] = await db
|
|
89
|
+
.select({ hub: federatedHubs })
|
|
90
|
+
.from(federatedHubs)
|
|
91
|
+
.where(and(eq(federatedHubs.actorUri, actorUri), eq(federatedHubs.status, 'accepted')))
|
|
92
|
+
.limit(1);
|
|
93
|
+
if (!row)
|
|
94
|
+
return null;
|
|
95
|
+
return {
|
|
96
|
+
id: row.hub.id,
|
|
97
|
+
name: row.hub.name,
|
|
98
|
+
slug: row.hub.remoteSlug,
|
|
99
|
+
description: row.hub.description,
|
|
100
|
+
hubType: row.hub.hubType,
|
|
101
|
+
iconUrl: row.hub.iconUrl,
|
|
102
|
+
bannerUrl: row.hub.bannerUrl,
|
|
103
|
+
memberCount: row.hub.remoteMemberCount,
|
|
104
|
+
postCount: row.hub.localPostCount,
|
|
105
|
+
originDomain: row.hub.originDomain,
|
|
106
|
+
url: row.hub.url,
|
|
107
|
+
actorUri: row.hub.actorUri,
|
|
108
|
+
receivedAt: row.hub.receivedAt,
|
|
109
|
+
source: 'federated',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// --- Hub Follow (Mirror Creation) ---
|
|
113
|
+
/**
|
|
114
|
+
* Follow a remote hub Group actor to start mirroring.
|
|
115
|
+
* Returns the created federated hub entry.
|
|
116
|
+
*/
|
|
117
|
+
export async function followRemoteHub(db, actorUri, metadata, followActivityUri) {
|
|
118
|
+
const metaFields = {
|
|
119
|
+
name: metadata.name,
|
|
120
|
+
description: metadata.description ?? null,
|
|
121
|
+
iconUrl: metadata.iconUrl ?? null,
|
|
122
|
+
bannerUrl: metadata.bannerUrl ?? null,
|
|
123
|
+
hubType: metadata.hubType ?? 'community',
|
|
124
|
+
remoteMemberCount: metadata.remoteMemberCount ?? 0,
|
|
125
|
+
remotePostCount: metadata.remotePostCount ?? 0,
|
|
126
|
+
url: metadata.url ?? null,
|
|
127
|
+
rules: metadata.rules ?? null,
|
|
128
|
+
categories: metadata.categories ?? null,
|
|
129
|
+
followActivityUri: followActivityUri ?? null,
|
|
130
|
+
updatedAt: new Date(),
|
|
131
|
+
};
|
|
132
|
+
// Try insert first — onConflictDoNothing to detect if it already existed
|
|
133
|
+
const inserted = await db.insert(federatedHubs).values({
|
|
134
|
+
actorUri,
|
|
135
|
+
originDomain: metadata.originDomain,
|
|
136
|
+
remoteSlug: metadata.remoteSlug,
|
|
137
|
+
status: 'pending',
|
|
138
|
+
...metaFields,
|
|
139
|
+
}).onConflictDoNothing({ target: federatedHubs.actorUri })
|
|
140
|
+
.returning({ id: federatedHubs.id });
|
|
141
|
+
if (inserted.length > 0) {
|
|
142
|
+
return { id: inserted[0].id, created: true };
|
|
143
|
+
}
|
|
144
|
+
// Already existed — update metadata
|
|
145
|
+
const [updated] = await db.update(federatedHubs).set(metaFields)
|
|
146
|
+
.where(eq(federatedHubs.actorUri, actorUri))
|
|
147
|
+
.returning({ id: federatedHubs.id });
|
|
148
|
+
return { id: updated.id, created: false };
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Accept a hub follow — called when we receive Accept(Follow) from the remote Group actor.
|
|
152
|
+
*/
|
|
153
|
+
export async function acceptHubFollow(db, actorUri) {
|
|
154
|
+
const result = await db
|
|
155
|
+
.update(federatedHubs)
|
|
156
|
+
.set({ status: 'accepted', updatedAt: new Date() })
|
|
157
|
+
.where(and(eq(federatedHubs.actorUri, actorUri), eq(federatedHubs.status, 'pending')))
|
|
158
|
+
.returning({ id: federatedHubs.id });
|
|
159
|
+
return result.length > 0;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Unfollow/cancel a hub mirror.
|
|
163
|
+
*/
|
|
164
|
+
export async function unfollowRemoteHub(db, actorUri) {
|
|
165
|
+
const result = await db
|
|
166
|
+
.update(federatedHubs)
|
|
167
|
+
.set({ isHidden: true, status: 'rejected', updatedAt: new Date() })
|
|
168
|
+
.where(eq(federatedHubs.actorUri, actorUri))
|
|
169
|
+
.returning({ id: federatedHubs.id });
|
|
170
|
+
return result.length > 0;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Queue an outbound Follow activity from the local instance actor to a remote Group actor.
|
|
174
|
+
* This is the AP handshake that initiates hub mirroring.
|
|
175
|
+
*/
|
|
176
|
+
export async function sendHubFollow(db, remoteGroupActorUri, domain) {
|
|
177
|
+
// Resolve & cache remote actor (delivery worker needs their inbox)
|
|
178
|
+
const { resolveRemoteActor } = await import('./federation.js');
|
|
179
|
+
const resolved = await resolveRemoteActor(db, remoteGroupActorUri).catch(() => null);
|
|
180
|
+
if (!resolved) {
|
|
181
|
+
throw new Error(`Could not resolve remote hub actor ${remoteGroupActorUri}`);
|
|
182
|
+
}
|
|
183
|
+
const localActorUri = `https://${domain}/actor`;
|
|
184
|
+
const followActivity = buildFollowActivity(domain, localActorUri, remoteGroupActorUri);
|
|
185
|
+
// Store follow relationship for the delivery system to find
|
|
186
|
+
await db.insert(followRelationships).values({
|
|
187
|
+
followerActorUri: localActorUri,
|
|
188
|
+
followingActorUri: remoteGroupActorUri,
|
|
189
|
+
status: 'pending',
|
|
190
|
+
}).onConflictDoNothing();
|
|
191
|
+
// Update the federatedHubs record with the Follow activity URI
|
|
192
|
+
await db.update(federatedHubs).set({
|
|
193
|
+
followActivityUri: followActivity.id,
|
|
194
|
+
updatedAt: new Date(),
|
|
195
|
+
}).where(eq(federatedHubs.actorUri, remoteGroupActorUri));
|
|
196
|
+
// Queue Follow for async delivery
|
|
197
|
+
await db.insert(activities).values({
|
|
198
|
+
type: 'Follow',
|
|
199
|
+
actorUri: localActorUri,
|
|
200
|
+
objectUri: remoteGroupActorUri,
|
|
201
|
+
payload: followActivity,
|
|
202
|
+
direction: 'outbound',
|
|
203
|
+
status: 'pending',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// --- Federated Hub Posts ---
|
|
207
|
+
/**
|
|
208
|
+
* Ingest a post from a federated hub (received via Announce from Group actor).
|
|
209
|
+
*/
|
|
210
|
+
export async function ingestFederatedHubPost(db, federatedHubId, post) {
|
|
211
|
+
// Resolve remote actor
|
|
212
|
+
const [actor] = await db
|
|
213
|
+
.select({ id: remoteActors.id })
|
|
214
|
+
.from(remoteActors)
|
|
215
|
+
.where(eq(remoteActors.actorUri, post.actorUri))
|
|
216
|
+
.limit(1);
|
|
217
|
+
// Try insert first — if conflict (already exists), update separately
|
|
218
|
+
const inserted = await db.insert(federatedHubPosts).values({
|
|
219
|
+
federatedHubId,
|
|
220
|
+
objectUri: post.objectUri,
|
|
221
|
+
actorUri: post.actorUri,
|
|
222
|
+
remoteActorId: actor?.id ?? null,
|
|
223
|
+
content: post.content,
|
|
224
|
+
postType: post.postType ?? 'text',
|
|
225
|
+
isPinned: post.isPinned ?? false,
|
|
226
|
+
remoteLikeCount: post.remoteLikeCount ?? 0,
|
|
227
|
+
remoteReplyCount: post.remoteReplyCount ?? 0,
|
|
228
|
+
publishedAt: post.publishedAt ?? null,
|
|
229
|
+
}).onConflictDoNothing({ target: federatedHubPosts.objectUri })
|
|
230
|
+
.returning({ id: federatedHubPosts.id });
|
|
231
|
+
if (inserted.length > 0) {
|
|
232
|
+
// Genuinely new post — increment count
|
|
233
|
+
await db.update(federatedHubs).set({
|
|
234
|
+
localPostCount: sql `${federatedHubs.localPostCount} + 1`,
|
|
235
|
+
lastSyncAt: new Date(),
|
|
236
|
+
}).where(eq(federatedHubs.id, federatedHubId));
|
|
237
|
+
return { id: inserted[0].id, created: true };
|
|
238
|
+
}
|
|
239
|
+
// Already existed — update content
|
|
240
|
+
const [existing] = await db.update(federatedHubPosts).set({
|
|
241
|
+
content: post.content,
|
|
242
|
+
postType: post.postType ?? 'text',
|
|
243
|
+
isPinned: post.isPinned ?? false,
|
|
244
|
+
remoteLikeCount: post.remoteLikeCount ?? 0,
|
|
245
|
+
remoteReplyCount: post.remoteReplyCount ?? 0,
|
|
246
|
+
}).where(eq(federatedHubPosts.objectUri, post.objectUri))
|
|
247
|
+
.returning({ id: federatedHubPosts.id });
|
|
248
|
+
return { id: existing.id, created: false };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* List posts for a federated hub.
|
|
252
|
+
*/
|
|
253
|
+
export async function listFederatedHubPosts(db, federatedHubId, opts = {}) {
|
|
254
|
+
const { limit, offset } = normalizePagination(opts);
|
|
255
|
+
const where = and(eq(federatedHubPosts.federatedHubId, federatedHubId), isNull(federatedHubPosts.deletedAt));
|
|
256
|
+
const [rows, countResult] = await Promise.all([
|
|
257
|
+
db
|
|
258
|
+
.select({
|
|
259
|
+
post: federatedHubPosts,
|
|
260
|
+
actor: {
|
|
261
|
+
actorUri: remoteActors.actorUri,
|
|
262
|
+
preferredUsername: remoteActors.preferredUsername,
|
|
263
|
+
displayName: remoteActors.displayName,
|
|
264
|
+
avatarUrl: remoteActors.avatarUrl,
|
|
265
|
+
instanceDomain: remoteActors.instanceDomain,
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
.from(federatedHubPosts)
|
|
269
|
+
.leftJoin(remoteActors, eq(federatedHubPosts.remoteActorId, remoteActors.id))
|
|
270
|
+
.where(where)
|
|
271
|
+
.orderBy(desc(federatedHubPosts.isPinned), desc(federatedHubPosts.receivedAt))
|
|
272
|
+
.limit(limit)
|
|
273
|
+
.offset(offset),
|
|
274
|
+
db
|
|
275
|
+
.select({ count: sql `count(*)::int` })
|
|
276
|
+
.from(federatedHubPosts)
|
|
277
|
+
.where(where),
|
|
278
|
+
]);
|
|
279
|
+
const total = countResult[0]?.count ?? 0;
|
|
280
|
+
const items = rows.map((row) => ({
|
|
281
|
+
id: row.post.id,
|
|
282
|
+
federatedHubId: row.post.federatedHubId,
|
|
283
|
+
content: row.post.content,
|
|
284
|
+
postType: row.post.postType,
|
|
285
|
+
isPinned: row.post.isPinned,
|
|
286
|
+
localLikeCount: row.post.localLikeCount,
|
|
287
|
+
localReplyCount: row.post.localReplyCount,
|
|
288
|
+
remoteLikeCount: row.post.remoteLikeCount,
|
|
289
|
+
remoteReplyCount: row.post.remoteReplyCount,
|
|
290
|
+
publishedAt: row.post.publishedAt,
|
|
291
|
+
receivedAt: row.post.receivedAt,
|
|
292
|
+
objectUri: row.post.objectUri,
|
|
293
|
+
author: {
|
|
294
|
+
actorUri: row.actor?.actorUri ?? row.post.actorUri,
|
|
295
|
+
preferredUsername: row.actor?.preferredUsername ?? null,
|
|
296
|
+
displayName: row.actor?.displayName ?? null,
|
|
297
|
+
avatarUrl: row.actor?.avatarUrl ?? null,
|
|
298
|
+
instanceDomain: row.actor?.instanceDomain ?? 'unknown',
|
|
299
|
+
},
|
|
300
|
+
source: 'federated',
|
|
301
|
+
}));
|
|
302
|
+
return { items, total };
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Delete a federated hub post (on receiving Delete activity).
|
|
306
|
+
* Validates actorUri matches the post author for defense in depth.
|
|
307
|
+
*/
|
|
308
|
+
export async function deleteFederatedHubPost(db, objectUri, actorUri) {
|
|
309
|
+
const conditions = [
|
|
310
|
+
eq(federatedHubPosts.objectUri, objectUri),
|
|
311
|
+
isNull(federatedHubPosts.deletedAt),
|
|
312
|
+
];
|
|
313
|
+
if (actorUri) {
|
|
314
|
+
conditions.push(eq(federatedHubPosts.actorUri, actorUri));
|
|
315
|
+
}
|
|
316
|
+
const result = await db
|
|
317
|
+
.update(federatedHubPosts)
|
|
318
|
+
.set({ deletedAt: new Date() })
|
|
319
|
+
.where(and(...conditions))
|
|
320
|
+
.returning({ id: federatedHubPosts.id, federatedHubId: federatedHubPosts.federatedHubId });
|
|
321
|
+
if (result.length === 0)
|
|
322
|
+
return false;
|
|
323
|
+
// Decrement local post count
|
|
324
|
+
await db.update(federatedHubs).set({
|
|
325
|
+
localPostCount: sql `GREATEST(${federatedHubs.localPostCount} - 1, 0)`,
|
|
326
|
+
}).where(eq(federatedHubs.id, result[0].federatedHubId));
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Like a federated hub post (increment local counter).
|
|
331
|
+
* The caller is responsible for queuing the outbound Like activity.
|
|
332
|
+
*/
|
|
333
|
+
export async function likeFederatedHubPost(db, postId) {
|
|
334
|
+
const result = await db
|
|
335
|
+
.update(federatedHubPosts)
|
|
336
|
+
.set({ localLikeCount: sql `${federatedHubPosts.localLikeCount} + 1` })
|
|
337
|
+
.where(eq(federatedHubPosts.id, postId))
|
|
338
|
+
.returning({ id: federatedHubPosts.id });
|
|
339
|
+
return result.length > 0;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Unlike a federated hub post (decrement local counter).
|
|
343
|
+
*/
|
|
344
|
+
export async function unlikeFederatedHubPost(db, postId) {
|
|
345
|
+
const result = await db
|
|
346
|
+
.update(federatedHubPosts)
|
|
347
|
+
.set({ localLikeCount: sql `GREATEST(${federatedHubPosts.localLikeCount} - 1, 0)` })
|
|
348
|
+
.where(eq(federatedHubPosts.id, postId))
|
|
349
|
+
.returning({ id: federatedHubPosts.id });
|
|
350
|
+
return result.length > 0;
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=hubMirroring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubMirroring.js","sourceRoot":"","sources":["../../src/federation/hubMirroring.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inboxHandlers.d.ts","sourceRoot":"","sources":["../../src/federation/inboxHandlers.ts"],"names":[],"mappings":"AAcA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAqBtC,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,EAAE,CAAC;IACP,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qCAAqC;IACrC,gBAAgB,CAAC,EAAE;QACjB,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,oEAAoE;IACpE,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,mBAAmB,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"inboxHandlers.d.ts","sourceRoot":"","sources":["../../src/federation/inboxHandlers.ts"],"names":[],"mappings":"AAcA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAqBtC,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,EAAE,CAAC;IACP,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qCAAqC;IACrC,gBAAgB,CAAC,EAAE;QACjB,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,oEAAoE;IACpE,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,mBAAmB,GAAG,cAAc,CAm3B7E"}
|
|
@@ -125,6 +125,12 @@ export function createInboxHandlers(opts) {
|
|
|
125
125
|
catch { /* non-critical */ }
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
+
// Check if this Accept is for a hub mirror (remote Group accepted our Follow)
|
|
129
|
+
try {
|
|
130
|
+
const { acceptHubFollow } = await import('./hubMirroring.js');
|
|
131
|
+
await acceptHubFollow(db, actorUri);
|
|
132
|
+
}
|
|
133
|
+
catch { /* non-critical — may not be a hub follow */ }
|
|
128
134
|
await db.insert(activities).values({
|
|
129
135
|
type: 'Accept',
|
|
130
136
|
actorUri,
|
|
@@ -536,6 +542,12 @@ export function createInboxHandlers(opts) {
|
|
|
536
542
|
.update(federatedContent)
|
|
537
543
|
.set({ deletedAt: new Date() })
|
|
538
544
|
.where(and(eq(federatedContent.objectUri, objectId), eq(federatedContent.actorUri, actorUri), isNull(federatedContent.deletedAt)));
|
|
545
|
+
// Also check federated hub posts (pass actorUri for author validation)
|
|
546
|
+
try {
|
|
547
|
+
const { deleteFederatedHubPost } = await import('./hubMirroring.js');
|
|
548
|
+
await deleteFederatedHubPost(db, objectId, actorUri);
|
|
549
|
+
}
|
|
550
|
+
catch { /* non-critical */ }
|
|
539
551
|
await db.insert(activities).values({
|
|
540
552
|
type: 'Delete',
|
|
541
553
|
actorUri,
|
|
@@ -714,13 +726,60 @@ export function createInboxHandlers(opts) {
|
|
|
714
726
|
}
|
|
715
727
|
catch { /* invalid URL */ }
|
|
716
728
|
}
|
|
717
|
-
// If not local, try federated content
|
|
729
|
+
// If not local, try federated content boost
|
|
718
730
|
if (!matched) {
|
|
719
731
|
await db
|
|
720
732
|
.update(federatedContent)
|
|
721
733
|
.set({ localBoostCount: sql `${federatedContent.localBoostCount} + 1` })
|
|
722
734
|
.where(eq(federatedContent.objectUri, objectUri));
|
|
723
735
|
}
|
|
736
|
+
// Check if this Announce is from a Group actor we're mirroring (hub post ingestion)
|
|
737
|
+
if (!matched) {
|
|
738
|
+
try {
|
|
739
|
+
// Loop prevention: skip Announces of our own content
|
|
740
|
+
const isOwnContent = new URL(objectUri).hostname === domain;
|
|
741
|
+
if (!isOwnContent) {
|
|
742
|
+
const { getFederatedHubByActorUri, ingestFederatedHubPost } = await import('./hubMirroring.js');
|
|
743
|
+
const mirroredHub = await getFederatedHubByActorUri(db, actorUri);
|
|
744
|
+
if (mirroredHub) {
|
|
745
|
+
// Dereference the announced Note to get its content
|
|
746
|
+
const noteResponse = await fetch(objectUri, {
|
|
747
|
+
headers: { 'Accept': 'application/activity+json, application/ld+json' },
|
|
748
|
+
signal: AbortSignal.timeout(10_000),
|
|
749
|
+
});
|
|
750
|
+
if (noteResponse.ok) {
|
|
751
|
+
const note = await noteResponse.json();
|
|
752
|
+
const noteContent = note.content ?? '';
|
|
753
|
+
// AP spec: attributedTo can be string, object with id, or array
|
|
754
|
+
let noteActorUri = actorUri;
|
|
755
|
+
const attr = note.attributedTo;
|
|
756
|
+
if (typeof attr === 'string') {
|
|
757
|
+
noteActorUri = attr;
|
|
758
|
+
}
|
|
759
|
+
else if (attr && typeof attr === 'object' && !Array.isArray(attr)) {
|
|
760
|
+
noteActorUri = attr.id ?? actorUri;
|
|
761
|
+
}
|
|
762
|
+
else if (Array.isArray(attr) && attr.length > 0) {
|
|
763
|
+
const first = attr[0];
|
|
764
|
+
noteActorUri = typeof first === 'string' ? first : first.id ?? actorUri;
|
|
765
|
+
}
|
|
766
|
+
// Resolve the post author
|
|
767
|
+
await resolveRemoteActor(db, noteActorUri);
|
|
768
|
+
await ingestFederatedHubPost(db, mirroredHub.id, {
|
|
769
|
+
objectUri,
|
|
770
|
+
actorUri: noteActorUri,
|
|
771
|
+
content: noteContent,
|
|
772
|
+
postType: note.cpubPostType ?? 'text',
|
|
773
|
+
publishedAt: note.published ? new Date(note.published) : undefined,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
catch (err) {
|
|
780
|
+
console.warn('[inbox] Hub post ingestion failed:', err instanceof Error ? err.message : err);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
724
783
|
await db.insert(activities).values({
|
|
725
784
|
type: 'Announce',
|
|
726
785
|
actorUri,
|