From 8a8d15810bb31ec7365b79dcb22af464276d88cd Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Thu, 28 Sep 2023 08:19:21 -1000 Subject: [PATCH] Add more COMMENTS --- database/entities/Application.ts | 24 ++++++- database/entities/Emoji.ts | 19 ++++++ database/entities/Instance.ts | 16 +++++ database/entities/RawObject.ts | 30 ++++++++- database/entities/Relationship.ts | 30 ++++++++- database/entities/Status.ts | 100 ++++++++++++++++++++---------- database/entities/Token.ts | 22 +++++-- database/entities/User.ts | 94 ++++++++++++++++++++++++++-- 8 files changed, 289 insertions(+), 46 deletions(-) diff --git a/database/entities/Application.ts b/database/entities/Application.ts index 5f23c9ae..c94e9354 100644 --- a/database/entities/Application.ts +++ b/database/entities/Application.ts @@ -3,41 +3,57 @@ import { APIApplication } from "~types/entities/application"; import { Token } from "./Token"; /** - * Applications from clients + * Represents an application that can authenticate with the API. + */ +/** + * Represents an application that can authenticate with the API. */ @Entity({ name: "applications", }) export class Application extends BaseEntity { + /** The unique identifier for this application. */ @PrimaryGeneratedColumn("uuid") id!: string; + /** The name of this application. */ @Column("varchar") name!: string; + /** The website associated with this application, if any. */ @Column("varchar", { nullable: true, }) website!: string | null; + /** The VAPID key associated with this application, if any. */ @Column("varchar", { nullable: true, }) vapid_key!: string | null; + /** The client ID associated with this application. */ @Column("varchar") client_id!: string; + /** The secret associated with this application. */ @Column("varchar") secret!: string; + /** The scopes associated with this application. */ @Column("varchar") scopes = "read"; + /** The redirect URIs associated with this application. */ @Column("varchar") redirect_uris = "urn:ietf:wg:oauth:2.0:oob"; - static async getFromToken(token: string) { + /** + * Retrieves the application associated with the given access token. + * @param token The access token to retrieve the application for. + * @returns The application associated with the given access token, or null if no such application exists. + */ + static async getFromToken(token: string): Promise { const dbToken = await Token.findOne({ where: { access_token: token, @@ -48,6 +64,10 @@ export class Application extends BaseEntity { return dbToken?.application || null; } + /** + * Converts this application to an API application. + * @returns The API application representation of this application. + */ // eslint-disable-next-line @typescript-eslint/require-await async toAPI(): Promise { return { diff --git a/database/entities/Emoji.ts b/database/entities/Emoji.ts index 920c0518..f0a27dea 100644 --- a/database/entities/Emoji.ts +++ b/database/entities/Emoji.ts @@ -1,22 +1,41 @@ import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; import { APIEmoji } from "~types/entities/emoji"; +/** + * Represents an emoji entity in the database. + */ @Entity({ name: "emojis", }) export class Emoji extends BaseEntity { + /** + * The unique identifier for the emoji. + */ @PrimaryGeneratedColumn("uuid") id!: string; + /** + * The shortcode for the emoji. + */ @Column("varchar") shortcode!: string; + /** + * The URL for the emoji. + */ @Column("varchar") url!: string; + /** + * Whether the emoji is visible in the picker. + */ @Column("boolean") visible_in_picker!: boolean; + /** + * Converts the emoji to an APIEmoji object. + * @returns The APIEmoji object. + */ // eslint-disable-next-line @typescript-eslint/require-await async toAPI(): Promise { return { diff --git a/database/entities/Instance.ts b/database/entities/Instance.ts index 6d0ac79d..71efe017 100644 --- a/database/entities/Instance.ts +++ b/database/entities/Instance.ts @@ -8,16 +8,28 @@ import { import { APIInstance } from "~types/entities/instance"; import { User } from "./User"; +/** + * Represents an instance in the database. + */ @Entity({ name: "instances", }) export class Instance extends BaseEntity { + /** + * The unique identifier of the instance. + */ @PrimaryGeneratedColumn("uuid") id!: string; + /** + * The contact account associated with the instance. + */ @ManyToOne(() => User, user => user.id) contact_account!: User; + /** + * The configuration of the instance. + */ @Column("jsonb", { default: { media_attachments: { @@ -43,6 +55,10 @@ export class Instance extends BaseEntity { }) configuration!: APIInstance["configuration"]; + /** + * Converts the instance to an API instance. + * @returns The API instance. + */ async toAPI(): Promise { return { uri: "", diff --git a/database/entities/RawObject.ts b/database/entities/RawObject.ts index fd894634..74e5cf22 100644 --- a/database/entities/RawObject.ts +++ b/database/entities/RawObject.ts @@ -8,18 +8,29 @@ import { APIAccount } from "~types/entities/account"; import { APIEmoji } from "~types/entities/emoji"; /** - * Stores an ActivityPub object as raw JSON-LD data + * Represents a raw ActivityPub object in the database. */ @Entity({ name: "objects", }) export class RawObject extends BaseEntity { + /** + * The unique identifier of the object. + */ @PrimaryGeneratedColumn("uuid") id!: string; + /** + * The data associated with the object. + */ @Column("jsonb") data!: APObject; + /** + * Retrieves a RawObject instance by its ID. + * @param id The ID of the RawObject to retrieve. + * @returns A Promise that resolves to the RawObject instance, or undefined if not found. + */ static async getById(id: string) { return await RawObject.createQueryBuilder("object") .where("object.data->>'id' = :id", { @@ -28,6 +39,10 @@ export class RawObject extends BaseEntity { .getOne(); } + /** + * Parses the emojis associated with the object. + * @returns A Promise that resolves to an array of APIEmoji objects. + */ // eslint-disable-next-line @typescript-eslint/require-await async parseEmojis() { const emojis = this.data.tag as { @@ -51,6 +66,10 @@ export class RawObject extends BaseEntity { })) as APIEmoji[]; } + /** + * Converts the RawObject instance to an APIStatus object. + * @returns A Promise that resolves to the APIStatus object. + */ async toAPI(): Promise { const mentions = ( await Promise.all( @@ -101,6 +120,10 @@ export class RawObject extends BaseEntity { }; } + /** + * Determines whether the object is filtered based on the note filters in the configuration. + * @returns A Promise that resolves to a boolean indicating whether the object is filtered. + */ async isObjectFiltered() { const config = getConfig(); @@ -130,6 +153,11 @@ export class RawObject extends BaseEntity { return filter_result.includes(true); } + /** + * Determines whether a RawObject instance with the given ID exists in the database. + * @param id The ID of the RawObject to check for existence. + * @returns A Promise that resolves to a boolean indicating whether the RawObject exists. + */ static async exists(id: string) { return !!(await RawObject.getById(id)); } diff --git a/database/entities/Relationship.ts b/database/entities/Relationship.ts index 8fcb5ce5..5bfe53bd 100644 --- a/database/entities/Relationship.ts +++ b/database/entities/Relationship.ts @@ -17,61 +17,85 @@ import { APIRelationship } from "~types/entities/relationship"; name: "relationships", }) export class Relationship extends BaseEntity { + /** The unique identifier for the relationship. */ @PrimaryGeneratedColumn("uuid") id!: string; + /** The user who owns the relationship. */ @ManyToOne(() => User, user => user.relationships) owner!: User; + /** The user who is the subject of the relationship. */ @ManyToOne(() => User) subject!: User; + /** Whether the owner is following the subject. */ @Column("boolean") following!: boolean; + /** Whether the owner is showing reblogs from the subject. */ @Column("boolean") showing_reblogs!: boolean; + /** Whether the owner is receiving notifications from the subject. */ @Column("boolean") notifying!: boolean; + /** Whether the owner is followed by the subject. */ @Column("boolean") followed_by!: boolean; + /** Whether the owner is blocking the subject. */ @Column("boolean") blocking!: boolean; + /** Whether the owner is blocked by the subject. */ @Column("boolean") blocked_by!: boolean; + /** Whether the owner is muting the subject. */ @Column("boolean") muting!: boolean; + /** Whether the owner is muting notifications from the subject. */ @Column("boolean") muting_notifications!: boolean; + /** Whether the owner has requested to follow the subject. */ @Column("boolean") requested!: boolean; + /** Whether the owner is blocking the subject's domain. */ @Column("boolean") domain_blocking!: boolean; + /** Whether the owner has endorsed the subject. */ @Column("boolean") endorsed!: boolean; + /** The languages the owner has specified for the subject. */ @Column("jsonb") languages!: string[]; + /** A note the owner has added for the subject. */ @Column("varchar") note!: string; + /** The date the relationship was created. */ @CreateDateColumn() created_at!: Date; + /** The date the relationship was last updated. */ @UpdateDateColumn() updated_at!: Date; - static async createNew(owner: User, other: User) { + /** + * Creates a new relationship between two users. + * @param owner The user who owns the relationship. + * @param other The user who is the subject of the relationship. + * @returns The newly created relationship. + */ + static async createNew(owner: User, other: User): Promise { const newRela = new Relationship(); newRela.owner = owner; newRela.subject = other; @@ -94,6 +118,10 @@ export class Relationship extends BaseEntity { return newRela; } + /** + * Converts the relationship to an API-friendly format. + * @returns The API-friendly relationship. + */ // eslint-disable-next-line @typescript-eslint/require-await async toAPI(): Promise { return { diff --git a/database/entities/Status.ts b/database/entities/Status.ts index ab40d613..82d34790 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -22,81 +22,137 @@ import { RawActor } from "./RawActor"; const config = getConfig(); /** - * Stores ActivityPub notes + * Represents a status (i.e. a post) */ @Entity({ name: "statuses", }) export class Status extends BaseEntity { + /** + * The unique identifier for this status. + */ @PrimaryGeneratedColumn("uuid") id!: string; + /** + * The user account that created this status. + */ @ManyToOne(() => User, user => user.id) account!: User; + /** + * The date and time when this status was created. + */ @CreateDateColumn() created_at!: Date; + /** + * The date and time when this status was last updated. + */ @UpdateDateColumn() updated_at!: Date; + /** + * The status that this status is a reblog of, if any. + */ @ManyToOne(() => Status, status => status.id, { nullable: true, }) reblog?: Status; + /** + * The raw object associated with this status. + */ @ManyToOne(() => RawObject, { nullable: true, onDelete: "SET NULL", }) object!: RawObject; + /** + * Whether this status is a reblog. + */ @Column("boolean") isReblog!: boolean; + /** + * The content of this status. + */ @Column("varchar", { default: "", }) content!: string; + /** + * The visibility of this status. + */ @Column("varchar") visibility!: APIStatus["visibility"]; + /** + * The raw object that this status is a reply to, if any. + */ @ManyToOne(() => RawObject, { nullable: true, }) in_reply_to_post!: RawObject | null; + /** + * The raw actor that this status is a reply to, if any. + */ @ManyToOne(() => RawActor, { nullable: true, }) in_reply_to_account!: RawActor | null; + /** + * Whether this status is sensitive. + */ @Column("boolean") sensitive!: boolean; + /** + * The spoiler text for this status. + */ @Column("varchar", { default: "", }) spoiler_text!: string; + /** + * The application associated with this status, if any. + */ @ManyToOne(() => Application, app => app.id, { nullable: true, }) application!: Application | null; + /** + * The emojis associated with this status. + */ @ManyToMany(() => Emoji, emoji => emoji.id) @JoinTable() emojis!: Emoji[]; + /** + * The activities that have liked this status. + */ @ManyToMany(() => RawActivity, activity => activity.id) @JoinTable() likes!: RawActivity[]; + /** + * The activities that have announced this status. + */ @ManyToMany(() => RawActivity, activity => activity.id) @JoinTable() announces!: RawActivity[]; + /** + * Removes this status from the database. + * @param options The options for removing this status. + * @returns A promise that resolves when the status has been removed. + */ async remove(options?: RemoveOptions | undefined) { // Delete object // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition @@ -105,6 +161,11 @@ export class Status extends BaseEntity { return await super.remove(options); } + /** + * Creates a new status and saves it to the database. + * @param data The data for the new status. + * @returns A promise that resolves with the new status. + */ static async createNew(data: { account: User; application: Application | null; @@ -233,40 +294,11 @@ export class Status extends BaseEntity { return newStatus; } + /** + * Converts this status to an API status. + * @returns A promise that resolves with the API status. + */ async toAPI(): Promise { return await this.object.toAPI(); - /* return { - account: await this.account.toAPI(), - application: (await this.application?.toAPI()) ?? null, - bookmarked: false, - created_at: this.created_at.toISOString(), - emojis: await Promise.all( - this.emojis.map(async emoji => await emoji.toAPI()) - ), - favourited: false, - favourites_count: this.likes.length, - id: this.object.id, - in_reply_to_account_id: null, - in_reply_to_id: null, - language: null, - media_attachments: [], - mentions: [], - muted: false, - pinned: false, - poll: null, - reblog: this.isReblog ? (await this.reblog?.toAPI()) ?? null : null, - reblogged: false, - reblogs_count: this.announces.length, - replies_count: 0, - sensitive: false, - spoiler_text: "", - tags: [], - card: null, - content: this.content, - uri: `${config.http.base_url}/@${this.account.username}/${this.id}`, - url: `${config.http.base_url}/@${this.account.username}/${this.id}`, - visibility: "public", - quote: null, - }; */ } } diff --git a/database/entities/Token.ts b/database/entities/Token.ts index ab619fd3..813b828a 100644 --- a/database/entities/Token.ts +++ b/database/entities/Token.ts @@ -9,35 +9,49 @@ import { import { User } from "./User"; import { Application } from "./Application"; -export enum TokenType { - BEARER = "Bearer", -} - +/** + * Represents an access token for a user or application. + */ @Entity({ name: "tokens", }) export class Token extends BaseEntity { + /** The unique identifier for the token. */ @PrimaryGeneratedColumn("uuid") id!: string; + /** The type of token. */ @Column("varchar") token_type: TokenType = TokenType.BEARER; + /** The scope of the token. */ @Column("varchar") scope!: string; + /** The access token string. */ @Column("varchar") access_token!: string; + /** The authorization code used to obtain the token. */ @Column("varchar") code!: string; + /** The date and time the token was created. */ @CreateDateColumn() created_at!: Date; + /** The user associated with the token. */ @ManyToOne(() => User, user => user.id) user!: User; + /** The application associated with the token. */ @ManyToOne(() => Application, application => application.id) application!: Application; } + +/** + * The type of token. + */ +enum TokenType { + BEARER = "bearer", +} diff --git a/database/entities/User.ts b/database/entities/User.ts index 25c14b24..9ad0afa2 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -24,72 +24,129 @@ import { Relationship } from "./Relationship"; const config = getConfig(); /** + * Represents a user in the database. * Stores local and remote users */ @Entity({ name: "users", }) export class User extends BaseEntity { + /** + * The unique identifier for the user. + */ @PrimaryGeneratedColumn("uuid") id!: string; + /** + * The username for the user. + */ @Column("varchar", { unique: true, }) username!: string; + /** + * The display name for the user. + */ @Column("varchar") display_name!: string; + /** + * The password for the user. + */ @Column("varchar") password!: string; + /** + * The email address for the user. + */ @Column("varchar", { unique: true, }) email!: string; + /** + * The note for the user. + */ @Column("varchar", { default: "", }) note!: string; + /** + * Whether the user is an admin or not. + */ @Column("boolean", { default: false, }) is_admin!: boolean; + /** + * The source for the user. + */ @Column("jsonb") source!: APISource; + /** + * The avatar for the user. + */ @Column("varchar") avatar!: string; + /** + * The header for the user. + */ @Column("varchar") header!: string; + /** + * The date the user was created. + */ @CreateDateColumn() created_at!: Date; + /** + * The date the user was last updated. + */ @UpdateDateColumn() updated_at!: Date; + /** + * The public key for the user. + */ @Column("varchar") public_key!: string; + /** + * The private key for the user. + */ @Column("varchar") private_key!: string; + /** + * The relationships for the user. + */ @OneToMany(() => Relationship, relationship => relationship.owner) relationships!: Relationship[]; + /** + * The actor for the user. + */ @ManyToOne(() => RawActor, actor => actor.id) actor!: RawActor; + /** + * The pinned notes for the user. + */ @ManyToMany(() => RawObject, object => object.id) @JoinTable() pinned_notes!: RawObject[]; + /** + * Gets a user by actor ID. + * @param id The actor ID to search for. + * @returns The user with the given actor ID. + */ static async getByActorId(id: string) { return await User.createQueryBuilder("user") // Objects is a many-to-many relationship @@ -103,6 +160,11 @@ export class User extends BaseEntity { .getOne(); } + /** + * Creates a new user. + * @param data The data for the new user. + * @returns The newly created user. + */ static async createNew(data: { username: string; display_name?: string; @@ -140,6 +202,11 @@ export class User extends BaseEntity { return user; } + /** + * Retrieves a user from a token. + * @param access_token The access token to retrieve the user from. + * @returns The user associated with the given access token. + */ static async retrieveFromToken(access_token: string) { if (!access_token) return null; @@ -160,6 +227,11 @@ export class User extends BaseEntity { return token.user; } + /** + * Gets the relationship to another user. + * @param other The other user to get the relationship to. + * @returns The relationship to the other user. + */ async getRelationshipToOtherUser(other: User) { const relationship = await Relationship.findOne({ where: { @@ -176,6 +248,11 @@ export class User extends BaseEntity { return relationship; } + /** + * Removes the user. + * @param options The options for removing the user. + * @returns The removed user. + */ async remove(options?: RemoveOptions | undefined) { // Clean up tokens const tokens = await Token.findBy({ @@ -210,6 +287,10 @@ export class User extends BaseEntity { return await super.remove(options); } + /** + * Gets the relationships for the user. + * @returns The relationships for the user. + */ async getRelationships() { const relationships = await Relationship.find({ where: { @@ -223,12 +304,14 @@ export class User extends BaseEntity { return relationships; } + /** + * Updates the actor for the user. + * @returns The updated actor. + */ async updateActor() { + const config = getConfig(); + // Check if actor exists - - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - - // Check is actor already exists const actorExists = await RawActor.getByActorId( `${config.http.base_url}/@${this.username}` ); @@ -278,6 +361,9 @@ export class User extends BaseEntity { return actor; } + /** + * Generates keys for the user. + */ async generateKeys(): Promise { // openssl genrsa -out private.pem 2048 // openssl rsa -in private.pem -outform PEM -pubout -out public.pem