diff --git a/database/entities/RawObject.ts b/database/entities/RawObject.ts index 53aafa48..120ffb32 100644 --- a/database/entities/RawObject.ts +++ b/database/entities/RawObject.ts @@ -6,13 +6,14 @@ import { PrimaryGeneratedColumn, } from "typeorm"; import { APImage, APObject, DateTime } from "activitypub-types"; -import { getConfig } from "@config"; +import { ConfigType, getConfig } from "@config"; import { appendFile } from "fs/promises"; import { APIStatus } from "~types/entities/status"; import { RawActor } from "./RawActor"; import { APIAccount } from "~types/entities/account"; import { APIEmoji } from "~types/entities/emoji"; import { User } from "./User"; +import { Status } from "./Status"; /** * Represents a raw ActivityPub object in the database. @@ -171,4 +172,57 @@ export class RawObject extends BaseEntity { static async exists(id: string) { return !!(await RawObject.getById(id)); } + + /** + * Creates a RawObject instance from a Status object. + * DOES NOT SAVE THE OBJECT TO THE DATABASE. + * @param status The Status object to create the RawObject from. + * @returns A Promise that resolves to the RawObject instance. + */ + static createFromStatus(status: Status, config: ConfigType) { + const object = new RawObject(); + + object.data = { + id: `${config.http.base_url}/users/${status.account.username}/statuses/${status.id}`, + type: "Note", + summary: status.spoiler_text, + content: status.content, + inReplyTo: status.in_reply_to_post?.object.data.id, + published: new Date().toISOString(), + tag: [], + attributedTo: `${config.http.base_url}/users/${status.account.username}`, + }; + + // Map status mentions to ActivityPub Actor IDs + const mentionedUsers = status.mentions.map( + user => user.actor.data.id as string + ); + + object.data.to = mentionedUsers; + + if (status.visibility === "private") { + object.data.cc = [ + `${config.http.base_url}/users/${status.account.username}/followers`, + ]; + } else if (status.visibility === "direct") { + // Add nothing else + } else if (status.visibility === "public") { + object.data.to = [ + ...object.data.to, + "https://www.w3.org/ns/activitystreams#Public", + ]; + object.data.cc = [ + `${config.http.base_url}/users/${status.account.username}/followers`, + ]; + } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + else if (status.visibility === "unlisted") { + object.data.to = [ + ...object.data.to, + "https://www.w3.org/ns/activitystreams#Public", + ]; + } + + return object; + } } diff --git a/database/entities/Status.ts b/database/entities/Status.ts index 7714b25c..f8f18586 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -12,7 +12,7 @@ import { UpdateDateColumn, } from "typeorm"; import { APIStatus } from "~types/entities/status"; -import { User } from "./User"; +import { User, userRelations } from "./User"; import { Application } from "./Application"; import { Emoji } from "./Emoji"; import { RawActivity } from "./RawActivity"; @@ -27,7 +27,6 @@ export const statusRelations = [ "object", "in_reply_to_post", "instance", - "in_reply_to_account", "in_reply_to_post.account", "application", "emojis", @@ -36,6 +35,16 @@ export const statusRelations = [ "announces", ]; +export const statusAndUserRelations = [ + ...statusRelations, + ...[ + "account.actor", + "account.relationships", + "account.pinned_notes", + "account.instance", + ], +]; + /** * Represents a status (i.e. a post) */ @@ -122,14 +131,6 @@ export class Status extends BaseEntity { }) instance!: Instance | null; - /** - * The raw actor that this status is a reply to, if any. - */ - @ManyToOne(() => User, { - nullable: true, - }) - in_reply_to_account!: User | null; - /** * Whether this status is sensitive. */ @@ -343,26 +344,9 @@ export class Status extends BaseEntity { newStatus.mentions = []; newStatus.instance = data.account.instance; - newStatus.object = new RawObject(); - if (data.reply) { newStatus.in_reply_to_post = data.reply.status; - newStatus.in_reply_to_account = data.reply.user; } - - newStatus.object.data = { - id: `${config.http.base_url}/users/${data.account.username}/statuses/${newStatus.id}`, - type: "Note", - summary: data.spoiler_text, - content: data.content, - inReplyTo: data.reply?.status - ? data.reply.status.object.data.id - : undefined, - published: new Date().toISOString(), - tag: [], - attributedTo: `${config.http.base_url}/users/${data.account.username}`, - }; - // Get people mentioned in the content const mentionedPeople = [ ...data.content.matchAll(/@([a-zA-Z0-9_]+)/g), @@ -370,79 +354,45 @@ export class Status extends BaseEntity { return `${config.http.base_url}/users/${match[1]}`; }); - // Map this to Users - const mentionedUsers = ( - await Promise.all( - mentionedPeople.map(async person => { - // Check if post is in format @username or @username@instance.com - // If is @username, the user is a local user - const instanceUrl = - person.split("@").length === 3 - ? person.split("@")[2] - : null; + // Get list of mentioned users + await Promise.all( + mentionedPeople.map(async person => { + // Check if post is in format @username or @username@instance.com + // If is @username, the user is a local user + const instanceUrl = + person.split("@").length === 3 + ? person.split("@")[2] + : null; - if (instanceUrl) { - const user = await User.findOne({ - where: { - username: person.split("@")[1], - // If contains instanceUrl - instance: { - base_url: instanceUrl, - }, + if (instanceUrl) { + const user = await User.findOne({ + where: { + username: person.split("@")[1], + // If contains instanceUrl + instance: { + base_url: instanceUrl, }, - relations: { - actor: true, - instance: true, - }, - }); + }, + relations: userRelations, + }); - newStatus.mentions.push(user as User); + newStatus.mentions.push(user as User); + } else { + const user = await User.findOne({ + where: { + username: person.split("@")[1], + }, + relations: userRelations, + }); - return user?.actor.data.id; - } else { - const user = await User.findOne({ - where: { - username: person.split("@")[1], - }, - relations: { - actor: true, - }, - }); + newStatus.mentions.push(user as User); + } + }) + ); - newStatus.mentions.push(user as User); + const object = RawObject.createFromStatus(newStatus, config); - return user?.actor.data.id; - } - }) - ) - ).map(user => user as string); - - newStatus.object.data.to = mentionedUsers; - - if (data.visibility === "private") { - newStatus.object.data.cc = [ - `${config.http.base_url}/users/${data.account.username}/followers`, - ]; - } else if (data.visibility === "direct") { - // Add nothing else - } else if (data.visibility === "public") { - newStatus.object.data.to = [ - ...newStatus.object.data.to, - "https://www.w3.org/ns/activitystreams#Public", - ]; - newStatus.object.data.cc = [ - `${config.http.base_url}/users/${data.account.username}/followers`, - ]; - } - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - else if (data.visibility === "unlisted") { - newStatus.object.data.to = [ - ...newStatus.object.data.to, - "https://www.w3.org/ns/activitystreams#Public", - ]; - } - - // TODO: Add default language + newStatus.object = object; await newStatus.object.save(); await newStatus.save(); return newStatus; @@ -453,11 +403,57 @@ export class Status extends BaseEntity { * @returns A promise that resolves with the API status. */ async toAPI(): Promise { + const reblogCount = await Status.count({ + where: { + reblog: { + id: this.id, + }, + }, + relations: ["reblog"], + }); + + const repliesCount = await Status.count({ + where: { + in_reply_to_post: { + id: this.id, + }, + }, + relations: ["in_reply_to_post"], + }); + return { - ...(await this.object.toAPI()), id: this.id, in_reply_to_id: this.in_reply_to_post?.id || null, in_reply_to_account_id: this.in_reply_to_post?.account.id || null, + account: await this.account.toAPI(), + created_at: new Date(this.created_at).toISOString(), + application: (await this.application?.toAPI()) || null, + card: null, + content: this.content, + emojis: await Promise.all(this.emojis.map(emoji => emoji.toAPI())), + favourited: false, + favourites_count: 0, + media_attachments: [], + mentions: await Promise.all( + this.mentions.map(async m => await m.toAPI()) + ), + language: null, + muted: false, + pinned: this.account.pinned_notes.some(note => note.id === this.id), + poll: null, + reblog: this.reblog ? await this.reblog.toAPI() : null, + reblogged: !!this.reblog, + reblogs_count: reblogCount, + replies_count: repliesCount, + sensitive: false, + spoiler_text: "", + tags: [], + uri: `${config.http.base_url}/users/${this.account.username}/statuses/${this.id}`, + visibility: "public", + url: `${config.http.base_url}/users/${this.account.username}/statuses/${this.id}`, + bookmarked: false, + quote: null, + quote_id: undefined, }; } } diff --git a/database/entities/User.ts b/database/entities/User.ts index 4778c86d..fb42427d 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -19,13 +19,19 @@ import { APCollectionPage, APOrderedCollectionPage, } from "activitypub-types"; -import { RawObject } from "./RawObject"; import { Token } from "./Token"; import { Status, statusRelations } from "./Status"; import { APISource } from "~types/entities/source"; import { Relationship } from "./Relationship"; import { Instance } from "./Instance"; +export const userRelations = [ + "actor", + "relationships", + "pinned_notes", + "instance", +]; + /** * Represents a user in the database. * Stores local and remote users @@ -156,9 +162,9 @@ export class User extends BaseEntity { /** * The pinned notes for the user. */ - @ManyToMany(() => RawObject, object => object.id) + @ManyToMany(() => Status, status => status.id) @JoinTable() - pinned_notes!: RawObject[]; + pinned_notes!: Status[]; /** * Get the user's avatar in raw URL format @@ -296,6 +302,8 @@ export class User extends BaseEntity { fields: [], }; + user.pinned_notes = []; + await user.generateKeys(); await user.save(); await user.updateActor(); @@ -315,12 +323,7 @@ export class User extends BaseEntity { where: { access_token, }, - relations: { - user: { - relationships: true, - actor: true, - }, - }, + relations: userRelations.map(r => `user.${r}`), }); if (!token) return null; @@ -524,11 +527,48 @@ export class User extends BaseEntity { relations: ["owner"], }); + const statusCount = await Status.count({ + where: { + account: { + id: this.id, + }, + }, + relations: ["account"], + }); + + const config = getConfig(); + return { - ...(await this.actor.toAPIAccount(isOwnAccount)), id: this.id, + username: this.username, + display_name: this.display_name, + note: this.note, + url: `${config.http.base_url}/users/${this.username}`, + avatar: this.getAvatarUrl(config) || config.defaults.avatar, + header: this.getHeaderUrl(config) || config.defaults.header, + locked: false, + created_at: new Date(this.created_at).toISOString(), followers_count: follower_count, following_count: following_count, + statuses_count: statusCount, + emojis: [], + fields: [], + bot: false, + source: isOwnAccount ? this.source : undefined, + avatar_static: "", + header_static: "", + acct: + this.instance === null + ? `${this.username}` + : `${this.username}@${this.instance.base_url}`, + limited: false, + moved: null, + noindex: false, + suspended: false, + discoverable: undefined, + mute_expires_at: undefined, + group: false, + role: undefined, }; } } diff --git a/server/api/.well-known/webfinger/index.ts b/server/api/.well-known/webfinger/index.ts index 2b8fb4a1..d9ecb5c6 100644 --- a/server/api/.well-known/webfinger/index.ts +++ b/server/api/.well-known/webfinger/index.ts @@ -1,6 +1,6 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { getConfig, getHost } from "@config"; import { applyConfig } from "@api"; @@ -34,7 +34,10 @@ export default async ( return errorResponse("User is a remote user", 404); } - const user = await User.findOneBy({ username: requestedUser.split("@")[0] }); + const user = await User.findOne({ + where: { username: requestedUser.split("@")[0] }, + relations: userRelations + }); if (!user) { return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/block.ts b/server/api/api/v1/accounts/[id]/block.ts index 25c39232..fe4cba5f 100644 --- a/server/api/api/v1/accounts/[id]/block.ts +++ b/server/api/api/v1/accounts/[id]/block.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -29,8 +29,11 @@ export default async ( if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/follow.ts b/server/api/api/v1/accounts/[id]/follow.ts index 0abf6b5e..3ac51e43 100644 --- a/server/api/api/v1/accounts/[id]/follow.ts +++ b/server/api/api/v1/accounts/[id]/follow.ts @@ -2,7 +2,7 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -36,8 +36,11 @@ export default async ( languages?: string[]; }>(req); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/index.ts b/server/api/api/v1/accounts/[id]/index.ts index c0f0c8f8..6dc4e479 100644 --- a/server/api/api/v1/accounts/[id]/index.ts +++ b/server/api/api/v1/accounts/[id]/index.ts @@ -1,6 +1,6 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -28,8 +28,11 @@ export default async ( let foundUser: User | null; try { - foundUser = await User.findOneBy({ - id, + foundUser = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); } catch (e) { return errorResponse("Invalid ID", 404); diff --git a/server/api/api/v1/accounts/[id]/mute.ts b/server/api/api/v1/accounts/[id]/mute.ts index 99759de0..39a99dbe 100644 --- a/server/api/api/v1/accounts/[id]/mute.ts +++ b/server/api/api/v1/accounts/[id]/mute.ts @@ -2,7 +2,7 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -36,8 +36,11 @@ export default async ( duration: number; }>(req); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/note.ts b/server/api/api/v1/accounts/[id]/note.ts index 2aeec023..db7f76e9 100644 --- a/server/api/api/v1/accounts/[id]/note.ts +++ b/server/api/api/v1/accounts/[id]/note.ts @@ -2,7 +2,7 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -34,8 +34,11 @@ export default async ( comment: string; }>(req); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/pin.ts b/server/api/api/v1/accounts/[id]/pin.ts index 76f21186..d894129d 100644 --- a/server/api/api/v1/accounts/[id]/pin.ts +++ b/server/api/api/v1/accounts/[id]/pin.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -29,8 +29,11 @@ export default async ( if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/remove_from_followers.ts b/server/api/api/v1/accounts/[id]/remove_from_followers.ts index f4beba31..d2318dca 100644 --- a/server/api/api/v1/accounts/[id]/remove_from_followers.ts +++ b/server/api/api/v1/accounts/[id]/remove_from_followers.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -29,8 +29,11 @@ export default async ( if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/statuses.ts b/server/api/api/v1/accounts/[id]/statuses.ts index 6d08114c..6d2bbee8 100644 --- a/server/api/api/v1/accounts/[id]/statuses.ts +++ b/server/api/api/v1/accounts/[id]/statuses.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { Status, statusRelations } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { Status, statusAndUserRelations } from "~database/entities/Status"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -42,8 +42,11 @@ export default async ( tagged?: string; } = matchedRoute.query; - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); @@ -60,7 +63,7 @@ export default async ( }, isReblog: exclude_reblogs ? true : undefined, }, - relations: statusRelations, + relations: statusAndUserRelations, order: { created_at: "DESC", }, diff --git a/server/api/api/v1/accounts/[id]/unblock.ts b/server/api/api/v1/accounts/[id]/unblock.ts index b28f9f28..b03d2121 100644 --- a/server/api/api/v1/accounts/[id]/unblock.ts +++ b/server/api/api/v1/accounts/[id]/unblock.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -29,8 +29,11 @@ export default async ( if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/unfollow.ts b/server/api/api/v1/accounts/[id]/unfollow.ts index faace4fd..60ac83cc 100644 --- a/server/api/api/v1/accounts/[id]/unfollow.ts +++ b/server/api/api/v1/accounts/[id]/unfollow.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -29,8 +29,11 @@ export default async ( if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/unmute.ts b/server/api/api/v1/accounts/[id]/unmute.ts index 1ad64bb2..2a84bc7a 100644 --- a/server/api/api/v1/accounts/[id]/unmute.ts +++ b/server/api/api/v1/accounts/[id]/unmute.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -29,8 +29,11 @@ export default async ( if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/accounts/[id]/unpin.ts b/server/api/api/v1/accounts/[id]/unpin.ts index c20cecc5..f51d0e0f 100644 --- a/server/api/api/v1/accounts/[id]/unpin.ts +++ b/server/api/api/v1/accounts/[id]/unpin.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -29,8 +29,11 @@ export default async ( if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOneBy({ - id, + const user = await User.findOne({ + where: { + id, + }, + relations: userRelations, }); if (!user) return errorResponse("User not found", 404); diff --git a/server/api/api/v1/statuses/[id]/index.ts b/server/api/api/v1/statuses/[id]/index.ts index 768a156f..c862da7c 100644 --- a/server/api/api/v1/statuses/[id]/index.ts +++ b/server/api/api/v1/statuses/[id]/index.ts @@ -1,7 +1,7 @@ import { applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { Status, statusRelations } from "~database/entities/Status"; +import { Status, statusAndUserRelations } from "~database/entities/Status"; import { User } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; @@ -35,7 +35,7 @@ export default async ( where: { id, }, - relations: statusRelations, + relations: statusAndUserRelations, }); } catch (e) { return errorResponse("Invalid ID", 404); diff --git a/server/api/api/v1/timelines/home.ts b/server/api/api/v1/timelines/home.ts index cfebf005..dc389e95 100644 --- a/server/api/api/v1/timelines/home.ts +++ b/server/api/api/v1/timelines/home.ts @@ -3,7 +3,7 @@ import { applyConfig } from "@api"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { FindManyOptions } from "typeorm"; -import { Status, statusRelations } from "~database/entities/Status"; +import { Status, statusAndUserRelations } from "~database/entities/Status"; import { User } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; @@ -60,7 +60,7 @@ export default async (req: Request): Promise => { created_at: "DESC", }, take: limit, - relations: statusRelations, + relations: statusAndUserRelations, }; if (max_id) { diff --git a/server/api/api/v1/timelines/public.ts b/server/api/api/v1/timelines/public.ts index ad102cad..fb5a4e11 100644 --- a/server/api/api/v1/timelines/public.ts +++ b/server/api/api/v1/timelines/public.ts @@ -3,7 +3,7 @@ import { applyConfig } from "@api"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { FindManyOptions, IsNull, Not } from "typeorm"; -import { Status, statusRelations } from "~database/entities/Status"; +import { Status, statusAndUserRelations } from "~database/entities/Status"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -56,7 +56,7 @@ export default async (req: Request): Promise => { created_at: "DESC", }, take: limit, - relations: statusRelations, + relations: statusAndUserRelations, }; if (max_id) { diff --git a/server/api/auth/login/index.ts b/server/api/auth/login/index.ts index 4f400578..2f631e66 100644 --- a/server/api/auth/login/index.ts +++ b/server/api/auth/login/index.ts @@ -4,7 +4,7 @@ import { MatchedRoute } from "bun"; import { randomBytes } from "crypto"; import { Application } from "~database/entities/Application"; import { Token } from "~database/entities/Token"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -45,8 +45,11 @@ export default async ( return errorResponse("Missing username or password", 400); // Get user - const user = await User.findOneBy({ - email, + const user = await User.findOne({ + where: { + email, + }, + relations: userRelations, }); if (!user || !(await Bun.password.verify(password, user.password || ""))) @@ -70,5 +73,5 @@ export default async ( await token.save(); // Redirect back to application - return Response.redirect(`${redirect_uri}?code=${token.code}`); + return Response.redirect(`${redirect_uri}?code=${token.code}`, 302); }; diff --git a/server/api/users/[username]/actor/index.ts b/server/api/users/[username]/actor/index.ts index f2991fbf..669dc0cd 100644 --- a/server/api/users/[username]/actor/index.ts +++ b/server/api/users/[username]/actor/index.ts @@ -1,6 +1,6 @@ import { errorResponse, jsonLdResponse } from "@response"; import { MatchedRoute } from "bun"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { getConfig, getHost } from "@config"; import { applyConfig } from "@api"; @@ -34,7 +34,10 @@ export default async ( const username = matchedRoute.params.username; - const user = await User.findOneBy({ username }); + const user = await User.findOne({ + where: { username }, + relations: userRelations, + }); if (!user) { return errorResponse("User not found", 404); diff --git a/server/api/users/[username]/outbox/index.ts b/server/api/users/[username]/outbox/index.ts index 492a5364..3a855163 100644 --- a/server/api/users/[username]/outbox/index.ts +++ b/server/api/users/[username]/outbox/index.ts @@ -1,6 +1,6 @@ import { errorResponse, jsonLdResponse } from "@response"; import { MatchedRoute } from "bun"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; import { getHost } from "@config"; import { NodeObject, compact } from "jsonld"; import { RawActivity } from "~database/entities/RawActivity"; @@ -30,7 +30,10 @@ export default async ( const min_id = matchedRoute.query.min_id || false; const max_id = matchedRoute.query.max_id || false; - const user = await User.findOneBy({ username }); + const user = await User.findOne({ + where: { username }, + relations: userRelations, + }); if (!user) { return errorResponse("User not found", 404); diff --git a/tests/actor.test.ts b/tests/actor.test.ts index a3c15f02..1455a2d3 100644 --- a/tests/actor.test.ts +++ b/tests/actor.test.ts @@ -5,7 +5,7 @@ import { APActor } from "activitypub-types"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { AppDataSource } from "~database/datasource"; import { RawActivity } from "~database/entities/RawActivity"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; const config = getConfig(); @@ -65,8 +65,11 @@ describe("POST /@test/actor", () => { afterAll(async () => { // Clean up user - const user = await User.findOneBy({ - username: "test", + const user = await User.findOne({ + where: { + username: "test", + }, + relations: userRelations, }); const activities = await RawActivity.createQueryBuilder("activity") diff --git a/tests/api.test.ts b/tests/api.test.ts index a627760f..d8bf09bd 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -278,7 +278,7 @@ describe("API Tests", () => { expect(account.created_at).toBeDefined(); expect(account.followers_count).toBe(0); expect(account.following_count).toBe(0); - expect(account.statuses_count).toBe(0); + expect(account.statuses_count).toBe(2); expect(account.note).toBe(""); expect(account.url).toBe( `${config.http.base_url}/users/${user.username}` diff --git a/tests/inbox.test.ts b/tests/inbox.test.ts index 7de0f7f8..d397febd 100644 --- a/tests/inbox.test.ts +++ b/tests/inbox.test.ts @@ -3,7 +3,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { AppDataSource } from "~database/datasource"; import { RawActivity } from "~database/entities/RawActivity"; import { Token } from "~database/entities/Token"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; const config = getConfig(); @@ -275,8 +275,11 @@ describe("POST /@test/inbox", () => { afterAll(async () => { // Clean up user - const user = await User.findOneBy({ - username: "test", + const user = await User.findOne({ + where: { + username: "test", + }, + relations: userRelations, }); // Clean up tokens diff --git a/tests/oauth.test.ts b/tests/oauth.test.ts index d7d66c07..e7bf0e56 100644 --- a/tests/oauth.test.ts +++ b/tests/oauth.test.ts @@ -3,7 +3,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { AppDataSource } from "~database/datasource"; import { Application } from "~database/entities/Application"; import { Token } from "~database/entities/Token"; -import { User } from "~database/entities/User"; +import { User, userRelations } from "~database/entities/User"; const config = getConfig(); @@ -150,8 +150,11 @@ describe("GET /api/v1/apps/verify_credentials", () => { afterAll(async () => { // Clean up user - const user = await User.findOneBy({ - username: "test", + const user = await User.findOne({ + where: { + username: "test", + }, + relations: userRelations, }); // Clean up tokens