Add resolving of threads, mentions and quote posts

This commit is contained in:
Jesse Wierzbinski 2024-04-09 22:37:58 -10:00
parent cf295a596a
commit 38a6f9a809
No known key found for this signature in database
3 changed files with 108 additions and 23 deletions

View file

@ -23,7 +23,7 @@ import { applicationToAPI } from "./Application";
import { attachmentToAPI, attachmentToLysand } from "./Attachment"; import { attachmentToAPI, attachmentToLysand } from "./Attachment";
import { emojiToAPI, emojiToLysand, parseEmojis } from "./Emoji"; import { emojiToAPI, emojiToLysand, parseEmojis } from "./Emoji";
import type { UserWithRelations } from "./User"; import type { UserWithRelations } from "./User";
import { getUserUri, userToAPI } from "./User"; import { getUserUri, resolveUser, userToAPI } from "./User";
import { statusAndUserRelations, userRelations } from "./relations"; import { statusAndUserRelations, userRelations } from "./relations";
import { objectToInboxRequest } from "./Federation"; import { objectToInboxRequest } from "./Federation";
@ -58,9 +58,84 @@ export const isViewableByUser = (status: Status, user: User | null) => {
return user && (status.mentions as User[]).includes(user); return user && (status.mentions as User[]).includes(user);
}; };
export const fetchFromRemote = async ( export const resolveStatus = async (
uri: string, uri?: string,
): Promise<StatusWithRelations | null> => {}; providedNote?: Lysand.Note,
): Promise<StatusWithRelations> => {
if (!uri && !providedNote) {
throw new Error("No URI or note provided");
}
// Check if status not already in database
const foundStatus = await client.status.findUnique({
where: {
uri: uri ?? providedNote?.uri,
},
include: statusAndUserRelations,
});
if (foundStatus) return foundStatus;
let note: Lysand.Note | null = providedNote ?? null;
if (uri) {
if (!URL.canParse(uri)) {
throw new Error(`Invalid URI to parse ${uri}`);
}
const response = await fetch(uri, {
method: "GET",
headers: {
Accept: "application/json",
},
});
note = (await response.json()) as Lysand.Note;
}
if (!note) {
throw new Error("No note was able to be fetched");
}
if (note.type !== "Note") {
throw new Error("Invalid object type");
}
if (!note.author) {
throw new Error("Invalid object author");
}
const author = await resolveUser(note.author);
if (!author) {
throw new Error("Invalid object author");
}
return await createNewStatus(
author,
note.content ?? {
"text/plain": {
content: "",
},
},
note.visibility as APIStatus["visibility"],
note.is_sensitive ?? false,
note.subject ?? "",
[],
note.uri,
await Promise.all(
(note.mentions ?? [])
.map((mention) => resolveUser(mention))
.filter(
(mention) => mention !== null,
) as Promise<UserWithRelations>[],
),
// TODO: Add attachments
[],
note.replies_to ? await resolveStatus(note.replies_to) : undefined,
note.quotes ? await resolveStatus(note.quotes) : undefined,
);
};
/** /**
* Return all the ancestors of this post, * Return all the ancestors of this post,

View file

@ -180,6 +180,26 @@ export const resolveUser = async (uri: string) => {
if (foundUser) return foundUser; if (foundUser) return foundUser;
// Check if URI is of a local user
if (uri.startsWith(config.http.base_url)) {
const uuid = uri.match(
/[0-9A-F]{8}-[0-9A-F]{4}-[7][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
);
if (!uuid) {
throw new Error(
`URI ${uri} is of a local user, but it could not be parsed`,
);
}
return client.user.findUnique({
where: {
id: uuid[0],
},
include: userRelations,
});
}
if (!URL.canParse(uri)) { if (!URL.canParse(uri)) {
throw new Error(`Invalid URI to parse ${uri}`); throw new Error(`Invalid URI to parse ${uri}`);
} }

View file

@ -3,7 +3,7 @@ import { errorResponse, response } from "@response";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { userRelations } from "~database/entities/relations"; import { userRelations } from "~database/entities/relations";
import type * as Lysand from "lysand-types"; import type * as Lysand from "lysand-types";
import { createNewStatus } from "~database/entities/Status"; import { createNewStatus, resolveStatus } from "~database/entities/Status";
import type { APIStatus } from "~types/entities/status"; import type { APIStatus } from "~types/entities/status";
import { import {
followAcceptToLysand, followAcceptToLysand,
@ -132,27 +132,17 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
return errorResponse("Author not found", 400); return errorResponse("Author not found", 400);
} }
await createNewStatus( const newStatus = await resolveStatus(undefined, note).catch(
account, (e) => {
note.content ?? { console.error(e);
"text/plain": { return null;
content: "",
}, },
},
note.visibility as APIStatus["visibility"],
note.is_sensitive ?? false,
note.subject ?? "",
[],
note.uri,
// TODO: Resolve mentions
[],
// TODO: Add attachments
[],
// TODO: Resolve replies and quoting
undefined,
undefined,
); );
if (!newStatus) {
return errorResponse("Failed to add status", 500);
}
return response("Note created", 201); return response("Note created", 201);
} }
case "Follow": { case "Follow": {