fix(mastodon-api): DM response "no data" — return full serialized status
The minimal bare JSON returned for visibility=direct DMs caused clients (Phanpy, Elk) to show "no data" — they expect a full Mastodon Status entity. Fix: build a proper ap_timeline document, store it with visibility=direct (home/public timelines already exclude direct items), and serialize it via serializeStatus() before returning. Also store the DM in ap_notifications for the thread view as before. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -284,12 +284,14 @@ router.post("/api/v1/statuses", async (req, res, next) => {
|
|||||||
});
|
});
|
||||||
console.info(`[Mastodon API] Sent DM to ${recipientActorUrl}`);
|
console.info(`[Mastodon API] Sent DM to ${recipientActorUrl}`);
|
||||||
|
|
||||||
// Store in ap_notifications so it appears in the DM thread view
|
const now = new Date().toISOString();
|
||||||
|
const hostname = new URL(publicationUrl).hostname;
|
||||||
|
const profile = await collections.ap_profile.findOne({});
|
||||||
|
|
||||||
|
// Store in ap_notifications for the DM thread view
|
||||||
try {
|
try {
|
||||||
const ap_notifications = collections.ap_notifications;
|
const ap_notifications = collections.ap_notifications;
|
||||||
if (ap_notifications) {
|
if (ap_notifications) {
|
||||||
const hostname = new URL(publicationUrl).hostname;
|
|
||||||
const profile = await collections.ap_profile.findOne({});
|
|
||||||
await addNotification({ ap_notifications }, {
|
await addNotification({ ap_notifications }, {
|
||||||
uid: noteId.href,
|
uid: noteId.href,
|
||||||
url: noteId.href,
|
url: noteId.href,
|
||||||
@@ -303,23 +305,54 @@ router.post("/api/v1/statuses", async (req, res, next) => {
|
|||||||
actorHandle: `@${handle}@${hostname}`,
|
actorHandle: `@${handle}@${hostname}`,
|
||||||
inReplyTo: inReplyTo || null,
|
inReplyTo: inReplyTo || null,
|
||||||
content: { text: (statusText || "").trim(), html: (statusText || "").trim() },
|
content: { text: (statusText || "").trim(), html: (statusText || "").trim() },
|
||||||
published: new Date().toISOString(),
|
published: now,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: now,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (storeError) {
|
} catch (storeError) {
|
||||||
console.warn("[Mastodon API] Failed to store outbound DM:", storeError.message);
|
console.warn("[Mastodon API] Failed to store outbound DM in notifications:", storeError.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a minimal status object so the client doesn't error
|
// Store in ap_timeline with visibility=direct so serializeStatus can
|
||||||
return res.json({
|
// produce a full Mastodon status object. Home/public timelines already
|
||||||
id: noteId.href,
|
// exclude direct-visibility items (visibility: { $nin: ["direct"] }).
|
||||||
created_at: new Date().toISOString(),
|
const timelineItem = {
|
||||||
content: (statusText || "").trim(),
|
uid: noteId.href,
|
||||||
visibility: "direct",
|
|
||||||
url: noteId.href,
|
url: noteId.href,
|
||||||
account: { acct: handle },
|
type: "note",
|
||||||
|
visibility: "direct",
|
||||||
|
content: {
|
||||||
|
text: (statusText || "").trim(),
|
||||||
|
html: (statusText || "").trim(),
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
name: profile?.name || handle,
|
||||||
|
url: actorUri.href,
|
||||||
|
photo: profile?.icon || "",
|
||||||
|
handle: `@${handle}@${hostname}`,
|
||||||
|
},
|
||||||
|
published: now,
|
||||||
|
createdAt: now,
|
||||||
|
inReplyTo: inReplyTo || null,
|
||||||
|
category: [],
|
||||||
|
counts: { likes: 0, boosts: 0, replies: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await addTimelineItem(collections.ap_timeline, timelineItem);
|
||||||
|
} catch (storeError) {
|
||||||
|
console.warn("[Mastodon API] Failed to store outbound DM in timeline:", storeError.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a full serialized status so clients (Phanpy, Elk) can render it
|
||||||
|
const status = serializeStatus(timelineItem, {
|
||||||
|
baseUrl,
|
||||||
|
favouritedIds: new Set(),
|
||||||
|
rebloggedIds: new Set(),
|
||||||
|
bookmarkedIds: new Set(),
|
||||||
|
pinnedIds: new Set(),
|
||||||
});
|
});
|
||||||
|
return res.json(status);
|
||||||
}
|
}
|
||||||
// ── End DM path ───────────────────────────────────────────────────────────
|
// ── End DM path ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user