feat(federation): Handle instances not existing

This commit is contained in:
Jesse Wierzbinski 2024-12-02 15:07:05 +01:00
parent deee65ad6d
commit 91da99c934
No known key found for this signature in database
4 changed files with 33 additions and 9 deletions

View file

@ -106,6 +106,13 @@ export default apiRoute((app) =>
// Try to fetch it from database // Try to fetch it from database
const instance = await Instance.resolveFromHost(instanceHost); const instance = await Instance.resolveFromHost(instanceHost);
if (!instance) {
return context.json(
{ error: `Instance ${instanceHost} not found` },
404,
);
}
const account = await User.fromSql( const account = await User.fromSql(
and( and(
eq(Users.username, username), eq(Users.username, username),

View file

@ -29,6 +29,7 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
visible_in_picker: z.boolean(), visible_in_picker: z.boolean(),
category: z.string().optional(), category: z.string().optional(),
static_url: z.string(), static_url: z.string(),
global: z.boolean(),
}); });
public static $type: EmojiWithInstance; public static $type: EmojiWithInstance;

View file

@ -319,7 +319,7 @@ export class Instance extends BaseInterface<typeof Instances> {
} }
} }
public static resolveFromHost(host: string): Promise<Instance> { public static resolveFromHost(host: string): Promise<Instance | null> {
if (host.startsWith("http")) { if (host.startsWith("http")) {
const url = new URL(host).host; const url = new URL(host).host;
@ -331,7 +331,7 @@ export class Instance extends BaseInterface<typeof Instances> {
return Instance.resolve(url.origin); return Instance.resolve(url.origin);
} }
public static async resolve(url: string): Promise<Instance> { public static async resolve(url: string): Promise<Instance | null> {
const logger = getLogger(["federation", "resolvers"]); const logger = getLogger(["federation", "resolvers"]);
const host = new URL(url).host; const host = new URL(url).host;
@ -347,7 +347,7 @@ export class Instance extends BaseInterface<typeof Instances> {
if (!output) { if (!output) {
logger.error`Failed to resolve instance ${chalk.bold(host)}`; logger.error`Failed to resolve instance ${chalk.bold(host)}`;
throw new Error("Failed to resolve instance"); return null;
} }
const { metadata, protocol } = output; const { metadata, protocol } = output;

View file

@ -599,18 +599,26 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
const updated = await User.saveFromRemote(this.getUri()); const updated = await User.saveFromRemote(this.getUri());
if (!updated) {
throw new Error("Failed to update user from remote");
}
this.data = updated.data; this.data = updated.data;
return this; return this;
} }
public static async saveFromRemote(uri: string): Promise<User> { public static async saveFromRemote(uri: string): Promise<User | null> {
if (!URL.canParse(uri)) { if (!URL.canParse(uri)) {
throw new Error(`Invalid URI: ${uri}`); throw new Error(`Invalid URI: ${uri}`);
} }
const instance = await Instance.resolve(uri); const instance = await Instance.resolve(uri);
if (!instance) {
return null;
}
if (instance.data.protocol === "versia") { if (instance.data.protocol === "versia") {
return await User.saveFromVersia(uri, instance); return await User.saveFromVersia(uri, instance);
} }
@ -636,12 +644,20 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
private static async saveFromVersia( private static async saveFromVersia(
uri: string, uri: string,
instance: Instance, instance: Instance,
): Promise<User> { ): Promise<User | null> {
const requester = await User.getFederationRequester(); const requester = await User.getFederationRequester();
const { data: json } = await requester.get<Partial<VersiaUser>>(uri, { const output = await requester
.get<Partial<VersiaUser>>(uri, {
// @ts-expect-error Bun extension // @ts-expect-error Bun extension
proxy: config.http.proxy.address, proxy: config.http.proxy.address,
}); })
.catch(() => null);
if (!output) {
return null;
}
const { data: json } = output;
const validator = new EntityValidator(); const validator = new EntityValidator();
const data = await validator.User(json); const data = await validator.User(json);