server/database/entities/Instance.ts

194 lines
4.6 KiB
TypeScript
Raw Normal View History

import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
2023-09-12 22:48:10 +02:00
import { APIInstance } from "~types/entities/instance";
import { APIAccount } from "~types/entities/account";
export interface NodeInfo {
software: {
name: string;
version: string;
};
protocols: string[];
version: string;
services: {
inbound: string[];
outbound: string[];
};
openRegistrations: boolean;
usage: {
users: {
total: number;
activeHalfyear: number;
activeMonth: number;
};
localPosts: number;
localComments?: number;
remotePosts?: number;
remoteComments?: number;
};
metadata: Partial<{
nodeName: string;
nodeDescription: string;
maintainer: {
name: string;
email: string;
};
langs: string[];
tosUrl: string;
repositoryUrl: string;
feedbackUrl: string;
disableRegistration: boolean;
disableLocalTimeline: boolean;
disableRecommendedTimeline: boolean;
disableGlobalTimeline: boolean;
emailRequiredForSignup: boolean;
searchFilters: boolean;
postEditing: boolean;
postImports: boolean;
enableHcaptcha: boolean;
enableRecaptcha: boolean;
maxNoteTextLength: number;
maxCaptionTextLength: number;
enableTwitterIntegration: boolean;
enableGithubIntegration: boolean;
enableDiscordIntegration: boolean;
enableEmail: boolean;
enableServiceWorker: boolean;
proxyAccountName: string | null;
themeColor: string;
}>;
}
2023-09-12 22:48:10 +02:00
2023-09-28 20:19:21 +02:00
/**
* Represents an instance in the database.
*/
2023-09-12 22:48:10 +02:00
@Entity({
name: "instances",
})
export class Instance extends BaseEntity {
2023-09-28 20:19:21 +02:00
/**
* The unique identifier of the instance.
*/
2023-09-12 22:48:10 +02:00
@PrimaryGeneratedColumn("uuid")
id!: string;
2023-09-28 20:19:21 +02:00
/**
* The base URL of the instance.
* Must not have the https:// or http:// prefix.
2023-09-28 20:19:21 +02:00
*/
@Column("varchar")
base_url!: string;
2023-09-12 22:48:10 +02:00
2023-09-28 20:19:21 +02:00
/**
* The configuration of the instance.
*/
2023-09-12 22:48:10 +02:00
@Column("jsonb", {
nullable: true,
2023-09-12 22:48:10 +02:00
})
instance_data?: APIInstance;
/**
* Instance nodeinfo data
*/
@Column("jsonb")
nodeinfo!: NodeInfo;
/**
* Adds an instance to the database if it doesn't already exist.
* @param url
* @returns Either the database instance if it already exists, or a newly created instance.
*/
static async addIfNotExists(url: string): Promise<Instance> {
const origin = new URL(url).origin;
const hostname = new URL(url).hostname;
const found = await Instance.findOne({
where: {
base_url: hostname,
},
});
if (found) return found;
const instance = new Instance();
instance.base_url = hostname;
// Fetch the instance configuration
const nodeinfo: NodeInfo = await fetch(`${origin}/nodeinfo/2.0`).then(
res => res.json()
);
// Try to fetch configuration from Mastodon-compatible instances
if (
["firefish", "iceshrimp", "mastodon", "akkoma", "pleroma"].includes(
nodeinfo.software.name
)
) {
const instanceData: APIInstance = await fetch(
`${origin}/api/v1/instance`
).then(res => res.json());
instance.instance_data = instanceData;
}
instance.nodeinfo = nodeinfo;
await instance.save();
return instance;
}
2023-09-12 22:48:10 +02:00
2023-09-28 20:19:21 +02:00
/**
* Converts the instance to an API instance.
* @returns The API instance.
*/
// eslint-disable-next-line @typescript-eslint/require-await
2023-09-12 22:48:10 +02:00
async toAPI(): Promise<APIInstance> {
return {
uri: this.instance_data?.uri || this.base_url,
approval_required: this.instance_data?.approval_required || false,
email: this.instance_data?.email || "",
thumbnail: this.instance_data?.thumbnail || "",
title: this.instance_data?.title || "",
version: this.instance_data?.version || "",
configuration: this.instance_data?.configuration || {
media_attachments: {
image_matrix_limit: 0,
image_size_limit: 0,
supported_mime_types: [],
video_frame_limit: 0,
video_matrix_limit: 0,
video_size_limit: 0,
},
polls: {
max_characters_per_option: 0,
max_expiration: 0,
max_options: 0,
min_expiration: 0,
},
statuses: {
characters_reserved_per_url: 0,
max_characters: 0,
max_media_attachments: 0,
},
},
contact_account:
this.instance_data?.contact_account || ({} as APIAccount),
description: this.instance_data?.description || "",
invites_enabled: this.instance_data?.invites_enabled || false,
languages: this.instance_data?.languages || [],
registrations: this.instance_data?.registrations || false,
rules: this.instance_data?.rules || [],
2023-09-12 22:48:10 +02:00
stats: {
domain_count: this.instance_data?.stats.domain_count || 0,
status_count: this.instance_data?.stats.status_count || 0,
user_count: this.instance_data?.stats.user_count || 0,
2023-09-12 22:48:10 +02:00
},
urls: {
streaming_api: this.instance_data?.urls.streaming_api || "",
2023-09-12 22:48:10 +02:00
},
max_toot_chars: this.instance_data?.max_toot_chars || 0,
2023-09-12 22:48:10 +02:00
};
}
2023-09-13 02:29:13 +02:00
}