mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
refactor(database): 🎨 Update database and schema names to be clearer
This commit is contained in:
parent
9081036c6d
commit
88b3ec7b43
50
cli.ts
50
cli.ts
|
|
@ -20,7 +20,7 @@ import {
|
|||
findManyUsers,
|
||||
} from "~database/entities/User";
|
||||
import { client, db } from "~drizzle/db";
|
||||
import { emoji, openIdAccount, status, user } from "~drizzle/schema";
|
||||
import { Emojis, Notes, OpenIdAccounts, Users } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
|
||||
await client.connect();
|
||||
|
|
@ -225,7 +225,7 @@ const cliBuilder = new CliBuilder([
|
|||
}
|
||||
}
|
||||
|
||||
await db.delete(user).where(eq(user.id, foundUser.id));
|
||||
await db.delete(Users).where(eq(Users.id, foundUser.id));
|
||||
|
||||
console.log(
|
||||
`${chalk.green("✓")} Deleted user ${chalk.blue(
|
||||
|
|
@ -640,13 +640,15 @@ const cliBuilder = new CliBuilder([
|
|||
return 1;
|
||||
}
|
||||
|
||||
const linkedOpenIdAccounts = await db.query.openIdAccount.findMany({
|
||||
where: (account, { eq, and }) =>
|
||||
and(
|
||||
eq(account.userId, user.id),
|
||||
eq(account.issuerId, issuerId),
|
||||
),
|
||||
});
|
||||
const linkedOpenIdAccounts = await db.query.OpenIdAccounts.findMany(
|
||||
{
|
||||
where: (account, { eq, and }) =>
|
||||
and(
|
||||
eq(account.userId, user.id),
|
||||
eq(account.issuerId, issuerId),
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
if (linkedOpenIdAccounts.find((a) => a.issuerId === issuerId)) {
|
||||
console.log(
|
||||
|
|
@ -658,7 +660,7 @@ const cliBuilder = new CliBuilder([
|
|||
}
|
||||
|
||||
// Connect the OpenID account
|
||||
await db.insert(openIdAccount).values({
|
||||
await db.insert(OpenIdAccounts).values({
|
||||
issuerId: issuerId,
|
||||
serverId: serverId,
|
||||
userId: user.id,
|
||||
|
|
@ -712,7 +714,7 @@ const cliBuilder = new CliBuilder([
|
|||
return 1;
|
||||
}
|
||||
|
||||
const account = await db.query.openIdAccount.findFirst({
|
||||
const account = await db.query.OpenIdAccounts.findFirst({
|
||||
where: (account, { eq }) => eq(account.serverId, id),
|
||||
});
|
||||
|
||||
|
|
@ -735,8 +737,8 @@ const cliBuilder = new CliBuilder([
|
|||
});
|
||||
|
||||
await db
|
||||
.delete(openIdAccount)
|
||||
.where(eq(openIdAccount.id, account.id));
|
||||
.delete(OpenIdAccounts)
|
||||
.where(eq(OpenIdAccounts.id, account.id));
|
||||
|
||||
console.log(
|
||||
`${chalk.green(
|
||||
|
|
@ -950,14 +952,14 @@ const cliBuilder = new CliBuilder([
|
|||
}
|
||||
|
||||
let instanceQuery: SQL<unknown> | undefined =
|
||||
sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`;
|
||||
sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NULL)`;
|
||||
|
||||
if (local && remote) {
|
||||
instanceQuery = undefined;
|
||||
} else if (local) {
|
||||
instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`;
|
||||
instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NULL)`;
|
||||
} else if (remote) {
|
||||
instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NOT NULL)`;
|
||||
instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NOT NULL)`;
|
||||
}
|
||||
|
||||
const notes = (
|
||||
|
|
@ -966,7 +968,7 @@ const cliBuilder = new CliBuilder([
|
|||
or(
|
||||
...fields.map((field) =>
|
||||
// @ts-expect-error
|
||||
like(status[field], `%${query}%`),
|
||||
like(Notes[field], `%${query}%`),
|
||||
),
|
||||
),
|
||||
instanceQuery,
|
||||
|
|
@ -1178,7 +1180,7 @@ const cliBuilder = new CliBuilder([
|
|||
}
|
||||
|
||||
// Check if emoji already exists
|
||||
const existingEmoji = await db.query.emoji.findFirst({
|
||||
const existingEmoji = await db.query.Emojis.findFirst({
|
||||
where: (emoji, { and, eq, isNull }) =>
|
||||
and(
|
||||
eq(emoji.shortcode, shortcode),
|
||||
|
|
@ -1246,7 +1248,7 @@ const cliBuilder = new CliBuilder([
|
|||
|
||||
const newEmoji = (
|
||||
await db
|
||||
.insert(emoji)
|
||||
.insert(Emojis)
|
||||
.values({
|
||||
shortcode: shortcode,
|
||||
url: newUrl,
|
||||
|
|
@ -1323,7 +1325,7 @@ const cliBuilder = new CliBuilder([
|
|||
return 1;
|
||||
}
|
||||
|
||||
const emojis = await db.query.emoji.findMany({
|
||||
const emojis = await db.query.Emojis.findMany({
|
||||
where: (emoji, { and, isNull, like }) =>
|
||||
and(
|
||||
like(emoji.shortcode, shortcode.replace(/\*/g, "%")),
|
||||
|
|
@ -1367,9 +1369,9 @@ const cliBuilder = new CliBuilder([
|
|||
}
|
||||
}
|
||||
|
||||
await db.delete(emoji).where(
|
||||
await db.delete(Emojis).where(
|
||||
inArray(
|
||||
emoji.id,
|
||||
Emojis.id,
|
||||
emojis.map((e) => e.id),
|
||||
),
|
||||
);
|
||||
|
|
@ -1426,7 +1428,7 @@ const cliBuilder = new CliBuilder([
|
|||
return 0;
|
||||
}
|
||||
|
||||
const emojis = await db.query.emoji.findMany({
|
||||
const emojis = await db.query.Emojis.findMany({
|
||||
where: (emoji, { isNull }) => isNull(emoji.instanceId),
|
||||
limit: Number(limit),
|
||||
});
|
||||
|
|
@ -1704,7 +1706,7 @@ const cliBuilder = new CliBuilder([
|
|||
).toString();
|
||||
|
||||
// Check if emoji already exists
|
||||
const existingEmoji = await db.query.emoji.findFirst({
|
||||
const existingEmoji = await db.query.Emojis.findFirst({
|
||||
where: (emoji, { and, eq, isNull }) =>
|
||||
and(
|
||||
eq(emoji.shortcode, shortcode),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import type { application } from "~drizzle/schema";
|
||||
import type { Applications } from "~drizzle/schema";
|
||||
import type { Application as APIApplication } from "~types/mastodon/application";
|
||||
|
||||
export type Application = InferSelectModel<typeof application>;
|
||||
export type Application = InferSelectModel<typeof Applications>;
|
||||
|
||||
/**
|
||||
* Retrieves the application associated with the given access token.
|
||||
|
|
@ -13,7 +13,7 @@ export type Application = InferSelectModel<typeof application>;
|
|||
export const getFromToken = async (
|
||||
token: string,
|
||||
): Promise<Application | null> => {
|
||||
const result = await db.query.token.findFirst({
|
||||
const result = await db.query.Tokens.findFirst({
|
||||
where: (tokens, { eq }) => eq(tokens.accessToken, token),
|
||||
with: {
|
||||
application: true,
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import type { InferSelectModel } from "drizzle-orm";
|
|||
import type * as Lysand from "lysand-types";
|
||||
import { MediaBackendType } from "media-manager";
|
||||
import { db } from "~drizzle/db";
|
||||
import { attachment } from "~drizzle/schema";
|
||||
import { Attachments } from "~drizzle/schema";
|
||||
import type { AsyncAttachment as APIAsyncAttachment } from "~types/mastodon/async_attachment";
|
||||
import type { Attachment as APIAttachment } from "~types/mastodon/attachment";
|
||||
|
||||
export type Attachment = InferSelectModel<typeof attachment>;
|
||||
export type Attachment = InferSelectModel<typeof Attachments>;
|
||||
|
||||
export const attachmentToAPI = (
|
||||
attachment: Attachment,
|
||||
|
|
@ -86,12 +86,12 @@ export const attachmentToLysand = (
|
|||
|
||||
export const attachmentFromLysand = async (
|
||||
attachmentToConvert: Lysand.ContentFormat,
|
||||
): Promise<InferSelectModel<typeof attachment>> => {
|
||||
): Promise<InferSelectModel<typeof Attachments>> => {
|
||||
const key = Object.keys(attachmentToConvert)[0];
|
||||
const value = attachmentToConvert[key];
|
||||
|
||||
const result = await db
|
||||
.insert(attachment)
|
||||
.insert(Attachments)
|
||||
.values({
|
||||
mimeType: key,
|
||||
url: value.content,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { type InferSelectModel, and, eq } from "drizzle-orm";
|
||||
import type * as Lysand from "lysand-types";
|
||||
import { db } from "~drizzle/db";
|
||||
import { emoji, instance } from "~drizzle/schema";
|
||||
import { Emojis, Instances } from "~drizzle/schema";
|
||||
import type { Emoji as APIEmoji } from "~types/mastodon/emoji";
|
||||
import { addInstanceIfNotExists } from "./Instance";
|
||||
|
||||
export type EmojiWithInstance = InferSelectModel<typeof emoji> & {
|
||||
instance: InferSelectModel<typeof instance> | null;
|
||||
export type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -18,7 +18,7 @@ export const parseEmojis = async (text: string) => {
|
|||
const regex = /:[a-zA-Z0-9_]+:/g;
|
||||
const matches = text.match(regex);
|
||||
if (!matches) return [];
|
||||
const emojis = await db.query.emoji.findMany({
|
||||
const emojis = await db.query.Emojis.findMany({
|
||||
where: (emoji, { eq, or }) =>
|
||||
or(
|
||||
...matches
|
||||
|
|
@ -45,27 +45,27 @@ export const fetchEmoji = async (
|
|||
): Promise<EmojiWithInstance> => {
|
||||
const existingEmoji = await db
|
||||
.select()
|
||||
.from(emoji)
|
||||
.innerJoin(instance, eq(emoji.instanceId, instance.id))
|
||||
.from(Emojis)
|
||||
.innerJoin(Instances, eq(Emojis.instanceId, Instances.id))
|
||||
.where(
|
||||
and(
|
||||
eq(emoji.shortcode, emojiToFetch.name),
|
||||
host ? eq(instance.baseUrl, host) : undefined,
|
||||
eq(Emojis.shortcode, emojiToFetch.name),
|
||||
host ? eq(Instances.baseUrl, host) : undefined,
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existingEmoji[0])
|
||||
return {
|
||||
...existingEmoji[0].Emoji,
|
||||
instance: existingEmoji[0].Instance,
|
||||
...existingEmoji[0].Emojis,
|
||||
instance: existingEmoji[0].Instances,
|
||||
};
|
||||
|
||||
const foundInstance = host ? await addInstanceIfNotExists(host) : null;
|
||||
|
||||
const result = (
|
||||
await db
|
||||
.insert(emoji)
|
||||
.insert(Emojis)
|
||||
.values({
|
||||
shortcode: emojiToFetch.name,
|
||||
url: Object.entries(emojiToFetch.url)[0][1].content,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type * as Lysand from "lysand-types";
|
||||
import { db } from "~drizzle/db";
|
||||
import { instance } from "~drizzle/schema";
|
||||
import { Instances } from "~drizzle/schema";
|
||||
|
||||
/**
|
||||
* Represents an instance in the database.
|
||||
|
|
@ -38,7 +38,7 @@ export const addInstanceIfNotExists = async (url: string) => {
|
|||
|
||||
return (
|
||||
await db
|
||||
.insert(instance)
|
||||
.insert(Instances)
|
||||
.values({
|
||||
baseUrl: host,
|
||||
name: metadata.name,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { config } from "config-manager";
|
|||
import { type InferSelectModel, and, eq } from "drizzle-orm";
|
||||
import type * as Lysand from "lysand-types";
|
||||
import { db } from "~drizzle/db";
|
||||
import { like, notification } from "~drizzle/schema";
|
||||
import { Likes, Notifications } from "~drizzle/schema";
|
||||
import type { StatusWithRelations } from "./Status";
|
||||
import type { UserWithRelations } from "./User";
|
||||
|
||||
export type Like = InferSelectModel<typeof like>;
|
||||
export type Like = InferSelectModel<typeof Likes>;
|
||||
|
||||
/**
|
||||
* Represents a Like entity in the database.
|
||||
|
|
@ -33,18 +33,18 @@ export const createLike = async (
|
|||
user: UserWithRelations,
|
||||
status: StatusWithRelations,
|
||||
) => {
|
||||
await db.insert(like).values({
|
||||
await db.insert(Likes).values({
|
||||
likedId: status.id,
|
||||
likerId: user.id,
|
||||
});
|
||||
|
||||
if (status.author.instanceId === user.instanceId) {
|
||||
// Notify the user that their post has been favourited
|
||||
await db.insert(notification).values({
|
||||
await db.insert(Notifications).values({
|
||||
accountId: user.id,
|
||||
type: "favourite",
|
||||
notifiedId: status.authorId,
|
||||
statusId: status.id,
|
||||
noteId: status.id,
|
||||
});
|
||||
} else {
|
||||
// TODO: Add database jobs for federating this
|
||||
|
|
@ -61,18 +61,18 @@ export const deleteLike = async (
|
|||
status: StatusWithRelations,
|
||||
) => {
|
||||
await db
|
||||
.delete(like)
|
||||
.where(and(eq(like.likedId, status.id), eq(like.likerId, user.id)));
|
||||
.delete(Likes)
|
||||
.where(and(eq(Likes.likedId, status.id), eq(Likes.likerId, user.id)));
|
||||
|
||||
// Notify the user that their post has been favourited
|
||||
await db
|
||||
.delete(notification)
|
||||
.delete(Notifications)
|
||||
.where(
|
||||
and(
|
||||
eq(notification.accountId, user.id),
|
||||
eq(notification.type, "favourite"),
|
||||
eq(notification.notifiedId, status.authorId),
|
||||
eq(notification.statusId, status.id),
|
||||
eq(Notifications.accountId, user.id),
|
||||
eq(Notifications.type, "favourite"),
|
||||
eq(Notifications.notifiedId, status.authorId),
|
||||
eq(Notifications.noteId, status.id),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import type { notification } from "~drizzle/schema";
|
||||
import type { Notifications } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
import type { Notification as APINotification } from "~types/mastodon/notification";
|
||||
import type { StatusWithRelations } from "./Status";
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
userToAPI,
|
||||
} from "./User";
|
||||
|
||||
export type Notification = InferSelectModel<typeof notification>;
|
||||
export type Notification = InferSelectModel<typeof Notifications>;
|
||||
|
||||
export type NotificationWithRelations = Notification & {
|
||||
status: StatusWithRelations | null;
|
||||
|
|
@ -20,9 +20,9 @@ export type NotificationWithRelations = Notification & {
|
|||
};
|
||||
|
||||
export const findManyNotifications = async (
|
||||
query: Parameters<typeof db.query.notification.findMany>[0],
|
||||
query: Parameters<typeof db.query.Notifications.findMany>[0],
|
||||
): Promise<NotificationWithRelations[]> => {
|
||||
const output = await db.query.notification.findMany({
|
||||
const output = await db.query.Notifications.findMany({
|
||||
...query,
|
||||
with: {
|
||||
...query?.with,
|
||||
|
|
@ -30,7 +30,7 @@ export const findManyNotifications = async (
|
|||
with: {
|
||||
...userRelations,
|
||||
},
|
||||
extras: userExtrasTemplate("notification_account"),
|
||||
extras: userExtrasTemplate("Notifications_account"),
|
||||
},
|
||||
},
|
||||
extras: {
|
||||
|
|
@ -42,7 +42,7 @@ export const findManyNotifications = async (
|
|||
output.map(async (notif) => ({
|
||||
...notif,
|
||||
account: transformOutputToUserWithRelations(notif.account),
|
||||
status: (await Note.fromId(notif.statusId))?.getStatus() ?? null,
|
||||
status: (await Note.fromId(notif.noteId))?.getStatus() ?? null,
|
||||
})),
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import type * as Lysand from "lysand-types";
|
||||
import { db } from "~drizzle/db";
|
||||
import { lysandObject } from "~drizzle/schema";
|
||||
import { LysandObjects } from "~drizzle/schema";
|
||||
import { findFirstUser } from "./User";
|
||||
|
||||
export type LysandObject = InferSelectModel<typeof lysandObject>;
|
||||
export type LysandObject = InferSelectModel<typeof LysandObjects>;
|
||||
|
||||
/**
|
||||
* Represents a Lysand object in the database.
|
||||
|
|
@ -29,7 +29,7 @@ export const createFromObject = async (
|
|||
where: (user, { eq }) => eq(user.uri, authorUri),
|
||||
});
|
||||
|
||||
return await db.insert(lysandObject).values({
|
||||
return await db.insert(LysandObjects).values({
|
||||
authorId: author?.id,
|
||||
createdAt: new Date(object.created_at).toISOString(),
|
||||
extensions: object.extensions,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
import type { Relationship as APIRelationship } from "~types/mastodon/relationship";
|
||||
import type { User } from "./User";
|
||||
|
||||
export type Relationship = InferSelectModel<typeof relationship>;
|
||||
export type Relationship = InferSelectModel<typeof Relationships>;
|
||||
|
||||
/**
|
||||
* Creates a new relationship between two users.
|
||||
|
|
@ -18,7 +18,7 @@ export const createNewRelationship = async (
|
|||
): Promise<Relationship> => {
|
||||
return (
|
||||
await db
|
||||
.insert(relationship)
|
||||
.insert(Relationships)
|
||||
.values({
|
||||
ownerId: owner.id,
|
||||
subjectId: other.id,
|
||||
|
|
@ -46,12 +46,12 @@ export const checkForBidirectionalRelationships = async (
|
|||
user2: User,
|
||||
createIfNotExists = true,
|
||||
): Promise<boolean> => {
|
||||
const relationship1 = await db.query.relationship.findFirst({
|
||||
const relationship1 = await db.query.Relationships.findFirst({
|
||||
where: (rel, { and, eq }) =>
|
||||
and(eq(rel.ownerId, user1.id), eq(rel.subjectId, user2.id)),
|
||||
});
|
||||
|
||||
const relationship2 = await db.query.relationship.findFirst({
|
||||
const relationship2 = await db.query.Relationships.findFirst({
|
||||
where: (rel, { and, eq }) =>
|
||||
and(eq(rel.ownerId, user2.id), eq(rel.subjectId, user1.id)),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ import {
|
|||
import { parse } from "marked";
|
||||
import { db } from "~drizzle/db";
|
||||
import {
|
||||
attachment,
|
||||
emojiToStatus,
|
||||
instance,
|
||||
notification,
|
||||
status,
|
||||
statusToMentions,
|
||||
user,
|
||||
Attachments,
|
||||
EmojiToNote,
|
||||
Instances,
|
||||
NoteToMentions,
|
||||
Notes,
|
||||
Notifications,
|
||||
Users,
|
||||
} from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
import { LogLevel } from "~packages/log-manager";
|
||||
|
|
@ -61,17 +61,17 @@ import {
|
|||
userRelations,
|
||||
} from "./User";
|
||||
|
||||
export type Status = InferSelectModel<typeof status>;
|
||||
export type Status = InferSelectModel<typeof Notes>;
|
||||
|
||||
export type StatusWithRelations = Status & {
|
||||
author: UserWithRelations;
|
||||
mentions: UserWithInstance[];
|
||||
attachments: InferSelectModel<typeof attachment>[];
|
||||
attachments: InferSelectModel<typeof Attachments>[];
|
||||
reblog: StatusWithoutRecursiveRelations | null;
|
||||
emojis: EmojiWithInstance[];
|
||||
likes: Like[];
|
||||
inReplyTo: Status | null;
|
||||
quoting: Status | null;
|
||||
reply: Status | null;
|
||||
quote: Status | null;
|
||||
application: Application | null;
|
||||
reblogCount: number;
|
||||
likeCount: number;
|
||||
|
|
@ -80,20 +80,20 @@ export type StatusWithRelations = Status & {
|
|||
|
||||
export type StatusWithoutRecursiveRelations = Omit<
|
||||
StatusWithRelations,
|
||||
"inReplyTo" | "quoting" | "reblog"
|
||||
"reply" | "quote" | "reblog"
|
||||
>;
|
||||
|
||||
export const noteExtras = {
|
||||
reblogCount:
|
||||
sql`(SELECT COUNT(*) FROM "Status" "status" WHERE "status"."reblogId" = "status".id)`.as(
|
||||
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes".id)`.as(
|
||||
"reblog_count",
|
||||
),
|
||||
likeCount:
|
||||
sql`(SELECT COUNT(*) FROM "Like" "like" WHERE "like"."likedId" = "status".id)`.as(
|
||||
sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes".id)`.as(
|
||||
"like_count",
|
||||
),
|
||||
replyCount:
|
||||
sql`(SELECT COUNT(*) FROM "Status" "status" WHERE "status"."inReplyToPostId" = "status".id)`.as(
|
||||
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes".id)`.as(
|
||||
"reply_count",
|
||||
),
|
||||
};
|
||||
|
|
@ -104,15 +104,15 @@ export const noteExtras = {
|
|||
* @returns
|
||||
*/
|
||||
export const findManyNotes = async (
|
||||
query: Parameters<typeof db.query.status.findMany>[0],
|
||||
query: Parameters<typeof db.query.Notes.findMany>[0],
|
||||
): Promise<StatusWithRelations[]> => {
|
||||
const output = await db.query.status.findMany({
|
||||
const output = await db.query.Notes.findMany({
|
||||
...query,
|
||||
with: {
|
||||
...query?.with,
|
||||
attachments: {
|
||||
where: (attachment, { eq }) =>
|
||||
eq(attachment.statusId, sql`"status"."id"`),
|
||||
eq(attachment.noteId, sql`"Notes"."id"`),
|
||||
},
|
||||
emojis: {
|
||||
with: {
|
||||
|
|
@ -127,7 +127,7 @@ export const findManyNotes = async (
|
|||
with: {
|
||||
...userRelations,
|
||||
},
|
||||
extras: userExtrasTemplate("status_author"),
|
||||
extras: userExtrasTemplate("Notes_author"),
|
||||
},
|
||||
mentions: {
|
||||
with: {
|
||||
|
|
@ -157,7 +157,7 @@ export const findManyNotes = async (
|
|||
user: {
|
||||
with: userRelations,
|
||||
extras: userExtrasTemplate(
|
||||
"status_reblog_mentions_user",
|
||||
"Notes_reblog_mentions_user",
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
@ -166,15 +166,15 @@ export const findManyNotes = async (
|
|||
with: {
|
||||
...userRelations,
|
||||
},
|
||||
extras: userExtrasTemplate("status_reblog_author"),
|
||||
extras: userExtrasTemplate("Notes_reblog_author"),
|
||||
},
|
||||
},
|
||||
extras: {
|
||||
...noteExtras,
|
||||
},
|
||||
},
|
||||
inReplyTo: true,
|
||||
quoting: true,
|
||||
reply: true,
|
||||
quote: true,
|
||||
},
|
||||
extras: {
|
||||
...noteExtras,
|
||||
|
|
@ -187,25 +187,17 @@ export const findManyNotes = async (
|
|||
author: transformOutputToUserWithRelations(post.author),
|
||||
mentions: post.mentions.map((mention) => ({
|
||||
...mention.user,
|
||||
endpoints: mention.user.endpoints as User["endpoints"],
|
||||
endpoints: mention.user.endpoints,
|
||||
})),
|
||||
emojis: (post.emojis ?? []).map(
|
||||
(emoji) =>
|
||||
(emoji as unknown as Record<string, object>)
|
||||
.emoji as EmojiWithInstance,
|
||||
),
|
||||
emojis: (post.emojis ?? []).map((emoji) => emoji.emoji),
|
||||
reblog: post.reblog && {
|
||||
...post.reblog,
|
||||
author: transformOutputToUserWithRelations(post.reblog.author),
|
||||
mentions: post.reblog.mentions.map((mention) => ({
|
||||
...mention.user,
|
||||
endpoints: mention.user.endpoints as User["endpoints"],
|
||||
endpoints: mention.user.endpoints,
|
||||
})),
|
||||
emojis: (post.reblog.emojis ?? []).map(
|
||||
(emoji) =>
|
||||
(emoji as unknown as Record<string, object>)
|
||||
.emoji as EmojiWithInstance,
|
||||
),
|
||||
emojis: (post.reblog.emojis ?? []).map((emoji) => emoji.emoji),
|
||||
reblogCount: Number(post.reblog.reblogCount),
|
||||
likeCount: Number(post.reblog.likeCount),
|
||||
replyCount: Number(post.reblog.replyCount),
|
||||
|
|
@ -217,15 +209,15 @@ export const findManyNotes = async (
|
|||
};
|
||||
|
||||
export const findFirstNote = async (
|
||||
query: Parameters<typeof db.query.status.findFirst>[0],
|
||||
query: Parameters<typeof db.query.Notes.findFirst>[0],
|
||||
): Promise<StatusWithRelations | null> => {
|
||||
const output = await db.query.status.findFirst({
|
||||
const output = await db.query.Notes.findFirst({
|
||||
...query,
|
||||
with: {
|
||||
...query?.with,
|
||||
attachments: {
|
||||
where: (attachment, { eq }) =>
|
||||
eq(attachment.statusId, sql`"status"."id"`),
|
||||
eq(attachment.noteId, sql`"Notes"."id"`),
|
||||
},
|
||||
emojis: {
|
||||
with: {
|
||||
|
|
@ -240,7 +232,7 @@ export const findFirstNote = async (
|
|||
with: {
|
||||
...userRelations,
|
||||
},
|
||||
extras: userExtrasTemplate("status_author"),
|
||||
extras: userExtrasTemplate("Notes_author"),
|
||||
},
|
||||
mentions: {
|
||||
with: {
|
||||
|
|
@ -270,7 +262,7 @@ export const findFirstNote = async (
|
|||
user: {
|
||||
with: userRelations,
|
||||
extras: userExtrasTemplate(
|
||||
"status_reblog_mentions_user",
|
||||
"Notes_reblog_mentions_user",
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
@ -279,15 +271,15 @@ export const findFirstNote = async (
|
|||
with: {
|
||||
...userRelations,
|
||||
},
|
||||
extras: userExtrasTemplate("status_reblog_author"),
|
||||
extras: userExtrasTemplate("Notes_reblog_author"),
|
||||
},
|
||||
},
|
||||
extras: {
|
||||
...noteExtras,
|
||||
},
|
||||
},
|
||||
inReplyTo: true,
|
||||
quoting: true,
|
||||
reply: true,
|
||||
quote: true,
|
||||
},
|
||||
extras: {
|
||||
...noteExtras,
|
||||
|
|
@ -302,25 +294,17 @@ export const findFirstNote = async (
|
|||
author: transformOutputToUserWithRelations(output.author),
|
||||
mentions: output.mentions.map((mention) => ({
|
||||
...mention.user,
|
||||
endpoints: mention.user.endpoints as User["endpoints"],
|
||||
endpoints: mention.user.endpoints,
|
||||
})),
|
||||
emojis: (output.emojis ?? []).map(
|
||||
(emoji) =>
|
||||
(emoji as unknown as Record<string, object>)
|
||||
.emoji as EmojiWithInstance,
|
||||
),
|
||||
emojis: (output.emojis ?? []).map((emoji) => emoji.emoji),
|
||||
reblog: output.reblog && {
|
||||
...output.reblog,
|
||||
author: transformOutputToUserWithRelations(output.reblog.author),
|
||||
mentions: output.reblog.mentions.map((mention) => ({
|
||||
...mention.user,
|
||||
endpoints: mention.user.endpoints as User["endpoints"],
|
||||
endpoints: mention.user.endpoints,
|
||||
})),
|
||||
emojis: (output.reblog.emojis ?? []).map(
|
||||
(emoji) =>
|
||||
(emoji as unknown as Record<string, object>)
|
||||
.emoji as EmojiWithInstance,
|
||||
),
|
||||
emojis: (output.reblog.emojis ?? []).map((emoji) => emoji.emoji),
|
||||
reblogCount: Number(output.reblog.reblogCount),
|
||||
likeCount: Number(output.reblog.likeCount),
|
||||
replyCount: Number(output.reblog.replyCount),
|
||||
|
|
@ -340,7 +324,7 @@ export const resolveNote = async (
|
|||
}
|
||||
|
||||
const foundStatus = await Note.fromSql(
|
||||
eq(status.uri, uri ?? providedNote?.uri ?? ""),
|
||||
eq(Notes.uri, uri ?? providedNote?.uri ?? ""),
|
||||
);
|
||||
|
||||
if (foundStatus) return foundStatus;
|
||||
|
|
@ -478,20 +462,20 @@ export const parseTextMentions = async (
|
|||
|
||||
const foundUsers = await db
|
||||
.select({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
baseUrl: instance.baseUrl,
|
||||
id: Users.id,
|
||||
username: Users.username,
|
||||
baseUrl: Instances.baseUrl,
|
||||
})
|
||||
.from(user)
|
||||
.leftJoin(instance, eq(user.instanceId, instance.id))
|
||||
.from(Users)
|
||||
.leftJoin(Instances, eq(Users.instanceId, Instances.id))
|
||||
.where(
|
||||
or(
|
||||
...mentionedPeople.map((person) =>
|
||||
and(
|
||||
eq(user.username, person?.[1] ?? ""),
|
||||
eq(Users.username, person?.[1] ?? ""),
|
||||
isLocal(person?.[2])
|
||||
? isNull(user.instanceId)
|
||||
: eq(instance.baseUrl, person?.[2] ?? ""),
|
||||
? isNull(Users.instanceId)
|
||||
: eq(Instances.baseUrl, person?.[2] ?? ""),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -701,10 +685,10 @@ export const editStatus = async (
|
|||
// Connect emojis
|
||||
for (const emoji of data.emojis) {
|
||||
await db
|
||||
.insert(emojiToStatus)
|
||||
.insert(EmojiToNote)
|
||||
.values({
|
||||
emojiId: emoji.id,
|
||||
statusId: updated.id,
|
||||
noteId: updated.id,
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
|
@ -712,9 +696,9 @@ export const editStatus = async (
|
|||
// Connect mentions
|
||||
for (const mention of mentions) {
|
||||
await db
|
||||
.insert(statusToMentions)
|
||||
.insert(NoteToMentions)
|
||||
.values({
|
||||
statusId: updated.id,
|
||||
noteId: updated.id,
|
||||
userId: mention.id,
|
||||
})
|
||||
.execute();
|
||||
|
|
@ -723,28 +707,28 @@ export const editStatus = async (
|
|||
// Send notifications for mentioned local users
|
||||
for (const mention of mentions ?? []) {
|
||||
if (mention.instanceId === null) {
|
||||
await db.insert(notification).values({
|
||||
await db.insert(Notifications).values({
|
||||
accountId: statusToEdit.authorId,
|
||||
notifiedId: mention.id,
|
||||
type: "mention",
|
||||
statusId: updated.id,
|
||||
noteId: updated.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Set attachment parents
|
||||
await db
|
||||
.update(attachment)
|
||||
.update(Attachments)
|
||||
.set({
|
||||
statusId: updated.id,
|
||||
noteId: updated.id,
|
||||
})
|
||||
.where(inArray(attachment.id, data.media_attachments ?? []));
|
||||
.where(inArray(Attachments.id, data.media_attachments ?? []));
|
||||
|
||||
return await Note.fromId(updated.id);
|
||||
};
|
||||
|
||||
export const isFavouritedBy = async (status: Status, user: User) => {
|
||||
return !!(await db.query.like.findFirst({
|
||||
return !!(await db.query.Likes.findFirst({
|
||||
where: (like, { and, eq }) =>
|
||||
and(eq(like.likerId, user.id), eq(like.likedId, status.id)),
|
||||
}));
|
||||
|
|
@ -779,8 +763,8 @@ export const statusToLysand = (status: StatusWithRelations): Lysand.Note => {
|
|||
),
|
||||
is_sensitive: status.sensitive,
|
||||
mentions: status.mentions.map((mention) => mention.uri || ""),
|
||||
quotes: getStatusUri(status.quoting) ?? undefined,
|
||||
replies_to: getStatusUri(status.inReplyTo) ?? undefined,
|
||||
quotes: getStatusUri(status.quote) ?? undefined,
|
||||
replies_to: getStatusUri(status.reply) ?? undefined,
|
||||
subject: status.spoilerText,
|
||||
visibility: status.visibility as Lysand.Visibility,
|
||||
extensions: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import type { token } from "~drizzle/schema";
|
||||
import type { Tokens } from "~drizzle/schema";
|
||||
|
||||
/**
|
||||
* The type of token.
|
||||
|
|
@ -8,4 +8,4 @@ export enum TokenType {
|
|||
BEARER = "Bearer",
|
||||
}
|
||||
|
||||
export type Token = InferSelectModel<typeof token>;
|
||||
export type Token = InferSelectModel<typeof Tokens>;
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import { htmlToText } from "html-to-text";
|
|||
import type * as Lysand from "lysand-types";
|
||||
import { db } from "~drizzle/db";
|
||||
import {
|
||||
application,
|
||||
emojiToUser,
|
||||
instance,
|
||||
notification,
|
||||
relationship,
|
||||
token,
|
||||
user,
|
||||
Applications,
|
||||
EmojiToUser,
|
||||
Instances,
|
||||
Notifications,
|
||||
Relationships,
|
||||
Tokens,
|
||||
Users,
|
||||
} from "~drizzle/schema";
|
||||
import { LogLevel } from "~packages/log-manager";
|
||||
import type { Account as APIAccount } from "~types/mastodon/account";
|
||||
|
|
@ -31,14 +31,14 @@ import { addInstanceIfNotExists } from "./Instance";
|
|||
import { createNewRelationship } from "./Relationship";
|
||||
import type { Token } from "./Token";
|
||||
|
||||
export type User = InferSelectModel<typeof user>;
|
||||
export type User = InferSelectModel<typeof Users>;
|
||||
|
||||
export type UserWithInstance = User & {
|
||||
instance: InferSelectModel<typeof instance> | null;
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
};
|
||||
|
||||
export type UserWithRelations = User & {
|
||||
instance: InferSelectModel<typeof instance> | null;
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
emojis: EmojiWithInstance[];
|
||||
followerCount: number;
|
||||
followingCount: number;
|
||||
|
|
@ -46,8 +46,8 @@ export type UserWithRelations = User & {
|
|||
};
|
||||
|
||||
export type UserWithRelationsAndRelationships = UserWithRelations & {
|
||||
relationships: InferSelectModel<typeof relationship>[];
|
||||
relationshipSubjects: InferSelectModel<typeof relationship>[];
|
||||
relationships: InferSelectModel<typeof Relationships>[];
|
||||
relationshipSubjects: InferSelectModel<typeof Relationships>[];
|
||||
};
|
||||
|
||||
export const userRelations: {
|
||||
|
|
@ -76,15 +76,15 @@ export const userRelations: {
|
|||
|
||||
export const userExtras = {
|
||||
followerCount:
|
||||
sql`(SELECT COUNT(*) FROM "Relationship" "relationships" WHERE ("relationships"."ownerId" = "user".id AND "relationships"."following" = true))`.as(
|
||||
sql`(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "Users".id AND "relationships"."following" = true))`.as(
|
||||
"follower_count",
|
||||
),
|
||||
followingCount:
|
||||
sql`(SELECT COUNT(*) FROM "Relationship" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "user".id AND "relationshipSubjects"."following" = true))`.as(
|
||||
sql`(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "Users".id AND "relationshipSubjects"."following" = true))`.as(
|
||||
"following_count",
|
||||
),
|
||||
statusCount:
|
||||
sql`(SELECT COUNT(*) FROM "Status" "statuses" WHERE "statuses"."authorId" = "user".id)`.as(
|
||||
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "Users".id)`.as(
|
||||
"status_count",
|
||||
),
|
||||
};
|
||||
|
|
@ -92,15 +92,15 @@ export const userExtras = {
|
|||
export const userExtrasTemplate = (name: string) => ({
|
||||
// @ts-ignore
|
||||
followerCount: sql([
|
||||
`(SELECT COUNT(*) FROM "Relationship" "relationships" WHERE ("relationships"."ownerId" = "${name}".id AND "relationships"."following" = true))`,
|
||||
`(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "${name}".id AND "relationships"."following" = true))`,
|
||||
]).as("follower_count"),
|
||||
// @ts-ignore
|
||||
followingCount: sql([
|
||||
`(SELECT COUNT(*) FROM "Relationship" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "${name}".id AND "relationshipSubjects"."following" = true))`,
|
||||
`(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "${name}".id AND "relationshipSubjects"."following" = true))`,
|
||||
]).as("following_count"),
|
||||
// @ts-ignore
|
||||
statusCount: sql([
|
||||
`(SELECT COUNT(*) FROM "Status" "statuses" WHERE "statuses"."authorId" = "${name}".id)`,
|
||||
`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "${name}".id)`,
|
||||
]).as("status_count"),
|
||||
});
|
||||
|
||||
|
|
@ -151,12 +151,12 @@ export const followRequestUser = async (
|
|||
reblogs = false,
|
||||
notify = false,
|
||||
languages: string[] = [],
|
||||
): Promise<InferSelectModel<typeof relationship>> => {
|
||||
): Promise<InferSelectModel<typeof Relationships>> => {
|
||||
const isRemote = followee.instanceId !== null;
|
||||
|
||||
const updatedRelationship = (
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
following: isRemote ? false : !followee.isLocked,
|
||||
requested: isRemote ? true : followee.isLocked,
|
||||
|
|
@ -164,7 +164,7 @@ export const followRequestUser = async (
|
|||
notifying: notify,
|
||||
languages: languages,
|
||||
})
|
||||
.where(eq(relationship.id, relationshipId))
|
||||
.where(eq(Relationships.id, relationshipId))
|
||||
.returning()
|
||||
)[0];
|
||||
|
||||
|
|
@ -195,17 +195,17 @@ export const followRequestUser = async (
|
|||
|
||||
return (
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
following: false,
|
||||
requested: false,
|
||||
})
|
||||
.where(eq(relationship.id, relationshipId))
|
||||
.where(eq(Relationships.id, relationshipId))
|
||||
.returning()
|
||||
)[0];
|
||||
}
|
||||
} else {
|
||||
await db.insert(notification).values({
|
||||
await db.insert(Notifications).values({
|
||||
accountId: follower.id,
|
||||
type: followee.isLocked ? "follow_request" : "follow",
|
||||
notifiedId: followee.id,
|
||||
|
|
@ -277,7 +277,7 @@ export const transformOutputToUserWithRelations = (
|
|||
emojiId: string;
|
||||
emoji?: EmojiWithInstance;
|
||||
}[];
|
||||
instance: InferSelectModel<typeof instance> | null;
|
||||
instance: InferSelectModel<typeof Instances> | null;
|
||||
endpoints: unknown;
|
||||
},
|
||||
): UserWithRelations => {
|
||||
|
|
@ -306,9 +306,9 @@ export const transformOutputToUserWithRelations = (
|
|||
};
|
||||
|
||||
export const findManyUsers = async (
|
||||
query: Parameters<typeof db.query.user.findMany>[0],
|
||||
query: Parameters<typeof db.query.Users.findMany>[0],
|
||||
): Promise<UserWithRelations[]> => {
|
||||
const output = await db.query.user.findMany({
|
||||
const output = await db.query.Users.findMany({
|
||||
...query,
|
||||
with: {
|
||||
...userRelations,
|
||||
|
|
@ -324,9 +324,9 @@ export const findManyUsers = async (
|
|||
};
|
||||
|
||||
export const findFirstUser = async (
|
||||
query: Parameters<typeof db.query.user.findFirst>[0],
|
||||
query: Parameters<typeof db.query.Users.findFirst>[0],
|
||||
): Promise<UserWithRelations | null> => {
|
||||
const output = await db.query.user.findFirst({
|
||||
const output = await db.query.Users.findFirst({
|
||||
...query,
|
||||
with: {
|
||||
...userRelations,
|
||||
|
|
@ -418,7 +418,7 @@ export const resolveUser = async (
|
|||
|
||||
const newUser = (
|
||||
await db
|
||||
.insert(user)
|
||||
.insert(Users)
|
||||
.values({
|
||||
username: data.username,
|
||||
uri: data.uri,
|
||||
|
|
@ -456,7 +456,7 @@ export const resolveUser = async (
|
|||
|
||||
// Add emojis to user
|
||||
if (emojis.length > 0) {
|
||||
await db.insert(emojiToUser).values(
|
||||
await db.insert(EmojiToUser).values(
|
||||
emojis.map((emoji) => ({
|
||||
emojiId: emoji.id,
|
||||
userId: newUser.id,
|
||||
|
|
@ -494,15 +494,15 @@ export const resolveWebFinger = async (
|
|||
// Check if user not already in database
|
||||
const foundUser = await db
|
||||
.select()
|
||||
.from(user)
|
||||
.innerJoin(instance, eq(user.instanceId, instance.id))
|
||||
.where(and(eq(user.username, identifier), eq(instance.baseUrl, host)))
|
||||
.from(Users)
|
||||
.innerJoin(Instances, eq(Users.instanceId, Instances.id))
|
||||
.where(and(eq(Users.username, identifier), eq(Instances.baseUrl, host)))
|
||||
.limit(1);
|
||||
|
||||
if (foundUser[0])
|
||||
return (
|
||||
(await findFirstUser({
|
||||
where: (user, { eq }) => eq(user.id, foundUser[0].User.id),
|
||||
where: (user, { eq }) => eq(user.id, foundUser[0].Users.id),
|
||||
})) || null
|
||||
);
|
||||
|
||||
|
|
@ -580,7 +580,7 @@ export const createNewLocalUser = async (data: {
|
|||
|
||||
const newUser = (
|
||||
await db
|
||||
.insert(user)
|
||||
.insert(Users)
|
||||
.values({
|
||||
username: data.username,
|
||||
displayName: data.display_name ?? data.username,
|
||||
|
|
@ -662,12 +662,12 @@ export const retrieveUserAndApplicationFromToken = async (
|
|||
const output = (
|
||||
await db
|
||||
.select({
|
||||
token: token,
|
||||
application: application,
|
||||
token: Tokens,
|
||||
application: Applications,
|
||||
})
|
||||
.from(token)
|
||||
.leftJoin(application, eq(token.applicationId, application.id))
|
||||
.where(eq(token.accessToken, access_token))
|
||||
.from(Tokens)
|
||||
.leftJoin(Applications, eq(Tokens.applicationId, Applications.id))
|
||||
.where(eq(Tokens.accessToken, access_token))
|
||||
.limit(1)
|
||||
)[0];
|
||||
|
||||
|
|
@ -686,7 +686,7 @@ export const retrieveToken = async (
|
|||
if (!access_token) return null;
|
||||
|
||||
return (
|
||||
(await db.query.token.findFirst({
|
||||
(await db.query.Tokens.findFirst({
|
||||
where: (tokens, { eq }) => eq(tokens.accessToken, access_token),
|
||||
})) ?? null
|
||||
);
|
||||
|
|
@ -700,8 +700,8 @@ export const retrieveToken = async (
|
|||
export const getRelationshipToOtherUser = async (
|
||||
user: UserWithRelations,
|
||||
other: User,
|
||||
): Promise<InferSelectModel<typeof relationship>> => {
|
||||
const foundRelationship = await db.query.relationship.findFirst({
|
||||
): Promise<InferSelectModel<typeof Relationships>> => {
|
||||
const foundRelationship = await db.query.Relationships.findFirst({
|
||||
where: (relationship, { and, eq }) =>
|
||||
and(
|
||||
eq(relationship.ownerId, user.id),
|
||||
|
|
|
|||
331
drizzle/0009_easy_slyde.sql
Normal file
331
drizzle/0009_easy_slyde.sql
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
ALTER TABLE "Application" RENAME TO "Applications";--> statement-breakpoint
|
||||
ALTER TABLE "Attachment" RENAME TO "Attachments";--> statement-breakpoint
|
||||
ALTER TABLE "Emoji" RENAME TO "Emojis";--> statement-breakpoint
|
||||
ALTER TABLE "EmojiToStatus" RENAME TO "EmojiToNote";--> statement-breakpoint
|
||||
ALTER TABLE "Flag" RENAME TO "Flags";--> statement-breakpoint
|
||||
ALTER TABLE "Instance" RENAME TO "Instances";--> statement-breakpoint
|
||||
ALTER TABLE "Like" RENAME TO "Likes";--> statement-breakpoint
|
||||
ALTER TABLE "ModNote" RENAME TO "ModNotes";--> statement-breakpoint
|
||||
ALTER TABLE "ModTag" RENAME TO "ModTags";--> statement-breakpoint
|
||||
ALTER TABLE "Notification" RENAME TO "Notifications";--> statement-breakpoint
|
||||
ALTER TABLE "OpenIdAccount" RENAME TO "OpenIdAccounts";--> statement-breakpoint
|
||||
ALTER TABLE "OpenIdLoginFlow" RENAME TO "OpenIdLoginFlows";--> statement-breakpoint
|
||||
ALTER TABLE "Relationship" RENAME TO "Relationships";--> statement-breakpoint
|
||||
ALTER TABLE "Status" RENAME TO "Notes";--> statement-breakpoint
|
||||
ALTER TABLE "StatusToMentions" RENAME TO "NoteToMentions";--> statement-breakpoint
|
||||
ALTER TABLE "Token" RENAME TO "Tokens";--> statement-breakpoint
|
||||
ALTER TABLE "User" RENAME TO "Users";--> statement-breakpoint
|
||||
ALTER TABLE "UserToPinnedNotes" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "Attachments" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "EmojiToNote" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "Flags" RENAME COLUMN "flaggeStatusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "Flags" RENAME COLUMN "flaggedUserId" TO "userId";--> statement-breakpoint
|
||||
ALTER TABLE "ModNotes" RENAME COLUMN "notedStatusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "ModNotes" RENAME COLUMN "notedUserId" TO "userId";--> statement-breakpoint
|
||||
ALTER TABLE "ModTags" RENAME COLUMN "taggedStatusId" TO "statusId";--> statement-breakpoint
|
||||
ALTER TABLE "ModTags" RENAME COLUMN "taggedUserId" TO "userId";--> statement-breakpoint
|
||||
ALTER TABLE "Notifications" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "Notes" RENAME COLUMN "inReplyToPostId" TO "replyId";--> statement-breakpoint
|
||||
ALTER TABLE "Notes" RENAME COLUMN "quotingPostId" TO "quoteId";--> statement-breakpoint
|
||||
ALTER TABLE "NoteToMentions" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "EmojiToUser" DROP CONSTRAINT "EmojiToUser_emojiId_Emoji_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "EmojiToUser" DROP CONSTRAINT "EmojiToUser_userId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_userId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_statusId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Attachments" DROP CONSTRAINT "Attachment_statusId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Emojis" DROP CONSTRAINT "Emoji_instanceId_Instance_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "EmojiToNote" DROP CONSTRAINT "EmojiToStatus_emojiId_Emoji_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "EmojiToNote" DROP CONSTRAINT "EmojiToStatus_statusId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Flags" DROP CONSTRAINT "Flag_flaggeStatusId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Flags" DROP CONSTRAINT "Flag_flaggedUserId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Likes" DROP CONSTRAINT "Like_likerId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Likes" DROP CONSTRAINT "Like_likedId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "ModNotes" DROP CONSTRAINT "ModNote_notedStatusId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "ModNotes" DROP CONSTRAINT "ModNote_notedUserId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "ModNotes" DROP CONSTRAINT "ModNote_modId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "ModTags" DROP CONSTRAINT "ModTag_taggedStatusId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "ModTags" DROP CONSTRAINT "ModTag_taggedUserId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "ModTags" DROP CONSTRAINT "ModTag_modId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notifications" DROP CONSTRAINT "Notification_notifiedId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notifications" DROP CONSTRAINT "Notification_accountId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notifications" DROP CONSTRAINT "Notification_statusId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "OpenIdAccounts" DROP CONSTRAINT "OpenIdAccount_userId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "OpenIdLoginFlows" DROP CONSTRAINT "OpenIdLoginFlow_applicationId_Application_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Relationships" DROP CONSTRAINT "Relationship_ownerId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Relationships" DROP CONSTRAINT "Relationship_subjectId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notes" DROP CONSTRAINT "Status_authorId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notes" DROP CONSTRAINT "Status_applicationId_Application_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notes" DROP CONSTRAINT "Status_reblogId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notes" DROP CONSTRAINT "Status_inReplyToPostId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Notes" DROP CONSTRAINT "Status_quotingPostId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "NoteToMentions" DROP CONSTRAINT "StatusToMentions_statusId_Status_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "NoteToMentions" DROP CONSTRAINT "StatusToMentions_userId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Tokens" DROP CONSTRAINT "Token_userId_User_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Tokens" DROP CONSTRAINT "Token_applicationId_Application_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Users" DROP CONSTRAINT "User_instanceId_Instance_id_fk";
|
||||
--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "UserToPinnedNotes_userId_statusId_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "UserToPinnedNotes_statusId_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "Application_client_id_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "EmojiToStatus_emojiId_statusId_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "EmojiToStatus_statusId_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "Status_uri_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "StatusToMentions_statusId_userId_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "StatusToMentions_userId_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "User_uri_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "User_username_index";--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS "User_email_index";--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "UserToPinnedNotes_userId_noteId_index" ON "UserToPinnedNotes" ("userId","noteId");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "UserToPinnedNotes_noteId_index" ON "UserToPinnedNotes" ("noteId");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "Applications_client_id_index" ON "Applications" ("client_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "EmojiToNote_emojiId_noteId_index" ON "EmojiToNote" ("emojiId","noteId");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "EmojiToNote_noteId_index" ON "EmojiToNote" ("noteId");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "Notes_uri_index" ON "Notes" ("uri");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "NoteToMentions_noteId_userId_index" ON "NoteToMentions" ("noteId","userId");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "NoteToMentions_userId_index" ON "NoteToMentions" ("userId");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "Users_uri_index" ON "Users" ("uri");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "Users_username_index" ON "Users" ("username");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "Users_email_index" ON "Users" ("email");--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "EmojiToUser" ADD CONSTRAINT "EmojiToUser_emojiId_Emojis_id_fk" FOREIGN KEY ("emojiId") REFERENCES "Emojis"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "EmojiToUser" ADD CONSTRAINT "EmojiToUser_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_userId_Notes_id_fk" FOREIGN KEY ("userId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_noteId_Users_id_fk" FOREIGN KEY ("noteId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Attachments" ADD CONSTRAINT "Attachments_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Emojis" ADD CONSTRAINT "Emojis_instanceId_Instances_id_fk" FOREIGN KEY ("instanceId") REFERENCES "Instances"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "EmojiToNote" ADD CONSTRAINT "EmojiToNote_emojiId_Emojis_id_fk" FOREIGN KEY ("emojiId") REFERENCES "Emojis"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "EmojiToNote" ADD CONSTRAINT "EmojiToNote_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Flags" ADD CONSTRAINT "Flags_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Flags" ADD CONSTRAINT "Flags_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Likes" ADD CONSTRAINT "Likes_likerId_Users_id_fk" FOREIGN KEY ("likerId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Likes" ADD CONSTRAINT "Likes_likedId_Notes_id_fk" FOREIGN KEY ("likedId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ModNotes" ADD CONSTRAINT "ModNotes_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ModNotes" ADD CONSTRAINT "ModNotes_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ModNotes" ADD CONSTRAINT "ModNotes_modId_Users_id_fk" FOREIGN KEY ("modId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_statusId_Notes_id_fk" FOREIGN KEY ("statusId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_modId_Users_id_fk" FOREIGN KEY ("modId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_notifiedId_Users_id_fk" FOREIGN KEY ("notifiedId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_accountId_Users_id_fk" FOREIGN KEY ("accountId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "OpenIdAccounts" ADD CONSTRAINT "OpenIdAccounts_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE set null ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "OpenIdLoginFlows" ADD CONSTRAINT "OpenIdLoginFlows_applicationId_Applications_id_fk" FOREIGN KEY ("applicationId") REFERENCES "Applications"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Relationships" ADD CONSTRAINT "Relationships_ownerId_Users_id_fk" FOREIGN KEY ("ownerId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Relationships" ADD CONSTRAINT "Relationships_subjectId_Users_id_fk" FOREIGN KEY ("subjectId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notes" ADD CONSTRAINT "Notes_authorId_Users_id_fk" FOREIGN KEY ("authorId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notes" ADD CONSTRAINT "Notes_applicationId_Applications_id_fk" FOREIGN KEY ("applicationId") REFERENCES "Applications"("id") ON DELETE set null ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notes" ADD CONSTRAINT "Notes_reblogId_Notes_id_fk" FOREIGN KEY ("reblogId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notes" ADD CONSTRAINT "Notes_replyId_Notes_id_fk" FOREIGN KEY ("replyId") REFERENCES "Notes"("id") ON DELETE set null ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Notes" ADD CONSTRAINT "Notes_quoteId_Notes_id_fk" FOREIGN KEY ("quoteId") REFERENCES "Notes"("id") ON DELETE set null ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "NoteToMentions" ADD CONSTRAINT "NoteToMentions_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "NoteToMentions" ADD CONSTRAINT "NoteToMentions_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Tokens" ADD CONSTRAINT "Tokens_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Tokens" ADD CONSTRAINT "Tokens_applicationId_Applications_id_fk" FOREIGN KEY ("applicationId") REFERENCES "Applications"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Users" ADD CONSTRAINT "Users_instanceId_Instances_id_fk" FOREIGN KEY ("instanceId") REFERENCES "Instances"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
8
drizzle/0010_daffy_frightful_four.sql
Normal file
8
drizzle/0010_daffy_frightful_four.sql
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ALTER TABLE "ModTags" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint
|
||||
ALTER TABLE "ModTags" DROP CONSTRAINT "ModTags_statusId_Notes_id_fk";
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
15
drizzle/0011_special_the_fury.sql
Normal file
15
drizzle/0011_special_the_fury.sql
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_userId_Notes_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_noteId_Users_id_fk";
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
1582
drizzle/meta/0009_snapshot.json
Normal file
1582
drizzle/meta/0009_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
1582
drizzle/meta/0010_snapshot.json
Normal file
1582
drizzle/meta/0010_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
1760
drizzle/meta/0011_snapshot.json
Normal file
1760
drizzle/meta/0011_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,69 +1,90 @@
|
|||
{
|
||||
"version": "5",
|
||||
"dialect": "pg",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "5",
|
||||
"when": 1712805159664,
|
||||
"tag": "0000_illegal_living_lightning",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "5",
|
||||
"when": 1713055774123,
|
||||
"tag": "0001_salty_night_thrasher",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "5",
|
||||
"when": 1713056370431,
|
||||
"tag": "0002_stiff_ares",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "5",
|
||||
"when": 1713056528340,
|
||||
"tag": "0003_spicy_arachne",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "5",
|
||||
"when": 1713056712218,
|
||||
"tag": "0004_burly_lockjaw",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "5",
|
||||
"when": 1713056917973,
|
||||
"tag": "0005_sleepy_puma",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "5",
|
||||
"when": 1713057159867,
|
||||
"tag": "0006_messy_network",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "5",
|
||||
"when": 1713227918208,
|
||||
"tag": "0007_naive_sleeper",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "5",
|
||||
"when": 1713246700119,
|
||||
"tag": "0008_flawless_brother_voodoo",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
"version": "5",
|
||||
"dialect": "pg",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "5",
|
||||
"when": 1712805159664,
|
||||
"tag": "0000_illegal_living_lightning",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "5",
|
||||
"when": 1713055774123,
|
||||
"tag": "0001_salty_night_thrasher",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "5",
|
||||
"when": 1713056370431,
|
||||
"tag": "0002_stiff_ares",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "5",
|
||||
"when": 1713056528340,
|
||||
"tag": "0003_spicy_arachne",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "5",
|
||||
"when": 1713056712218,
|
||||
"tag": "0004_burly_lockjaw",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "5",
|
||||
"when": 1713056917973,
|
||||
"tag": "0005_sleepy_puma",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "5",
|
||||
"when": 1713057159867,
|
||||
"tag": "0006_messy_network",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "5",
|
||||
"when": 1713227918208,
|
||||
"tag": "0007_naive_sleeper",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "5",
|
||||
"when": 1713246700119,
|
||||
"tag": "0008_flawless_brother_voodoo",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"version": "5",
|
||||
"when": 1713327832438,
|
||||
"tag": "0009_easy_slyde",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"version": "5",
|
||||
"when": 1713327880929,
|
||||
"tag": "0010_daffy_frightful_four",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"version": "5",
|
||||
"when": 1713333611707,
|
||||
"tag": "0011_special_the_fury",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -11,31 +11,32 @@ import {
|
|||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import type { Source as APISource } from "~types/mastodon/source";
|
||||
|
||||
export const emoji = pgTable("Emoji", {
|
||||
export const Emojis = pgTable("Emojis", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
shortcode: text("shortcode").notNull(),
|
||||
url: text("url").notNull(),
|
||||
visibleInPicker: boolean("visible_in_picker").notNull(),
|
||||
alt: text("alt"),
|
||||
contentType: text("content_type").notNull(),
|
||||
instanceId: uuid("instanceId").references(() => instance.id, {
|
||||
instanceId: uuid("instanceId").references(() => Instances.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const like = pgTable("Like", {
|
||||
export const Likes = pgTable("Likes", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
likerId: uuid("likerId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
likedId: uuid("likedId")
|
||||
.notNull()
|
||||
.references(() => status.id, {
|
||||
.references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -44,7 +45,7 @@ export const like = pgTable("Like", {
|
|||
.notNull(),
|
||||
});
|
||||
|
||||
export const lysandObject = pgTable(
|
||||
export const LysandObjects = pgTable(
|
||||
"LysandObject",
|
||||
{
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
|
|
@ -72,17 +73,17 @@ export const lysandObject = pgTable(
|
|||
},
|
||||
);
|
||||
|
||||
export const relationship = pgTable("Relationship", {
|
||||
export const Relationships = pgTable("Relationships", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
ownerId: uuid("ownerId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
subjectId: uuid("subjectId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -110,8 +111,8 @@ export const relationship = pgTable("Relationship", {
|
|||
.notNull(),
|
||||
});
|
||||
|
||||
export const application = pgTable(
|
||||
"Application",
|
||||
export const Applications = pgTable(
|
||||
"Applications",
|
||||
{
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
name: text("name").notNull(),
|
||||
|
|
@ -129,12 +130,12 @@ export const application = pgTable(
|
|||
},
|
||||
);
|
||||
|
||||
export const applicationRelations = relations(application, ({ many }) => ({
|
||||
tokens: many(token),
|
||||
loginFlows: many(openIdLoginFlow),
|
||||
export const ApplicationsRelations = relations(Applications, ({ many }) => ({
|
||||
tokens: many(Tokens),
|
||||
loginFlows: many(OpenIdLoginFlows),
|
||||
}));
|
||||
|
||||
export const token = pgTable("Token", {
|
||||
export const Tokens = pgTable("Tokens", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
tokenType: text("token_type").notNull(),
|
||||
scope: text("scope").notNull(),
|
||||
|
|
@ -143,17 +144,17 @@ export const token = pgTable("Token", {
|
|||
createdAt: timestamp("created_at", { precision: 3, mode: "string" })
|
||||
.defaultNow()
|
||||
.notNull(),
|
||||
userId: uuid("userId").references(() => user.id, {
|
||||
userId: uuid("userId").references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
applicationId: uuid("applicationId").references(() => application.id, {
|
||||
applicationId: uuid("applicationId").references(() => Applications.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const attachment = pgTable("Attachment", {
|
||||
export const Attachments = pgTable("Attachments", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
url: text("url").notNull(),
|
||||
remoteUrl: text("remote_url"),
|
||||
|
|
@ -167,13 +168,13 @@ export const attachment = pgTable("Attachment", {
|
|||
width: integer("width"),
|
||||
height: integer("height"),
|
||||
size: integer("size"),
|
||||
statusId: uuid("statusId").references(() => status.id, {
|
||||
noteId: uuid("noteId").references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const notification = pgTable("Notification", {
|
||||
export const Notifications = pgTable("Notifications", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
type: text("type").notNull(),
|
||||
createdAt: timestamp("createdAt", { precision: 3, mode: "string" })
|
||||
|
|
@ -181,31 +182,31 @@ export const notification = pgTable("Notification", {
|
|||
.notNull(),
|
||||
notifiedId: uuid("notifiedId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
accountId: uuid("accountId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
statusId: uuid("statusId").references(() => status.id, {
|
||||
noteId: uuid("noteId").references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
dismissed: boolean("dismissed").default(false).notNull(),
|
||||
});
|
||||
|
||||
export const status = pgTable(
|
||||
"Status",
|
||||
export const Notes = pgTable(
|
||||
"Notes",
|
||||
{
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
uri: text("uri"),
|
||||
authorId: uuid("authorId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -222,11 +223,11 @@ export const status = pgTable(
|
|||
content: text("content").default("").notNull(),
|
||||
contentType: text("content_type").default("text/plain").notNull(),
|
||||
visibility: text("visibility").notNull(),
|
||||
inReplyToPostId: uuid("inReplyToPostId"),
|
||||
quotingPostId: uuid("quotingPostId"),
|
||||
replyId: uuid("replyId"),
|
||||
quotingId: uuid("quoteId"),
|
||||
sensitive: boolean("sensitive").notNull(),
|
||||
spoilerText: text("spoiler_text").default("").notNull(),
|
||||
applicationId: uuid("applicationId").references(() => application.id, {
|
||||
applicationId: uuid("applicationId").references(() => Applications.id, {
|
||||
onDelete: "set null",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -235,20 +236,20 @@ export const status = pgTable(
|
|||
(table) => {
|
||||
return {
|
||||
uriKey: uniqueIndex().on(table.uri),
|
||||
statusReblogIdFkey: foreignKey({
|
||||
noteReblogIdFkey: foreignKey({
|
||||
columns: [table.reblogId],
|
||||
foreignColumns: [table.id],
|
||||
})
|
||||
.onUpdate("cascade")
|
||||
.onDelete("cascade"),
|
||||
statusInReplyToPostIdFkey: foreignKey({
|
||||
columns: [table.inReplyToPostId],
|
||||
noteReplyIdFkey: foreignKey({
|
||||
columns: [table.replyId],
|
||||
foreignColumns: [table.id],
|
||||
})
|
||||
.onUpdate("cascade")
|
||||
.onDelete("set null"),
|
||||
statusQuotingPostIdFkey: foreignKey({
|
||||
columns: [table.quotingPostId],
|
||||
noteQuotingIdFkey: foreignKey({
|
||||
columns: [table.quotingId],
|
||||
foreignColumns: [table.id],
|
||||
})
|
||||
.onUpdate("cascade")
|
||||
|
|
@ -257,7 +258,7 @@ export const status = pgTable(
|
|||
},
|
||||
);
|
||||
|
||||
export const instance = pgTable("Instance", {
|
||||
export const Instances = pgTable("Instances", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
baseUrl: text("base_url").notNull(),
|
||||
name: text("name").notNull(),
|
||||
|
|
@ -268,9 +269,9 @@ export const instance = pgTable("Instance", {
|
|||
.notNull(),
|
||||
});
|
||||
|
||||
export const openIdAccount = pgTable("OpenIdAccount", {
|
||||
export const OpenIdAccounts = pgTable("OpenIdAccounts", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
userId: uuid("userId").references(() => user.id, {
|
||||
userId: uuid("userId").references(() => Users.id, {
|
||||
onDelete: "set null",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -278,8 +279,8 @@ export const openIdAccount = pgTable("OpenIdAccount", {
|
|||
issuerId: text("issuer_id").notNull(),
|
||||
});
|
||||
|
||||
export const user = pgTable(
|
||||
"User",
|
||||
export const Users = pgTable(
|
||||
"Users",
|
||||
{
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
uri: text("uri"),
|
||||
|
|
@ -298,7 +299,7 @@ export const user = pgTable(
|
|||
inbox: string;
|
||||
outbox: string;
|
||||
}> | null>(),
|
||||
source: jsonb("source").notNull(),
|
||||
source: jsonb("source").notNull().$type<APISource>(),
|
||||
avatar: text("avatar").notNull(),
|
||||
header: text("header").notNull(),
|
||||
createdAt: timestamp("created_at", { precision: 3, mode: "string" })
|
||||
|
|
@ -316,7 +317,7 @@ export const user = pgTable(
|
|||
sanctions: text("sanctions").array(),
|
||||
publicKey: text("public_key").notNull(),
|
||||
privateKey: text("private_key"),
|
||||
instanceId: uuid("instanceId").references(() => instance.id, {
|
||||
instanceId: uuid("instanceId").references(() => Instances.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -333,55 +334,55 @@ export const user = pgTable(
|
|||
},
|
||||
);
|
||||
|
||||
export const openIdLoginFlow = pgTable("OpenIdLoginFlow", {
|
||||
export const OpenIdLoginFlows = pgTable("OpenIdLoginFlows", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
codeVerifier: text("code_verifier").notNull(),
|
||||
applicationId: uuid("applicationId").references(() => application.id, {
|
||||
applicationId: uuid("applicationId").references(() => Applications.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
issuerId: text("issuer_id").notNull(),
|
||||
});
|
||||
|
||||
export const openIdLoginFlowRelations = relations(
|
||||
openIdLoginFlow,
|
||||
export const OpenIdLoginFlowsRelations = relations(
|
||||
OpenIdLoginFlows,
|
||||
({ one }) => ({
|
||||
application: one(application, {
|
||||
fields: [openIdLoginFlow.applicationId],
|
||||
references: [application.id],
|
||||
application: one(Applications, {
|
||||
fields: [OpenIdLoginFlows.applicationId],
|
||||
references: [Applications.id],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export const flag = pgTable("Flag", {
|
||||
export const Flags = pgTable("Flags", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
flagType: text("flag_type").default("other").notNull(),
|
||||
createdAt: timestamp("created_at", { precision: 3, mode: "string" })
|
||||
.defaultNow()
|
||||
.notNull(),
|
||||
flaggeStatusId: uuid("flaggeStatusId").references(() => status.id, {
|
||||
noteId: uuid("noteId").references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
flaggedUserId: uuid("flaggedUserId").references(() => user.id, {
|
||||
userId: uuid("userId").references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const modNote = pgTable("ModNote", {
|
||||
export const ModNotes = pgTable("ModNotes", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
notedStatusId: uuid("notedStatusId").references(() => status.id, {
|
||||
nodeId: uuid("noteId").references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
notedUserId: uuid("notedUserId").references(() => user.id, {
|
||||
userId: uuid("userId").references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
modId: uuid("modId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -391,19 +392,19 @@ export const modNote = pgTable("ModNote", {
|
|||
.notNull(),
|
||||
});
|
||||
|
||||
export const modTag = pgTable("ModTag", {
|
||||
export const ModTags = pgTable("ModTags", {
|
||||
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
|
||||
taggedStatusId: uuid("taggedStatusId").references(() => status.id, {
|
||||
noteId: uuid("noteId").references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
taggedUserId: uuid("taggedUserId").references(() => user.id, {
|
||||
userId: uuid("userId").references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
modId: uuid("modId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -413,18 +414,18 @@ export const modTag = pgTable("ModTag", {
|
|||
.notNull(),
|
||||
});
|
||||
|
||||
export const emojiToUser = pgTable(
|
||||
export const EmojiToUser = pgTable(
|
||||
"EmojiToUser",
|
||||
{
|
||||
emojiId: uuid("emojiId")
|
||||
.notNull()
|
||||
.references(() => emoji.id, {
|
||||
.references(() => Emojis.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
|
|
@ -437,267 +438,267 @@ export const emojiToUser = pgTable(
|
|||
},
|
||||
);
|
||||
|
||||
export const emojiToUserRelations = relations(emojiToUser, ({ one }) => ({
|
||||
emoji: one(emoji, {
|
||||
fields: [emojiToUser.emojiId],
|
||||
references: [emoji.id],
|
||||
export const EmojiToUserRelations = relations(EmojiToUser, ({ one }) => ({
|
||||
emoji: one(Emojis, {
|
||||
fields: [EmojiToUser.emojiId],
|
||||
references: [Emojis.id],
|
||||
}),
|
||||
user: one(user, {
|
||||
fields: [emojiToUser.userId],
|
||||
references: [user.id],
|
||||
user: one(Users, {
|
||||
fields: [EmojiToUser.userId],
|
||||
references: [Users.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const emojiToStatus = pgTable(
|
||||
"EmojiToStatus",
|
||||
export const EmojiToNote = pgTable(
|
||||
"EmojiToNote",
|
||||
{
|
||||
emojiId: uuid("emojiId")
|
||||
.notNull()
|
||||
.references(() => emoji.id, {
|
||||
.references(() => Emojis.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
statusId: uuid("statusId")
|
||||
noteId: uuid("noteId")
|
||||
.notNull()
|
||||
.references(() => status.id, {
|
||||
.references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
abUnique: uniqueIndex().on(table.emojiId, table.statusId),
|
||||
bIdx: index().on(table.statusId),
|
||||
abUnique: uniqueIndex().on(table.emojiId, table.noteId),
|
||||
bIdx: index().on(table.noteId),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const statusToMentions = pgTable(
|
||||
"StatusToMentions",
|
||||
export const NoteToMentions = pgTable(
|
||||
"NoteToMentions",
|
||||
{
|
||||
statusId: uuid("statusId")
|
||||
noteId: uuid("noteId")
|
||||
.notNull()
|
||||
.references(() => status.id, {
|
||||
.references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
abUnique: uniqueIndex().on(table.statusId, table.userId),
|
||||
abUnique: uniqueIndex().on(table.noteId, table.userId),
|
||||
bIdx: index().on(table.userId),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const userPinnedNotes = pgTable(
|
||||
export const UserToPinnedNotes = pgTable(
|
||||
"UserToPinnedNotes",
|
||||
{
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => status.id, {
|
||||
.references(() => Users.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
statusId: uuid("statusId")
|
||||
noteId: uuid("noteId")
|
||||
.notNull()
|
||||
.references(() => user.id, {
|
||||
.references(() => Notes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
abUnique: uniqueIndex().on(table.userId, table.statusId),
|
||||
bIdx: index().on(table.statusId),
|
||||
abUnique: uniqueIndex().on(table.userId, table.noteId),
|
||||
bIdx: index().on(table.noteId),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const attachmentRelations = relations(attachment, ({ one }) => ({
|
||||
status: one(status, {
|
||||
fields: [attachment.statusId],
|
||||
references: [status.id],
|
||||
export const AttachmentsRelations = relations(Attachments, ({ one }) => ({
|
||||
notes: one(Notes, {
|
||||
fields: [Attachments.noteId],
|
||||
references: [Notes.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const userRelations = relations(user, ({ many, one }) => ({
|
||||
emojis: many(emojiToUser),
|
||||
pinnedNotes: many(userPinnedNotes),
|
||||
statuses: many(status, {
|
||||
relationName: "StatusToAuthor",
|
||||
export const UsersRelations = relations(Users, ({ many, one }) => ({
|
||||
emojis: many(EmojiToUser),
|
||||
pinnedNotes: many(UserToPinnedNotes),
|
||||
notes: many(Notes, {
|
||||
relationName: "NoteToAuthor",
|
||||
}),
|
||||
likes: many(like),
|
||||
relationships: many(relationship, {
|
||||
likes: many(Likes),
|
||||
relationships: many(Relationships, {
|
||||
relationName: "RelationshipToOwner",
|
||||
}),
|
||||
relationshipSubjects: many(relationship, {
|
||||
relationshipSubjects: many(Relationships, {
|
||||
relationName: "RelationshipToSubject",
|
||||
}),
|
||||
notificationsMade: many(notification, {
|
||||
notificationsMade: many(Notifications, {
|
||||
relationName: "NotificationToAccount",
|
||||
}),
|
||||
notificationsReceived: many(notification, {
|
||||
notificationsReceived: many(Notifications, {
|
||||
relationName: "NotificationToNotified",
|
||||
}),
|
||||
openIdAccounts: many(openIdAccount),
|
||||
flags: many(flag),
|
||||
modNotes: many(modNote),
|
||||
modTags: many(modTag),
|
||||
tokens: many(token),
|
||||
instance: one(instance, {
|
||||
fields: [user.instanceId],
|
||||
references: [instance.id],
|
||||
openIdAccounts: many(OpenIdAccounts),
|
||||
flags: many(Flags),
|
||||
modNotes: many(ModNotes),
|
||||
modTags: many(ModTags),
|
||||
tokens: many(Tokens),
|
||||
instance: one(Instances, {
|
||||
fields: [Users.instanceId],
|
||||
references: [Instances.id],
|
||||
}),
|
||||
mentionedIn: many(statusToMentions),
|
||||
mentionedIn: many(NoteToMentions),
|
||||
}));
|
||||
|
||||
export const relationshipRelations = relations(relationship, ({ one }) => ({
|
||||
owner: one(user, {
|
||||
fields: [relationship.ownerId],
|
||||
references: [user.id],
|
||||
export const RelationshipsRelations = relations(Relationships, ({ one }) => ({
|
||||
owner: one(Users, {
|
||||
fields: [Relationships.ownerId],
|
||||
references: [Users.id],
|
||||
relationName: "RelationshipToOwner",
|
||||
}),
|
||||
subject: one(user, {
|
||||
fields: [relationship.subjectId],
|
||||
references: [user.id],
|
||||
subject: one(Users, {
|
||||
fields: [Relationships.subjectId],
|
||||
references: [Users.id],
|
||||
relationName: "RelationshipToSubject",
|
||||
}),
|
||||
}));
|
||||
|
||||
export const tokenRelations = relations(token, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [token.userId],
|
||||
references: [user.id],
|
||||
export const TokensRelations = relations(Tokens, ({ one }) => ({
|
||||
user: one(Users, {
|
||||
fields: [Tokens.userId],
|
||||
references: [Users.id],
|
||||
}),
|
||||
application: one(application, {
|
||||
fields: [token.applicationId],
|
||||
references: [application.id],
|
||||
application: one(Applications, {
|
||||
fields: [Tokens.applicationId],
|
||||
references: [Applications.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const statusToUserRelations = relations(statusToMentions, ({ one }) => ({
|
||||
status: one(status, {
|
||||
fields: [statusToMentions.statusId],
|
||||
references: [status.id],
|
||||
export const NotesToUsersRelations = relations(NoteToMentions, ({ one }) => ({
|
||||
note: one(Notes, {
|
||||
fields: [NoteToMentions.noteId],
|
||||
references: [Notes.id],
|
||||
}),
|
||||
user: one(user, {
|
||||
fields: [statusToMentions.userId],
|
||||
references: [user.id],
|
||||
user: one(Users, {
|
||||
fields: [NoteToMentions.userId],
|
||||
references: [Users.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const userPinnedNotesRelations = relations(
|
||||
userPinnedNotes,
|
||||
export const UserToPinnedNotesRelations = relations(
|
||||
UserToPinnedNotes,
|
||||
({ one }) => ({
|
||||
status: one(status, {
|
||||
fields: [userPinnedNotes.statusId],
|
||||
references: [status.id],
|
||||
note: one(Notes, {
|
||||
fields: [UserToPinnedNotes.noteId],
|
||||
references: [Notes.id],
|
||||
}),
|
||||
user: one(user, {
|
||||
fields: [userPinnedNotes.userId],
|
||||
references: [user.id],
|
||||
user: one(Users, {
|
||||
fields: [UserToPinnedNotes.userId],
|
||||
references: [Users.id],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export const statusRelations = relations(status, ({ many, one }) => ({
|
||||
emojis: many(emojiToStatus),
|
||||
author: one(user, {
|
||||
fields: [status.authorId],
|
||||
references: [user.id],
|
||||
relationName: "StatusToAuthor",
|
||||
export const NotesRelations = relations(Notes, ({ many, one }) => ({
|
||||
emojis: many(EmojiToNote),
|
||||
author: one(Users, {
|
||||
fields: [Notes.authorId],
|
||||
references: [Users.id],
|
||||
relationName: "NoteToAuthor",
|
||||
}),
|
||||
attachments: many(attachment),
|
||||
mentions: many(statusToMentions),
|
||||
reblog: one(status, {
|
||||
fields: [status.reblogId],
|
||||
references: [status.id],
|
||||
relationName: "StatusToReblog",
|
||||
attachments: many(Attachments),
|
||||
mentions: many(NoteToMentions),
|
||||
reblog: one(Notes, {
|
||||
fields: [Notes.reblogId],
|
||||
references: [Notes.id],
|
||||
relationName: "NoteToReblogs",
|
||||
}),
|
||||
usersThatHavePinned: many(userPinnedNotes),
|
||||
inReplyTo: one(status, {
|
||||
fields: [status.inReplyToPostId],
|
||||
references: [status.id],
|
||||
relationName: "StatusToReplying",
|
||||
usersThatHavePinned: many(UserToPinnedNotes),
|
||||
reply: one(Notes, {
|
||||
fields: [Notes.replyId],
|
||||
references: [Notes.id],
|
||||
relationName: "NoteToReplies",
|
||||
}),
|
||||
quoting: one(status, {
|
||||
fields: [status.quotingPostId],
|
||||
references: [status.id],
|
||||
relationName: "StatusToQuoting",
|
||||
quote: one(Notes, {
|
||||
fields: [Notes.quotingId],
|
||||
references: [Notes.id],
|
||||
relationName: "NoteToQuotes",
|
||||
}),
|
||||
application: one(application, {
|
||||
fields: [status.applicationId],
|
||||
references: [application.id],
|
||||
application: one(Applications, {
|
||||
fields: [Notes.applicationId],
|
||||
references: [Applications.id],
|
||||
}),
|
||||
quotes: many(status, {
|
||||
relationName: "StatusToQuoting",
|
||||
quotes: many(Notes, {
|
||||
relationName: "NoteToQuotes",
|
||||
}),
|
||||
replies: many(status, {
|
||||
relationName: "StatusToReplying",
|
||||
replies: many(Notes, {
|
||||
relationName: "NoteToReplies",
|
||||
}),
|
||||
likes: many(like),
|
||||
reblogs: many(status, {
|
||||
relationName: "StatusToReblog",
|
||||
likes: many(Likes),
|
||||
reblogs: many(Notes, {
|
||||
relationName: "NoteToReblogs",
|
||||
}),
|
||||
notifications: many(notification),
|
||||
notifications: many(Notifications),
|
||||
}));
|
||||
|
||||
export const notificationRelations = relations(notification, ({ one }) => ({
|
||||
account: one(user, {
|
||||
fields: [notification.accountId],
|
||||
references: [user.id],
|
||||
export const NotificationsRelations = relations(Notifications, ({ one }) => ({
|
||||
account: one(Users, {
|
||||
fields: [Notifications.accountId],
|
||||
references: [Users.id],
|
||||
relationName: "NotificationToAccount",
|
||||
}),
|
||||
notified: one(user, {
|
||||
fields: [notification.notifiedId],
|
||||
references: [user.id],
|
||||
notified: one(Users, {
|
||||
fields: [Notifications.notifiedId],
|
||||
references: [Users.id],
|
||||
relationName: "NotificationToNotified",
|
||||
}),
|
||||
status: one(status, {
|
||||
fields: [notification.statusId],
|
||||
references: [status.id],
|
||||
note: one(Notes, {
|
||||
fields: [Notifications.noteId],
|
||||
references: [Notes.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const likeRelations = relations(like, ({ one }) => ({
|
||||
liker: one(user, {
|
||||
fields: [like.likerId],
|
||||
references: [user.id],
|
||||
export const LikesRelations = relations(Likes, ({ one }) => ({
|
||||
liker: one(Users, {
|
||||
fields: [Likes.likerId],
|
||||
references: [Users.id],
|
||||
}),
|
||||
liked: one(status, {
|
||||
fields: [like.likedId],
|
||||
references: [status.id],
|
||||
liked: one(Notes, {
|
||||
fields: [Likes.likedId],
|
||||
references: [Notes.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const emojiRelations = relations(emoji, ({ one, many }) => ({
|
||||
instance: one(instance, {
|
||||
fields: [emoji.instanceId],
|
||||
references: [instance.id],
|
||||
export const EmojisRelations = relations(Emojis, ({ one, many }) => ({
|
||||
instance: one(Instances, {
|
||||
fields: [Emojis.instanceId],
|
||||
references: [Instances.id],
|
||||
}),
|
||||
users: many(emojiToUser),
|
||||
statuses: many(emojiToStatus),
|
||||
users: many(EmojiToUser),
|
||||
notes: many(EmojiToNote),
|
||||
}));
|
||||
|
||||
export const instanceRelations = relations(instance, ({ many }) => ({
|
||||
users: many(user),
|
||||
emojis: many(emoji),
|
||||
export const InstancesRelations = relations(Instances, ({ many }) => ({
|
||||
users: many(Users),
|
||||
emojis: many(Emojis),
|
||||
}));
|
||||
|
||||
export const emojiToStatusRelations = relations(emojiToStatus, ({ one }) => ({
|
||||
emoji: one(emoji, {
|
||||
fields: [emojiToStatus.emojiId],
|
||||
references: [emoji.id],
|
||||
export const EmojisToNotesRelations = relations(EmojiToNote, ({ one }) => ({
|
||||
emoji: one(Emojis, {
|
||||
fields: [EmojiToNote.emojiId],
|
||||
references: [Emojis.id],
|
||||
}),
|
||||
status: one(status, {
|
||||
fields: [emojiToStatus.statusId],
|
||||
references: [status.id],
|
||||
note: one(Notes, {
|
||||
fields: [EmojiToNote.noteId],
|
||||
references: [Notes.id],
|
||||
}),
|
||||
}));
|
||||
|
|
|
|||
4
index.ts
4
index.ts
|
|
@ -4,7 +4,7 @@ import { config } from "config-manager";
|
|||
import { count } from "drizzle-orm";
|
||||
import { LogLevel, LogManager, type MultiLogManager } from "log-manager";
|
||||
import { db, setupDatabase } from "~drizzle/db";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { createServer } from "~server";
|
||||
|
||||
const timeAtStart = performance.now();
|
||||
|
|
@ -35,7 +35,7 @@ try {
|
|||
.select({
|
||||
count: count(),
|
||||
})
|
||||
.from(status)
|
||||
.from(Notes)
|
||||
)[0].count;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
|
|
|
|||
|
|
@ -43,13 +43,14 @@ import {
|
|||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import {
|
||||
attachment,
|
||||
emojiToStatus,
|
||||
notification,
|
||||
status,
|
||||
statusToMentions,
|
||||
user,
|
||||
userRelations,
|
||||
Attachments,
|
||||
EmojiToNote,
|
||||
NoteToMentions,
|
||||
Notes,
|
||||
Notifications,
|
||||
UserToPinnedNotes,
|
||||
Users,
|
||||
UsersRelations,
|
||||
} from "~drizzle/schema";
|
||||
import { config } from "~packages/config-manager";
|
||||
import type { Attachment as APIAttachment } from "~types/mastodon/attachment";
|
||||
|
|
@ -64,16 +65,16 @@ export class Note {
|
|||
static async fromId(id: string | null): Promise<Note | null> {
|
||||
if (!id) return null;
|
||||
|
||||
return await Note.fromSql(eq(status.id, id));
|
||||
return await Note.fromSql(eq(Notes.id, id));
|
||||
}
|
||||
|
||||
static async fromIds(ids: string[]): Promise<Note[]> {
|
||||
return await Note.manyFromSql(inArray(status.id, ids));
|
||||
return await Note.manyFromSql(inArray(Notes.id, ids));
|
||||
}
|
||||
|
||||
static async fromSql(
|
||||
sql: SQL<unknown> | undefined,
|
||||
orderBy: SQL<unknown> | undefined = desc(status.id),
|
||||
orderBy: SQL<unknown> | undefined = desc(Notes.id),
|
||||
) {
|
||||
const found = await findFirstNote({
|
||||
where: sql,
|
||||
|
|
@ -86,7 +87,7 @@ export class Note {
|
|||
|
||||
static async manyFromSql(
|
||||
sql: SQL<unknown> | undefined,
|
||||
orderBy: SQL<unknown> | undefined = desc(status.id),
|
||||
orderBy: SQL<unknown> | undefined = desc(Notes.id),
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
) {
|
||||
|
|
@ -121,11 +122,10 @@ export class Note {
|
|||
const usersThatCanSeePost = await findManyUsers({
|
||||
where: (user, { isNotNull }) => isNotNull(user.instanceId),
|
||||
with: {
|
||||
...userRelations,
|
||||
relationships: {
|
||||
where: (relationship, { eq, and }) =>
|
||||
and(
|
||||
eq(relationship.subjectId, user.id),
|
||||
eq(relationship.subjectId, Users.id),
|
||||
eq(relationship.following, true),
|
||||
),
|
||||
},
|
||||
|
|
@ -163,24 +163,37 @@ export class Note {
|
|||
}
|
||||
|
||||
async getReplyChildren() {
|
||||
return await Note.manyFromSql(
|
||||
eq(status.inReplyToPostId, this.status.id),
|
||||
);
|
||||
return await Note.manyFromSql(eq(Notes.replyId, this.status.id));
|
||||
}
|
||||
|
||||
async pin(pinner: User) {
|
||||
return (
|
||||
await db
|
||||
.insert(UserToPinnedNotes)
|
||||
.values({
|
||||
noteId: this.status.id,
|
||||
userId: pinner.id,
|
||||
})
|
||||
.returning()
|
||||
)[0];
|
||||
}
|
||||
|
||||
async unpin(unpinner: User) {
|
||||
return await db
|
||||
.delete(statusToMentions)
|
||||
.where(
|
||||
and(
|
||||
eq(statusToMentions.statusId, this.status.id),
|
||||
eq(statusToMentions.userId, unpinner.id),
|
||||
),
|
||||
);
|
||||
return (
|
||||
await db
|
||||
.delete(UserToPinnedNotes)
|
||||
.where(
|
||||
and(
|
||||
eq(NoteToMentions.noteId, this.status.id),
|
||||
eq(NoteToMentions.userId, unpinner.id),
|
||||
),
|
||||
)
|
||||
.returning()
|
||||
)[0];
|
||||
}
|
||||
|
||||
static async insert(values: InferInsertModel<typeof status>) {
|
||||
return (await db.insert(status).values(values).returning())[0];
|
||||
static async insert(values: InferInsertModel<typeof Notes>) {
|
||||
return (await db.insert(Notes).values(values).returning())[0];
|
||||
}
|
||||
|
||||
static async fromData(
|
||||
|
|
@ -225,18 +238,18 @@ export class Note {
|
|||
sensitive: is_sensitive,
|
||||
spoilerText: spoiler_text,
|
||||
uri: uri || null,
|
||||
inReplyToPostId: replyId ?? null,
|
||||
quotingPostId: quoteId ?? null,
|
||||
replyId: replyId ?? null,
|
||||
quotingId: quoteId ?? null,
|
||||
applicationId: application?.id ?? null,
|
||||
});
|
||||
|
||||
// Connect emojis
|
||||
for (const emoji of foundEmojis) {
|
||||
await db
|
||||
.insert(emojiToStatus)
|
||||
.insert(EmojiToNote)
|
||||
.values({
|
||||
emojiId: emoji.id,
|
||||
statusId: newNote.id,
|
||||
noteId: newNote.id,
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
|
@ -244,9 +257,9 @@ export class Note {
|
|||
// Connect mentions
|
||||
for (const mention of mentions ?? []) {
|
||||
await db
|
||||
.insert(statusToMentions)
|
||||
.insert(NoteToMentions)
|
||||
.values({
|
||||
statusId: newNote.id,
|
||||
noteId: newNote.id,
|
||||
userId: mention.id,
|
||||
})
|
||||
.execute();
|
||||
|
|
@ -255,21 +268,21 @@ export class Note {
|
|||
// Set attachment parents
|
||||
if (media_attachments && media_attachments.length > 0) {
|
||||
await db
|
||||
.update(attachment)
|
||||
.update(Attachments)
|
||||
.set({
|
||||
statusId: newNote.id,
|
||||
noteId: newNote.id,
|
||||
})
|
||||
.where(inArray(attachment.id, media_attachments));
|
||||
.where(inArray(Attachments.id, media_attachments));
|
||||
}
|
||||
|
||||
// Send notifications for mentioned local users
|
||||
for (const mention of mentions ?? []) {
|
||||
if (mention.instanceId === null) {
|
||||
await db.insert(notification).values({
|
||||
await db.insert(Notifications).values({
|
||||
accountId: author.id,
|
||||
notifiedId: mention.id,
|
||||
type: "mention",
|
||||
statusId: newNote.id,
|
||||
noteId: newNote.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -319,29 +332,29 @@ export class Note {
|
|||
|
||||
// Connect emojis
|
||||
await db
|
||||
.delete(emojiToStatus)
|
||||
.where(eq(emojiToStatus.statusId, this.status.id));
|
||||
.delete(EmojiToNote)
|
||||
.where(eq(EmojiToNote.noteId, this.status.id));
|
||||
|
||||
for (const emoji of foundEmojis) {
|
||||
await db
|
||||
.insert(emojiToStatus)
|
||||
.insert(EmojiToNote)
|
||||
.values({
|
||||
emojiId: emoji.id,
|
||||
statusId: this.status.id,
|
||||
noteId: this.status.id,
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
// Connect mentions
|
||||
await db
|
||||
.delete(statusToMentions)
|
||||
.where(eq(statusToMentions.statusId, this.status.id));
|
||||
.delete(NoteToMentions)
|
||||
.where(eq(NoteToMentions.noteId, this.status.id));
|
||||
|
||||
for (const mention of mentions ?? []) {
|
||||
await db
|
||||
.insert(statusToMentions)
|
||||
.insert(NoteToMentions)
|
||||
.values({
|
||||
statusId: this.status.id,
|
||||
noteId: this.status.id,
|
||||
userId: mention.id,
|
||||
})
|
||||
.execute();
|
||||
|
|
@ -350,11 +363,11 @@ export class Note {
|
|||
// Set attachment parents
|
||||
if (media_attachments && media_attachments.length > 0) {
|
||||
await db
|
||||
.update(attachment)
|
||||
.update(Attachments)
|
||||
.set({
|
||||
statusId: this.status.id,
|
||||
noteId: this.status.id,
|
||||
})
|
||||
.where(inArray(attachment.id, media_attachments));
|
||||
.where(inArray(Attachments.id, media_attachments));
|
||||
}
|
||||
|
||||
return await Note.fromId(newNote.id);
|
||||
|
|
@ -363,8 +376,8 @@ export class Note {
|
|||
async delete() {
|
||||
return (
|
||||
await db
|
||||
.delete(status)
|
||||
.where(eq(status.id, this.status.id))
|
||||
.delete(Notes)
|
||||
.where(eq(Notes.id, this.status.id))
|
||||
.returning()
|
||||
)[0];
|
||||
}
|
||||
|
|
@ -372,18 +385,15 @@ export class Note {
|
|||
async update(newStatus: Partial<Status>) {
|
||||
return (
|
||||
await db
|
||||
.update(status)
|
||||
.update(Notes)
|
||||
.set(newStatus)
|
||||
.where(eq(status.id, this.status.id))
|
||||
.where(eq(Notes.id, this.status.id))
|
||||
.returning()
|
||||
)[0];
|
||||
}
|
||||
|
||||
static async deleteMany(ids: string[]) {
|
||||
return await db
|
||||
.delete(status)
|
||||
.where(inArray(status.id, ids))
|
||||
.returning();
|
||||
return await db.delete(Notes).where(inArray(Notes.id, ids)).returning();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -397,11 +407,11 @@ export class Note {
|
|||
if (this.getStatus().visibility === "unlisted") return true;
|
||||
if (this.getStatus().visibility === "private") {
|
||||
return user
|
||||
? await db.query.relationship.findFirst({
|
||||
? await db.query.Relationships.findFirst({
|
||||
where: (relationship, { and, eq }) =>
|
||||
and(
|
||||
eq(relationship.ownerId, user?.id),
|
||||
eq(relationship.subjectId, status.authorId),
|
||||
eq(relationship.subjectId, Notes.authorId),
|
||||
eq(relationship.following, true),
|
||||
),
|
||||
})
|
||||
|
|
@ -416,10 +426,10 @@ export class Note {
|
|||
async toAPI(userFetching?: UserWithRelations | null): Promise<APIStatus> {
|
||||
const data = this.getStatus();
|
||||
const wasPinnedByUser = userFetching
|
||||
? !!(await db.query.userPinnedNotes.findFirst({
|
||||
? !!(await db.query.UserToPinnedNotes.findFirst({
|
||||
where: (relation, { and, eq }) =>
|
||||
and(
|
||||
eq(relation.statusId, data.id),
|
||||
eq(relation.noteId, data.id),
|
||||
eq(relation.userId, userFetching?.id),
|
||||
),
|
||||
}))
|
||||
|
|
@ -428,14 +438,14 @@ export class Note {
|
|||
const wasRebloggedByUser = userFetching
|
||||
? !!(await Note.fromSql(
|
||||
and(
|
||||
eq(status.authorId, userFetching?.id),
|
||||
eq(status.reblogId, data.id),
|
||||
eq(Notes.authorId, userFetching?.id),
|
||||
eq(Notes.reblogId, data.id),
|
||||
),
|
||||
))
|
||||
: false;
|
||||
|
||||
const wasMutedByUser = userFetching
|
||||
? !!(await db.query.relationship.findFirst({
|
||||
? !!(await db.query.Relationships.findFirst({
|
||||
where: (relationship, { and, eq }) =>
|
||||
and(
|
||||
eq(relationship.ownerId, userFetching.id),
|
||||
|
|
@ -468,8 +478,8 @@ export class Note {
|
|||
|
||||
return {
|
||||
id: data.id,
|
||||
in_reply_to_id: data.inReplyToPostId || null,
|
||||
in_reply_to_account_id: data.inReplyTo?.authorId || null,
|
||||
in_reply_to_id: data.replyId || null,
|
||||
in_reply_to_account_id: data.reply?.authorId || null,
|
||||
account: userToAPI(data.author),
|
||||
created_at: new Date(data.createdAt).toISOString(),
|
||||
application: data.application
|
||||
|
|
@ -511,9 +521,9 @@ export class Note {
|
|||
visibility: data.visibility as APIStatus["visibility"],
|
||||
url: data.uri || this.getMastoURI(),
|
||||
bookmarked: false,
|
||||
quote: !!data.quotingPostId,
|
||||
quote: !!data.quotingId,
|
||||
// @ts-expect-error Pleroma extension
|
||||
quote_id: data.quotingPostId || undefined,
|
||||
quote_id: data.quotingId || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -546,8 +556,8 @@ export class Note {
|
|||
),
|
||||
is_sensitive: status.sensitive,
|
||||
mentions: status.mentions.map((mention) => mention.uri || ""),
|
||||
quotes: getStatusUri(status.quoting) ?? undefined,
|
||||
replies_to: getStatusUri(status.inReplyTo) ?? undefined,
|
||||
quotes: getStatusUri(status.quote) ?? undefined,
|
||||
replies_to: getStatusUri(status.reply) ?? undefined,
|
||||
subject: status.spoilerText,
|
||||
visibility: status.visibility as Lysand.Visibility,
|
||||
extensions: {
|
||||
|
|
@ -567,10 +577,8 @@ export class Note {
|
|||
|
||||
let currentStatus: Note = this;
|
||||
|
||||
while (currentStatus.getStatus().inReplyToPostId) {
|
||||
const parent = await Note.fromId(
|
||||
currentStatus.getStatus().inReplyToPostId,
|
||||
);
|
||||
while (currentStatus.getStatus().replyId) {
|
||||
const parent = await Note.fromId(currentStatus.getStatus().replyId);
|
||||
|
||||
if (!parent) {
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { type SQL, gt } from "drizzle-orm";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { config } from "~packages/config-manager";
|
||||
import { Note } from "./note";
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ export class Timeline {
|
|||
switch (this.type) {
|
||||
case TimelineType.NOTE: {
|
||||
const objectBefore = await Note.fromSql(
|
||||
gt(status.id, objects[0].getStatus().id),
|
||||
gt(Notes.id, objects[0].getStatus().id),
|
||||
);
|
||||
|
||||
if (objectBefore) {
|
||||
|
|
@ -57,7 +57,7 @@ export class Timeline {
|
|||
if (objects.length >= (limit ?? 20)) {
|
||||
const objectAfter = await Note.fromSql(
|
||||
gt(
|
||||
status.id,
|
||||
Notes.id,
|
||||
objects[objects.length - 1].getStatus().id,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export const processRoute = async (
|
|||
return null;
|
||||
});
|
||||
|
||||
if (!route) {
|
||||
if (!route?.meta) {
|
||||
return errorResponse("Route not found", 404);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { z } from "zod";
|
|||
import { TokenType } from "~database/entities/Token";
|
||||
import { findFirstUser } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { token } from "~drizzle/schema";
|
||||
import { Tokens } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -62,7 +62,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
)
|
||||
return redirectToLogin("Invalid username or password");
|
||||
|
||||
const application = await db.query.application.findFirst({
|
||||
const application = await db.query.Applications.findFirst({
|
||||
where: (app, { eq }) => eq(app.clientId, client_id),
|
||||
});
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const code = randomBytes(32).toString("hex");
|
||||
|
||||
await db.insert(token).values({
|
||||
await db.insert(Tokens).values({
|
||||
accessToken: randomBytes(64).toString("base64url"),
|
||||
code: code,
|
||||
scope: scopes.join(" "),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { z } from "zod";
|
|||
import { TokenType } from "~database/entities/Token";
|
||||
import { findFirstUser } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { token } from "~drizzle/schema";
|
||||
import { Tokens } from "~drizzle/schema";
|
||||
import { config } from "~packages/config-manager";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -54,7 +54,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
const code = randomBytes(32).toString("hex");
|
||||
const accessToken = randomBytes(64).toString("base64url");
|
||||
|
||||
await db.insert(token).values({
|
||||
await db.insert(Tokens).values({
|
||||
accessToken,
|
||||
code: code,
|
||||
scope: "read write follow push",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,4 @@
|
|||
import { randomBytes } from "node:crypto";
|
||||
import { apiRoute, applyConfig } from "@api";
|
||||
import { z } from "zod";
|
||||
import { TokenType } from "~database/entities/Token";
|
||||
import { findFirstUser } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { token } from "~drizzle/schema";
|
||||
import { config } from "~packages/config-manager";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import { application, token } from "~drizzle/schema";
|
||||
import { Applications, Tokens } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -18,10 +18,7 @@ export const meta = applyConfig({
|
|||
/**
|
||||
* OAuth Code flow
|
||||
*/
|
||||
export default apiRoute<{
|
||||
email: string;
|
||||
password: string;
|
||||
}>(async (req, matchedRoute) => {
|
||||
export default apiRoute(async (req, matchedRoute) => {
|
||||
const redirect_uri = decodeURIComponent(matchedRoute.query.redirect_uri);
|
||||
const client_id = matchedRoute.query.client_id;
|
||||
const code = matchedRoute.query.code;
|
||||
|
|
@ -37,9 +34,9 @@ export default apiRoute<{
|
|||
|
||||
const foundToken = await db
|
||||
.select()
|
||||
.from(token)
|
||||
.leftJoin(application, eq(token.applicationId, application.id))
|
||||
.where(and(eq(token.code, code), eq(application.clientId, client_id)))
|
||||
.from(Tokens)
|
||||
.leftJoin(Applications, eq(Tokens.applicationId, Applications.id))
|
||||
.where(and(eq(Tokens.code, code), eq(Applications.clientId, client_id)))
|
||||
.limit(1);
|
||||
|
||||
if (!foundToken || foundToken.length <= 0)
|
||||
|
|
|
|||
98
server/api/api/v1/accounts/[id]/block.test.ts
Normal file
98
server/api/api/v1/accounts/[id]/block.test.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { afterAll, describe, expect, test } from "bun:test";
|
||||
import { config } from "config-manager";
|
||||
import {
|
||||
deleteOldTestUsers,
|
||||
getTestUsers,
|
||||
sendTestRequest,
|
||||
} from "~tests/utils";
|
||||
import { meta } from "./block";
|
||||
import type { Relationship as APIRelationship } from "~types/mastodon/relationship";
|
||||
|
||||
await deleteOldTestUsers();
|
||||
|
||||
const { users, tokens, deleteUsers } = await getTestUsers(2);
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUsers();
|
||||
});
|
||||
|
||||
// /api/v1/accounts/:id/block
|
||||
describe(meta.route, () => {
|
||||
test("should return 401 if not authenticated", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
test("should return 404 if user not found", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(
|
||||
":id",
|
||||
"00000000-0000-0000-0000-000000000000",
|
||||
),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
test("should block user", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.blocking).toBe(true);
|
||||
});
|
||||
|
||||
test("should return 200 if user already blocked", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.blocking).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -48,11 +48,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
}
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
blocking: true,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
|
||||
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||
});
|
||||
|
|
|
|||
98
server/api/api/v1/accounts/[id]/follow.test.ts
Normal file
98
server/api/api/v1/accounts/[id]/follow.test.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { afterAll, describe, expect, test } from "bun:test";
|
||||
import { config } from "config-manager";
|
||||
import {
|
||||
deleteOldTestUsers,
|
||||
getTestUsers,
|
||||
sendTestRequest,
|
||||
} from "~tests/utils";
|
||||
import { meta } from "./follow";
|
||||
import type { Relationship as APIRelationship } from "~types/mastodon/relationship";
|
||||
|
||||
await deleteOldTestUsers();
|
||||
|
||||
const { users, tokens, deleteUsers } = await getTestUsers(2);
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUsers();
|
||||
});
|
||||
|
||||
// /api/v1/accounts/:id/follow
|
||||
describe(meta.route, () => {
|
||||
test("should return 401 if not authenticated", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
test("should return 404 if user not found", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(
|
||||
":id",
|
||||
"00000000-0000-0000-0000-000000000000",
|
||||
),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
test("should follow user", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.following).toBe(true);
|
||||
});
|
||||
|
||||
test("should return 200 if user already followed", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.following).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -57,7 +57,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
max_id ? lt(follower.id, max_id) : undefined,
|
||||
since_id ? gte(follower.id, since_id) : undefined,
|
||||
min_id ? gt(follower.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${otherUser.id} AND "Relationship"."ownerId" = ${follower.id} AND "Relationship"."following" = true)`,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${otherUser.id} AND "Relationships"."ownerId" = ${follower.id} AND "Relationships"."following" = true)`,
|
||||
),
|
||||
// @ts-expect-error Yes I KNOW the types are wrong
|
||||
orderBy: (liker, { desc }) => desc(liker.id),
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
max_id ? lt(following.id, max_id) : undefined,
|
||||
since_id ? gte(following.id, since_id) : undefined,
|
||||
min_id ? gt(following.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${following.id} AND "Relationship"."ownerId" = ${otherUser.id} AND "Relationship"."following" = true)`,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${following.id} AND "Relationships"."ownerId" = ${otherUser.id} AND "Relationships"."following" = true)`,
|
||||
),
|
||||
// @ts-expect-error Yes I KNOW the types are wrong
|
||||
orderBy: (liker, { desc }) => desc(liker.id),
|
||||
|
|
|
|||
98
server/api/api/v1/accounts/[id]/mute.test.ts
Normal file
98
server/api/api/v1/accounts/[id]/mute.test.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { afterAll, describe, expect, test } from "bun:test";
|
||||
import { config } from "config-manager";
|
||||
import {
|
||||
deleteOldTestUsers,
|
||||
getTestUsers,
|
||||
sendTestRequest,
|
||||
} from "~tests/utils";
|
||||
import { meta } from "./mute";
|
||||
import type { Relationship as APIRelationship } from "~types/mastodon/relationship";
|
||||
|
||||
await deleteOldTestUsers();
|
||||
|
||||
const { users, tokens, deleteUsers } = await getTestUsers(2);
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUsers();
|
||||
});
|
||||
|
||||
// /api/v1/accounts/:id/mute
|
||||
describe(meta.route, () => {
|
||||
test("should return 401 if not authenticated", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
test("should return 404 if user not found", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(
|
||||
":id",
|
||||
"00000000-0000-0000-0000-000000000000",
|
||||
),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
test("should mute user", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.muting).toBe(true);
|
||||
});
|
||||
|
||||
test("should return 200 if user already muted", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.muting).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -8,7 +8,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -66,12 +66,12 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
}
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
muting: true,
|
||||
mutingNotifications: notifications ?? true,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
|
||||
// TODO: Implement duration
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -58,11 +58,11 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
foundRelationship.note = comment ?? "";
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
note: foundRelationship.note,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
|
||||
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -48,11 +48,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
foundRelationship.endorsed = true;
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
endorsed: true,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
}
|
||||
|
||||
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -48,23 +48,23 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
foundRelationship.followedBy = false;
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
followedBy: false,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
|
||||
if (otherUser.instanceId === null) {
|
||||
// Also remove from followers list
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
following: false,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(relationship.ownerId, otherUser.id),
|
||||
eq(relationship.subjectId, self.id),
|
||||
eq(Relationships.ownerId, otherUser.id),
|
||||
eq(Relationships.subjectId, self.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
200
server/api/api/v1/accounts/[id]/statuses.test.ts
Normal file
200
server/api/api/v1/accounts/[id]/statuses.test.ts
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { config } from "config-manager";
|
||||
import {
|
||||
deleteOldTestUsers,
|
||||
getTestStatuses,
|
||||
getTestUsers,
|
||||
sendTestRequest,
|
||||
} from "~tests/utils";
|
||||
import type { Account as APIAccount } from "~types/mastodon/account";
|
||||
import { meta } from "./statuses";
|
||||
import type { Status as APIStatus } from "~types/mastodon/status";
|
||||
import { db } from "~drizzle/db";
|
||||
|
||||
await deleteOldTestUsers();
|
||||
|
||||
const { users, tokens, deleteUsers } = await getTestUsers(5);
|
||||
const timeline = (await getTestStatuses(40, users[1])).toReversed();
|
||||
const timeline2 = (await getTestStatuses(40, users[2])).toReversed();
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUsers();
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`/api/v1/statuses/${timeline2[0].id}/reblog`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
// /api/v1/accounts/:id/statuses
|
||||
describe(meta.route, () => {
|
||||
test("should return 200 with statuses", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const data = (await response.json()) as APIStatus[];
|
||||
|
||||
expect(data.length).toBe(20);
|
||||
// Should have reblogs
|
||||
expect(data[0].reblog).toBeDefined();
|
||||
});
|
||||
|
||||
test("should exclude reblogs", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`${meta.route.replace(
|
||||
":id",
|
||||
users[1].id,
|
||||
)}?exclude_reblogs=true`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const data = (await response.json()) as APIStatus[];
|
||||
|
||||
expect(data.length).toBe(20);
|
||||
// Should not have reblogs
|
||||
expect(data[0].reblog).toBeNull();
|
||||
});
|
||||
|
||||
test("should exclude replies", async () => {
|
||||
// Create reply
|
||||
const replyResponse = await sendTestRequest(
|
||||
new Request(new URL("/api/v1/statuses", config.http.base_url), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
status: "Reply",
|
||||
in_reply_to_id: timeline[0].id,
|
||||
federate: false,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(replyResponse.status).toBe(200);
|
||||
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`${meta.route.replace(
|
||||
":id",
|
||||
users[1].id,
|
||||
)}?exclude_replies=true`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const data = (await response.json()) as APIStatus[];
|
||||
|
||||
expect(data.length).toBe(20);
|
||||
// Should not have replies
|
||||
expect(data[0].in_reply_to_id).toBeNull();
|
||||
});
|
||||
|
||||
test("should only include pins", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`${meta.route.replace(":id", users[1].id)}?pinned=true`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const data = (await response.json()) as APIStatus[];
|
||||
|
||||
expect(data.length).toBe(0);
|
||||
|
||||
// Create pin
|
||||
const pinResponse = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`/api/v1/statuses/${timeline[3].id}/pin`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(pinResponse.status).toBe(200);
|
||||
|
||||
const response2 = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`${meta.route.replace(":id", users[1].id)}?pinned=true`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response2.status).toBe(200);
|
||||
|
||||
const data2 = (await response2.json()) as APIStatus[];
|
||||
|
||||
expect(data2.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { and, eq, gt, gte, isNull, lt, sql } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { findFirstUser } from "~database/entities/User";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { Timeline } from "~packages/database-interface/timeline";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -41,7 +41,6 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
return errorResponse("Invalid ID, must be of type UUIDv7", 404);
|
||||
}
|
||||
|
||||
// TODO: Add pinned
|
||||
const {
|
||||
max_id,
|
||||
min_id,
|
||||
|
|
@ -49,6 +48,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
limit,
|
||||
exclude_reblogs,
|
||||
only_media,
|
||||
exclude_replies,
|
||||
pinned,
|
||||
} = extraData.parsedRequest;
|
||||
|
||||
|
|
@ -58,41 +58,20 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
if (!user) return errorResponse("User not found", 404);
|
||||
|
||||
if (pinned) {
|
||||
const { objects, link } = await Timeline.getNoteTimeline(
|
||||
and(
|
||||
max_id ? lt(status.id, max_id) : undefined,
|
||||
since_id ? gte(status.id, since_id) : undefined,
|
||||
min_id ? gt(status.id, min_id) : undefined,
|
||||
eq(status.authorId, id),
|
||||
sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."statusId" = ${status.id} AND "UserToPinnedNotes"."userId" = ${user.id})`,
|
||||
only_media
|
||||
? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})`
|
||||
: undefined,
|
||||
),
|
||||
limit,
|
||||
req.url,
|
||||
);
|
||||
|
||||
return jsonResponse(
|
||||
await Promise.all(objects.map((note) => note.toAPI(user))),
|
||||
200,
|
||||
{
|
||||
Link: link,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const { objects, link } = await Timeline.getNoteTimeline(
|
||||
and(
|
||||
max_id ? lt(status.id, max_id) : undefined,
|
||||
since_id ? gte(status.id, since_id) : undefined,
|
||||
min_id ? gt(status.id, min_id) : undefined,
|
||||
eq(status.authorId, id),
|
||||
max_id ? lt(Notes.id, max_id) : undefined,
|
||||
since_id ? gte(Notes.id, since_id) : undefined,
|
||||
min_id ? gt(Notes.id, min_id) : undefined,
|
||||
eq(Notes.authorId, id),
|
||||
only_media
|
||||
? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})`
|
||||
? sql`EXISTS (SELECT 1 FROM "Attachments" WHERE "Attachments"."noteId" = ${Notes.id})`
|
||||
: undefined,
|
||||
exclude_reblogs ? isNull(status.reblogId) : undefined,
|
||||
pinned
|
||||
? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = ${Notes.id} AND "UserToPinnedNotes"."userId" = ${user.id})`
|
||||
: undefined,
|
||||
exclude_reblogs ? isNull(Notes.reblogId) : undefined,
|
||||
exclude_replies ? isNull(Notes.replyId) : undefined,
|
||||
),
|
||||
limit,
|
||||
req.url,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -44,11 +44,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
foundRelationship.blocking = false;
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
blocking: false,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
}
|
||||
|
||||
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -48,12 +48,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
foundRelationship.following = false;
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
following: false,
|
||||
requested: false,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
}
|
||||
|
||||
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||
|
|
|
|||
115
server/api/api/v1/accounts/[id]/unmute.test.ts
Normal file
115
server/api/api/v1/accounts/[id]/unmute.test.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { config } from "config-manager";
|
||||
import {
|
||||
deleteOldTestUsers,
|
||||
getTestUsers,
|
||||
sendTestRequest,
|
||||
} from "~tests/utils";
|
||||
import { meta } from "./unmute";
|
||||
import type { Relationship as APIRelationship } from "~types/mastodon/relationship";
|
||||
|
||||
await deleteOldTestUsers();
|
||||
|
||||
const { users, tokens, deleteUsers } = await getTestUsers(2);
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUsers();
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`/api/v1/accounts/${users[0].id}/mute`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[1].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
// /api/v1/accounts/:id/unmute
|
||||
describe(meta.route, () => {
|
||||
test("should return 401 if not authenticated", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
test("should return 404 if user not found", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(
|
||||
":id",
|
||||
"00000000-0000-0000-0000-000000000000",
|
||||
),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
test("should unmute user", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.muting).toBe(false);
|
||||
});
|
||||
|
||||
test("should return 200 if user already unmuted", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
expect(relationship.muting).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -49,12 +49,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
foundRelationship.mutingNotifications = false;
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
muting: false,
|
||||
mutingNotifications: false,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
}
|
||||
|
||||
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRelationshipToOtherUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -48,11 +48,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
foundRelationship.endorsed = false;
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
endorsed: false,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
}
|
||||
|
||||
return jsonResponse(relationshipToAPI(foundRelationship));
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const { id: ids } = extraData.parsedRequest;
|
||||
|
||||
const idFollowerRelationships = await db.query.relationship.findMany({
|
||||
const idFollowerRelationships = await db.query.Relationships.findMany({
|
||||
columns: {
|
||||
ownerId: true,
|
||||
},
|
||||
|
|
@ -48,7 +48,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
}
|
||||
|
||||
// Find users that you follow in idFollowerRelationships
|
||||
const relevantRelationships = await db.query.relationship.findMany({
|
||||
const relevantRelationships = await db.query.Relationships.findMany({
|
||||
columns: {
|
||||
subjectId: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const { id: ids } = extraData.parsedRequest;
|
||||
|
||||
const relationships = await db.query.relationship.findMany({
|
||||
const relationships = await db.query.Relationships.findMany({
|
||||
where: (relationship, { inArray, and, eq }) =>
|
||||
and(
|
||||
inArray(relationship.subjectId, ids),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
resolveWebFinger,
|
||||
userToAPI,
|
||||
} from "~database/entities/User";
|
||||
import { user } from "~drizzle/schema";
|
||||
import { Users } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -94,7 +94,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
like(account.displayName, `%${q}%`),
|
||||
like(account.username, `%${q}%`),
|
||||
following
|
||||
? sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${user.id} AND "Relationship"."ownerId" = ${account.id} AND "Relationship"."following" = true)`
|
||||
? sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${Users.id} AND "Relationships"."ownerId" = ${account.id} AND "Relationships"."following" = true)`
|
||||
: undefined,
|
||||
),
|
||||
offset,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { getUrl } from "~database/entities/Attachment";
|
|||
import { parseEmojis } from "~database/entities/Emoji";
|
||||
import { findFirstUser, userToAPI } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { emojiToUser, user } from "~drizzle/schema";
|
||||
import { EmojiToUser, Users } from "~drizzle/schema";
|
||||
import type { Source as APISource } from "~types/mastodon/source";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -194,7 +194,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
);
|
||||
|
||||
await db
|
||||
.update(user)
|
||||
.update(Users)
|
||||
.set({
|
||||
displayName: self.displayName,
|
||||
note: self.note,
|
||||
|
|
@ -205,22 +205,22 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
isDiscoverable: self.isDiscoverable,
|
||||
source: self.source || undefined,
|
||||
})
|
||||
.where(eq(user.id, self.id));
|
||||
.where(eq(Users.id, self.id));
|
||||
|
||||
// Connect emojis, if any
|
||||
for (const emoji of self.emojis) {
|
||||
await db
|
||||
.delete(emojiToUser)
|
||||
.delete(EmojiToUser)
|
||||
.where(
|
||||
and(
|
||||
eq(emojiToUser.emojiId, emoji.id),
|
||||
eq(emojiToUser.userId, self.id),
|
||||
eq(EmojiToUser.emojiId, emoji.id),
|
||||
eq(EmojiToUser.userId, self.id),
|
||||
),
|
||||
)
|
||||
.execute();
|
||||
|
||||
await db
|
||||
.insert(emojiToUser)
|
||||
.insert(EmojiToUser)
|
||||
.values({
|
||||
emojiId: emoji.id,
|
||||
userId: self.id,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { apiRoute, applyConfig } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { z } from "zod";
|
||||
import { db } from "~drizzle/db";
|
||||
import { application } from "~drizzle/schema";
|
||||
import { Applications } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -34,7 +34,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const app = (
|
||||
await db
|
||||
.insert(application)
|
||||
.insert(Applications)
|
||||
.values({
|
||||
name: client_name || "",
|
||||
redirectUris: redirect_uris || "",
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
max_id ? lt(subject.id, max_id) : undefined,
|
||||
since_id ? gte(subject.id, since_id) : undefined,
|
||||
min_id ? gt(subject.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${subject.id} AND "Relationship"."ownerId" = ${user.id} AND "Relationship"."blocking" = true)`,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${subject.id} AND "Relationships"."ownerId" = ${user.id} AND "Relationships"."blocking" = true)`,
|
||||
),
|
||||
limit,
|
||||
// @ts-expect-error Yes I KNOW the types are wrong
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export const meta = applyConfig({
|
|||
});
|
||||
|
||||
export default apiRoute(async () => {
|
||||
const emojis = await db.query.emoji.findMany({
|
||||
const emojis = await db.query.Emojis.findMany({
|
||||
where: (emoji, { isNull }) => isNull(emoji.instanceId),
|
||||
with: {
|
||||
instance: true,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { and, gt, gte, lt, sql } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { Timeline } from "~packages/database-interface/timeline";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -34,10 +34,10 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const { objects, link } = await Timeline.getNoteTimeline(
|
||||
and(
|
||||
max_id ? lt(status.id, max_id) : undefined,
|
||||
since_id ? gte(status.id, since_id) : undefined,
|
||||
min_id ? gt(status.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Like" WHERE "Like"."likedId" = ${status.id} AND "Like"."likerId" = ${user.id})`,
|
||||
max_id ? lt(Notes.id, max_id) : undefined,
|
||||
since_id ? gte(Notes.id, since_id) : undefined,
|
||||
min_id ? gt(Notes.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = ${Notes.id} AND "Likes"."likerId" = ${user.id})`,
|
||||
),
|
||||
limit,
|
||||
req.url,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
sendFollowAccept,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -43,28 +43,28 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
// Authorize follow request
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
requested: false,
|
||||
following: true,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(relationship.subjectId, user.id),
|
||||
eq(relationship.ownerId, account.id),
|
||||
eq(Relationships.subjectId, user.id),
|
||||
eq(Relationships.ownerId, account.id),
|
||||
),
|
||||
);
|
||||
|
||||
// Update followedBy for other user
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
followedBy: true,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(relationship.subjectId, account.id),
|
||||
eq(relationship.ownerId, user.id),
|
||||
eq(Relationships.subjectId, account.id),
|
||||
eq(Relationships.ownerId, user.id),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
sendFollowReject,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { relationship } from "~drizzle/schema";
|
||||
import { Relationships } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -43,28 +43,28 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
// Reject follow request
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
requested: false,
|
||||
following: false,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(relationship.subjectId, user.id),
|
||||
eq(relationship.ownerId, account.id),
|
||||
eq(Relationships.subjectId, user.id),
|
||||
eq(Relationships.ownerId, account.id),
|
||||
),
|
||||
);
|
||||
|
||||
// Update followedBy for other user
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
followedBy: false,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(relationship.subjectId, account.id),
|
||||
eq(relationship.ownerId, user.id),
|
||||
eq(Relationships.subjectId, account.id),
|
||||
eq(Relationships.ownerId, user.id),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
max_id ? lt(subject.id, max_id) : undefined,
|
||||
since_id ? gte(subject.id, since_id) : undefined,
|
||||
min_id ? gt(subject.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${user.id} AND "Relationship"."ownerId" = ${subject.id} AND "Relationship"."requested" = true)`,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${user.id} AND "Relationships"."ownerId" = ${subject.id} AND "Relationships"."requested" = true)`,
|
||||
),
|
||||
limit,
|
||||
// @ts-expect-error Yes I KNOW the types are wrong
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { jsonResponse } from "@response";
|
|||
import { and, count, countDistinct, eq, gte, isNull, sql } from "drizzle-orm";
|
||||
import { findFirstUser, userToAPI } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { instance, status, user } from "~drizzle/schema";
|
||||
import { Instances, Notes, Users } from "~drizzle/schema";
|
||||
import manifest from "~package.json";
|
||||
import type { Instance as APIInstance } from "~types/mastodon/instance";
|
||||
|
||||
|
|
@ -30,9 +30,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
.select({
|
||||
count: count(),
|
||||
})
|
||||
.from(status)
|
||||
.from(Notes)
|
||||
.where(
|
||||
sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`,
|
||||
sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NULL)`,
|
||||
)
|
||||
)[0].count;
|
||||
|
||||
|
|
@ -41,8 +41,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
.select({
|
||||
count: count(),
|
||||
})
|
||||
.from(user)
|
||||
.where(isNull(user.instanceId))
|
||||
.from(Users)
|
||||
.where(isNull(Users.instanceId))
|
||||
)[0].count;
|
||||
|
||||
const contactAccount = await findFirstUser({
|
||||
|
|
@ -54,15 +54,15 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
const monthlyActiveUsers = (
|
||||
await db
|
||||
.select({
|
||||
count: countDistinct(user),
|
||||
count: countDistinct(Users),
|
||||
})
|
||||
.from(user)
|
||||
.leftJoin(status, eq(user.id, status.authorId))
|
||||
.from(Users)
|
||||
.leftJoin(Notes, eq(Users.id, Notes.authorId))
|
||||
.where(
|
||||
and(
|
||||
isNull(user.instanceId),
|
||||
isNull(Users.instanceId),
|
||||
gte(
|
||||
status.createdAt,
|
||||
Notes.createdAt,
|
||||
new Date(
|
||||
Date.now() - 30 * 24 * 60 * 60 * 1000,
|
||||
).toISOString(),
|
||||
|
|
@ -76,7 +76,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
.select({
|
||||
count: count(),
|
||||
})
|
||||
.from(instance)
|
||||
.from(Instances)
|
||||
)[0].count;
|
||||
|
||||
// TODO: fill in more values
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { LocalMediaBackend, S3MediaBackend } from "media-manager";
|
|||
import { z } from "zod";
|
||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||
import { db } from "~drizzle/db";
|
||||
import { attachment } from "~drizzle/schema";
|
||||
import { Attachments } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET", "PUT"],
|
||||
|
|
@ -48,7 +48,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
return errorResponse("Invalid ID, must be of type UUIDv7", 404);
|
||||
}
|
||||
|
||||
const foundAttachment = await db.query.attachment.findFirst({
|
||||
const foundAttachment = await db.query.Attachments.findFirst({
|
||||
where: (attachment, { eq }) => eq(attachment.id, id),
|
||||
});
|
||||
|
||||
|
|
@ -98,12 +98,12 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
) {
|
||||
const newAttachment = (
|
||||
await db
|
||||
.update(attachment)
|
||||
.update(Attachments)
|
||||
.set({
|
||||
description: descriptionText,
|
||||
thumbnailUrl,
|
||||
})
|
||||
.where(eq(attachment.id, id))
|
||||
.where(eq(Attachments.id, id))
|
||||
.returning()
|
||||
)[0];
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import sharp from "sharp";
|
|||
import { z } from "zod";
|
||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||
import { db } from "~drizzle/db";
|
||||
import { attachment } from "~drizzle/schema";
|
||||
import { Attachments } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -128,7 +128,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const newAttachment = (
|
||||
await db
|
||||
.insert(attachment)
|
||||
.insert(Attachments)
|
||||
.values({
|
||||
url,
|
||||
thumbnailUrl,
|
||||
|
|
|
|||
102
server/api/api/v1/mutes/index.test.ts
Normal file
102
server/api/api/v1/mutes/index.test.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { config } from "config-manager";
|
||||
import {
|
||||
deleteOldTestUsers,
|
||||
getTestUsers,
|
||||
sendTestRequest,
|
||||
} from "~tests/utils";
|
||||
import { meta } from "./index";
|
||||
|
||||
await deleteOldTestUsers();
|
||||
|
||||
const { users, tokens, deleteUsers } = await getTestUsers(3);
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUsers();
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`/api/v1/accounts/${users[1].id}/mute`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
// /api/v1/mutes
|
||||
describe(meta.route, () => {
|
||||
test("should return 401 if not authenticated", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
meta.route.replace(":id", users[1].id),
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "GET",
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
test("should return mutes", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(new URL(meta.route, config.http.base_url), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
const body = await response.json();
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: users[1].id,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test("should return mutes after unmute", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
new URL(
|
||||
`/api/v1/accounts/${users[1].id}/unmute`,
|
||||
config.http.base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const response2 = await sendTestRequest(
|
||||
new Request(new URL(meta.route, config.http.base_url), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens[0].accessToken}`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(response2.status).toBe(200);
|
||||
const body = await response2.json();
|
||||
expect(body).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
@ -45,7 +45,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
max_id ? lt(subject.id, max_id) : undefined,
|
||||
since_id ? gte(subject.id, since_id) : undefined,
|
||||
min_id ? gt(subject.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${subject.id} AND "Relationship"."ownerId" = ${user.id} AND "Relationship"."muting" = true)`,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${subject.id} AND "Relationships"."ownerId" = ${user.id} AND "Relationships"."muting" = true)`,
|
||||
),
|
||||
limit,
|
||||
// @ts-expect-error Yes I KNOW the types are wrong
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import { notification } from "~drizzle/schema";
|
||||
import { Notifications } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -27,11 +27,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
await db
|
||||
.update(notification)
|
||||
.update(Notifications)
|
||||
.set({
|
||||
dismissed: true,
|
||||
})
|
||||
.where(eq(notification.id, id));
|
||||
.where(eq(Notifications.id, id));
|
||||
|
||||
return jsonResponse({});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { apiRoute, applyConfig } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import { notification } from "~drizzle/schema";
|
||||
import { Notifications } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -22,11 +22,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
if (!user) return errorResponse("Unauthorized", 401);
|
||||
|
||||
await db
|
||||
.update(notification)
|
||||
.update(Notifications)
|
||||
.set({
|
||||
dismissed: true,
|
||||
})
|
||||
.where(eq(notification.notifiedId, user.id));
|
||||
.where(eq(Notifications.notifiedId, user.id));
|
||||
|
||||
return jsonResponse({});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { inArray } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { db } from "~drizzle/db";
|
||||
import { notification } from "~drizzle/schema";
|
||||
import { Notifications } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["DELETE"],
|
||||
|
|
@ -30,11 +30,11 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
const { ids } = extraData.parsedRequest;
|
||||
|
||||
await db
|
||||
.update(notification)
|
||||
.update(Notifications)
|
||||
.set({
|
||||
dismissed: true,
|
||||
})
|
||||
.where(inArray(notification.id, ids));
|
||||
.where(inArray(Notifications.id, ids));
|
||||
|
||||
return jsonResponse({});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { eq } from "drizzle-orm";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { user } from "~drizzle/schema";
|
||||
import { Users } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["DELETE"],
|
||||
|
|
@ -25,7 +25,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
await db.update(user).set({ avatar: "" }).where(eq(user.id, self.id));
|
||||
await db.update(Users).set({ avatar: "" }).where(eq(Users.id, self.id));
|
||||
|
||||
return jsonResponse(
|
||||
userToAPI({
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { eq } from "drizzle-orm";
|
||||
import { userToAPI } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { user } from "~drizzle/schema";
|
||||
import { Users } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["DELETE"],
|
||||
|
|
@ -26,7 +26,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
if (!self) return errorResponse("Unauthorized", 401);
|
||||
|
||||
// Delete user header
|
||||
await db.update(user).set({ header: "" }).where(eq(user.id, self.id));
|
||||
await db.update(Users).set({ header: "" }).where(eq(Users.id, self.id));
|
||||
|
||||
return jsonResponse(
|
||||
userToAPI({
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
if (!foundStatus) return errorResponse("Record not found", 404);
|
||||
|
||||
const relations = user
|
||||
? await db.query.relationship.findMany({
|
||||
? await db.query.Relationships.findMany({
|
||||
where: (relationship, { eq }) =>
|
||||
eq(relationship.ownerId, user.id),
|
||||
})
|
||||
: null;
|
||||
|
||||
const relationSubjects = user
|
||||
? await db.query.relationship.findMany({
|
||||
? await db.query.Relationships.findMany({
|
||||
where: (relationship, { eq }) =>
|
||||
eq(relationship.subjectId, user.id),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
if (!status?.isViewableByUser(user))
|
||||
return errorResponse("Record not found", 404);
|
||||
|
||||
const existingLike = await db.query.like.findFirst({
|
||||
const existingLike = await db.query.Likes.findFirst({
|
||||
where: (like, { and, eq }) =>
|
||||
and(
|
||||
eq(like.likedId, status.getStatus().id),
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
max_id ? lt(liker.id, max_id) : undefined,
|
||||
since_id ? gte(liker.id, since_id) : undefined,
|
||||
min_id ? gt(liker.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Like" WHERE "Like"."likedId" = ${
|
||||
sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = ${
|
||||
status.getStatus().id
|
||||
} AND "Like"."likerId" = ${liker.id})`,
|
||||
} AND "Likes"."likerId" = ${liker.id})`,
|
||||
),
|
||||
// @ts-expect-error Yes I KNOW the types are wrong
|
||||
orderBy: (liker, { desc }) => desc(liker.id),
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
// Check if media attachments are all valid
|
||||
if (media_ids && media_ids.length > 0) {
|
||||
const foundAttachments = await db.query.attachment.findMany({
|
||||
const foundAttachments = await db.query.Attachments.findMany({
|
||||
where: (attachment, { inArray }) =>
|
||||
inArray(attachment.id, media_ids),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { apiRoute, applyConfig, idValidator } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { db } from "~drizzle/db";
|
||||
import { statusToMentions } from "~drizzle/schema";
|
||||
import { NoteToMentions, UserToPinnedNotes } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -40,10 +40,10 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
// Check if post is already pinned
|
||||
if (
|
||||
await db.query.userPinnedNotes.findFirst({
|
||||
await db.query.UserToPinnedNotes.findFirst({
|
||||
where: (userPinnedNote, { and, eq }) =>
|
||||
and(
|
||||
eq(userPinnedNote.statusId, foundStatus.getStatus().id),
|
||||
eq(userPinnedNote.noteId, foundStatus.getStatus().id),
|
||||
eq(userPinnedNote.userId, user.id),
|
||||
),
|
||||
})
|
||||
|
|
@ -51,10 +51,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
return errorResponse("Already pinned", 422);
|
||||
}
|
||||
|
||||
await db.insert(statusToMentions).values({
|
||||
statusId: foundStatus.getStatus().id,
|
||||
userId: user.id,
|
||||
});
|
||||
await foundStatus.pin(user);
|
||||
|
||||
return jsonResponse(await foundStatus.toAPI(user));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response";
|
|||
import { and, eq } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { db } from "~drizzle/db";
|
||||
import { notification, status } from "~drizzle/schema";
|
||||
import { Notes, Notifications } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -44,13 +44,12 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
if (!foundStatus?.isViewableByUser(user))
|
||||
return errorResponse("Record not found", 404);
|
||||
|
||||
const existingReblog = await db.query.status.findFirst({
|
||||
where: (status, { and, eq }) =>
|
||||
and(
|
||||
eq(status.authorId, user.id),
|
||||
eq(status.reblogId, status.id),
|
||||
),
|
||||
});
|
||||
const existingReblog = await Note.fromSql(
|
||||
and(
|
||||
eq(Notes.authorId, user.id),
|
||||
eq(Notes.reblogId, foundStatus.getStatus().id),
|
||||
),
|
||||
);
|
||||
|
||||
if (existingReblog) {
|
||||
return errorResponse("Already reblogged", 422);
|
||||
|
|
@ -77,11 +76,11 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
// Create notification for reblog if reblogged user is on the same instance
|
||||
if (foundStatus.getAuthor().instanceId === user.instanceId) {
|
||||
await db.insert(notification).values({
|
||||
await db.insert(Notifications).values({
|
||||
accountId: user.id,
|
||||
notifiedId: foundStatus.getAuthor().id,
|
||||
type: "reblog",
|
||||
statusId: foundStatus.getStatus().reblogId,
|
||||
noteId: foundStatus.getStatus().reblogId,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
max_id ? lt(reblogger.id, max_id) : undefined,
|
||||
since_id ? gte(reblogger.id, since_id) : undefined,
|
||||
min_id ? gt(reblogger.id, min_id) : undefined,
|
||||
sql`EXISTS (SELECT 1 FROM "Status" WHERE "Status"."reblogId" = ${
|
||||
sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."reblogId" = ${
|
||||
status.getStatus().id
|
||||
} AND "Status"."authorId" = ${reblogger.id})`,
|
||||
} AND "Notes"."authorId" = ${reblogger.id})`,
|
||||
),
|
||||
// @ts-expect-error Yes I KNOW the types are wrong
|
||||
orderBy: (liker, { desc }) => desc(liker.id),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { apiRoute, applyConfig, idValidator } from "@api";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
import type { Status as APIStatus } from "~types/mastodon/status";
|
||||
|
||||
|
|
@ -38,8 +38,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
const existingReblog = await Note.fromSql(
|
||||
and(
|
||||
eq(status.authorId, user.id),
|
||||
eq(status.reblogId, foundStatus.getStatus().id),
|
||||
eq(Notes.authorId, user.id),
|
||||
eq(Notes.reblogId, foundStatus.getStatus().id),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
const {
|
||||
status,
|
||||
media_ids,
|
||||
"poll[expires_in]": expires_in,
|
||||
"poll[options]": options,
|
||||
in_reply_to_id,
|
||||
quote_id,
|
||||
|
|
@ -119,12 +118,6 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
sanitizedStatus = await sanitizeHtml(status ?? "");
|
||||
}
|
||||
|
||||
// Get reply account and status if exists
|
||||
const replyStatus: StatusWithRelations | null =
|
||||
(await Note.fromId(in_reply_to_id ?? null))?.getStatus() ?? null;
|
||||
const quote: StatusWithRelations | null =
|
||||
(await Note.fromId(quote_id ?? null))?.getStatus() ?? null;
|
||||
|
||||
// Check if status body doesnt match filters
|
||||
if (
|
||||
config.filters.note_content.some((filter) => status?.match(filter))
|
||||
|
|
@ -134,18 +127,31 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
// Check if media attachments are all valid
|
||||
if (media_ids && media_ids.length > 0) {
|
||||
const foundAttachments = await db.query.attachment
|
||||
.findMany({
|
||||
where: (attachment, { inArray }) =>
|
||||
inArray(attachment.id, media_ids),
|
||||
})
|
||||
.catch(() => []);
|
||||
const foundAttachments = await db.query.Attachments.findMany({
|
||||
where: (attachment, { inArray }) =>
|
||||
inArray(attachment.id, media_ids),
|
||||
}).catch(() => []);
|
||||
|
||||
if (foundAttachments.length !== (media_ids ?? []).length) {
|
||||
return errorResponse("Invalid media IDs", 422);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that in_reply_to_id and quote_id are real posts if provided
|
||||
if (in_reply_to_id) {
|
||||
const foundReply = await Note.fromId(in_reply_to_id);
|
||||
if (!foundReply) {
|
||||
return errorResponse("Invalid in_reply_to_id (not found)", 422);
|
||||
}
|
||||
}
|
||||
|
||||
if (quote_id) {
|
||||
const foundQuote = await Note.fromId(quote_id);
|
||||
if (!foundQuote) {
|
||||
return errorResponse("Invalid quote_id (not found)", 422);
|
||||
}
|
||||
}
|
||||
|
||||
const mentions = await parseTextMentions(sanitizedStatus);
|
||||
|
||||
const newNote = await Note.fromData(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { and, eq, gt, gte, lt, or, sql } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { Timeline } from "~packages/database-interface/timeline";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -38,18 +38,16 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
const { objects, link } = await Timeline.getNoteTimeline(
|
||||
and(
|
||||
and(
|
||||
max_id ? lt(status.id, max_id) : undefined,
|
||||
since_id ? gte(status.id, since_id) : undefined,
|
||||
min_id ? gt(status.id, min_id) : undefined,
|
||||
max_id ? lt(Notes.id, max_id) : undefined,
|
||||
since_id ? gte(Notes.id, since_id) : undefined,
|
||||
min_id ? gt(Notes.id, min_id) : undefined,
|
||||
),
|
||||
or(
|
||||
eq(status.authorId, user.id),
|
||||
// All statuses where the user is mentioned, using table _StatusToUser which has a: status.id and b: user.id
|
||||
// WHERE format (... = ...)
|
||||
sql`EXISTS (SELECT 1 FROM "StatusToMentions" WHERE "StatusToMentions"."statusId" = ${status.id} AND "StatusToMentions"."userId" = ${user.id})`,
|
||||
eq(Notes.authorId, user.id),
|
||||
sql`EXISTS (SELECT 1 FROM "NoteToMentions" WHERE "NoteToMentions"."noteId" = ${Notes.id} AND "NoteToMentions"."userId" = ${user.id})`,
|
||||
// All statuses from users that the user is following
|
||||
// WHERE format (... = ...)
|
||||
sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${status.authorId} AND "Relationship"."ownerId" = ${user.id} AND "Relationship"."following" = true)`,
|
||||
sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${Notes.authorId} AND "Relationships"."ownerId" = ${user.id} AND "Relationships"."following" = true)`,
|
||||
),
|
||||
),
|
||||
limit,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api";
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { and, gt, gte, lt, sql } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { Timeline } from "~packages/database-interface/timeline";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -39,18 +39,18 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const { objects, link } = await Timeline.getNoteTimeline(
|
||||
and(
|
||||
max_id ? lt(status.id, max_id) : undefined,
|
||||
since_id ? gte(status.id, since_id) : undefined,
|
||||
min_id ? gt(status.id, min_id) : undefined,
|
||||
max_id ? lt(Notes.id, max_id) : undefined,
|
||||
since_id ? gte(Notes.id, since_id) : undefined,
|
||||
min_id ? gt(Notes.id, min_id) : undefined,
|
||||
// use authorId to grab user, then use user.instanceId to filter local/remote statuses
|
||||
remote
|
||||
? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NOT NULL)`
|
||||
? sql`EXISTS (SELECT 1 FROM "Users" WHERE "Users"."id" = ${Notes.authorId} AND "Users"."instanceId" IS NOT NULL)`
|
||||
: undefined,
|
||||
local
|
||||
? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`
|
||||
? sql`EXISTS (SELECT 1 FROM "Users" WHERE "Users"."id" = ${Notes.authorId} AND "Users"."instanceId" IS NULL)`
|
||||
: undefined,
|
||||
only_media
|
||||
? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})`
|
||||
? sql`EXISTS (SELECT 1 FROM "Attachments" WHERE "Attachments"."noteId" = ${Notes.id})`
|
||||
: undefined,
|
||||
),
|
||||
limit,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { jsonResponse } from "@response";
|
|||
import { and, countDistinct, eq, gte, isNull } from "drizzle-orm";
|
||||
import { findFirstUser, userToAPI } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { status, user } from "~drizzle/schema";
|
||||
import { Notes, Users } from "~drizzle/schema";
|
||||
import manifest from "~package.json";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -32,15 +32,15 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
const monthlyActiveUsers = (
|
||||
await db
|
||||
.select({
|
||||
count: countDistinct(user),
|
||||
count: countDistinct(Users),
|
||||
})
|
||||
.from(user)
|
||||
.leftJoin(status, eq(user.id, status.authorId))
|
||||
.from(Users)
|
||||
.leftJoin(Notes, eq(Users.id, Notes.authorId))
|
||||
.where(
|
||||
and(
|
||||
isNull(user.instanceId),
|
||||
isNull(Users.instanceId),
|
||||
gte(
|
||||
status.createdAt,
|
||||
Notes.createdAt,
|
||||
new Date(
|
||||
Date.now() - 30 * 24 * 60 * 60 * 1000,
|
||||
).toISOString(),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import sharp from "sharp";
|
|||
import { z } from "zod";
|
||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||
import { db } from "~drizzle/db";
|
||||
import { attachment } from "~drizzle/schema";
|
||||
import { Attachments } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["POST"],
|
||||
|
|
@ -124,7 +124,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
|
||||
const newAttachment = (
|
||||
await db
|
||||
.insert(attachment)
|
||||
.insert(Attachments)
|
||||
.values({
|
||||
url,
|
||||
thumbnailUrl,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
userToAPI,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { instance, status, user } from "~drizzle/schema";
|
||||
import { Instances, Notes, Users } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
import { LogLevel } from "~packages/log-manager";
|
||||
|
||||
|
|
@ -88,14 +88,14 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
const accountId = (
|
||||
await db
|
||||
.select({
|
||||
id: user.id,
|
||||
id: Users.id,
|
||||
})
|
||||
.from(user)
|
||||
.leftJoin(instance, eq(user.instanceId, instance.id))
|
||||
.from(Users)
|
||||
.leftJoin(Instances, eq(Users.instanceId, Instances.id))
|
||||
.where(
|
||||
and(
|
||||
eq(user.username, username),
|
||||
eq(instance.baseUrl, domain),
|
||||
eq(Users.username, username),
|
||||
eq(Instances.baseUrl, domain),
|
||||
),
|
||||
)
|
||||
)[0]?.id;
|
||||
|
|
@ -181,16 +181,16 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
const statuses = await Note.manyFromSql(
|
||||
and(
|
||||
inArray(
|
||||
status.id,
|
||||
Notes.id,
|
||||
statusResults.map((hit) => hit.id),
|
||||
),
|
||||
account_id ? eq(status.authorId, account_id) : undefined,
|
||||
account_id ? eq(Notes.authorId, account_id) : undefined,
|
||||
self
|
||||
? sql`EXISTS (SELECT 1 FROM Relationships WHERE Relationships.subjectId = ${
|
||||
self?.id
|
||||
} AND Relationships.following = ${
|
||||
following ? true : false
|
||||
} AND Relationships.ownerId = ${status.authorId})`
|
||||
} AND Relationships.ownerId = ${Notes.authorId})`
|
||||
: undefined,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
processDiscoveryResponse,
|
||||
} from "oauth4webapi";
|
||||
import { db } from "~drizzle/db";
|
||||
import { openIdLoginFlow } from "~drizzle/schema";
|
||||
import { OpenIdLoginFlows } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -61,7 +61,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
const codeVerifier = generateRandomCodeVerifier();
|
||||
|
||||
const application = await db.query.application.findFirst({
|
||||
const application = await db.query.Applications.findFirst({
|
||||
where: (application, { eq }) => eq(application.clientId, clientId),
|
||||
});
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
// Store into database
|
||||
const newFlow = (
|
||||
await db
|
||||
.insert(openIdLoginFlow)
|
||||
.insert(OpenIdLoginFlows)
|
||||
.values({
|
||||
codeVerifier,
|
||||
applicationId: application.id,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { TokenType } from "~database/entities/Token";
|
||||
import { findFirstUser } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { token } from "~drizzle/schema";
|
||||
import { Tokens } from "~drizzle/schema";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
|
|
@ -166,7 +166,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
const code = randomBytes(32).toString("hex");
|
||||
|
||||
await db.insert(token).values({
|
||||
await db.insert(Tokens).values({
|
||||
accessToken: randomBytes(64).toString("base64url"),
|
||||
code: code,
|
||||
scope: flow.application.scopes,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
);
|
||||
|
||||
// Get associated token
|
||||
const application = await db.query.application.findFirst({
|
||||
const application = await db.query.Applications.findFirst({
|
||||
where: (application, { eq, and }) =>
|
||||
and(
|
||||
eq(application.clientId, client_id),
|
||||
|
|
@ -61,7 +61,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
|||
401,
|
||||
);
|
||||
|
||||
const token = await db.query.token.findFirst({
|
||||
const token = await db.query.Tokens.findFirst({
|
||||
where: (token, { eq }) =>
|
||||
eq(token.code, code) && eq(token.applicationId, application.id),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { and, eq, inArray, sql } from "drizzle-orm";
|
|||
import type * as Lysand from "lysand-types";
|
||||
import { type Like, likeToLysand } from "~database/entities/Like";
|
||||
import { db } from "~drizzle/db";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -27,15 +27,15 @@ export default apiRoute(async (req, matchedRoute) => {
|
|||
|
||||
foundObject = await Note.fromSql(
|
||||
and(
|
||||
eq(status.id, uuid),
|
||||
inArray(status.visibility, ["public", "unlisted"]),
|
||||
eq(Notes.id, uuid),
|
||||
inArray(Notes.visibility, ["public", "unlisted"]),
|
||||
),
|
||||
);
|
||||
apiObject = foundObject ? foundObject.toLysand() : null;
|
||||
|
||||
if (!foundObject) {
|
||||
foundObject =
|
||||
(await db.query.like.findFirst({
|
||||
(await db.query.Likes.findFirst({
|
||||
where: (like, { eq, and }) =>
|
||||
and(
|
||||
eq(like.id, uuid),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
sendFollowAccept,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { notification, relationship } from "~drizzle/schema";
|
||||
import { Notifications, Relationships } from "~drizzle/schema";
|
||||
import { LogLevel } from "~packages/log-manager";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -161,7 +161,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
}
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
following: !user.isLocked,
|
||||
requested: user.isLocked,
|
||||
|
|
@ -169,9 +169,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
notifying: true,
|
||||
languages: [],
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
|
||||
await db.insert(notification).values({
|
||||
await db.insert(Notifications).values({
|
||||
accountId: account.id,
|
||||
type: user.isLocked ? "follow_request" : "follow",
|
||||
notifiedId: user.id,
|
||||
|
|
@ -209,12 +209,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
}
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
following: true,
|
||||
requested: false,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
|
||||
return response("Follow request accepted", 200);
|
||||
}
|
||||
|
|
@ -237,12 +237,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
}
|
||||
|
||||
await db
|
||||
.update(relationship)
|
||||
.update(Relationships)
|
||||
.set({
|
||||
requested: false,
|
||||
following: false,
|
||||
})
|
||||
.where(eq(relationship.id, foundRelationship.id));
|
||||
.where(eq(Relationships.id, foundRelationship.id));
|
||||
|
||||
return response("Follow request rejected", 200);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { apiRoute, applyConfig } from "@api";
|
|||
import { jsonResponse } from "@response";
|
||||
import { and, count, eq, inArray } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import { status } from "~drizzle/schema";
|
||||
import { Notes } from "~drizzle/schema";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
@ -25,8 +25,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
|
||||
const notes = await Note.manyFromSql(
|
||||
and(
|
||||
eq(status.authorId, uuid),
|
||||
inArray(status.visibility, ["public", "unlisted"]),
|
||||
eq(Notes.authorId, uuid),
|
||||
inArray(Notes.visibility, ["public", "unlisted"]),
|
||||
),
|
||||
undefined,
|
||||
20,
|
||||
|
|
@ -37,11 +37,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => {
|
|||
.select({
|
||||
count: count(),
|
||||
})
|
||||
.from(status)
|
||||
.from(Notes)
|
||||
.where(
|
||||
and(
|
||||
eq(status.authorId, uuid),
|
||||
inArray(status.visibility, ["public", "unlisted"]),
|
||||
eq(Notes.authorId, uuid),
|
||||
inArray(Notes.visibility, ["public", "unlisted"]),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|||
import { config } from "config-manager";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "~drizzle/db";
|
||||
import { emoji } from "~drizzle/schema";
|
||||
import { Emojis } from "~drizzle/schema";
|
||||
import type { Emoji as APIEmoji } from "~types/mastodon/emoji";
|
||||
import type { Instance as APIInstance } from "~types/mastodon/instance";
|
||||
import { getTestUsers, sendTestRequest, wrapRelativeUrl } from "./utils";
|
||||
|
|
@ -18,7 +18,7 @@ describe("API Tests", () => {
|
|||
|
||||
describe("GET /api/v1/custom_emojis", () => {
|
||||
beforeAll(async () => {
|
||||
await db.insert(emoji).values({
|
||||
await db.insert(Emojis).values({
|
||||
shortcode: "test",
|
||||
url: "https://example.com/test.png",
|
||||
contentType: "image/png",
|
||||
|
|
@ -55,7 +55,7 @@ describe("API Tests", () => {
|
|||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.delete(emoji).where(eq(emoji.shortcode, "test"));
|
||||
await db.delete(Emojis).where(eq(Emojis.shortcode, "test"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -124,68 +124,6 @@ describe("API Tests", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("POST /api/v1/accounts/:id/follow", () => {
|
||||
test("should follow the specified user and return an APIRelationship object", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
wrapRelativeUrl(
|
||||
`/api/v1/accounts/${user2.id}/follow`,
|
||||
base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe(
|
||||
"application/json",
|
||||
);
|
||||
|
||||
const relationship = (await response.json()) as APIRelationship;
|
||||
|
||||
expect(relationship.id).toBe(user2.id);
|
||||
expect(relationship.following).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/v1/accounts/:id/unfollow", () => {
|
||||
test("should unfollow the specified user and return an APIRelationship object", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
wrapRelativeUrl(
|
||||
`/api/v1/accounts/${user2.id}/unfollow`,
|
||||
base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe(
|
||||
"application/json",
|
||||
);
|
||||
|
||||
const account = (await response.json()) as APIRelationship;
|
||||
|
||||
expect(account.id).toBe(user2.id);
|
||||
expect(account.following).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/v1/accounts/:id/remove_from_followers", () => {
|
||||
test("should remove the specified user from the authenticated user's followers and return an APIRelationship object", async () => {
|
||||
const response = await sendTestRequest(
|
||||
|
|
@ -302,123 +240,6 @@ describe("API Tests", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("POST /api/v1/accounts/:id/mute with notifications parameter", () => {
|
||||
test("should mute the specified user and return an APIRelationship object with notifications set to false", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
wrapRelativeUrl(
|
||||
`/api/v1/accounts/${user2.id}/mute`,
|
||||
base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ notifications: true }),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe(
|
||||
"application/json",
|
||||
);
|
||||
|
||||
const account = (await response.json()) as APIRelationship;
|
||||
|
||||
expect(account.id).toBe(user2.id);
|
||||
expect(account.muting).toBe(true);
|
||||
expect(account.muting_notifications).toBe(true);
|
||||
});
|
||||
|
||||
test("should mute the specified user and return an APIRelationship object with notifications set to true", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
wrapRelativeUrl(
|
||||
`/api/v1/accounts/${user2.id}/mute`,
|
||||
base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ notifications: false }),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe(
|
||||
"application/json",
|
||||
);
|
||||
|
||||
const account = (await response.json()) as APIRelationship;
|
||||
|
||||
expect(account.id).toBe(user2.id);
|
||||
expect(account.muting).toBe(true);
|
||||
expect(account.muting_notifications).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GET /api/v1/mutes", () => {
|
||||
test("should return an array of APIAccount objects for the user's muted accounts", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(wrapRelativeUrl("/api/v1/mutes", base_url), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe(
|
||||
"application/json",
|
||||
);
|
||||
|
||||
const body = (await response.json()) as APIAccount[];
|
||||
|
||||
expect(Array.isArray(body)).toBe(true);
|
||||
expect(body.length).toBe(1);
|
||||
expect(body[0].id).toBe(user2.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/v1/accounts/:id/unmute", () => {
|
||||
test("should unmute the specified user and return an APIRelationship object", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
wrapRelativeUrl(
|
||||
`/api/v1/accounts/${user2.id}/unmute`,
|
||||
base_url,
|
||||
),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe(
|
||||
"application/json",
|
||||
);
|
||||
|
||||
const account = (await response.json()) as APIRelationship;
|
||||
|
||||
expect(account.id).toBe(user2.id);
|
||||
expect(account.muting).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/v1/accounts/:id/pin", () => {
|
||||
test("should pin the specified user and return an APIRelationship object", async () => {
|
||||
const response = await sendTestRequest(
|
||||
|
|
|
|||
|
|
@ -344,35 +344,6 @@ describe("API Tests", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("GET /api/v1/statuses/:id/favourited_by", () => {
|
||||
test("should return an array of User objects who favourited the specified status", async () => {
|
||||
const response = await sendTestRequest(
|
||||
new Request(
|
||||
wrapRelativeUrl(
|
||||
`${base_url}/api/v1/statuses/${status?.id}/favourited_by`,
|
||||
base_url,
|
||||
),
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token.accessToken}`,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toBe(
|
||||
"application/json",
|
||||
);
|
||||
|
||||
const users = (await response.json()) as APIAccount[];
|
||||
|
||||
expect(users.length).toBe(1);
|
||||
expect(users[0].id).toBe(user.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/v1/statuses/:id/unfavourite", () => {
|
||||
test("should unfavourite the specified status object", async () => {
|
||||
// Unfavourite the status
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
createNewLocalUser,
|
||||
} from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { status, token, user } from "~drizzle/schema";
|
||||
import { Notes, Tokens, Users } from "~drizzle/schema";
|
||||
import { server } from "~index";
|
||||
import { Note } from "~packages/database-interface/note";
|
||||
/**
|
||||
|
|
@ -26,7 +26,7 @@ export function wrapRelativeUrl(url: string, base_url: string) {
|
|||
|
||||
export const deleteOldTestUsers = async () => {
|
||||
// Deletes all users that match the test username (test-<32 random characters>)
|
||||
await db.delete(user).where(like(user.username, "test-%"));
|
||||
await db.delete(Users).where(like(Users.username, "test-%"));
|
||||
};
|
||||
|
||||
export const getTestUsers = async (count: number) => {
|
||||
|
|
@ -51,7 +51,7 @@ export const getTestUsers = async (count: number) => {
|
|||
}
|
||||
|
||||
const tokens = await db
|
||||
.insert(token)
|
||||
.insert(Tokens)
|
||||
.values(
|
||||
users.map((u) => ({
|
||||
accessToken: randomBytes(32).toString("hex"),
|
||||
|
|
@ -69,9 +69,9 @@ export const getTestUsers = async (count: number) => {
|
|||
tokens,
|
||||
passwords,
|
||||
deleteUsers: async () => {
|
||||
await db.delete(user).where(
|
||||
await db.delete(Users).where(
|
||||
inArray(
|
||||
user.id,
|
||||
Users.id,
|
||||
users.map((u) => u.id),
|
||||
),
|
||||
);
|
||||
|
|
@ -107,10 +107,10 @@ export const getTestStatuses = async (
|
|||
return (
|
||||
await Note.manyFromSql(
|
||||
inArray(
|
||||
status.id,
|
||||
Notes.id,
|
||||
statuses.map((s) => s.id),
|
||||
),
|
||||
asc(status.id),
|
||||
asc(Notes.id),
|
||||
)
|
||||
).map((n) => n.getStatus());
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Meilisearch } from "meilisearch";
|
|||
import type { Status } from "~database/entities/Status";
|
||||
import type { User } from "~database/entities/User";
|
||||
import { db } from "~drizzle/db";
|
||||
import { status, user } from "~drizzle/schema";
|
||||
import { Notes, Users } from "~drizzle/schema";
|
||||
|
||||
export const meilisearch = new Meilisearch({
|
||||
host: `${config.meilisearch.host}:${config.meilisearch.port}`,
|
||||
|
|
@ -83,7 +83,7 @@ export const getNthDatabaseAccountBatch = (
|
|||
n: number,
|
||||
batchSize = 1000,
|
||||
): Promise<Record<string, string | Date>[]> => {
|
||||
return db.query.user.findMany({
|
||||
return db.query.Users.findMany({
|
||||
offset: n * batchSize,
|
||||
limit: batchSize,
|
||||
columns: {
|
||||
|
|
@ -101,7 +101,7 @@ export const getNthDatabaseStatusBatch = (
|
|||
n: number,
|
||||
batchSize = 1000,
|
||||
): Promise<Record<string, string | Date>[]> => {
|
||||
return db.query.status.findMany({
|
||||
return db.query.Notes.findMany({
|
||||
offset: n * batchSize,
|
||||
limit: batchSize,
|
||||
columns: {
|
||||
|
|
@ -123,7 +123,7 @@ export const rebuildSearchIndexes = async (
|
|||
.select({
|
||||
count: count(),
|
||||
})
|
||||
.from(user)
|
||||
.from(Users)
|
||||
)[0].count;
|
||||
|
||||
for (let i = 0; i < accountCount / batchSize; i++) {
|
||||
|
|
@ -156,7 +156,7 @@ export const rebuildSearchIndexes = async (
|
|||
.select({
|
||||
count: count(),
|
||||
})
|
||||
.from(status)
|
||||
.from(Notes)
|
||||
)[0].count;
|
||||
|
||||
for (let i = 0; i < statusCount / batchSize; i++) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export async function fetchTimeline<T extends User | Status | Notification>(
|
|||
args:
|
||||
| Parameters<typeof findManyNotes>[0]
|
||||
| Parameters<typeof findManyUsers>[0]
|
||||
| Parameters<typeof db.query.notification.findMany>[0],
|
||||
| Parameters<typeof db.query.Notifications.findMany>[0],
|
||||
req: Request,
|
||||
) {
|
||||
// BEFORE: Before in a top-to-bottom order, so the most recent posts
|
||||
|
|
|
|||
Loading…
Reference in a new issue