mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 05:49:16 +01:00
refactor(api): ♻️ Use URL literal instead of strings
This commit is contained in:
parent
99fac323c8
commit
76d1ccc859
50 changed files with 343 additions and 256 deletions
|
|
@ -188,8 +188,8 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiType> {
|
|||
return {
|
||||
id: this.id,
|
||||
shortcode: this.data.shortcode,
|
||||
static_url: proxyUrl(this.media.getUrl()) ?? "", // TODO: Add static version
|
||||
url: proxyUrl(this.media.getUrl()) ?? "",
|
||||
static_url: proxyUrl(this.media.getUrl()).toString(),
|
||||
url: proxyUrl(this.media.getUrl()).toString(),
|
||||
visible_in_picker: this.data.visibleInPicker,
|
||||
category: this.data.category ?? undefined,
|
||||
global: this.data.ownerId === null,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
return this.data.id;
|
||||
}
|
||||
|
||||
public static async fetchMetadata(url: string): Promise<{
|
||||
public static async fetchMetadata(url: URL): Promise<{
|
||||
metadata: InstanceMetadata;
|
||||
protocol: "versia" | "activitypub";
|
||||
}> {
|
||||
|
|
@ -184,7 +184,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
}
|
||||
|
||||
private static async fetchActivityPubMetadata(
|
||||
url: string,
|
||||
url: URL,
|
||||
): Promise<InstanceMetadata | null> {
|
||||
const origin = new URL(url).origin;
|
||||
const wellKnownUrl = new URL("/.well-known/nodeinfo", origin);
|
||||
|
|
@ -311,18 +311,18 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
|
||||
public static resolveFromHost(host: string): Promise<Instance> {
|
||||
if (host.startsWith("http")) {
|
||||
const url = new URL(host).host;
|
||||
const url = new URL(host);
|
||||
|
||||
return Instance.resolve(url);
|
||||
}
|
||||
|
||||
const url = new URL(`https://${host}`);
|
||||
|
||||
return Instance.resolve(url.origin);
|
||||
return Instance.resolve(url);
|
||||
}
|
||||
|
||||
public static async resolve(url: string): Promise<Instance> {
|
||||
const host = new URL(url).host;
|
||||
public static async resolve(url: URL): Promise<Instance> {
|
||||
const host = url.host;
|
||||
|
||||
const existingInstance = await Instance.fromSql(
|
||||
eq(Instances.baseUrl, host),
|
||||
|
|
@ -352,7 +352,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
const logger = getLogger(["federation", "resolvers"]);
|
||||
|
||||
const output = await Instance.fetchMetadata(
|
||||
`https://${this.data.baseUrl}`,
|
||||
new URL(`https://${this.data.baseUrl}`),
|
||||
);
|
||||
|
||||
if (!output) {
|
||||
|
|
|
|||
|
|
@ -167,15 +167,17 @@ export class Like extends BaseInterface<typeof Likes, LikeType> {
|
|||
id: this.data.id,
|
||||
author: User.getUri(
|
||||
this.data.liker.id,
|
||||
this.data.liker.uri,
|
||||
config.http.base_url,
|
||||
),
|
||||
this.data.liker.uri ? new URL(this.data.liker.uri) : null,
|
||||
).toString(),
|
||||
type: "pub.versia:likes/Like",
|
||||
created_at: new Date(this.data.createdAt).toISOString(),
|
||||
liked: Note.getUri(
|
||||
this.data.liked.id,
|
||||
this.data.liked.uri,
|
||||
) as string,
|
||||
liked:
|
||||
Note.getUri(
|
||||
this.data.liked.id,
|
||||
this.data.liked.uri
|
||||
? new URL(this.data.liked.uri)
|
||||
: undefined,
|
||||
)?.toString() ?? "",
|
||||
uri: this.getUri().toString(),
|
||||
};
|
||||
}
|
||||
|
|
@ -187,9 +189,12 @@ export class Like extends BaseInterface<typeof Likes, LikeType> {
|
|||
created_at: new Date().toISOString(),
|
||||
author: User.getUri(
|
||||
unliker?.id ?? this.data.liker.id,
|
||||
unliker?.data.uri ?? this.data.liker.uri,
|
||||
config.http.base_url,
|
||||
),
|
||||
unliker?.data.uri
|
||||
? new URL(unliker.data.uri)
|
||||
: this.data.liker.uri
|
||||
? new URL(this.data.liker.uri)
|
||||
: null,
|
||||
).toString(),
|
||||
deleted_type: "pub.versia:likes/Like",
|
||||
deleted: this.getUri().toString(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ export class Media extends BaseInterface<typeof Medias> {
|
|||
|
||||
const url = Media.getUrl(path);
|
||||
|
||||
let thumbnailUrl = "";
|
||||
let thumbnailUrl: URL | null = null;
|
||||
|
||||
if (options?.thumbnail) {
|
||||
const { path } = await Media.upload(options.thumbnail);
|
||||
|
|
@ -220,11 +220,16 @@ export class Media extends BaseInterface<typeof Medias> {
|
|||
const content = await Media.fileToContentFormat(file, url, {
|
||||
description: options?.description,
|
||||
});
|
||||
const thumbnailContent = options?.thumbnail
|
||||
? await Media.fileToContentFormat(options.thumbnail, thumbnailUrl, {
|
||||
description: options?.description,
|
||||
})
|
||||
: undefined;
|
||||
const thumbnailContent =
|
||||
thumbnailUrl && options?.thumbnail
|
||||
? await Media.fileToContentFormat(
|
||||
options.thumbnail,
|
||||
thumbnailUrl,
|
||||
{
|
||||
description: options?.description,
|
||||
},
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const newAttachment = await Media.insert({
|
||||
content,
|
||||
|
|
@ -246,6 +251,12 @@ export class Media extends BaseInterface<typeof Medias> {
|
|||
return newAttachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a new media attachment from a URL
|
||||
* @param uri
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
public static async fromUrl(
|
||||
uri: URL,
|
||||
options?: {
|
||||
|
|
@ -377,20 +388,21 @@ export class Media extends BaseInterface<typeof Medias> {
|
|||
return this.data.id;
|
||||
}
|
||||
|
||||
public static getUrl(name: string): string {
|
||||
public static getUrl(name: string): URL {
|
||||
if (config.media.backend === MediaBackendType.Local) {
|
||||
return new URL(`/media/${name}`, config.http.base_url).toString();
|
||||
return new URL(`/media/${name}`, config.http.base_url);
|
||||
}
|
||||
if (config.media.backend === MediaBackendType.S3) {
|
||||
return new URL(`/${name}`, config.s3?.public_url).toString();
|
||||
return new URL(`/${name}`, config.s3?.public_url);
|
||||
}
|
||||
return "";
|
||||
|
||||
throw new Error("Unknown media backend");
|
||||
}
|
||||
|
||||
public getUrl(): string {
|
||||
public getUrl(): URL {
|
||||
const type = this.getPreferredMimeType();
|
||||
|
||||
return this.data.content[type]?.content;
|
||||
return new URL(this.data.content[type]?.content ?? "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -461,7 +473,7 @@ export class Media extends BaseInterface<typeof Medias> {
|
|||
*/
|
||||
public static async fileToContentFormat(
|
||||
file: File,
|
||||
uri: string,
|
||||
uri: URL,
|
||||
options?: Partial<{
|
||||
description: string;
|
||||
}>,
|
||||
|
|
@ -475,7 +487,7 @@ export class Media extends BaseInterface<typeof Medias> {
|
|||
// Thumbhash should be added in a worker after the file is uploaded
|
||||
return {
|
||||
[file.type]: {
|
||||
content: uri,
|
||||
content: uri.toString(),
|
||||
remote: true,
|
||||
hash: {
|
||||
sha256: hash,
|
||||
|
|
@ -528,9 +540,11 @@ export class Media extends BaseInterface<typeof Medias> {
|
|||
return {
|
||||
id: this.data.id,
|
||||
type: this.getMastodonType(),
|
||||
url: proxyUrl(data.content) ?? "",
|
||||
url: proxyUrl(new URL(data.content)).toString(),
|
||||
remote_url: null,
|
||||
preview_url: proxyUrl(thumbnailData?.content),
|
||||
preview_url: thumbnailData?.content
|
||||
? proxyUrl(new URL(thumbnailData.content)).toString()
|
||||
: null,
|
||||
text_url: null,
|
||||
meta: this.toApiMeta(),
|
||||
description: data.description || null,
|
||||
|
|
|
|||
|
|
@ -646,17 +646,17 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
* @param uri - The URI of the note to resolve
|
||||
* @returns The resolved note
|
||||
*/
|
||||
public static async resolve(uri: string): Promise<Note | null> {
|
||||
public static async resolve(uri: URL): Promise<Note | null> {
|
||||
// Check if note not already in database
|
||||
const foundNote = await Note.fromSql(eq(Notes.uri, uri));
|
||||
const foundNote = await Note.fromSql(eq(Notes.uri, uri.toString()));
|
||||
|
||||
if (foundNote) {
|
||||
return foundNote;
|
||||
}
|
||||
|
||||
// Check if URI is of a local note
|
||||
if (uri.startsWith(config.http.base_url)) {
|
||||
const uuid = uri.match(idValidator);
|
||||
if (uri.origin === config.http.base_url.origin) {
|
||||
const uuid = uri.pathname.match(idValidator);
|
||||
|
||||
if (!uuid?.[0]) {
|
||||
throw new Error(
|
||||
|
|
@ -675,7 +675,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
* @param uri - The URI of the note to save
|
||||
* @returns The saved note, or null if the note could not be fetched
|
||||
*/
|
||||
public static async fetchFromRemote(uri: string): Promise<Note | null> {
|
||||
public static async fetchFromRemote(uri: URL): Promise<Note | null> {
|
||||
const instance = await Instance.resolve(uri);
|
||||
|
||||
if (!instance) {
|
||||
|
|
@ -691,7 +691,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
|
||||
const note = await new EntityValidator().Note(data);
|
||||
|
||||
const author = await User.resolve(note.author);
|
||||
const author = await User.resolve(new URL(note.author));
|
||||
|
||||
if (!author) {
|
||||
throw new Error("Invalid object author");
|
||||
|
|
@ -773,15 +773,15 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
uri: note.uri,
|
||||
mentions: await Promise.all(
|
||||
(note.mentions ?? [])
|
||||
.map((mention) => User.resolve(mention))
|
||||
.map((mention) => User.resolve(new URL(mention)))
|
||||
.filter((mention) => mention !== null) as Promise<User>[],
|
||||
),
|
||||
mediaAttachments: attachments,
|
||||
replyId: note.replies_to
|
||||
? (await Note.resolve(note.replies_to))?.data.id
|
||||
? (await Note.resolve(new URL(note.replies_to)))?.data.id
|
||||
: undefined,
|
||||
quoteId: note.quotes
|
||||
? (await Note.resolve(note.quotes))?.data.id
|
||||
? (await Note.resolve(new URL(note.quotes)))?.data.id
|
||||
: undefined,
|
||||
};
|
||||
|
||||
|
|
@ -908,7 +908,10 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
mention.username,
|
||||
mention.instance?.baseUrl,
|
||||
),
|
||||
url: User.getUri(mention.id, mention.uri, config.http.base_url),
|
||||
url: User.getUri(
|
||||
mention.id,
|
||||
mention.uri ? new URL(mention.uri) : null,
|
||||
).toString(),
|
||||
username: mention.username,
|
||||
})),
|
||||
language: null,
|
||||
|
|
@ -927,9 +930,9 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
sensitive: data.sensitive,
|
||||
spoiler_text: data.spoilerText,
|
||||
tags: [],
|
||||
uri: data.uri || this.getUri(),
|
||||
uri: data.uri || this.getUri().toString(),
|
||||
visibility: data.visibility as ApiStatus["visibility"],
|
||||
url: data.uri || this.getMastoUri(),
|
||||
url: data.uri || this.getMastoUri().toString(),
|
||||
bookmarked: false,
|
||||
quote: data.quotingId
|
||||
? ((await Note.fromId(data.quotingId, userFetching?.id).then(
|
||||
|
|
@ -944,14 +947,11 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
};
|
||||
}
|
||||
|
||||
public getUri(): string {
|
||||
return this.data.uri || localObjectUri(this.id);
|
||||
public getUri(): URL {
|
||||
return new URL(this.data.uri || localObjectUri(this.id));
|
||||
}
|
||||
|
||||
public static getUri(
|
||||
id: string | null,
|
||||
uri?: string | null,
|
||||
): string | null {
|
||||
public static getUri(id: string | null, uri?: URL | null): URL | null {
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -962,11 +962,11 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
* Get the frontend URI of this note
|
||||
* @returns The frontend URI of this note
|
||||
*/
|
||||
public getMastoUri(): string {
|
||||
public getMastoUri(): URL {
|
||||
return new URL(
|
||||
`/@${this.author.data.username}/${this.id}`,
|
||||
config.http.base_url,
|
||||
).toString();
|
||||
);
|
||||
}
|
||||
|
||||
public deleteToVersia(): VersiaDelete {
|
||||
|
|
@ -975,9 +975,9 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
return {
|
||||
type: "Delete",
|
||||
id,
|
||||
author: this.author.getUri(),
|
||||
author: this.author.getUri().toString(),
|
||||
deleted_type: "Note",
|
||||
deleted: this.getUri(),
|
||||
deleted: this.getUri().toString(),
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
|
@ -992,8 +992,8 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
type: "Note",
|
||||
created_at: new Date(status.createdAt).toISOString(),
|
||||
id: status.id,
|
||||
author: this.author.getUri(),
|
||||
uri: this.getUri(),
|
||||
author: this.author.getUri().toString(),
|
||||
uri: this.getUri().toString(),
|
||||
content: {
|
||||
"text/html": {
|
||||
content: status.content,
|
||||
|
|
@ -1009,12 +1009,19 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
|||
),
|
||||
is_sensitive: status.sensitive,
|
||||
mentions: status.mentions.map((mention) =>
|
||||
User.getUri(mention.id, mention.uri, config.http.base_url),
|
||||
User.getUri(
|
||||
mention.id,
|
||||
mention.uri ? new URL(mention.uri) : null,
|
||||
).toString(),
|
||||
),
|
||||
quotes:
|
||||
Note.getUri(status.quotingId, status.quote?.uri) ?? undefined,
|
||||
replies_to:
|
||||
Note.getUri(status.replyId, status.reply?.uri) ?? undefined,
|
||||
quotes: Note.getUri(
|
||||
status.quotingId,
|
||||
status.quote?.uri ? new URL(status.quote.uri) : null,
|
||||
)?.toString(),
|
||||
replies_to: Note.getUri(
|
||||
status.replyId,
|
||||
status.reply?.uri ? new URL(status.reply.uri) : null,
|
||||
)?.toString(),
|
||||
subject: status.spoilerText,
|
||||
// TODO: Refactor as part of groups
|
||||
group: status.visibility === "public" ? "public" : "followers",
|
||||
|
|
|
|||
|
|
@ -179,14 +179,13 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
|
|||
};
|
||||
}
|
||||
|
||||
public getUri(baseUrl: string): string {
|
||||
return (
|
||||
this.data.uri ||
|
||||
new URL(
|
||||
`/objects/${this.data.noteId}/reactions/${this.id}`,
|
||||
baseUrl,
|
||||
).toString()
|
||||
);
|
||||
public getUri(baseUrl: URL): URL {
|
||||
return this.data.uri
|
||||
? new URL(this.data.uri)
|
||||
: new URL(
|
||||
`/objects/${this.data.noteId}/reactions/${this.id}`,
|
||||
baseUrl,
|
||||
);
|
||||
}
|
||||
|
||||
public isLocal(): boolean {
|
||||
|
|
@ -203,19 +202,21 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
|
|||
}
|
||||
|
||||
return {
|
||||
uri: this.getUri(config.http.base_url),
|
||||
uri: this.getUri(config.http.base_url).toString(),
|
||||
type: "pub.versia:reactions/Reaction",
|
||||
author: User.getUri(
|
||||
this.data.authorId,
|
||||
this.data.author.uri,
|
||||
config.http.base_url,
|
||||
),
|
||||
this.data.author.uri ? new URL(this.data.author.uri) : null,
|
||||
).toString(),
|
||||
created_at: new Date(this.data.createdAt).toISOString(),
|
||||
id: this.id,
|
||||
object: Note.getUri(
|
||||
this.data.note.id,
|
||||
this.data.note.uri,
|
||||
) as string,
|
||||
object:
|
||||
Note.getUri(
|
||||
this.data.note.id,
|
||||
this.data.note.uri
|
||||
? new URL(this.data.note.uri)
|
||||
: undefined,
|
||||
)?.toString() ?? "",
|
||||
content: this.hasCustomEmoji()
|
||||
? `:${this.data.emoji?.shortcode}:`
|
||||
: this.data.emojiText || "",
|
||||
|
|
|
|||
|
|
@ -227,7 +227,9 @@ export class Role extends BaseInterface<typeof Roles> {
|
|||
priority: this.data.priority,
|
||||
description: this.data.description ?? undefined,
|
||||
visible: this.data.visible,
|
||||
icon: proxyUrl(this.data.icon) ?? undefined,
|
||||
icon: this.data.icon
|
||||
? proxyUrl(new URL(this.data.icon)).toString()
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
public static getNoteTimeline(
|
||||
sql: SQL<unknown> | undefined,
|
||||
limit: number,
|
||||
url: string,
|
||||
url: URL,
|
||||
userId?: string,
|
||||
): Promise<{ link: string; objects: Note[] }> {
|
||||
return new Timeline<Note>(TimelineType.Note).fetchTimeline(
|
||||
|
|
@ -31,7 +31,7 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
public static getUserTimeline(
|
||||
sql: SQL<unknown> | undefined,
|
||||
limit: number,
|
||||
url: string,
|
||||
url: URL,
|
||||
): Promise<{ link: string; objects: User[] }> {
|
||||
return new Timeline<User>(TimelineType.User).fetchTimeline(
|
||||
sql,
|
||||
|
|
@ -43,7 +43,7 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
public static getNotificationTimeline(
|
||||
sql: SQL<unknown> | undefined,
|
||||
limit: number,
|
||||
url: string,
|
||||
url: URL,
|
||||
userId?: string,
|
||||
): Promise<{ link: string; objects: Notification[] }> {
|
||||
return new Timeline<Notification>(
|
||||
|
|
@ -85,14 +85,11 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
|
||||
private async fetchLinkHeader(
|
||||
objects: Type[],
|
||||
url: string,
|
||||
url: URL,
|
||||
limit: number,
|
||||
): Promise<string> {
|
||||
const linkHeader: string[] = [];
|
||||
const urlWithoutQuery = new URL(
|
||||
new URL(url).pathname,
|
||||
config.http.base_url,
|
||||
).toString();
|
||||
const urlWithoutQuery = new URL(url.pathname, config.http.base_url);
|
||||
|
||||
if (objects.length > 0) {
|
||||
switch (this.type) {
|
||||
|
|
@ -130,7 +127,7 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
|
||||
private static async fetchNoteLinkHeader(
|
||||
notes: Note[],
|
||||
urlWithoutQuery: string,
|
||||
urlWithoutQuery: URL,
|
||||
limit: number,
|
||||
): Promise<string[]> {
|
||||
const linkHeader: string[] = [];
|
||||
|
|
@ -158,7 +155,7 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
|
||||
private static async fetchUserLinkHeader(
|
||||
users: User[],
|
||||
urlWithoutQuery: string,
|
||||
urlWithoutQuery: URL,
|
||||
limit: number,
|
||||
): Promise<string[]> {
|
||||
const linkHeader: string[] = [];
|
||||
|
|
@ -186,7 +183,7 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
|
||||
private static async fetchNotificationLinkHeader(
|
||||
notifications: Notification[],
|
||||
urlWithoutQuery: string,
|
||||
urlWithoutQuery: URL,
|
||||
limit: number,
|
||||
): Promise<string[]> {
|
||||
const linkHeader: string[] = [];
|
||||
|
|
@ -220,7 +217,7 @@ export class Timeline<Type extends Note | User | Notification> {
|
|||
private async fetchTimeline(
|
||||
sql: SQL<unknown> | undefined,
|
||||
limit: number,
|
||||
url: string,
|
||||
url: URL,
|
||||
userId?: string,
|
||||
): Promise<{ link: string; objects: Type[] }> {
|
||||
const objects = await this.fetchObjects(sql, limit, userId);
|
||||
|
|
|
|||
|
|
@ -228,19 +228,14 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return !this.isLocal();
|
||||
}
|
||||
|
||||
public getUri(): string {
|
||||
return (
|
||||
this.data.uri ||
|
||||
new URL(`/users/${this.data.id}`, config.http.base_url).toString()
|
||||
);
|
||||
public getUri(): URL {
|
||||
return this.data.uri
|
||||
? new URL(this.data.uri)
|
||||
: new URL(`/users/${this.data.id}`, config.http.base_url);
|
||||
}
|
||||
|
||||
public static getUri(
|
||||
id: string,
|
||||
uri: string | null,
|
||||
baseUrl: string,
|
||||
): string {
|
||||
return uri || new URL(`/users/${id}`, baseUrl).toString();
|
||||
public static getUri(id: string, uri: URL | null): URL {
|
||||
return uri ? uri : new URL(`/users/${id}`, config.http.base_url);
|
||||
}
|
||||
|
||||
public hasPermission(permission: RolePermissions): boolean {
|
||||
|
|
@ -290,8 +285,8 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
entity: {
|
||||
type: "Follow",
|
||||
id: crypto.randomUUID(),
|
||||
author: this.getUri(),
|
||||
followee: otherUser.getUri(),
|
||||
author: this.getUri().toString(),
|
||||
followee: otherUser.getUri().toString(),
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
recipientId: otherUser.id,
|
||||
|
|
@ -329,9 +324,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return {
|
||||
type: "Unfollow",
|
||||
id,
|
||||
author: this.getUri(),
|
||||
author: this.getUri().toString(),
|
||||
created_at: new Date().toISOString(),
|
||||
followee: followee.getUri(),
|
||||
followee: followee.getUri().toString(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -347,9 +342,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
const entity: VersiaFollowAccept = {
|
||||
type: "FollowAccept",
|
||||
id: crypto.randomUUID(),
|
||||
author: this.getUri(),
|
||||
author: this.getUri().toString(),
|
||||
created_at: new Date().toISOString(),
|
||||
follower: follower.getUri(),
|
||||
follower: follower.getUri().toString(),
|
||||
};
|
||||
|
||||
await deliveryQueue.add(DeliveryJobType.FederateEntity, {
|
||||
|
|
@ -371,9 +366,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
const entity: VersiaFollowReject = {
|
||||
type: "FollowReject",
|
||||
id: crypto.randomUUID(),
|
||||
author: this.getUri(),
|
||||
author: this.getUri().toString(),
|
||||
created_at: new Date().toISOString(),
|
||||
follower: follower.getUri(),
|
||||
follower: follower.getUri().toString(),
|
||||
};
|
||||
|
||||
await deliveryQueue.add(DeliveryJobType.FederateEntity, {
|
||||
|
|
@ -390,19 +385,21 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
* @param hostname
|
||||
* @returns URI, or null if not found
|
||||
*/
|
||||
public static webFinger(
|
||||
public static async webFinger(
|
||||
manager: FederationRequester,
|
||||
username: string,
|
||||
hostname: string,
|
||||
): Promise<string | null> {
|
||||
): Promise<URL | null> {
|
||||
try {
|
||||
return manager.webFinger(username, hostname);
|
||||
return new URL(await manager.webFinger(username, hostname));
|
||||
} catch {
|
||||
try {
|
||||
return manager.webFinger(
|
||||
username,
|
||||
hostname,
|
||||
"application/activity+json",
|
||||
return new URL(
|
||||
await manager.webFinger(
|
||||
username,
|
||||
hostname,
|
||||
"application/activity+json",
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
return Promise.resolve(null);
|
||||
|
|
@ -511,7 +508,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
id: issuer.id,
|
||||
name: issuer.name,
|
||||
url: issuer.url,
|
||||
icon: proxyUrl(issuer.icon) || undefined,
|
||||
icon: issuer.icon
|
||||
? proxyUrl(new URL(issuer.icon)).toString()
|
||||
: undefined,
|
||||
server_id: account.serverId,
|
||||
};
|
||||
})
|
||||
|
|
@ -662,11 +661,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public static async fetchFromRemote(uri: string): Promise<User | null> {
|
||||
if (!URL.canParse(uri)) {
|
||||
throw new Error(`Invalid URI: ${uri}`);
|
||||
}
|
||||
|
||||
public static async fetchFromRemote(uri: URL): Promise<User | null> {
|
||||
const instance = await Instance.resolve(uri);
|
||||
|
||||
if (!instance) {
|
||||
|
|
@ -684,19 +679,19 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
|
||||
const bridgeUri = new URL(
|
||||
`/apbridge/versia/query?${new URLSearchParams({
|
||||
user_url: uri,
|
||||
user_url: uri.toString(),
|
||||
})}`,
|
||||
config.federation.bridge.url,
|
||||
);
|
||||
|
||||
return await User.saveFromVersia(bridgeUri.toString(), instance);
|
||||
return await User.saveFromVersia(bridgeUri, instance);
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported protocol: ${instance.data.protocol}`);
|
||||
}
|
||||
|
||||
private static async saveFromVersia(
|
||||
uri: string,
|
||||
uri: URL,
|
||||
instance: Instance,
|
||||
): Promise<User> {
|
||||
const requester = await User.getFederationRequester();
|
||||
|
|
@ -859,19 +854,19 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return user;
|
||||
}
|
||||
|
||||
public static async resolve(uri: string): Promise<User | null> {
|
||||
public static async resolve(uri: URL): Promise<User | null> {
|
||||
getLogger(["federation", "resolvers"])
|
||||
.debug`Resolving user ${chalk.gray(uri)}`;
|
||||
// Check if user not already in database
|
||||
const foundUser = await User.fromSql(eq(Users.uri, uri));
|
||||
const foundUser = await User.fromSql(eq(Users.uri, uri.toString()));
|
||||
|
||||
if (foundUser) {
|
||||
return foundUser;
|
||||
}
|
||||
|
||||
// Check if URI is of a local user
|
||||
if (uri.startsWith(config.http.base_url)) {
|
||||
const uuid = uri.match(idValidator);
|
||||
if (uri.origin === config.http.base_url.origin) {
|
||||
const uuid = uri.href.match(idValidator);
|
||||
|
||||
if (!uuid?.[0]) {
|
||||
throw new Error(
|
||||
|
|
@ -893,11 +888,13 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
* @param config The config to use
|
||||
* @returns The raw URL for the user's avatar
|
||||
*/
|
||||
public getAvatarUrl(config: Config): string {
|
||||
public getAvatarUrl(config: Config): URL {
|
||||
if (!this.avatar) {
|
||||
return (
|
||||
config.defaults.avatar ||
|
||||
`https://api.dicebear.com/8.x/${config.defaults.placeholder_style}/svg?seed=${this.data.username}`
|
||||
new URL(
|
||||
`https://api.dicebear.com/8.x/${config.defaults.placeholder_style}/svg?seed=${this.data.username}`,
|
||||
)
|
||||
);
|
||||
}
|
||||
return this.avatar?.getUrl();
|
||||
|
|
@ -988,9 +985,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
* @param config The config to use
|
||||
* @returns The raw URL for the user's header
|
||||
*/
|
||||
public getHeaderUrl(config: Config): string {
|
||||
public getHeaderUrl(config: Config): URL | null {
|
||||
if (!this.header) {
|
||||
return config.defaults.header || "";
|
||||
return config.defaults.header ?? null;
|
||||
}
|
||||
return this.header.getUrl();
|
||||
}
|
||||
|
|
@ -1052,7 +1049,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
*/
|
||||
public async sign(
|
||||
entity: KnownEntity | Collection,
|
||||
signatureUrl: string | URL,
|
||||
signatureUrl: URL,
|
||||
signatureMethod: HttpVerb = "POST",
|
||||
): Promise<{
|
||||
headers: Headers;
|
||||
|
|
@ -1065,7 +1062,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
|
||||
const output = await signatureConstructor.sign(
|
||||
signatureMethod,
|
||||
new URL(signatureUrl),
|
||||
signatureUrl,
|
||||
JSON.stringify(entity),
|
||||
);
|
||||
|
||||
|
|
@ -1155,7 +1152,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
);
|
||||
}
|
||||
|
||||
const { headers } = await this.sign(entity, inbox);
|
||||
const { headers } = await this.sign(entity, new URL(inbox));
|
||||
|
||||
try {
|
||||
await new FederationRequester().post(inbox, entity, {
|
||||
|
|
@ -1185,12 +1182,14 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
username: user.username,
|
||||
display_name: user.displayName,
|
||||
note: user.note,
|
||||
uri: this.getUri(),
|
||||
uri: this.getUri().toString(),
|
||||
url:
|
||||
user.uri ||
|
||||
new URL(`/@${user.username}`, config.http.base_url).toString(),
|
||||
avatar: proxyUrl(this.getAvatarUrl(config)) ?? "",
|
||||
header: proxyUrl(this.getHeaderUrl(config)) ?? "",
|
||||
avatar: proxyUrl(this.getAvatarUrl(config)).toString(),
|
||||
header: this.getHeaderUrl(config)
|
||||
? proxyUrl(this.getHeaderUrl(config) as URL).toString()
|
||||
: "",
|
||||
locked: user.isLocked,
|
||||
created_at: new Date(user.createdAt).toISOString(),
|
||||
followers_count: user.followerCount,
|
||||
|
|
@ -1204,8 +1203,10 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
bot: user.isBot,
|
||||
source: isOwnAccount ? user.source : undefined,
|
||||
// TODO: Add static avatar and header
|
||||
avatar_static: proxyUrl(this.getAvatarUrl(config)) ?? "",
|
||||
header_static: proxyUrl(this.getHeaderUrl(config)) ?? "",
|
||||
avatar_static: proxyUrl(this.getAvatarUrl(config)).toString(),
|
||||
header_static: this.getHeaderUrl(config)
|
||||
? proxyUrl(this.getHeaderUrl(config) as URL).toString()
|
||||
: "",
|
||||
acct: this.getAcct(),
|
||||
// TODO: Add these fields
|
||||
limited: false,
|
||||
|
|
@ -1258,7 +1259,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return {
|
||||
id: user.id,
|
||||
type: "User",
|
||||
uri: this.getUri(),
|
||||
uri: this.getUri().toString(),
|
||||
bio: {
|
||||
"text/html": {
|
||||
content: user.note,
|
||||
|
|
@ -1308,11 +1309,12 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
this.getAvatarUrl(config),
|
||||
this.data.source.avatar?.content_type,
|
||||
) ?? undefined,
|
||||
header:
|
||||
urlToContentFormat(
|
||||
this.getHeaderUrl(config),
|
||||
this.data.source.header?.content_type,
|
||||
) ?? undefined,
|
||||
header: this.getHeaderUrl(config)
|
||||
? (urlToContentFormat(
|
||||
this.getHeaderUrl(config) as URL,
|
||||
this.data.source.header?.content_type,
|
||||
) ?? undefined)
|
||||
: undefined,
|
||||
display_name: user.displayName,
|
||||
fields: user.fields,
|
||||
public_key: {
|
||||
|
|
@ -1335,7 +1337,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
|
||||
public toMention(): ApiMention {
|
||||
return {
|
||||
url: this.getUri(),
|
||||
url: this.getUri().toString(),
|
||||
username: this.data.username,
|
||||
acct: this.getAcct(),
|
||||
id: this.id,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ mock.module("~/packages/config-manager/index.ts", () => ({
|
|||
|
||||
describe("InboxProcessor", () => {
|
||||
let mockRequest: {
|
||||
url: string;
|
||||
url: URL;
|
||||
method: string;
|
||||
body: string;
|
||||
};
|
||||
|
|
@ -95,7 +95,7 @@ describe("InboxProcessor", () => {
|
|||
|
||||
// Setup basic mock context
|
||||
mockRequest = {
|
||||
url: "https://test.com",
|
||||
url: new URL("https://test.com"),
|
||||
method: "POST",
|
||||
body: "test-body",
|
||||
};
|
||||
|
|
@ -196,7 +196,10 @@ describe("InboxProcessor", () => {
|
|||
|
||||
describe("processNote", () => {
|
||||
test("successfully processes valid note", async () => {
|
||||
const mockNote = { author: "test-author" };
|
||||
const mockNote = {
|
||||
author: "https://example.com",
|
||||
uri: "https://note.example.com",
|
||||
};
|
||||
const mockAuthor = { id: "test-id" };
|
||||
const mockInstance = { id: "test-id" };
|
||||
|
||||
|
|
@ -209,7 +212,9 @@ describe("InboxProcessor", () => {
|
|||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const result = await processor["processNote"]();
|
||||
|
||||
expect(User.resolve).toHaveBeenCalledWith("test-author");
|
||||
expect(User.resolve).toHaveBeenCalledWith(
|
||||
new URL("https://example.com"),
|
||||
);
|
||||
expect(Note.fromVersia).toHaveBeenCalledWith(
|
||||
mockNote,
|
||||
mockAuthor,
|
||||
|
|
@ -220,7 +225,13 @@ describe("InboxProcessor", () => {
|
|||
|
||||
test("returns 404 when author not found", async () => {
|
||||
User.resolve = jest.fn().mockResolvedValue(null);
|
||||
const mockNote = {
|
||||
author: "https://example.com",
|
||||
uri: "https://note.example.com",
|
||||
};
|
||||
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private variable
|
||||
processor["body"] = mockNote as VersiaNote;
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const result = await processor["processNote"]();
|
||||
|
||||
|
|
@ -233,8 +244,8 @@ describe("InboxProcessor", () => {
|
|||
describe("processFollowRequest", () => {
|
||||
test("successfully processes follow request for unlocked account", async () => {
|
||||
const mockFollow = {
|
||||
author: "test-author",
|
||||
followee: "test-followee",
|
||||
author: "https://example.com",
|
||||
followee: "https://followee.note.com",
|
||||
};
|
||||
const mockAuthor = { id: "author-id" };
|
||||
const mockFollowee = {
|
||||
|
|
@ -273,7 +284,13 @@ describe("InboxProcessor", () => {
|
|||
|
||||
test("returns 404 when author not found", async () => {
|
||||
User.resolve = jest.fn().mockResolvedValue(null);
|
||||
const mockFollow = {
|
||||
author: "https://example.com",
|
||||
followee: "https://followee.note.com",
|
||||
};
|
||||
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private variable
|
||||
processor["body"] = mockFollow as unknown as Entity;
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const result = await processor["processFollowRequest"]();
|
||||
|
||||
|
|
@ -287,7 +304,7 @@ describe("InboxProcessor", () => {
|
|||
test("successfully deletes a note", async () => {
|
||||
const mockDelete = {
|
||||
deleted_type: "Note",
|
||||
deleted: "test-uri",
|
||||
deleted: "https://example.com",
|
||||
};
|
||||
const mockNote = {
|
||||
delete: jest.fn(),
|
||||
|
|
@ -307,7 +324,7 @@ describe("InboxProcessor", () => {
|
|||
test("returns 404 when note not found", async () => {
|
||||
const mockDelete = {
|
||||
deleted_type: "Note",
|
||||
deleted: "test-uri",
|
||||
deleted: "https://example.com",
|
||||
};
|
||||
|
||||
Note.fromSql = jest.fn().mockResolvedValue(null);
|
||||
|
|
@ -331,9 +348,9 @@ describe("InboxProcessor", () => {
|
|||
describe("processLikeRequest", () => {
|
||||
test("successfully processes like request", async () => {
|
||||
const mockLike = {
|
||||
author: "test-author",
|
||||
liked: "test-note",
|
||||
uri: "test-uri",
|
||||
author: "https://example.com",
|
||||
liked: "https://example.note.com",
|
||||
uri: "https://example.com",
|
||||
};
|
||||
const mockAuthor = {
|
||||
like: jest.fn(),
|
||||
|
|
@ -348,13 +365,23 @@ describe("InboxProcessor", () => {
|
|||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const result = await processor["processLikeRequest"]();
|
||||
|
||||
expect(mockAuthor.like).toHaveBeenCalledWith(mockNote, "test-uri");
|
||||
expect(mockAuthor.like).toHaveBeenCalledWith(
|
||||
mockNote,
|
||||
"https://example.com",
|
||||
);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
test("returns 404 when author not found", async () => {
|
||||
User.resolve = jest.fn().mockResolvedValue(null);
|
||||
const mockLike = {
|
||||
author: "https://example.com",
|
||||
liked: "https://example.note.com",
|
||||
uri: "https://example.com",
|
||||
};
|
||||
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private variable
|
||||
processor["body"] = mockLike as unknown as Entity;
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const result = await processor["processLikeRequest"]();
|
||||
|
||||
|
|
@ -367,7 +394,7 @@ describe("InboxProcessor", () => {
|
|||
describe("processUserRequest", () => {
|
||||
test("successfully processes user update", async () => {
|
||||
const mockUser = {
|
||||
uri: "test-uri",
|
||||
uri: "https://example.com",
|
||||
};
|
||||
const mockUpdatedUser = { id: "user-id" };
|
||||
|
||||
|
|
@ -378,13 +405,20 @@ describe("InboxProcessor", () => {
|
|||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const result = await processor["processUserRequest"]();
|
||||
|
||||
expect(User.fetchFromRemote).toHaveBeenCalledWith("test-uri");
|
||||
expect(User.fetchFromRemote).toHaveBeenCalledWith(
|
||||
new URL("https://example.com"),
|
||||
);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
test("returns 500 when update fails", async () => {
|
||||
const mockUser = {
|
||||
uri: "https://example.com",
|
||||
};
|
||||
User.fetchFromRemote = jest.fn().mockResolvedValue(null);
|
||||
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private variable
|
||||
processor["body"] = mockUser as unknown as Entity;
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const result = await processor["processUserRequest"]();
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function isDefederated(hostname: string): boolean {
|
|||
|
||||
return (
|
||||
config.federation.blocked.find(
|
||||
(blocked) => pattern.match(blocked) !== null,
|
||||
(blocked) => pattern.match(blocked.toString()) !== null,
|
||||
) !== undefined
|
||||
);
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ export class InboxProcessor {
|
|||
*/
|
||||
public constructor(
|
||||
private request: {
|
||||
url: string;
|
||||
url: URL;
|
||||
method: string;
|
||||
body: string;
|
||||
},
|
||||
|
|
@ -269,8 +269,8 @@ export class InboxProcessor {
|
|||
*/
|
||||
private async processNote(): Promise<Response | null> {
|
||||
const note = this.body as VersiaNote;
|
||||
const author = await User.resolve(note.author);
|
||||
const instance = await Instance.resolve(note.uri);
|
||||
const author = await User.resolve(new URL(note.author));
|
||||
const instance = await Instance.resolve(new URL(note.uri));
|
||||
|
||||
if (!instance) {
|
||||
return Response.json(
|
||||
|
|
@ -298,8 +298,8 @@ export class InboxProcessor {
|
|||
*/
|
||||
private async processFollowRequest(): Promise<Response | null> {
|
||||
const follow = this.body as unknown as VersiaFollow;
|
||||
const author = await User.resolve(follow.author);
|
||||
const followee = await User.resolve(follow.followee);
|
||||
const author = await User.resolve(new URL(follow.author));
|
||||
const followee = await User.resolve(new URL(follow.followee));
|
||||
|
||||
if (!author) {
|
||||
return Response.json(
|
||||
|
|
@ -352,8 +352,8 @@ export class InboxProcessor {
|
|||
*/
|
||||
private async processFollowAccept(): Promise<Response | null> {
|
||||
const followAccept = this.body as unknown as VersiaFollowAccept;
|
||||
const author = await User.resolve(followAccept.author);
|
||||
const follower = await User.resolve(followAccept.follower);
|
||||
const author = await User.resolve(new URL(followAccept.author));
|
||||
const follower = await User.resolve(new URL(followAccept.follower));
|
||||
|
||||
if (!author) {
|
||||
return Response.json(
|
||||
|
|
@ -393,8 +393,8 @@ export class InboxProcessor {
|
|||
*/
|
||||
private async processFollowReject(): Promise<Response | null> {
|
||||
const followReject = this.body as unknown as VersiaFollowReject;
|
||||
const author = await User.resolve(followReject.author);
|
||||
const follower = await User.resolve(followReject.follower);
|
||||
const author = await User.resolve(new URL(followReject.author));
|
||||
const follower = await User.resolve(new URL(followReject.follower));
|
||||
|
||||
if (!author) {
|
||||
return Response.json(
|
||||
|
|
@ -438,7 +438,7 @@ export class InboxProcessor {
|
|||
const toDelete = delete_.deleted;
|
||||
|
||||
const author = delete_.author
|
||||
? await User.resolve(delete_.author)
|
||||
? await User.resolve(new URL(delete_.author))
|
||||
: null;
|
||||
|
||||
switch (delete_.deleted_type) {
|
||||
|
|
@ -461,7 +461,7 @@ export class InboxProcessor {
|
|||
return null;
|
||||
}
|
||||
case "User": {
|
||||
const userToDelete = await User.resolve(toDelete);
|
||||
const userToDelete = await User.resolve(new URL(toDelete));
|
||||
|
||||
if (!userToDelete) {
|
||||
return Response.json(
|
||||
|
|
@ -516,8 +516,8 @@ export class InboxProcessor {
|
|||
*/
|
||||
private async processLikeRequest(): Promise<Response | null> {
|
||||
const like = this.body as unknown as VersiaLikeExtension;
|
||||
const author = await User.resolve(like.author);
|
||||
const likedNote = await Note.resolve(like.liked);
|
||||
const author = await User.resolve(new URL(like.author));
|
||||
const likedNote = await Note.resolve(new URL(like.liked));
|
||||
|
||||
if (!author) {
|
||||
return Response.json(
|
||||
|
|
@ -546,7 +546,7 @@ export class InboxProcessor {
|
|||
private async processUserRequest(): Promise<Response | null> {
|
||||
const user = this.body as unknown as VersiaUser;
|
||||
// FIXME: Instead of refetching the remote user, we should read the incoming json and update from that
|
||||
const updatedAccount = await User.fetchFromRemote(user.uri);
|
||||
const updatedAccount = await User.fetchFromRemote(new URL(user.uri));
|
||||
|
||||
if (!updatedAccount) {
|
||||
return Response.json(
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export const getFetchWorker = (): Worker<FetchJobData, void, FetchJobType> =>
|
|||
return;
|
||||
}
|
||||
|
||||
await Instance.resolve(uri);
|
||||
await Instance.resolve(new URL(uri));
|
||||
|
||||
await job.log(
|
||||
`✔ Finished fetching instance metadata from [${uri}]`,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ export const getInboxWorker = (): Worker<InboxJobData, void, InboxJobType> =>
|
|||
|
||||
if (headers.authorization) {
|
||||
const processor = new InboxProcessor(
|
||||
request,
|
||||
{
|
||||
...request,
|
||||
url: new URL(request.url),
|
||||
},
|
||||
data,
|
||||
null,
|
||||
{
|
||||
|
|
@ -67,7 +70,7 @@ export const getInboxWorker = (): Worker<InboxJobData, void, InboxJobType> =>
|
|||
"x-signed-by": string;
|
||||
};
|
||||
|
||||
const sender = await User.resolve(signedBy);
|
||||
const sender = await User.resolve(new URL(signedBy));
|
||||
|
||||
if (!(sender || signedBy.startsWith("instance "))) {
|
||||
await job.log(
|
||||
|
|
@ -106,7 +109,10 @@ export const getInboxWorker = (): Worker<InboxJobData, void, InboxJobType> =>
|
|||
}
|
||||
|
||||
const processor = new InboxProcessor(
|
||||
request,
|
||||
{
|
||||
...request,
|
||||
url: new URL(request.url),
|
||||
},
|
||||
data,
|
||||
{
|
||||
instance: remoteInstance,
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export const getPushWorker = (): Worker<PushJobData, void, PushJobType> =>
|
|||
vapidDetails: {
|
||||
subject:
|
||||
config.notifications.push.vapid.subject ||
|
||||
config.http.base_url,
|
||||
config.http.base_url.origin,
|
||||
privateKey: config.notifications.push.vapid.private,
|
||||
publicKey: config.notifications.push.vapid.public,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue