@rmdes/indiekit-endpoint-activitypub 3.12.2 → 3.12.4
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.
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { serializeAccount } from "./account.js";
|
|
17
17
|
import { sanitizeHtml } from "./sanitize.js";
|
|
18
|
+
import { remoteActorId } from "../helpers/id-mapping.js";
|
|
18
19
|
|
|
19
20
|
// Module-level defaults set once at startup via setLocalIdentity()
|
|
20
21
|
let _localPublicationUrl = "";
|
|
@@ -178,13 +179,17 @@ export function serializeStatus(item, { baseUrl, favouritedIds, rebloggedIds, bo
|
|
|
178
179
|
url: `${baseUrl}/tags/${encodeURIComponent(tag)}`,
|
|
179
180
|
}));
|
|
180
181
|
|
|
181
|
-
// Mentions
|
|
182
|
-
const mentions = (item.mentions || []).map((m) =>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
// Mentions — use actorUrl for deterministic ID, parse acct from handle
|
|
183
|
+
const mentions = (item.mentions || []).map((m) => {
|
|
184
|
+
const handle = (m.name || "").replace(/^@/, "");
|
|
185
|
+
const parts = handle.split("@");
|
|
186
|
+
return {
|
|
187
|
+
id: m.actorUrl ? remoteActorId(m.actorUrl) : "0",
|
|
188
|
+
username: parts[0] || handle,
|
|
189
|
+
url: m.url || m.actorUrl || "",
|
|
190
|
+
acct: handle,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
188
193
|
|
|
189
194
|
// Custom emojis
|
|
190
195
|
const emojis = (item.emojis || []).map((e) => ({
|
|
@@ -467,7 +467,6 @@ router.post("/oauth/token", async (req, res, next) => {
|
|
|
467
467
|
accessToken,
|
|
468
468
|
createdAt: new Date(),
|
|
469
469
|
grantType: "client_credentials",
|
|
470
|
-
expiresAt: new Date(Date.now() + 3600 * 1000),
|
|
471
470
|
});
|
|
472
471
|
|
|
473
472
|
return res.json({
|
|
@@ -475,7 +474,6 @@ router.post("/oauth/token", async (req, res, next) => {
|
|
|
475
474
|
token_type: "Bearer",
|
|
476
475
|
scope: "read",
|
|
477
476
|
created_at: Math.floor(Date.now() / 1000),
|
|
478
|
-
expires_in: 3600,
|
|
479
477
|
});
|
|
480
478
|
}
|
|
481
479
|
|
|
@@ -510,9 +508,9 @@ router.post("/oauth/token", async (req, res, next) => {
|
|
|
510
508
|
$set: {
|
|
511
509
|
accessToken: newAccessToken,
|
|
512
510
|
refreshToken: newRefreshToken,
|
|
513
|
-
expiresAt: new Date(Date.now() + 3600 * 1000),
|
|
514
511
|
refreshExpiresAt: new Date(Date.now() + 90 * 24 * 3600 * 1000),
|
|
515
512
|
},
|
|
513
|
+
$unset: { expiresAt: "" },
|
|
516
514
|
},
|
|
517
515
|
);
|
|
518
516
|
|
|
@@ -522,7 +520,6 @@ router.post("/oauth/token", async (req, res, next) => {
|
|
|
522
520
|
scope: existing.scopes.join(" "),
|
|
523
521
|
created_at: Math.floor(existing.createdAt.getTime() / 1000),
|
|
524
522
|
refresh_token: newRefreshToken,
|
|
525
|
-
expires_in: 3600,
|
|
526
523
|
});
|
|
527
524
|
}
|
|
528
525
|
|
|
@@ -590,8 +587,9 @@ router.post("/oauth/token", async (req, res, next) => {
|
|
|
590
587
|
}
|
|
591
588
|
}
|
|
592
589
|
|
|
593
|
-
// Generate access token and refresh token
|
|
594
|
-
|
|
590
|
+
// Generate access token and refresh token.
|
|
591
|
+
// Access tokens do not expire (matching Mastodon behavior — valid until revoked).
|
|
592
|
+
// Refresh tokens expire after 90 days as a safety measure.
|
|
595
593
|
const REFRESH_TOKEN_TTL = 90 * 24 * 3600 * 1000; // 90 days
|
|
596
594
|
const accessToken = randomHex(64);
|
|
597
595
|
const refreshToken = randomHex(64);
|
|
@@ -601,7 +599,6 @@ router.post("/oauth/token", async (req, res, next) => {
|
|
|
601
599
|
$set: {
|
|
602
600
|
accessToken,
|
|
603
601
|
refreshToken,
|
|
604
|
-
expiresAt: new Date(Date.now() + ACCESS_TOKEN_TTL),
|
|
605
602
|
refreshExpiresAt: new Date(Date.now() + REFRESH_TOKEN_TTL),
|
|
606
603
|
},
|
|
607
604
|
},
|
|
@@ -613,7 +610,6 @@ router.post("/oauth/token", async (req, res, next) => {
|
|
|
613
610
|
scope: grant.scopes.join(" "),
|
|
614
611
|
created_at: Math.floor(grant.createdAt.getTime() / 1000),
|
|
615
612
|
refresh_token: refreshToken,
|
|
616
|
-
expires_in: 3600,
|
|
617
613
|
});
|
|
618
614
|
} catch (error) {
|
|
619
615
|
next(error);
|
package/lib/syndicator.js
CHANGED
|
@@ -227,11 +227,39 @@ export function createSyndicator(plugin) {
|
|
|
227
227
|
const content = buildTimelineContent(properties);
|
|
228
228
|
// Permalink is appended at read time by serializeStatus, not here.
|
|
229
229
|
|
|
230
|
+
// Linkify @mentions in content using resolved WebFinger data.
|
|
231
|
+
// This ensures the ap_timeline HTML has proper <a> links for
|
|
232
|
+
// mentions, matching what the federated AS2 activity contains.
|
|
233
|
+
if (resolvedMentions.length > 0 && content.html) {
|
|
234
|
+
const { default: jf2Mod } = await import("./jf2-to-as2.js");
|
|
235
|
+
// Import linkifyMentions — it's not exported, so inline the logic
|
|
236
|
+
for (const { handle, profileUrl, actorUrl } of resolvedMentions) {
|
|
237
|
+
const escaped = handle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
238
|
+
const pattern = new RegExp(`(?<!["\\/\\w])@${escaped}(?![\\w])`, "gi");
|
|
239
|
+
const parts = handle.split("@");
|
|
240
|
+
const url = profileUrl || (actorUrl ? actorUrl : `https://${parts[1]}/@${parts[0]}`);
|
|
241
|
+
content.html = content.html.replace(
|
|
242
|
+
pattern,
|
|
243
|
+
`<a href="${url}" class="mention" rel="nofollow noopener" target="_blank">@${handle}</a>`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Store resolved mentions for Mastodon API serialization
|
|
249
|
+
const timelineMentions = resolvedMentions
|
|
250
|
+
.filter(m => m.actorUrl)
|
|
251
|
+
.map(m => ({
|
|
252
|
+
name: `@${m.handle}`,
|
|
253
|
+
url: m.profileUrl || m.actorUrl,
|
|
254
|
+
actorUrl: m.actorUrl,
|
|
255
|
+
}));
|
|
256
|
+
|
|
230
257
|
const timelineItem = {
|
|
231
258
|
uid: properties.url,
|
|
232
259
|
url: properties.url,
|
|
233
260
|
type: mapPostType(properties["post-type"]),
|
|
234
261
|
content,
|
|
262
|
+
mentions: timelineMentions,
|
|
235
263
|
author: {
|
|
236
264
|
name: profile?.name || handle,
|
|
237
265
|
url: profile?.url || plugin._publicationUrl,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.4",
|
|
4
4
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"indiekit",
|