mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
refactor(federation): 🔥 Remove old code and simplify federation requests
This commit is contained in:
parent
ad9ed2598c
commit
2f823317c2
|
|
@ -1,27 +0,0 @@
|
|||
import { emojiValidatorWithColons } from "@/api";
|
||||
import { type InferSelectModel, inArray } from "drizzle-orm";
|
||||
import { Emojis, type Instances } from "~/drizzle/schema";
|
||||
import { Emoji } from "~/packages/database-interface/emoji";
|
||||
|
||||
export type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for parsing emojis from local text
|
||||
* @param text The text to parse
|
||||
* @returns An array of emojis
|
||||
*/
|
||||
export const parseEmojis = async (text: string): Promise<Emoji[]> => {
|
||||
const matches = text.match(emojiValidatorWithColons);
|
||||
if (!matches || matches.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Emoji.manyFromSql(
|
||||
inArray(
|
||||
Emojis.shortcode,
|
||||
matches.map((match) => match.replace(/:/g, "")),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
|
@ -1,65 +1,10 @@
|
|||
import { debugRequest } from "@/api";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import { SignatureConstructor } from "@lysand-org/federation";
|
||||
import type { Entity, Undo } from "@lysand-org/federation/types";
|
||||
import type { Undo } from "@lysand-org/federation/types";
|
||||
import { config } from "config-manager";
|
||||
import type { User } from "~/packages/database-interface/user";
|
||||
|
||||
export const localObjectUri = (id: string) =>
|
||||
new URL(`/objects/${id}`, config.http.base_url).toString();
|
||||
|
||||
export const objectToInboxRequest = async (
|
||||
object: Entity,
|
||||
author: User,
|
||||
userToSendTo: User,
|
||||
): Promise<Request> => {
|
||||
if (userToSendTo.isLocal() || !userToSendTo.data.endpoints?.inbox) {
|
||||
throw new Error("UserToSendTo has no inbox or is a local user");
|
||||
}
|
||||
|
||||
if (author.isRemote()) {
|
||||
throw new Error("Author is a remote user");
|
||||
}
|
||||
|
||||
const privateKey = await crypto.subtle.importKey(
|
||||
"pkcs8",
|
||||
Buffer.from(author.data.privateKey ?? "", "base64"),
|
||||
"Ed25519",
|
||||
false,
|
||||
["sign"],
|
||||
);
|
||||
|
||||
const ctor = new SignatureConstructor(privateKey, author.getUri());
|
||||
|
||||
const userInbox = new URL(userToSendTo.data.endpoints?.inbox ?? "");
|
||||
|
||||
const request = new Request(userInbox, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Origin: new URL(config.http.base_url).host,
|
||||
},
|
||||
body: JSON.stringify(object),
|
||||
});
|
||||
|
||||
const { request: signed, signedString } = await ctor.sign(request);
|
||||
|
||||
if (config.debug.federation) {
|
||||
// Debug request
|
||||
await debugRequest(signed);
|
||||
|
||||
const logger = getLogger("federation");
|
||||
|
||||
// Log public key
|
||||
logger.debug`Sender public key: ${author.data.publicKey}`;
|
||||
|
||||
// Log signed string
|
||||
logger.debug`Signed string:\n${signedString}`;
|
||||
}
|
||||
|
||||
return signed;
|
||||
};
|
||||
|
||||
export const undoFederationRequest = (undoer: User, uri: string): Undo => {
|
||||
const id = crypto.randomUUID();
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
import { mentionValidator } from "@/api";
|
||||
import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization";
|
||||
import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import {
|
||||
FederationRequester,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import type { ContentFormat } from "@lysand-org/federation/types";
|
||||
import { config } from "config-manager";
|
||||
import {
|
||||
|
|
@ -37,11 +32,9 @@ import {
|
|||
type Notes,
|
||||
Users,
|
||||
} from "~/drizzle/schema";
|
||||
import type { Note } from "~/packages/database-interface/note";
|
||||
import type { EmojiWithInstance } from "~/packages/database-interface/emoji";
|
||||
import { User } from "~/packages/database-interface/user";
|
||||
import type { Application } from "./application";
|
||||
import type { EmojiWithInstance } from "./emoji";
|
||||
import { objectToInboxRequest } from "./federation";
|
||||
import {
|
||||
type UserWithInstance,
|
||||
type UserWithRelations,
|
||||
|
|
@ -316,11 +309,7 @@ export const parseTextMentions = async (
|
|||
|
||||
// Attempt to resolve mentions that were not found
|
||||
for (const person of notFoundRemoteUsers) {
|
||||
const signatureConstructor = await SignatureConstructor.fromStringKey(
|
||||
author.data.privateKey ?? "",
|
||||
author.getUri(),
|
||||
);
|
||||
const manager = new FederationRequester(signatureConstructor);
|
||||
const manager = await author.getFederationRequester();
|
||||
|
||||
const uri = await User.webFinger(
|
||||
manager,
|
||||
|
|
@ -451,26 +440,3 @@ export const getMarkdownRenderer = () => {
|
|||
|
||||
return renderer;
|
||||
};
|
||||
|
||||
export const federateNote = async (note: Note) => {
|
||||
for (const user of await note.getUsersToFederateTo()) {
|
||||
// TODO: Add queue system
|
||||
const request = await objectToInboxRequest(
|
||||
note.toLysand(),
|
||||
note.author,
|
||||
user,
|
||||
);
|
||||
|
||||
// Send request
|
||||
const response = await fetch(request, {
|
||||
proxy: config.http.proxy.address,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const logger = getLogger("federation");
|
||||
|
||||
logger.debug`${await response.text()}`;
|
||||
logger.error`Failed to federate status ${note.data.id} to ${user.getUri()}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
/**
|
||||
* Old code that needs to be rewritten
|
||||
*/
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import type {
|
||||
Follow,
|
||||
FollowAccept,
|
||||
|
|
@ -19,10 +15,9 @@ import {
|
|||
Tokens,
|
||||
type Users,
|
||||
} from "~/drizzle/schema";
|
||||
import type { EmojiWithInstance } from "~/packages/database-interface/emoji";
|
||||
import { User } from "~/packages/database-interface/user";
|
||||
import type { Application } from "./application";
|
||||
import type { EmojiWithInstance } from "./emoji";
|
||||
import { objectToInboxRequest } from "./federation";
|
||||
import {
|
||||
type Relationship,
|
||||
checkForBidirectionalRelationships,
|
||||
|
|
@ -168,25 +163,12 @@ export const followRequestUser = async (
|
|||
}
|
||||
|
||||
if (isRemote) {
|
||||
// Federate
|
||||
// TODO: Make database job
|
||||
const request = await objectToInboxRequest(
|
||||
const { ok } = await follower.federateToUser(
|
||||
followRequestToLysand(follower, followee),
|
||||
follower,
|
||||
followee,
|
||||
);
|
||||
|
||||
// Send request
|
||||
const response = await fetch(request, {
|
||||
proxy: config.http.proxy.address,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const logger = getLogger("federation");
|
||||
|
||||
logger.debug`${await response.text()}`;
|
||||
logger.error`Failed to federate follow request from ${follower.id} to ${followee.getUri()}`;
|
||||
|
||||
if (!ok) {
|
||||
await db
|
||||
.update(Relationships)
|
||||
.set({
|
||||
|
|
@ -217,45 +199,17 @@ export const followRequestUser = async (
|
|||
};
|
||||
|
||||
export const sendFollowAccept = async (follower: User, followee: User) => {
|
||||
// TODO: Make database job
|
||||
const request = await objectToInboxRequest(
|
||||
await follower.federateToUser(
|
||||
followAcceptToLysand(follower, followee),
|
||||
followee,
|
||||
follower,
|
||||
);
|
||||
|
||||
// Send request
|
||||
const response = await fetch(request, {
|
||||
proxy: config.http.proxy.address,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const logger = getLogger("federation");
|
||||
|
||||
logger.debug`${await response.text()}`;
|
||||
logger.error`Failed to federate follow accept from ${followee.id} to ${follower.getUri()}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const sendFollowReject = async (follower: User, followee: User) => {
|
||||
// TODO: Make database job
|
||||
const request = await objectToInboxRequest(
|
||||
await follower.federateToUser(
|
||||
followRejectToLysand(follower, followee),
|
||||
followee,
|
||||
follower,
|
||||
);
|
||||
|
||||
// Send request
|
||||
const response = await fetch(request, {
|
||||
proxy: config.http.proxy.address,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const logger = getLogger("federation");
|
||||
|
||||
logger.debug`${await response.text()}`;
|
||||
logger.error`Failed to federate follow reject from ${followee.id} to ${follower.getUri()}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const transformOutputToUserWithRelations = (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
import { parseUserAddress, userAddressValidator } from "@/api";
|
||||
import {
|
||||
FederationRequester,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import { Args } from "@oclif/core";
|
||||
import chalk from "chalk";
|
||||
import ora from "ora";
|
||||
|
|
@ -47,11 +43,7 @@ export default class FederationUserFetch extends BaseCommand<
|
|||
|
||||
const requester = await User.getServerActor();
|
||||
|
||||
const signatureConstructor = await SignatureConstructor.fromStringKey(
|
||||
requester.data.privateKey ?? "",
|
||||
requester.getUri(),
|
||||
);
|
||||
const manager = new FederationRequester(signatureConstructor);
|
||||
const manager = await requester.getFederationRequester();
|
||||
|
||||
const uri = await User.webFinger(manager, username, host);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
import { parseUserAddress, userAddressValidator } from "@/api";
|
||||
import {
|
||||
FederationRequester,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import { Args } from "@oclif/core";
|
||||
import chalk from "chalk";
|
||||
import ora from "ora";
|
||||
|
|
@ -47,11 +43,7 @@ export default class FederationUserFinger extends BaseCommand<
|
|||
|
||||
const requester = await User.getServerActor();
|
||||
|
||||
const signatureConstructor = await SignatureConstructor.fromStringKey(
|
||||
requester.data.privateKey ?? "",
|
||||
requester.getUri(),
|
||||
);
|
||||
const manager = new FederationRequester(signatureConstructor);
|
||||
const manager = await requester.getFederationRequester();
|
||||
|
||||
const uri = await User.webFinger(manager, username, host);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
import { emojiValidatorWithColons } from "@/api";
|
||||
import { proxyUrl } from "@/response";
|
||||
import type { Emoji as ApiEmoji } from "@lysand-org/client/types";
|
||||
import type { CustomEmojiExtension } from "@lysand-org/federation/types";
|
||||
import {
|
||||
type InferInsertModel,
|
||||
type InferSelectModel,
|
||||
type SQL,
|
||||
and,
|
||||
desc,
|
||||
eq,
|
||||
inArray,
|
||||
} from "drizzle-orm";
|
||||
import type { EmojiWithInstance } from "~/classes/functions/emoji";
|
||||
import { db } from "~/drizzle/db";
|
||||
import { Emojis, Instances } from "~/drizzle/schema";
|
||||
import { BaseInterface } from "./base";
|
||||
import { Instance } from "./instance";
|
||||
|
||||
export type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
};
|
||||
|
||||
export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
||||
async reload(): Promise<void> {
|
||||
const reloaded = await Emoji.fromId(this.data.id);
|
||||
|
|
@ -152,6 +157,26 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
|||
return this.data.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse emojis from text
|
||||
*
|
||||
* @param text The text to parse
|
||||
* @returns An array of emojis
|
||||
*/
|
||||
public static async parseFromText(text: string): Promise<Emoji[]> {
|
||||
const matches = text.match(emojiValidatorWithColons);
|
||||
if (!matches || matches.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Emoji.manyFromSql(
|
||||
inArray(
|
||||
Emojis.shortcode,
|
||||
matches.map((match) => match.replace(/:/g, "")),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public toApi(): ApiEmoji {
|
||||
return {
|
||||
// @ts-expect-error ID is not in regular Mastodon API
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import {
|
||||
EntityValidator,
|
||||
FederationRequester,
|
||||
type ResponseError,
|
||||
type ValidationError,
|
||||
} from "@lysand-org/federation";
|
||||
|
|
@ -20,6 +19,7 @@ import {
|
|||
import { db } from "~/drizzle/db";
|
||||
import { Instances } from "~/drizzle/schema";
|
||||
import { BaseInterface } from "./base";
|
||||
import { User } from "./user";
|
||||
|
||||
export type AttachmentType = InferSelectModel<typeof Instances>;
|
||||
|
||||
|
|
@ -139,8 +139,10 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
const wellKnownUrl = new URL("/.well-known/lysand", origin);
|
||||
const logger = getLogger("federation");
|
||||
|
||||
const requester = await User.getServerActor().getFederationRequester();
|
||||
|
||||
try {
|
||||
const { ok, raw, data } = await new FederationRequester()
|
||||
const { ok, raw, data } = await requester
|
||||
.get(wellKnownUrl, {
|
||||
// @ts-expect-error Bun extension
|
||||
proxy: config.http.proxy.address,
|
||||
|
|
@ -193,13 +195,14 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
// Go to endpoint, then follow the links to the actual metadata
|
||||
|
||||
const logger = getLogger("federation");
|
||||
const requester = await User.getServerActor().getFederationRequester();
|
||||
|
||||
try {
|
||||
const {
|
||||
raw: response,
|
||||
ok,
|
||||
data: wellKnown,
|
||||
} = await new FederationRequester()
|
||||
} = await requester
|
||||
.get<{
|
||||
links: { rel: string; href: string }[];
|
||||
}>(wellKnownUrl, {
|
||||
|
|
@ -245,7 +248,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
raw: metadataResponse,
|
||||
ok: ok2,
|
||||
data: metadata,
|
||||
} = await new FederationRequester()
|
||||
} = await requester
|
||||
.get<{
|
||||
metadata: {
|
||||
nodeName?: string;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type {
|
|||
Attachment as ApiAttachment,
|
||||
Status as ApiStatus,
|
||||
} from "@lysand-org/client/types";
|
||||
import { EntityValidator, FederationRequester } from "@lysand-org/federation";
|
||||
import { EntityValidator } from "@lysand-org/federation";
|
||||
import type {
|
||||
ContentFormat,
|
||||
Note as LysandNote,
|
||||
|
|
@ -29,7 +29,6 @@ import {
|
|||
type Application,
|
||||
applicationToApi,
|
||||
} from "~/classes/functions/application";
|
||||
import { parseEmojis } from "~/classes/functions/emoji";
|
||||
import { localObjectUri } from "~/classes/functions/federation";
|
||||
import {
|
||||
type StatusWithRelations,
|
||||
|
|
@ -190,6 +189,14 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
|
|||
return this.data.id;
|
||||
}
|
||||
|
||||
async federateToUsers(): Promise<void> {
|
||||
const users = await this.getUsersToFederateTo();
|
||||
|
||||
for (const user of users) {
|
||||
await this.author.federateToUser(this.toLysand(), user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the users that should be federated to for this note
|
||||
*
|
||||
|
|
@ -336,7 +343,7 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
|
|||
|
||||
const parsedEmojis = [
|
||||
...(data.emojis ?? []),
|
||||
...(await parseEmojis(plaintextContent)),
|
||||
...(await Emoji.parseFromText(plaintextContent)),
|
||||
// Deduplicate by .id
|
||||
].filter(
|
||||
(emoji, index, self) =>
|
||||
|
|
@ -429,7 +436,9 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
|
|||
|
||||
const parsedEmojis = [
|
||||
...(data.emojis ?? []),
|
||||
...(plaintextContent ? await parseEmojis(plaintextContent) : []),
|
||||
...(plaintextContent
|
||||
? await Emoji.parseFromText(plaintextContent)
|
||||
: []),
|
||||
// Deduplicate by .id
|
||||
].filter(
|
||||
(emoji, index, self) =>
|
||||
|
|
@ -592,7 +601,10 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
|
|||
throw new Error(`Invalid URI to parse ${uri}`);
|
||||
}
|
||||
|
||||
const { data } = await new FederationRequester().get(uri, {
|
||||
const requester =
|
||||
await User.getServerActor().getFederationRequester();
|
||||
|
||||
const { data } = await requester.get(uri, {
|
||||
// @ts-expect-error Bun extension
|
||||
proxy: config.http.proxy.address,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,12 +2,20 @@ import { idValidator } from "@/api";
|
|||
import { getBestContentType, urlToContentFormat } from "@/content_types";
|
||||
import { randomString } from "@/math";
|
||||
import { proxyUrl } from "@/response";
|
||||
import { sentry } from "@/sentry";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import type {
|
||||
Account as ApiAccount,
|
||||
Mention as ApiMention,
|
||||
} from "@lysand-org/client/types";
|
||||
import { EntityValidator, FederationRequester } from "@lysand-org/federation";
|
||||
import {
|
||||
EntityValidator,
|
||||
FederationRequester,
|
||||
type HttpVerb,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import type { Entity, User as LysandUser } from "@lysand-org/federation/types";
|
||||
import chalk from "chalk";
|
||||
import {
|
||||
type InferInsertModel,
|
||||
type SQL,
|
||||
|
|
@ -23,7 +31,6 @@ import {
|
|||
sql,
|
||||
} from "drizzle-orm";
|
||||
import { htmlToText } from "html-to-text";
|
||||
import { objectToInboxRequest } from "~/classes/functions/federation";
|
||||
import {
|
||||
type UserWithRelations,
|
||||
findManyUsers,
|
||||
|
|
@ -341,9 +348,8 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
uri: string,
|
||||
instance: Instance,
|
||||
): Promise<User> {
|
||||
const { data: json } = await new FederationRequester().get<
|
||||
Partial<LysandUser>
|
||||
>(uri, {
|
||||
const requester = await User.getServerActor().getFederationRequester();
|
||||
const { data: json } = await requester.get<Partial<LysandUser>>(uri, {
|
||||
// @ts-expect-error Bun extension
|
||||
proxy: config.http.proxy.address,
|
||||
});
|
||||
|
|
@ -617,7 +623,66 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return updated.data;
|
||||
}
|
||||
|
||||
async federateToFollowers(object: Entity) {
|
||||
/**
|
||||
* Signs a Lysand entity with that user's private key
|
||||
*
|
||||
* @param entity Entity to sign
|
||||
* @param signatureUrl URL to embed in signature (must be the same URI of queries made with this signature)
|
||||
* @param signatureMethod HTTP method to embed in signature (default: POST)
|
||||
* @returns The signed string and headers to send with the request
|
||||
*/
|
||||
async sign(
|
||||
entity: Entity,
|
||||
signatureUrl: string | URL,
|
||||
signatureMethod: HttpVerb = "POST",
|
||||
): Promise<{
|
||||
headers: Headers;
|
||||
signedString: string;
|
||||
}> {
|
||||
const signatureConstructor = await SignatureConstructor.fromStringKey(
|
||||
this.data.privateKey ?? "",
|
||||
this.getUri(),
|
||||
);
|
||||
|
||||
const output = await signatureConstructor.sign(
|
||||
signatureMethod,
|
||||
new URL(signatureUrl),
|
||||
JSON.stringify(entity),
|
||||
);
|
||||
|
||||
if (config.debug.federation) {
|
||||
const logger = getLogger("federation");
|
||||
|
||||
// Log public key
|
||||
logger.debug`Sender public key: ${this.data.publicKey}`;
|
||||
|
||||
// Log signed string
|
||||
logger.debug`Signed string:\n${output.signedString}`;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the appropriate Lysand SDK requester with this user's private key
|
||||
*
|
||||
* @returns The requester
|
||||
*/
|
||||
async getFederationRequester(): Promise<FederationRequester> {
|
||||
const signatureConstructor = await SignatureConstructor.fromStringKey(
|
||||
this.data.privateKey ?? "",
|
||||
this.getUri(),
|
||||
);
|
||||
|
||||
return new FederationRequester(signatureConstructor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Federates an entity to all followers of the user
|
||||
*
|
||||
* @param entity Entity to federate
|
||||
*/
|
||||
async federateToFollowers(entity: Entity): Promise<void> {
|
||||
// Get followers
|
||||
const followers = await User.manyFromSql(
|
||||
and(
|
||||
|
|
@ -627,19 +692,45 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
);
|
||||
|
||||
for (const follower of followers) {
|
||||
const federationRequest = await objectToInboxRequest(
|
||||
object,
|
||||
this,
|
||||
follower,
|
||||
);
|
||||
|
||||
// FIXME: Add to new queue system when it's implemented
|
||||
fetch(federationRequest, {
|
||||
proxy: config.http.proxy.address,
|
||||
});
|
||||
await this.federateToUser(entity, follower);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Federates an entity to any user.
|
||||
*
|
||||
* @param entity Entity to federate
|
||||
* @param user User to federate to
|
||||
* @returns Whether the federation was successful
|
||||
*/
|
||||
async federateToUser(entity: Entity, user: User): Promise<{ ok: boolean }> {
|
||||
const { headers } = await this.sign(
|
||||
entity,
|
||||
user.data.endpoints?.inbox ?? "",
|
||||
);
|
||||
|
||||
try {
|
||||
await new FederationRequester().post(
|
||||
user.data.endpoints?.inbox ?? "",
|
||||
entity,
|
||||
{
|
||||
// @ts-expect-error Bun extension
|
||||
proxy: config.http.proxy.address,
|
||||
headers,
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
getLogger("federation")
|
||||
.error`Federating ${chalk.gray(entity.type)} to ${user.getUri()} ${chalk.bold.red("failed")}`;
|
||||
getLogger("federation").error`${e}`;
|
||||
sentry?.captureException(e);
|
||||
|
||||
return { ok: false };
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
toApi(isOwnAccount = false): ApiAccount {
|
||||
const user = this.data;
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@ import { applyConfig, auth, handleZodError } from "@/api";
|
|||
import { errorResponse, jsonResponse } from "@/response";
|
||||
import type { Hono } from "@hono/hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import {
|
||||
FederationRequester,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import { eq } from "drizzle-orm";
|
||||
import {
|
||||
anyOf,
|
||||
|
|
@ -84,12 +80,7 @@ export default (app: Hono) =>
|
|||
|
||||
const requester = user ?? User.getServerActor();
|
||||
|
||||
const signatureConstructor =
|
||||
await SignatureConstructor.fromStringKey(
|
||||
requester.data.privateKey ?? "",
|
||||
requester.getUri(),
|
||||
);
|
||||
const manager = new FederationRequester(signatureConstructor);
|
||||
const manager = await requester.getFederationRequester();
|
||||
|
||||
const uri = await User.webFinger(manager, username, domain);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@ import { applyConfig, auth, handleZodError } from "@/api";
|
|||
import { errorResponse, jsonResponse } from "@/response";
|
||||
import type { Hono } from "@hono/hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import {
|
||||
FederationRequester,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import { eq, ilike, not, or, sql } from "drizzle-orm";
|
||||
import {
|
||||
anyOf,
|
||||
|
|
@ -95,12 +91,7 @@ export default (app: Hono) =>
|
|||
if (resolve && username && host) {
|
||||
const requester = self ?? User.getServerActor();
|
||||
|
||||
const signatureConstructor =
|
||||
await SignatureConstructor.fromStringKey(
|
||||
requester.data.privateKey ?? "",
|
||||
requester.getUri(),
|
||||
);
|
||||
const manager = new FederationRequester(signatureConstructor);
|
||||
const manager = await requester.getFederationRequester();
|
||||
|
||||
const uri = await User.webFinger(manager, username, host);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,12 @@ import { config } from "config-manager";
|
|||
import { and, eq, isNull } from "drizzle-orm";
|
||||
import ISO6391 from "iso-639-1";
|
||||
import { z } from "zod";
|
||||
import { parseEmojis } from "~/classes/functions/emoji";
|
||||
import { contentToHtml } from "~/classes/functions/status";
|
||||
import { MediaManager } from "~/classes/media/media-manager";
|
||||
import { db } from "~/drizzle/db";
|
||||
import { EmojiToUser, RolePermissions, Users } from "~/drizzle/schema";
|
||||
import { Attachment } from "~/packages/database-interface/attachment";
|
||||
import type { Emoji } from "~/packages/database-interface/emoji";
|
||||
import { Emoji } from "~/packages/database-interface/emoji";
|
||||
import { User } from "~/packages/database-interface/user";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -252,8 +251,8 @@ export default (app: Hono) =>
|
|||
);
|
||||
|
||||
// Parse emojis
|
||||
const nameEmojis = await parseEmojis(parsedName);
|
||||
const valueEmojis = await parseEmojis(parsedValue);
|
||||
const nameEmojis = await Emoji.parseFromText(parsedName);
|
||||
const valueEmojis = await Emoji.parseFromText(parsedValue);
|
||||
|
||||
fieldEmojis.push(...nameEmojis, ...valueEmojis);
|
||||
|
||||
|
|
@ -279,8 +278,9 @@ export default (app: Hono) =>
|
|||
}
|
||||
|
||||
// Parse emojis
|
||||
const displaynameEmojis = await parseEmojis(sanitizedDisplayName);
|
||||
const noteEmojis = await parseEmojis(self.note);
|
||||
const displaynameEmojis =
|
||||
await Emoji.parseFromText(sanitizedDisplayName);
|
||||
const noteEmojis = await Emoji.parseFromText(self.note);
|
||||
|
||||
self.emojis = [
|
||||
...displaynameEmojis,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { zValidator } from "@hono/zod-validator";
|
|||
import { config } from "config-manager";
|
||||
import ISO6391 from "iso-639-1";
|
||||
import { z } from "zod";
|
||||
import { federateNote } from "~/classes/functions/status";
|
||||
import { RolePermissions } from "~/drizzle/schema";
|
||||
import { Attachment } from "~/packages/database-interface/attachment";
|
||||
import { Note } from "~/packages/database-interface/note";
|
||||
|
|
@ -164,7 +163,7 @@ export default (app: Hono) =>
|
|||
});
|
||||
|
||||
if (!local_only) {
|
||||
await federateNote(newNote);
|
||||
await newNote.federateToUsers();
|
||||
}
|
||||
|
||||
return jsonResponse(await newNote.toApi(user));
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ import {
|
|||
import { errorResponse, jsonResponse } from "@/response";
|
||||
import type { Hono } from "@hono/hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import {
|
||||
FederationRequester,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import { and, eq, inArray, sql } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { searchManager } from "~/classes/search/search-manager";
|
||||
|
|
@ -133,14 +129,8 @@ export default (app: Hono) =>
|
|||
if (resolve) {
|
||||
const requester = self ?? User.getServerActor();
|
||||
|
||||
const signatureConstructor =
|
||||
await SignatureConstructor.fromStringKey(
|
||||
requester.data.privateKey ?? "",
|
||||
requester.getUri(),
|
||||
);
|
||||
const manager = new FederationRequester(
|
||||
signatureConstructor,
|
||||
);
|
||||
const manager =
|
||||
await requester.getFederationRequester();
|
||||
|
||||
const uri = await User.webFinger(
|
||||
manager,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import { applyConfig, handleZodError } from "@/api";
|
|||
import { errorResponse, response } from "@/response";
|
||||
import type { Hono } from "@hono/hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import { SignatureConstructor } from "@lysand-org/federation";
|
||||
import type { Entity } from "@lysand-org/federation/types";
|
||||
import { and, eq, inArray, sql } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
|
|
@ -100,22 +98,7 @@ export default (app: Hono) =>
|
|||
|
||||
const author = foundAuthor ?? User.getServerActor();
|
||||
|
||||
const { headers, signedString } = await (
|
||||
await SignatureConstructor.fromStringKey(
|
||||
author.data.privateKey ?? "",
|
||||
author.getUri(),
|
||||
)
|
||||
).sign("POST", reqUrl, objectString);
|
||||
|
||||
if (config.debug.federation) {
|
||||
const logger = getLogger("federation");
|
||||
|
||||
// Log public key
|
||||
logger.debug`Sender public key: ${author.data.publicKey}`;
|
||||
|
||||
// Log signed string
|
||||
logger.debug`Signed string:\n${signedString}`;
|
||||
}
|
||||
const { headers } = await author.sign(apiObject, reqUrl, "GET");
|
||||
|
||||
return response(objectString, 200, {
|
||||
"Content-Type": "application/json",
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import { applyConfig, handleZodError } from "@/api";
|
|||
import { errorResponse, redirect, response } from "@/response";
|
||||
import type { Hono } from "@hono/hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import { SignatureConstructor } from "@lysand-org/federation";
|
||||
import { z } from "zod";
|
||||
import { config } from "~/packages/config-manager";
|
||||
import { User } from "~/packages/database-interface/user";
|
||||
|
|
@ -84,22 +82,7 @@ export default (app: Hono) =>
|
|||
reqUrl.protocol = "https:";
|
||||
}
|
||||
|
||||
const { headers, signedString } = await (
|
||||
await SignatureConstructor.fromStringKey(
|
||||
user.data.privateKey ?? "",
|
||||
user.getUri(),
|
||||
)
|
||||
).sign("POST", reqUrl, userString);
|
||||
|
||||
if (config.debug.federation) {
|
||||
const logger = getLogger("federation");
|
||||
|
||||
// Log public key
|
||||
logger.debug`Sender public key: ${user.data.publicKey}`;
|
||||
|
||||
// Log signed string
|
||||
logger.debug`Signed string:\n${signedString}`;
|
||||
}
|
||||
const { headers } = await user.sign(user.toLysand(), reqUrl, "GET");
|
||||
|
||||
return response(userString, 200, {
|
||||
"Content-Type": "application/json",
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@ import { errorResponse, jsonResponse } from "@/response";
|
|||
import type { Hono } from "@hono/hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import {
|
||||
FederationRequester,
|
||||
type ResponseError,
|
||||
SignatureConstructor,
|
||||
} from "@lysand-org/federation";
|
||||
import type { ResponseError } from "@lysand-org/federation";
|
||||
import { and, eq, isNull } from "drizzle-orm";
|
||||
import { lookup } from "mime-types";
|
||||
import { z } from "zod";
|
||||
|
|
@ -84,13 +80,7 @@ export default (app: Hono) =>
|
|||
if (config.federation.bridge.enabled) {
|
||||
const requester = await User.getServerActor();
|
||||
|
||||
const signatureConstructor =
|
||||
await SignatureConstructor.fromStringKey(
|
||||
requester.data.privateKey ?? "",
|
||||
requester.getUri(),
|
||||
);
|
||||
|
||||
const manager = new FederationRequester(signatureConstructor);
|
||||
const manager = await requester.getFederationRequester();
|
||||
|
||||
try {
|
||||
activityPubUrl = await manager.webFinger(
|
||||
|
|
|
|||
Loading…
Reference in a new issue