mirror of
https://github.com/versia-pub/server.git
synced 2026-04-27 12:49:16 +02:00
refactor(federation): Make references always have domains
Some checks failed
CodeQL Scan / Analyze (push) Failing after 0s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
Build Docker Images / lint (push) Has been cancelled
Build Docker Images / check (push) Has been cancelled
Build Docker Images / tests (push) Has been cancelled
Build Docker Images / detect-circular (push) Has been cancelled
Mirror to Codeberg / Mirror (push) Has been cancelled
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been cancelled
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been cancelled
Some checks failed
CodeQL Scan / Analyze (push) Failing after 0s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
Build Docker Images / lint (push) Has been cancelled
Build Docker Images / check (push) Has been cancelled
Build Docker Images / tests (push) Has been cancelled
Build Docker Images / detect-circular (push) Has been cancelled
Mirror to Codeberg / Mirror (push) Has been cancelled
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been cancelled
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been cancelled
This commit is contained in:
parent
df2a5ce260
commit
709e1c6087
22 changed files with 688 additions and 477 deletions
|
|
@ -4,6 +4,7 @@ import {
|
|||
EntitySchema,
|
||||
URICollectionSchema,
|
||||
} from "@versia/sdk/schemas";
|
||||
import { config } from "@versia-server/config";
|
||||
import { ApiError } from "@versia-server/kit";
|
||||
import { apiRoute, handleZodError } from "@versia-server/kit/api";
|
||||
import { db, Instance, Note, User } from "@versia-server/kit/db";
|
||||
|
|
@ -112,13 +113,16 @@ export default apiRoute((app) =>
|
|||
),
|
||||
);
|
||||
|
||||
entity = new VersiaEntities.URICollection({
|
||||
author: note.author.id,
|
||||
total: replyCount,
|
||||
items: replies.map((reply) =>
|
||||
reply.reference.toString(),
|
||||
),
|
||||
});
|
||||
entity = new VersiaEntities.URICollection(
|
||||
{
|
||||
author: note.author.id,
|
||||
total: replyCount,
|
||||
items: replies.map((reply) =>
|
||||
reply.reference.toString(),
|
||||
),
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "quotes": {
|
||||
|
|
@ -146,13 +150,16 @@ export default apiRoute((app) =>
|
|||
),
|
||||
);
|
||||
|
||||
entity = new VersiaEntities.URICollection({
|
||||
author: note.author.id,
|
||||
total: quoteCount,
|
||||
items: quotes.map((quote) =>
|
||||
quote.reference.toString(),
|
||||
),
|
||||
});
|
||||
entity = new VersiaEntities.URICollection(
|
||||
{
|
||||
author: note.author.id,
|
||||
total: quoteCount,
|
||||
items: quotes.map((quote) =>
|
||||
quote.reference.toString(),
|
||||
),
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "pub.versia:share/Shares": {
|
||||
|
|
@ -180,13 +187,16 @@ export default apiRoute((app) =>
|
|||
),
|
||||
);
|
||||
|
||||
entity = new VersiaEntities.URICollection({
|
||||
author: note.author.id,
|
||||
total: shareCount,
|
||||
items: shares.map((share) =>
|
||||
share.reference.toString(),
|
||||
),
|
||||
});
|
||||
entity = new VersiaEntities.URICollection(
|
||||
{
|
||||
author: note.author.id,
|
||||
total: shareCount,
|
||||
items: shares.map((share) =>
|
||||
share.reference.toString(),
|
||||
),
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -226,23 +236,29 @@ export default apiRoute((app) =>
|
|||
offset,
|
||||
);
|
||||
|
||||
entity = new VersiaEntities.Collection({
|
||||
author: user.id,
|
||||
total,
|
||||
items: outboxItems.map((note) =>
|
||||
note.toVersia(),
|
||||
),
|
||||
});
|
||||
entity = new VersiaEntities.Collection(
|
||||
{
|
||||
author: user.id,
|
||||
total,
|
||||
items: outboxItems.map((note) =>
|
||||
note.toVersia(),
|
||||
),
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "followers": {
|
||||
if (user.data.isHidingCollections) {
|
||||
entity = new VersiaEntities.URICollection({
|
||||
author: user.id,
|
||||
items: [],
|
||||
total: 0,
|
||||
});
|
||||
entity = new VersiaEntities.URICollection(
|
||||
{
|
||||
author: user.id,
|
||||
items: [],
|
||||
total: 0,
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -258,23 +274,29 @@ export default apiRoute((app) =>
|
|||
offset,
|
||||
);
|
||||
|
||||
entity = new VersiaEntities.URICollection({
|
||||
author: user.id,
|
||||
items: followers.map((follower) =>
|
||||
follower.reference.toString(),
|
||||
),
|
||||
total,
|
||||
});
|
||||
entity = new VersiaEntities.URICollection(
|
||||
{
|
||||
author: user.id,
|
||||
items: followers.map((follower) =>
|
||||
follower.reference.toString(),
|
||||
),
|
||||
total,
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "following": {
|
||||
if (user.data.isHidingCollections) {
|
||||
entity = new VersiaEntities.URICollection({
|
||||
author: user.id,
|
||||
items: [],
|
||||
total: 0,
|
||||
});
|
||||
entity = new VersiaEntities.URICollection(
|
||||
{
|
||||
author: user.id,
|
||||
items: [],
|
||||
total: 0,
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -290,13 +312,16 @@ export default apiRoute((app) =>
|
|||
offset,
|
||||
);
|
||||
|
||||
entity = new VersiaEntities.URICollection({
|
||||
author: user.id,
|
||||
items: following.map((followed) =>
|
||||
followed.reference.toString(),
|
||||
),
|
||||
total,
|
||||
});
|
||||
entity = new VersiaEntities.URICollection(
|
||||
{
|
||||
author: user.id,
|
||||
items: following.map((followed) =>
|
||||
followed.reference.toString(),
|
||||
),
|
||||
total,
|
||||
},
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
182
packages/api/routes/versia/v0.6/inbox.test.ts
vendored
182
packages/api/routes/versia/v0.6/inbox.test.ts
vendored
|
|
@ -83,15 +83,18 @@ mock(new URL(`/.versia/v0.6/entities/User/${userId}`, instanceUrl).href, {
|
|||
headers: {
|
||||
"Content-Type": "application/vnd.versia+json; charset=utf-8",
|
||||
},
|
||||
data: new VersiaEntities.User({
|
||||
id: userId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "User",
|
||||
username: "testuser",
|
||||
fields: [],
|
||||
manually_approves_followers: false,
|
||||
indexable: true,
|
||||
}).toJSON(),
|
||||
data: new VersiaEntities.User(
|
||||
{
|
||||
id: userId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "User",
|
||||
username: "testuser",
|
||||
fields: [],
|
||||
manually_approves_followers: false,
|
||||
indexable: true,
|
||||
},
|
||||
instanceUrl.hostname,
|
||||
).toJSON(),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -111,35 +114,38 @@ afterAll(async () => {
|
|||
|
||||
describe("Inbox Tests", () => {
|
||||
test("should correctly process inbox request", async () => {
|
||||
const exampleNote = new VersiaEntities.Note({
|
||||
id: noteId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "Note",
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: [],
|
||||
const exampleNote = new VersiaEntities.Note(
|
||||
{
|
||||
id: noteId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "Note",
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: [],
|
||||
},
|
||||
},
|
||||
previews: [],
|
||||
attachments: [],
|
||||
author: userId,
|
||||
content: {
|
||||
"text/html": {
|
||||
content: "<p>Hello!</p>",
|
||||
remote: false,
|
||||
},
|
||||
"text/plain": {
|
||||
content: "Hello!",
|
||||
remote: false,
|
||||
},
|
||||
},
|
||||
group: "public",
|
||||
is_sensitive: false,
|
||||
mentions: [],
|
||||
quotes: null,
|
||||
replies_to: null,
|
||||
subject: "",
|
||||
},
|
||||
previews: [],
|
||||
attachments: [],
|
||||
author: userId,
|
||||
content: {
|
||||
"text/html": {
|
||||
content: "<p>Hello!</p>",
|
||||
remote: false,
|
||||
},
|
||||
"text/plain": {
|
||||
content: "Hello!",
|
||||
remote: false,
|
||||
},
|
||||
},
|
||||
group: "public",
|
||||
is_sensitive: false,
|
||||
mentions: [],
|
||||
quotes: null,
|
||||
replies_to: null,
|
||||
subject: "",
|
||||
});
|
||||
instanceUrl.hostname,
|
||||
);
|
||||
|
||||
const signedRequest = await sign(
|
||||
instanceKeys.privateKey,
|
||||
|
|
@ -177,13 +183,16 @@ describe("Inbox Tests", () => {
|
|||
});
|
||||
|
||||
test("should correctly process Share", async () => {
|
||||
const exampleRequest = new VersiaEntities.Share({
|
||||
id: shareId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "pub.versia:share/Share",
|
||||
author: userId,
|
||||
shared: noteId,
|
||||
});
|
||||
const exampleRequest = new VersiaEntities.Share(
|
||||
{
|
||||
id: shareId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "pub.versia:share/Share",
|
||||
author: userId,
|
||||
shared: noteId,
|
||||
},
|
||||
instanceUrl.hostname,
|
||||
);
|
||||
|
||||
const signedRequest = await sign(
|
||||
instanceKeys.privateKey,
|
||||
|
|
@ -229,14 +238,17 @@ describe("Inbox Tests", () => {
|
|||
});
|
||||
|
||||
test("should correctly process Reaction", async () => {
|
||||
const exampleRequest = new VersiaEntities.Reaction({
|
||||
id: reactionId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "pub.versia:reactions/Reaction",
|
||||
author: userId,
|
||||
object: noteId,
|
||||
content: "👍",
|
||||
});
|
||||
const exampleRequest = new VersiaEntities.Reaction(
|
||||
{
|
||||
id: reactionId,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "pub.versia:reactions/Reaction",
|
||||
author: userId,
|
||||
object: noteId,
|
||||
content: "👍",
|
||||
},
|
||||
instanceUrl.hostname,
|
||||
);
|
||||
|
||||
const signedRequest = await sign(
|
||||
instanceKeys.privateKey,
|
||||
|
|
@ -304,34 +316,37 @@ describe("Inbox Tests", () => {
|
|||
});
|
||||
|
||||
test("should correctly process Reaction with custom emoji", async () => {
|
||||
const exampleRequest = new VersiaEntities.Reaction({
|
||||
id: reaction2Id,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "pub.versia:reactions/Reaction",
|
||||
author: userId,
|
||||
object: noteId,
|
||||
content: ":neocat:",
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: [
|
||||
{
|
||||
name: ":neocat:",
|
||||
url: {
|
||||
"image/webp": {
|
||||
hash: "e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649",
|
||||
size: 4664,
|
||||
width: 256,
|
||||
height: 256,
|
||||
remote: true,
|
||||
content:
|
||||
"https://cdn.cpluspatch.com/versia-cpp/e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649/neocat.webp",
|
||||
const exampleRequest = new VersiaEntities.Reaction(
|
||||
{
|
||||
id: reaction2Id,
|
||||
created_at: "2025-04-18T10:32:01.427Z",
|
||||
type: "pub.versia:reactions/Reaction",
|
||||
author: userId,
|
||||
object: noteId,
|
||||
content: ":neocat:",
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: [
|
||||
{
|
||||
name: ":neocat:",
|
||||
url: {
|
||||
"image/webp": {
|
||||
hash: "e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649",
|
||||
size: 4664,
|
||||
width: 256,
|
||||
height: 256,
|
||||
remote: true,
|
||||
content:
|
||||
"https://cdn.cpluspatch.com/versia-cpp/e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649/neocat.webp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
instanceUrl.hostname,
|
||||
);
|
||||
|
||||
const signedRequest = await sign(
|
||||
instanceKeys.privateKey,
|
||||
|
|
@ -405,13 +420,16 @@ describe("Inbox Tests", () => {
|
|||
expect(noteToDelete).not.toBeNull();
|
||||
|
||||
// Create a Delete request
|
||||
const exampleRequest = new VersiaEntities.Delete({
|
||||
created_at: new Date().toISOString(),
|
||||
type: "Delete",
|
||||
author: userId,
|
||||
deleted_type: "Note",
|
||||
deleted: noteId,
|
||||
});
|
||||
const exampleRequest = new VersiaEntities.Delete(
|
||||
{
|
||||
created_at: new Date().toISOString(),
|
||||
type: "Delete",
|
||||
author: userId,
|
||||
deleted_type: "Note",
|
||||
deleted: noteId,
|
||||
},
|
||||
instanceUrl.hostname,
|
||||
);
|
||||
|
||||
const signedRequest = await sign(
|
||||
instanceKeys.privateKey,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import * as VersiaEntities from "@versia/sdk/entities";
|
||||
import { config } from "@versia-server/config";
|
||||
import {
|
||||
and,
|
||||
desc,
|
||||
|
|
@ -20,7 +21,9 @@ import { BaseInterface } from "./base.ts";
|
|||
import type { User } from "./user.ts";
|
||||
|
||||
type LikeType = InferSelectModel<typeof Likes> & {
|
||||
liker: InferSelectModel<typeof Users>;
|
||||
liker: InferSelectModel<typeof Users> & {
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
};
|
||||
liked: InferSelectModel<typeof Notes> & {
|
||||
author: InferSelectModel<typeof Users> & {
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
|
|
@ -70,7 +73,11 @@ export class Like extends BaseInterface<typeof Likes, LikeType> {
|
|||
},
|
||||
},
|
||||
},
|
||||
liker: true,
|
||||
liker: {
|
||||
with: {
|
||||
instance: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -102,7 +109,11 @@ export class Like extends BaseInterface<typeof Likes, LikeType> {
|
|||
},
|
||||
},
|
||||
},
|
||||
liker: true,
|
||||
liker: {
|
||||
with: {
|
||||
instance: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -167,22 +178,28 @@ export class Like extends BaseInterface<typeof Likes, LikeType> {
|
|||
likedReference = `${this.data.liked.author.instance.domain}:${this.data.liked.remoteId}`;
|
||||
}
|
||||
|
||||
return new VersiaEntities.Like({
|
||||
id: this.id,
|
||||
author: this.data.liker.id,
|
||||
type: "pub.versia:likes/Like",
|
||||
created_at: this.data.createdAt.toISOString(),
|
||||
liked: likedReference,
|
||||
});
|
||||
return new VersiaEntities.Like(
|
||||
{
|
||||
id: this.id,
|
||||
author: this.data.liker.id,
|
||||
type: "pub.versia:likes/Like",
|
||||
created_at: this.data.createdAt.toISOString(),
|
||||
liked: likedReference,
|
||||
},
|
||||
this.data.liker.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
public unlikeToVersia(unliker?: User): VersiaEntities.Delete {
|
||||
return new VersiaEntities.Delete({
|
||||
type: "Delete",
|
||||
created_at: new Date().toISOString(),
|
||||
author: unliker ? unliker.id : this.data.liker.id,
|
||||
deleted_type: "pub.versia:likes/Like",
|
||||
deleted: this.id,
|
||||
});
|
||||
return new VersiaEntities.Delete(
|
||||
{
|
||||
type: "Delete",
|
||||
created_at: new Date().toISOString(),
|
||||
author: unliker ? unliker.id : this.data.liker.id,
|
||||
deleted_type: "pub.versia:likes/Like",
|
||||
deleted: this.id,
|
||||
},
|
||||
this.data.liker.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,13 +426,13 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
|
||||
public get reference(): VersiaEntities.Reference {
|
||||
if (this.remote) {
|
||||
const instanceUrl = new URL(
|
||||
return new VersiaEntities.Reference(
|
||||
this.id,
|
||||
this.author.data.instance?.domain || "",
|
||||
);
|
||||
return new VersiaEntities.Reference(this.id, instanceUrl.hostname);
|
||||
}
|
||||
|
||||
return new VersiaEntities.Reference(this.id);
|
||||
return new VersiaEntities.Reference(this.id, config.http.base_url.host);
|
||||
}
|
||||
|
||||
public async federateToUsers(): Promise<void> {
|
||||
|
|
@ -941,19 +941,13 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
*/
|
||||
public static async resolve(
|
||||
reference: VersiaEntities.Reference,
|
||||
defaultInstance?: Instance,
|
||||
): Promise<Note | null> {
|
||||
// Check if note not already in database
|
||||
if (
|
||||
!(reference.domain || defaultInstance) ||
|
||||
reference.domain === config.http.base_url.hostname
|
||||
) {
|
||||
if (reference.domain === config.http.base_url.hostname) {
|
||||
return await Note.fromId(reference.id);
|
||||
}
|
||||
|
||||
const instance = reference.domain
|
||||
? await Instance.resolve(reference.domain)
|
||||
: (defaultInstance as Instance);
|
||||
const instance = await Instance.resolve(reference.domain);
|
||||
|
||||
const foundNote = await Note.fromSql(
|
||||
and(
|
||||
|
|
@ -973,14 +967,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
return foundNote;
|
||||
}
|
||||
|
||||
return Note.fromVersia(
|
||||
reference.domain
|
||||
? reference
|
||||
: new VersiaEntities.Reference(
|
||||
reference.id,
|
||||
instance.data.domain,
|
||||
),
|
||||
);
|
||||
return Note.fromVersia(reference);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1003,12 +990,6 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
instance?: Instance,
|
||||
): Promise<Note> {
|
||||
if (versiaNote instanceof VersiaEntities.Reference) {
|
||||
if (!versiaNote.domain) {
|
||||
throw new Error(
|
||||
"Cannot fetch Versia note from reference without domain",
|
||||
);
|
||||
}
|
||||
|
||||
// No bridge support for notes yet
|
||||
const note = await Instance.federationRequester.fetchEntity(
|
||||
versiaNote,
|
||||
|
|
@ -1027,7 +1008,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
const { created_at, extensions, group, id, is_sensitive, subject } =
|
||||
versiaNote.data;
|
||||
|
||||
const author = await User.resolve(versiaNote.author, instance);
|
||||
const author = await User.resolve(versiaNote.author);
|
||||
|
||||
if (!author) {
|
||||
throw new Error("Entity author could not be resolved");
|
||||
|
|
@ -1064,7 +1045,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
|
||||
const mentions = (
|
||||
await Promise.all(
|
||||
versiaNote.mentions.map((m) => User.resolve(m, instance)) ?? [],
|
||||
versiaNote.mentions.map((m) => User.resolve(m)) ?? [],
|
||||
)
|
||||
).filter((m) => m !== null);
|
||||
|
||||
|
|
@ -1075,10 +1056,10 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
: (group as "public" | "followers" | "unlisted");
|
||||
|
||||
const reply = versiaNote.repliesTo
|
||||
? await Note.resolve(versiaNote.repliesTo, instance)
|
||||
? await Note.resolve(versiaNote.repliesTo)
|
||||
: null;
|
||||
const quote = versiaNote.quotes
|
||||
? await Note.resolve(versiaNote.quotes, instance)
|
||||
? await Note.resolve(versiaNote.quotes)
|
||||
: null;
|
||||
const spoiler = subject ? await sanitizedHtmlStrip(subject) : undefined;
|
||||
|
||||
|
|
@ -1296,13 +1277,16 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
}
|
||||
|
||||
public deleteToVersia(): VersiaEntities.Delete {
|
||||
return new VersiaEntities.Delete({
|
||||
type: "Delete",
|
||||
author: this.author.id,
|
||||
deleted_type: "Note",
|
||||
deleted: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
});
|
||||
return new VersiaEntities.Delete(
|
||||
{
|
||||
type: "Delete",
|
||||
author: this.author.id,
|
||||
deleted_type: "Note",
|
||||
deleted: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
this.data.author.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1324,48 +1308,51 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
replyReference = `${status.reply.author.instance.domain}:${status.reply.remoteId}`;
|
||||
}
|
||||
|
||||
return new VersiaEntities.Note({
|
||||
type: "Note",
|
||||
created_at: status.createdAt.toISOString(),
|
||||
id: status.id,
|
||||
author: this.author.id,
|
||||
content: {
|
||||
"text/html": {
|
||||
content: status.content,
|
||||
remote: false,
|
||||
return new VersiaEntities.Note(
|
||||
{
|
||||
type: "Note",
|
||||
created_at: status.createdAt.toISOString(),
|
||||
id: status.id,
|
||||
author: this.author.id,
|
||||
content: {
|
||||
"text/html": {
|
||||
content: status.content,
|
||||
remote: false,
|
||||
},
|
||||
"text/plain": {
|
||||
content: htmlToText(status.content),
|
||||
remote: false,
|
||||
},
|
||||
},
|
||||
"text/plain": {
|
||||
content: htmlToText(status.content),
|
||||
remote: false,
|
||||
previews: [],
|
||||
attachments: status.attachments.map(
|
||||
(attachment) =>
|
||||
new Media(attachment).toVersia().data as z.infer<
|
||||
typeof NonTextContentFormatSchema
|
||||
>,
|
||||
),
|
||||
is_sensitive: status.sensitive,
|
||||
mentions: status.mentions.map((mention) =>
|
||||
mention.instance
|
||||
? `${mention.instance.domain}:${mention.id}`
|
||||
: mention.id,
|
||||
),
|
||||
quotes: quoteReference,
|
||||
replies_to: replyReference,
|
||||
subject: status.spoilerText,
|
||||
// TODO: Refactor as part of groups
|
||||
group: status.visibility === "public" ? "public" : "followers",
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: status.emojis.map((emoji) =>
|
||||
new Emoji(emoji).toVersia(),
|
||||
),
|
||||
},
|
||||
// TODO: Add polls and reactions
|
||||
},
|
||||
},
|
||||
previews: [],
|
||||
attachments: status.attachments.map(
|
||||
(attachment) =>
|
||||
new Media(attachment).toVersia().data as z.infer<
|
||||
typeof NonTextContentFormatSchema
|
||||
>,
|
||||
),
|
||||
is_sensitive: status.sensitive,
|
||||
mentions: status.mentions.map((mention) =>
|
||||
mention.instance
|
||||
? `${mention.instance.domain}:${mention.id}`
|
||||
: mention.id,
|
||||
),
|
||||
quotes: quoteReference,
|
||||
replies_to: replyReference,
|
||||
subject: status.spoilerText,
|
||||
// TODO: Refactor as part of groups
|
||||
group: status.visibility === "public" ? "public" : "followers",
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: status.emojis.map((emoji) =>
|
||||
new Emoji(emoji).toVersia(),
|
||||
),
|
||||
},
|
||||
// TODO: Add polls and reactions
|
||||
},
|
||||
});
|
||||
status.author.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
public toVersiaShare(): VersiaEntities.Share {
|
||||
|
|
@ -1373,25 +1360,31 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
throw new Error("Cannot share a non-reblogged note");
|
||||
}
|
||||
|
||||
return new VersiaEntities.Share({
|
||||
type: "pub.versia:share/Share",
|
||||
author: this.author.id,
|
||||
id: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
shared: this.data.reblog.author.instance
|
||||
? `${this.data.reblog.author.instance.domain}:${this.data.reblog.id}`
|
||||
: this.data.reblog.id,
|
||||
});
|
||||
return new VersiaEntities.Share(
|
||||
{
|
||||
type: "pub.versia:share/Share",
|
||||
author: this.author.id,
|
||||
id: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
shared: this.data.reblog.author.instance
|
||||
? `${this.data.reblog.author.instance.domain}:${this.data.reblog.id}`
|
||||
: this.data.reblog.id,
|
||||
},
|
||||
this.data.author.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
public toVersiaUnshare(): VersiaEntities.Delete {
|
||||
return new VersiaEntities.Delete({
|
||||
type: "Delete",
|
||||
created_at: new Date().toISOString(),
|
||||
author: this.author.id,
|
||||
deleted_type: "pub.versia:share/Share",
|
||||
deleted: this.id,
|
||||
});
|
||||
return new VersiaEntities.Delete(
|
||||
{
|
||||
type: "Delete",
|
||||
created_at: new Date().toISOString(),
|
||||
author: this.author.id,
|
||||
deleted_type: "pub.versia:share/Share",
|
||||
deleted: this.id,
|
||||
},
|
||||
this.data.author.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ import type { User } from "./user.ts";
|
|||
|
||||
type ReactionType = InferSelectModel<typeof Reactions> & {
|
||||
emoji: typeof Emoji.$type | null;
|
||||
author: InferSelectModel<typeof Users>;
|
||||
author: InferSelectModel<typeof Users> & {
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
};
|
||||
note: InferSelectModel<typeof Notes> & {
|
||||
author: InferSelectModel<typeof Users> & {
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
|
|
@ -72,7 +74,11 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
|
|||
media: true,
|
||||
},
|
||||
},
|
||||
author: true,
|
||||
author: {
|
||||
with: {
|
||||
instance: true,
|
||||
},
|
||||
},
|
||||
note: {
|
||||
with: {
|
||||
author: {
|
||||
|
|
@ -112,7 +118,11 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
|
|||
media: true,
|
||||
},
|
||||
},
|
||||
author: true,
|
||||
author: {
|
||||
with: {
|
||||
instance: true,
|
||||
},
|
||||
},
|
||||
note: {
|
||||
with: {
|
||||
author: {
|
||||
|
|
@ -245,37 +255,43 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
|
|||
noteReference = `${this.data.note.author.instance.domain}:${this.data.note.remoteId}`;
|
||||
}
|
||||
|
||||
return new VersiaEntities.Reaction({
|
||||
type: "pub.versia:reactions/Reaction",
|
||||
author: this.data.author.id,
|
||||
created_at: this.data.createdAt.toISOString(),
|
||||
id: this.id,
|
||||
object: noteReference,
|
||||
content: this.hasCustomEmoji()
|
||||
? `:${this.data.emoji?.shortcode}:`
|
||||
: this.data.emojiText || "",
|
||||
extensions: this.hasCustomEmoji()
|
||||
? {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: [
|
||||
new Emoji(
|
||||
this.data.emoji as typeof Emoji.$type,
|
||||
).toVersia(),
|
||||
],
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
return new VersiaEntities.Reaction(
|
||||
{
|
||||
type: "pub.versia:reactions/Reaction",
|
||||
author: this.data.author.id,
|
||||
created_at: this.data.createdAt.toISOString(),
|
||||
id: this.id,
|
||||
object: noteReference,
|
||||
content: this.hasCustomEmoji()
|
||||
? `:${this.data.emoji?.shortcode}:`
|
||||
: this.data.emojiText || "",
|
||||
extensions: this.hasCustomEmoji()
|
||||
? {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: [
|
||||
new Emoji(
|
||||
this.data.emoji as typeof Emoji.$type,
|
||||
).toVersia(),
|
||||
],
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
this.data.author.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
public toVersiaUnreact(): VersiaEntities.Delete {
|
||||
return new VersiaEntities.Delete({
|
||||
type: "Delete",
|
||||
created_at: new Date().toISOString(),
|
||||
author: this.data.authorId,
|
||||
deleted_type: "pub.versia:reactions/Reaction",
|
||||
deleted: this.id,
|
||||
});
|
||||
return new VersiaEntities.Delete(
|
||||
{
|
||||
type: "Delete",
|
||||
created_at: new Date().toISOString(),
|
||||
author: this.data.authorId,
|
||||
deleted_type: "pub.versia:reactions/Reaction",
|
||||
deleted: this.id,
|
||||
},
|
||||
this.data.author.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
public static async fromVersia(
|
||||
|
|
|
|||
|
|
@ -224,7 +224,10 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
|
||||
public get reference(): VersiaEntities.Reference {
|
||||
if (this.local) {
|
||||
return new VersiaEntities.Reference(this.id);
|
||||
return new VersiaEntities.Reference(
|
||||
this.id,
|
||||
config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
return new VersiaEntities.Reference(
|
||||
|
|
@ -330,14 +333,17 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
}
|
||||
|
||||
private unfollowToVersia(followee: User): VersiaEntities.Unfollow {
|
||||
return new VersiaEntities.Unfollow({
|
||||
type: "Unfollow",
|
||||
author: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
followee: followee.data.instance
|
||||
? `${followee.data.instance.domain}:${followee.id}`
|
||||
: followee.id,
|
||||
});
|
||||
return new VersiaEntities.Unfollow(
|
||||
{
|
||||
type: "Unfollow",
|
||||
author: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
followee: followee.data.instance
|
||||
? `${followee.data.instance.domain}:${followee.id}`
|
||||
: followee.id,
|
||||
},
|
||||
this.data.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
public async acceptFollowRequest(follower: User): Promise<void> {
|
||||
|
|
@ -352,14 +358,17 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
await follower.recalculateFollowerCount();
|
||||
await this.recalculateFollowingCount();
|
||||
|
||||
const entity = new VersiaEntities.FollowAccept({
|
||||
type: "FollowAccept",
|
||||
author: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
follower: follower.data.instance
|
||||
? `${follower.data.instance.domain}:${follower.id}`
|
||||
: follower.id,
|
||||
});
|
||||
const entity = new VersiaEntities.FollowAccept(
|
||||
{
|
||||
type: "FollowAccept",
|
||||
author: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
follower: follower.data.instance
|
||||
? `${follower.data.instance.domain}:${follower.id}`
|
||||
: follower.id,
|
||||
},
|
||||
this.data.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
|
||||
await deliveryQueue.add(DeliveryJobType.FederateEntity, {
|
||||
entity: entity.toJSON(),
|
||||
|
|
@ -377,14 +386,17 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
throw new Error("Followee must be a local user");
|
||||
}
|
||||
|
||||
const entity = new VersiaEntities.FollowReject({
|
||||
type: "FollowReject",
|
||||
author: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
follower: follower.data.instance
|
||||
? `${follower.data.instance.domain}:${follower.id}`
|
||||
: follower.id,
|
||||
});
|
||||
const entity = new VersiaEntities.FollowReject(
|
||||
{
|
||||
type: "FollowReject",
|
||||
author: this.id,
|
||||
created_at: new Date().toISOString(),
|
||||
follower: follower.data.instance
|
||||
? `${follower.data.instance.domain}:${follower.id}`
|
||||
: follower.id,
|
||||
},
|
||||
this.data.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
|
||||
await deliveryQueue.add(DeliveryJobType.FederateEntity, {
|
||||
entity: entity.toJSON(),
|
||||
|
|
@ -686,12 +698,6 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
instance?: Instance,
|
||||
): Promise<User> {
|
||||
if (versiaUser instanceof VersiaEntities.Reference) {
|
||||
if (!versiaUser.domain) {
|
||||
throw new Error(
|
||||
"Cannot fetch Versia user from reference without domain",
|
||||
);
|
||||
}
|
||||
|
||||
const user = await Instance.federationRequester.fetchEntity(
|
||||
versiaUser,
|
||||
VersiaEntities.User,
|
||||
|
|
@ -802,13 +808,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
|
||||
public static async resolve(
|
||||
reference: VersiaEntities.Reference,
|
||||
defaultInstance?: Instance,
|
||||
): Promise<User> {
|
||||
// Check if user not already in database
|
||||
if (
|
||||
!(reference.domain || defaultInstance) ||
|
||||
reference.domain === config.http.base_url.hostname
|
||||
) {
|
||||
if (reference.domain === config.http.base_url.hostname) {
|
||||
const user = await User.fromId(reference.id);
|
||||
|
||||
if (!user) {
|
||||
|
|
@ -820,9 +822,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return user;
|
||||
}
|
||||
|
||||
const instance = reference.domain
|
||||
? await Instance.resolve(reference.domain)
|
||||
: (defaultInstance as Instance);
|
||||
const instance = await Instance.resolve(reference.domain);
|
||||
|
||||
const foundUser = await User.fromSql(
|
||||
and(
|
||||
|
|
@ -836,12 +836,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
}
|
||||
|
||||
return User.fromVersia(
|
||||
reference.domain
|
||||
? reference
|
||||
: new VersiaEntities.Reference(
|
||||
reference.id,
|
||||
instance.data.domain,
|
||||
),
|
||||
new VersiaEntities.Reference(reference.id, instance.data.domain),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1088,39 +1083,42 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
|
||||
const user = this.data;
|
||||
|
||||
return new VersiaEntities.User({
|
||||
id: user.id,
|
||||
type: "User",
|
||||
bio: {
|
||||
"text/html": {
|
||||
content: user.note,
|
||||
remote: false,
|
||||
return new VersiaEntities.User(
|
||||
{
|
||||
id: user.id,
|
||||
type: "User",
|
||||
bio: {
|
||||
"text/html": {
|
||||
content: user.note,
|
||||
remote: false,
|
||||
},
|
||||
"text/plain": {
|
||||
content: htmlToText(user.note),
|
||||
remote: false,
|
||||
},
|
||||
},
|
||||
"text/plain": {
|
||||
content: htmlToText(user.note),
|
||||
remote: false,
|
||||
created_at: user.createdAt.toISOString(),
|
||||
indexable: this.data.isIndexable,
|
||||
username: user.username,
|
||||
manually_approves_followers: this.data.isLocked,
|
||||
avatar: this.avatar?.toVersia().data as z.infer<
|
||||
typeof ImageContentFormatSchema
|
||||
>,
|
||||
header: this.header?.toVersia().data as z.infer<
|
||||
typeof ImageContentFormatSchema
|
||||
>,
|
||||
display_name: user.displayName,
|
||||
fields: user.fields,
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: user.emojis.map((emoji) =>
|
||||
new Emoji(emoji).toVersia(),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
created_at: user.createdAt.toISOString(),
|
||||
indexable: this.data.isIndexable,
|
||||
username: user.username,
|
||||
manually_approves_followers: this.data.isLocked,
|
||||
avatar: this.avatar?.toVersia().data as z.infer<
|
||||
typeof ImageContentFormatSchema
|
||||
>,
|
||||
header: this.header?.toVersia().data as z.infer<
|
||||
typeof ImageContentFormatSchema
|
||||
>,
|
||||
display_name: user.displayName,
|
||||
fields: user.fields,
|
||||
extensions: {
|
||||
"pub.versia:custom_emojis": {
|
||||
emojis: user.emojis.map((emoji) =>
|
||||
new Emoji(emoji).toVersia(),
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
this.data.instance?.domain ?? config.http.base_url.hostname,
|
||||
);
|
||||
}
|
||||
|
||||
public toMention(): z.infer<typeof MentionSchema> {
|
||||
|
|
|
|||
|
|
@ -185,33 +185,31 @@ export class InboxProcessor {
|
|||
// TODO: Rip out bridge code so this is never null
|
||||
const instance = this.sender?.instance as Instance;
|
||||
|
||||
await new EntitySorter(this.body)
|
||||
await new EntitySorter(this.body, instance.data.domain)
|
||||
.on(VersiaEntities.Note, (n) =>
|
||||
InboxProcessor.processNote(n, instance),
|
||||
)
|
||||
.on(VersiaEntities.Follow, (f) =>
|
||||
InboxProcessor.processFollowRequest(f, instance),
|
||||
InboxProcessor.processFollowRequest(f),
|
||||
)
|
||||
.on(VersiaEntities.FollowAccept, (f) =>
|
||||
InboxProcessor.processFollowAccept(f, instance),
|
||||
InboxProcessor.processFollowAccept(f),
|
||||
)
|
||||
.on(VersiaEntities.FollowReject, (f) =>
|
||||
InboxProcessor.processFollowReject(f, instance),
|
||||
InboxProcessor.processFollowReject(f),
|
||||
)
|
||||
.on(VersiaEntities.Like, (l) =>
|
||||
InboxProcessor.processLikeRequest(l, instance),
|
||||
InboxProcessor.processLikeRequest(l),
|
||||
)
|
||||
.on(VersiaEntities.Delete, (d) =>
|
||||
InboxProcessor.processDelete(d, instance),
|
||||
InboxProcessor.processDelete(d),
|
||||
)
|
||||
.on(VersiaEntities.User, (u) =>
|
||||
InboxProcessor.processUser(u, instance),
|
||||
)
|
||||
.on(VersiaEntities.Share, (s) =>
|
||||
InboxProcessor.processShare(s, instance),
|
||||
)
|
||||
.on(VersiaEntities.Share, (s) => InboxProcessor.processShare(s))
|
||||
.on(VersiaEntities.Reaction, (r) =>
|
||||
InboxProcessor.processReaction(r, instance),
|
||||
InboxProcessor.processReaction(r),
|
||||
)
|
||||
.sort(() => {
|
||||
throw new ApiError(400, "Unknown entity type");
|
||||
|
|
@ -229,10 +227,9 @@ export class InboxProcessor {
|
|||
*/
|
||||
private static async processReaction(
|
||||
reaction: VersiaEntities.Reaction,
|
||||
sender: Instance,
|
||||
): Promise<void> {
|
||||
const author = await User.resolve(reaction.author, sender);
|
||||
const note = await Note.resolve(reaction.object, sender);
|
||||
const author = await User.resolve(reaction.author);
|
||||
const note = await Note.resolve(reaction.object);
|
||||
|
||||
if (!author) {
|
||||
throw new ApiError(404, "Author not found");
|
||||
|
|
@ -322,10 +319,9 @@ export class InboxProcessor {
|
|||
*/
|
||||
private static async processFollowRequest(
|
||||
follow: VersiaEntities.Follow,
|
||||
sender: Instance,
|
||||
): Promise<void> {
|
||||
const author = await User.resolve(follow.author, sender);
|
||||
const followee = await User.resolve(follow.followee, sender);
|
||||
const author = await User.resolve(follow.author);
|
||||
const followee = await User.resolve(follow.followee);
|
||||
|
||||
if (!author) {
|
||||
throw new ApiError(404, "Author not found");
|
||||
|
|
@ -371,10 +367,9 @@ export class InboxProcessor {
|
|||
*/
|
||||
private static async processFollowAccept(
|
||||
followAccept: VersiaEntities.FollowAccept,
|
||||
sender: Instance,
|
||||
): Promise<void> {
|
||||
const author = await User.resolve(followAccept.author, sender);
|
||||
const follower = await User.resolve(followAccept.follower, sender);
|
||||
const author = await User.resolve(followAccept.author);
|
||||
const follower = await User.resolve(followAccept.follower);
|
||||
|
||||
if (!author) {
|
||||
throw new ApiError(404, "Author not found");
|
||||
|
|
@ -407,10 +402,9 @@ export class InboxProcessor {
|
|||
*/
|
||||
private static async processFollowReject(
|
||||
followReject: VersiaEntities.FollowReject,
|
||||
sender: Instance,
|
||||
): Promise<void> {
|
||||
const author = await User.resolve(followReject.author, sender);
|
||||
const follower = await User.resolve(followReject.follower, sender);
|
||||
const author = await User.resolve(followReject.author);
|
||||
const follower = await User.resolve(followReject.follower);
|
||||
|
||||
if (!author) {
|
||||
throw new ApiError(404, "Author not found");
|
||||
|
|
@ -443,10 +437,9 @@ export class InboxProcessor {
|
|||
*/
|
||||
private static async processShare(
|
||||
share: VersiaEntities.Share,
|
||||
sender: Instance,
|
||||
): Promise<void> {
|
||||
const author = await User.resolve(share.author, sender);
|
||||
const sharedNote = await Note.resolve(share.shared, sender);
|
||||
const author = await User.resolve(share.author);
|
||||
const sharedNote = await Note.resolve(share.shared);
|
||||
|
||||
if (!author) {
|
||||
throw new ApiError(404, "Author not found");
|
||||
|
|
@ -467,11 +460,10 @@ export class InboxProcessor {
|
|||
*/ // JS doesn't allow the use of `delete` as a variable name
|
||||
public static async processDelete(
|
||||
delete_: VersiaEntities.Delete,
|
||||
sender: Instance,
|
||||
): Promise<void> {
|
||||
const toDelete = delete_.deleted;
|
||||
|
||||
const author = await User.resolve(delete_.author, sender);
|
||||
const author = await User.resolve(delete_.author);
|
||||
|
||||
switch (delete_.data.deleted_type) {
|
||||
case "Note": {
|
||||
|
|
@ -491,7 +483,7 @@ export class InboxProcessor {
|
|||
return;
|
||||
}
|
||||
case "User": {
|
||||
const userToDelete = await User.resolve(toDelete, sender);
|
||||
const userToDelete = await User.resolve(toDelete);
|
||||
|
||||
if (!userToDelete) {
|
||||
throw new ApiError(404, "User to delete not found");
|
||||
|
|
@ -586,10 +578,9 @@ export class InboxProcessor {
|
|||
*/
|
||||
private static async processLikeRequest(
|
||||
like: VersiaEntities.Like,
|
||||
sender: Instance,
|
||||
): Promise<void> {
|
||||
const author = await User.resolve(like.author, sender);
|
||||
const likedNote = await Note.resolve(like.liked, sender);
|
||||
const author = await User.resolve(like.author);
|
||||
const likedNote = await Note.resolve(like.liked);
|
||||
|
||||
if (!author) {
|
||||
throw new ApiError(404, "Author not found");
|
||||
|
|
|
|||
|
|
@ -62,7 +62,10 @@ export const getDeliveryWorker = (): Worker<
|
|||
}
|
||||
|
||||
await sender.federateToUser(
|
||||
await entityCtor.fromJSON(entity),
|
||||
await entityCtor.fromJSON(
|
||||
entity,
|
||||
config.http.base_url.hostname,
|
||||
),
|
||||
recipient,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,25 +7,37 @@ import type { JSONObject } from "../types.ts";
|
|||
import { Entity } from "./entity.ts";
|
||||
|
||||
export class Collection extends Entity {
|
||||
public constructor(public override data: z.infer<typeof CollectionSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof CollectionSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Collection> {
|
||||
return CollectionSchema.parseAsync(json).then((u) => new Collection(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Collection> {
|
||||
return CollectionSchema.parseAsync(json).then(
|
||||
(u) => new Collection(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class URICollection extends Entity {
|
||||
public constructor(
|
||||
public override data: z.infer<typeof URICollectionSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data);
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<URICollection> {
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<URICollection> {
|
||||
return URICollectionSchema.parseAsync(json).then(
|
||||
(u) => new URICollection(u),
|
||||
(u) => new URICollection(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,27 @@ import { Entity, Reference } from "./entity.ts";
|
|||
export class Delete extends Entity {
|
||||
public static override name = "Delete";
|
||||
|
||||
public constructor(public override data: z.infer<typeof DeleteSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof DeleteSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get deleted(): Reference {
|
||||
return Reference.fromString(this.data.deleted);
|
||||
return Reference.fromString(this.data.deleted, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Delete> {
|
||||
return DeleteSchema.parseAsync(json).then((u) => new Delete(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Delete> {
|
||||
return DeleteSchema.parseAsync(json).then(
|
||||
(u) => new Delete(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,19 @@ import type { JSONObject } from "../types.ts";
|
|||
export class Entity {
|
||||
public static name = "Entity";
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: This is a base class that is never instanciated directly
|
||||
public constructor(public data: any) {}
|
||||
public constructor(
|
||||
// biome-ignore lint/suspicious/noExplicitAny: This is a base class that is never instanciated directly
|
||||
public data: any,
|
||||
public instanceDomain: string,
|
||||
) {}
|
||||
|
||||
public static fromJSON(json: JSONObject): Promise<Entity> {
|
||||
return EntitySchema.parseAsync(json).then((u) => new Entity(u));
|
||||
public static fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Entity> {
|
||||
return EntitySchema.parseAsync(json).then(
|
||||
(u) => new Entity(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
|
||||
public toJSON(): JSONObject {
|
||||
|
|
@ -19,10 +27,19 @@ export class Entity {
|
|||
export class Reference {
|
||||
public constructor(
|
||||
public id: string,
|
||||
public domain?: string,
|
||||
public domain: string,
|
||||
) {}
|
||||
|
||||
public static fromString(str: string): Reference {
|
||||
/**
|
||||
* Parses a reference from a string. The string can be in the format "domain:id" or just "id" if the domain is the local instance (in which case a default domain must be provided).
|
||||
* @param str
|
||||
* @param defaultDomain
|
||||
* @returns
|
||||
*/
|
||||
public static fromString(
|
||||
str: string,
|
||||
defaultDomain: URL | string,
|
||||
): Reference {
|
||||
// Expect format: domain:id or id (if domain is the local instance)
|
||||
// Handle IPv6 addresses in brackets
|
||||
const chunks = str.split(":");
|
||||
|
|
@ -36,10 +53,21 @@ export class Reference {
|
|||
return new Reference(id, domain);
|
||||
}
|
||||
|
||||
return new Reference(str);
|
||||
if (!defaultDomain) {
|
||||
throw new Error(
|
||||
`Invalid reference string: ${str}. Expected format "domain:id" or "id" with a default domain provided.`,
|
||||
);
|
||||
}
|
||||
|
||||
return new Reference(
|
||||
str,
|
||||
defaultDomain instanceof URL
|
||||
? defaultDomain.hostname
|
||||
: defaultDomain,
|
||||
);
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.domain ? `${this.domain}:${this.id}` : this.id;
|
||||
return `${this.domain}:${this.id}`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,39 +6,55 @@ import { Entity, Reference } from "../entity.ts";
|
|||
export class Like extends Entity {
|
||||
public static override name = "pub.versia:likes/Like";
|
||||
|
||||
public constructor(public override data: z.infer<typeof LikeSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof LikeSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get liked(): Reference {
|
||||
return Reference.fromString(this.data.liked);
|
||||
return Reference.fromString(this.data.liked, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Like> {
|
||||
return LikeSchema.parseAsync(json).then((u) => new Like(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Like> {
|
||||
return LikeSchema.parseAsync(json).then(
|
||||
(u) => new Like(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Dislike extends Entity {
|
||||
public static override name = "pub.versia:likes/Dislike";
|
||||
|
||||
public constructor(public override data: z.infer<typeof DislikeSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof DislikeSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get disliked(): Reference {
|
||||
return Reference.fromString(this.data.disliked);
|
||||
return Reference.fromString(this.data.disliked, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Dislike> {
|
||||
return DislikeSchema.parseAsync(json).then((u) => new Dislike(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Dislike> {
|
||||
return DislikeSchema.parseAsync(json).then(
|
||||
(u) => new Dislike(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,27 @@ import { Entity, Reference } from "../entity.ts";
|
|||
export class Vote extends Entity {
|
||||
public static override name = "pub.versia:polls/Vote";
|
||||
|
||||
public constructor(public override data: z.infer<typeof VoteSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof VoteSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get poll(): Reference {
|
||||
return Reference.fromString(this.data.poll);
|
||||
return Reference.fromString(this.data.poll, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Vote> {
|
||||
return VoteSchema.parseAsync(json).then((u) => new Vote(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Vote> {
|
||||
return VoteSchema.parseAsync(json).then(
|
||||
(u) => new Vote(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,27 @@ import { Entity, Reference } from "../entity.ts";
|
|||
export class Reaction extends Entity {
|
||||
public static override name = "pub.versia:reactions/Reaction";
|
||||
|
||||
public constructor(public override data: z.infer<typeof ReactionSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof ReactionSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get object(): Reference {
|
||||
return Reference.fromString(this.data.object);
|
||||
return Reference.fromString(this.data.object, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Reaction> {
|
||||
return ReactionSchema.parseAsync(json).then((u) => new Reaction(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Reaction> {
|
||||
return ReactionSchema.parseAsync(json).then(
|
||||
(u) => new Reaction(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,31 @@ import { Entity, Reference } from "../entity.ts";
|
|||
export class Report extends Entity {
|
||||
public static override name = "pub.versia:reports/Report";
|
||||
|
||||
public constructor(public override data: z.infer<typeof ReportSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof ReportSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference | null {
|
||||
return this.data.author ? Reference.fromString(this.data.author) : null;
|
||||
return this.data.author
|
||||
? Reference.fromString(this.data.author, this.instanceDomain)
|
||||
: null;
|
||||
}
|
||||
|
||||
public get reported(): Reference[] {
|
||||
return this.data.reported.map((r) => Reference.fromString(r));
|
||||
return this.data.reported.map((r) =>
|
||||
Reference.fromString(r, this.instanceDomain),
|
||||
);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Report> {
|
||||
return ReportSchema.parseAsync(json).then((u) => new Report(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Report> {
|
||||
return ReportSchema.parseAsync(json).then(
|
||||
(u) => new Report(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,27 @@ import { Entity, Reference } from "../entity.ts";
|
|||
export class Share extends Entity {
|
||||
public static override name = "pub.versia:share/Share";
|
||||
|
||||
public constructor(public override data: z.infer<typeof ShareSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof ShareSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get shared(): Reference {
|
||||
return Reference.fromString(this.data.shared);
|
||||
return Reference.fromString(this.data.shared, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Share> {
|
||||
return ShareSchema.parseAsync(json).then((u) => new Share(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Share> {
|
||||
return ShareSchema.parseAsync(json).then(
|
||||
(u) => new Share(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,20 +11,28 @@ import { Entity, Reference } from "./entity.ts";
|
|||
export class Follow extends Entity {
|
||||
public static override name = "Follow";
|
||||
|
||||
public constructor(public override data: z.infer<typeof FollowSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof FollowSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get followee(): Reference {
|
||||
return Reference.fromString(this.data.followee);
|
||||
return Reference.fromString(this.data.followee, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Follow> {
|
||||
return FollowSchema.parseAsync(json).then((u) => new Follow(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Follow> {
|
||||
return FollowSchema.parseAsync(json).then(
|
||||
(u) => new Follow(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33,21 +41,25 @@ export class FollowAccept extends Entity {
|
|||
|
||||
public constructor(
|
||||
public override data: z.infer<typeof FollowAcceptSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data);
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get follower(): Reference {
|
||||
return Reference.fromString(this.data.follower);
|
||||
return Reference.fromString(this.data.follower, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<FollowAccept> {
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<FollowAccept> {
|
||||
return FollowAcceptSchema.parseAsync(json).then(
|
||||
(u) => new FollowAccept(u),
|
||||
(u) => new FollowAccept(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -57,21 +69,25 @@ export class FollowReject extends Entity {
|
|||
|
||||
public constructor(
|
||||
public override data: z.infer<typeof FollowRejectSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data);
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get follower(): Reference {
|
||||
return Reference.fromString(this.data.follower);
|
||||
return Reference.fromString(this.data.follower, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<FollowReject> {
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<FollowReject> {
|
||||
return FollowRejectSchema.parseAsync(json).then(
|
||||
(u) => new FollowReject(u),
|
||||
(u) => new FollowReject(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -79,19 +95,27 @@ export class FollowReject extends Entity {
|
|||
export class Unfollow extends Entity {
|
||||
public static override name = "Unfollow";
|
||||
|
||||
public constructor(public override data: z.infer<typeof UnfollowSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof UnfollowSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get followee(): Reference {
|
||||
return Reference.fromString(this.data.followee);
|
||||
return Reference.fromString(this.data.followee, this.instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Unfollow> {
|
||||
return UnfollowSchema.parseAsync(json).then((u) => new Unfollow(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Unfollow> {
|
||||
return UnfollowSchema.parseAsync(json).then(
|
||||
(u) => new Unfollow(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export class InstanceMetadata extends Entity {
|
|||
public constructor(
|
||||
public override data: z.infer<typeof InstanceMetadataSchema>,
|
||||
) {
|
||||
super(data);
|
||||
super(data, data.domain);
|
||||
}
|
||||
|
||||
public get logo(): ImageContentFormat | undefined {
|
||||
|
|
|
|||
|
|
@ -7,16 +7,24 @@ import { Entity, Reference } from "./entity.ts";
|
|||
export class Note extends Entity {
|
||||
public static override name = "Note";
|
||||
|
||||
public constructor(public override data: z.infer<typeof NoteSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof NoteSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<Note> {
|
||||
return NoteSchema.parseAsync(json).then((n) => new Note(n));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<Note> {
|
||||
return NoteSchema.parseAsync(json).then(
|
||||
(n) => new Note(n, instanceDomain),
|
||||
);
|
||||
}
|
||||
|
||||
public get author(): Reference {
|
||||
return Reference.fromString(this.data.author);
|
||||
return Reference.fromString(this.data.author, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get group(): Reference | null {
|
||||
|
|
@ -27,20 +35,24 @@ export class Note extends Entity {
|
|||
return null;
|
||||
}
|
||||
|
||||
return Reference.fromString(this.data.group);
|
||||
return Reference.fromString(this.data.group, this.instanceDomain);
|
||||
}
|
||||
|
||||
public get mentions(): Reference[] {
|
||||
return this.data.mentions.map((m) => Reference.fromString(m));
|
||||
return this.data.mentions.map((m) =>
|
||||
Reference.fromString(m, this.instanceDomain),
|
||||
);
|
||||
}
|
||||
|
||||
public get quotes(): Reference | null {
|
||||
return this.data.quotes ? Reference.fromString(this.data.quotes) : null;
|
||||
return this.data.quotes
|
||||
? Reference.fromString(this.data.quotes, this.instanceDomain)
|
||||
: null;
|
||||
}
|
||||
|
||||
public get repliesTo(): Reference | null {
|
||||
return this.data.replies_to
|
||||
? Reference.fromString(this.data.replies_to)
|
||||
? Reference.fromString(this.data.replies_to, this.instanceDomain)
|
||||
: null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,20 @@ import { Entity } from "./entity.ts";
|
|||
export class User extends Entity {
|
||||
public static override name = "User";
|
||||
|
||||
public constructor(public override data: z.infer<typeof UserSchema>) {
|
||||
super(data);
|
||||
public constructor(
|
||||
public override data: z.infer<typeof UserSchema>,
|
||||
instanceDomain: string,
|
||||
) {
|
||||
super(data, instanceDomain);
|
||||
}
|
||||
|
||||
public static override fromJSON(json: JSONObject): Promise<User> {
|
||||
return UserSchema.parseAsync(json).then((u) => new User(u));
|
||||
public static override fromJSON(
|
||||
json: JSONObject,
|
||||
instanceDomain: string,
|
||||
): Promise<User> {
|
||||
return UserSchema.parseAsync(json).then(
|
||||
(u) => new User(u, instanceDomain),
|
||||
);
|
||||
}
|
||||
|
||||
public get avatar(): ImageContentFormat | undefined {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ export class FederationRequester {
|
|||
);
|
||||
}
|
||||
|
||||
const entity = await entityType.fromJSON(jsonData);
|
||||
const entity = await entityType.fromJSON(jsonData, url.hostname);
|
||||
|
||||
return entity as InstanceType<T>;
|
||||
}
|
||||
|
|
@ -150,7 +150,10 @@ export class FederationRequester {
|
|||
entities.push(
|
||||
...collection.data.items.map(
|
||||
(item) =>
|
||||
collectionItemType.fromJSON(item) as InstanceType<T>,
|
||||
collectionItemType.fromJSON(
|
||||
item,
|
||||
reference.domain,
|
||||
) as InstanceType<T>,
|
||||
),
|
||||
);
|
||||
limit -= collection.data.items.length;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ type MaybePromise<T> = T | Promise<T>;
|
|||
export class EntitySorter {
|
||||
private readonly handlers: EntitySorterHandlers = new Map();
|
||||
|
||||
public constructor(private readonly jsonData: JSONObject) {}
|
||||
public constructor(
|
||||
private readonly jsonData: JSONObject,
|
||||
public instanceDomain: string,
|
||||
) {}
|
||||
|
||||
public on<T extends typeof Entity>(
|
||||
entity: T,
|
||||
|
|
@ -45,7 +48,7 @@ export class EntitySorter {
|
|||
|
||||
if (entity) {
|
||||
await this.handlers.get(entity)?.(
|
||||
await entity.fromJSON(this.jsonData),
|
||||
await entity.fromJSON(this.jsonData, this.instanceDomain),
|
||||
);
|
||||
} else {
|
||||
await defaultHandler?.();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue