Fix not working routes

This commit is contained in:
Jesse Wierzbinski 2024-04-11 02:12:16 -10:00
parent f7abe06a60
commit df939a6a7a
No known key found for this signature in database
8 changed files with 146 additions and 69 deletions

View file

@ -1,8 +1,19 @@
import type { APINotification } from "~types/entities/notification"; import type { APINotification } from "~types/entities/notification";
import { type StatusWithRelations, statusToAPI } from "./Status"; import {
import { type UserWithRelations, userToAPI } from "./User"; type StatusWithRelations,
statusToAPI,
findFirstStatuses,
} from "./Status";
import {
type UserWithRelations,
userToAPI,
userRelations,
userExtrasTemplate,
transformOutputToUserWithRelations,
} from "./User";
import type { InferSelectModel } from "drizzle-orm"; import type { InferSelectModel } from "drizzle-orm";
import type { notification } from "~drizzle/schema"; import type { notification } from "~drizzle/schema";
import { db } from "~drizzle/db";
export type Notification = InferSelectModel<typeof notification>; export type Notification = InferSelectModel<typeof notification>;
@ -11,6 +22,39 @@ export type NotificationWithRelations = Notification & {
account: UserWithRelations; account: UserWithRelations;
}; };
export const findManyNotifications = async (
query: Parameters<typeof db.query.notification.findMany>[0],
): Promise<NotificationWithRelations[]> => {
const output = await db.query.notification.findMany({
...query,
with: {
...query?.with,
account: {
with: {
...userRelations,
},
extras: userExtrasTemplate("notification_account"),
},
},
extras: {
...query?.extras,
},
});
return await Promise.all(
output.map(async (notif) => ({
...notif,
account: transformOutputToUserWithRelations(notif.account),
status: notif.statusId
? await findFirstStatuses({
where: (status, { eq }) =>
eq(status.id, notif.statusId ?? ""),
})
: null,
})),
);
};
export const notificationToAPI = async ( export const notificationToAPI = async (
notification: NotificationWithRelations, notification: NotificationWithRelations,
): Promise<APINotification> => { ): Promise<APINotification> => {

View file

@ -100,6 +100,21 @@ export const statusExtras = {
), ),
}; };
export const statusExtrasTemplate = (name: string) => ({
// @ts-ignore
reblogCount: sql([
`(SELECT COUNT(*) FROM "Status" "status" WHERE "status"."reblogId" = ${name}.id)`,
]).as("reblog_count"),
// @ts-ignore
likeCount: sql([
`(SELECT COUNT(*) FROM "Like" "like" WHERE "like"."likedId" = ${name}.id)`,
]).as("like_count"),
// @ts-ignore
replyCount: sql([
`(SELECT COUNT(*) FROM "Status" "status" WHERE "status"."inReplyToPostId" = ${name}.id)`,
]).as("reply_count"),
});
/** /**
* Returns whether this status is viewable by a user. * Returns whether this status is viewable by a user.
* @param user The user to check. * @param user The user to check.
@ -138,6 +153,15 @@ export const findManyStatuses = async (
where: (attachment, { eq }) => where: (attachment, { eq }) =>
eq(attachment.statusId, sql`"status"."id"`), eq(attachment.statusId, sql`"status"."id"`),
}, },
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
author: { author: {
with: { with: {
...userRelations, ...userRelations,

View file

@ -536,7 +536,12 @@ export const userRelations = relations(user, ({ many, one }) => ({
relationshipSubjects: many(relationship, { relationshipSubjects: many(relationship, {
relationName: "RelationshipToSubject", relationName: "RelationshipToSubject",
}), }),
notifications: many(notification), notificationsMade: many(notification, {
relationName: "NotificationToAccount",
}),
notificationsReceived: many(notification, {
relationName: "NotificationToNotified",
}),
openIdAccounts: many(openIdAccount), openIdAccounts: many(openIdAccount),
flags: many(flag), flags: many(flag),
modNotes: many(modNote), modNotes: many(modNote),
@ -637,6 +642,24 @@ export const statusRelations = relations(status, ({ many, one }) => ({
reblogs: many(status, { reblogs: many(status, {
relationName: "StatusToReblog", relationName: "StatusToReblog",
}), }),
notifications: many(notification),
}));
export const notificationRelations = relations(notification, ({ one }) => ({
account: one(user, {
fields: [notification.accountId],
references: [user.id],
relationName: "NotificationToAccount",
}),
notified: one(user, {
fields: [notification.notifiedId],
references: [user.id],
relationName: "NotificationToNotified",
}),
status: one(status, {
fields: [notification.statusId],
references: [status.id],
}),
})); }));
export const likeRelations = relations(like, ({ one }) => ({ export const likeRelations = relations(like, ({ one }) => ({

View file

@ -1,9 +1,11 @@
import { apiRoute, applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines"; import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource"; import {
import { userToAPI, type UserWithRelations } from "~database/entities/User"; findManyUsers,
import { userRelations } from "~database/entities/relations"; userToAPI,
type UserWithRelations,
} from "~database/entities/User";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],
@ -34,26 +36,19 @@ export default apiRoute<{
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
const { objects, link } = await fetchTimeline<UserWithRelations>( const { objects, link } = await fetchTimeline<UserWithRelations>(
client.user, findManyUsers,
{ {
where: { // @ts-expect-error Yes I KNOW the types are wrong
id: { where: (subject, { lt, gte, gt, and, sql }) =>
lt: max_id ?? undefined, and(
gte: since_id ?? undefined, max_id ? lt(subject.id, max_id) : undefined,
gt: min_id ?? undefined, since_id ? gte(subject.id, since_id) : undefined,
}, min_id ? gt(subject.id, min_id) : undefined,
relationships: { sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${user.id} AND "Relationship"."ownerId" = ${subject.id} AND "Relationship"."requested" = true)`,
some: { ),
subjectId: user.id, limit: Number(limit),
requested: true, // @ts-expect-error Yes I KNOW the types are wrong
}, orderBy: (subject, { desc }) => desc(subject.id),
},
},
include: userRelations,
take: Number(limit),
orderBy: {
id: "desc",
},
}, },
req, req,
); );

View file

@ -1,13 +1,20 @@
import { apiRoute, applyConfig } from "@api"; import { apiRoute, applyConfig } from "@api";
import type { Prisma } from "@prisma/client";
import { errorResponse, jsonResponse } from "@response"; import { errorResponse, jsonResponse } from "@response";
import { fetchTimeline } from "@timelines"; import { fetchTimeline } from "@timelines";
import { client } from "~database/datasource"; import { client } from "~database/datasource";
import { notificationToAPI } from "~database/entities/Notification"; import {
findManyNotifications,
notificationToAPI,
} from "~database/entities/Notification";
import { import {
statusAndUserRelations, statusAndUserRelations,
userRelations, userRelations,
} from "~database/entities/relations"; } from "~database/entities/relations";
import type {
Notification,
NotificationWithRelations,
} from "~database/entities/Notification";
import { db } from "~drizzle/db";
export const meta = applyConfig({ export const meta = applyConfig({
allowedMethods: ["GET"], allowedMethods: ["GET"],
@ -52,41 +59,24 @@ export default apiRoute<{
return errorResponse("Can't use both types and exclude_types", 400); return errorResponse("Can't use both types and exclude_types", 400);
} }
const { objects, link } = await fetchTimeline< const { objects, link } = await fetchTimeline<NotificationWithRelations>(
Prisma.NotificationGetPayload<{ findManyNotifications,
include: {
account: {
include: typeof userRelations;
};
status: {
include: typeof statusAndUserRelations;
};
};
}>
>(
client.notification,
{ {
where: { // @ts-expect-error Yes I KNOW the types are wrong
id: { where: (notification, { lt, gte, gt, and, or, eq, inArray, sql }) =>
lt: max_id ?? undefined, or(
gte: since_id ?? undefined, and(
gt: min_id ?? undefined, max_id ? lt(notification.id, max_id) : undefined,
}, since_id ? gte(notification.id, since_id) : undefined,
notifiedId: user.id, min_id ? gt(notification.id, min_id) : undefined,
accountId: account_id, ),
}, eq(notification.notifiedId, user.id),
include: { eq(notification.accountId, account_id),
account: { ),
include: userRelations, with: {},
}, limit: Number(limit),
status: { // @ts-expect-error Yes I KNOW the types are wrong
include: statusAndUserRelations, orderBy: (notification, { desc }) => desc(notification.id),
},
},
orderBy: {
id: "desc",
},
take: Number(limit),
}, },
req, req,
); );

View file

@ -65,9 +65,9 @@ export default apiRoute<{
status.authorId, status.authorId,
followers.map((f) => f.ownerId), followers.map((f) => f.ownerId),
), */ ), */
// All statuses where the user is mentioned, using table StatusToUser which has a: status.id and b: user.id // All statuses where the user is mentioned, using table _StatusToUser which has a: status.id and b: user.id
// WHERE format (... = ...) // WHERE format (... = ...)
sql`EXISTS (SELECT 1 FROM "StatusToUser" WHERE "StatusToUser"."a" = ${status.id} AND "StatusToUser"."b" = ${user.id})`, sql`EXISTS (SELECT 1 FROM "_StatusToUser" WHERE "_StatusToUser"."a" = ${status.id} AND "_StatusToUser"."b" = ${user.id})`,
// All statuses from users that the user is following // All statuses from users that the user is following
// WHERE format (... = ...) // 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 "Relationship" WHERE "Relationship"."subjectId" = ${status.authorId} AND "Relationship"."ownerId" = ${user.id} AND "Relationship"."following" = true)`,

View file

@ -1,4 +1,4 @@
// import { server } from "~index"; import { server } from "~index";
/** /**
* This allows us to send a test request to the server even when it isnt running * This allows us to send a test request to the server even when it isnt running
@ -7,9 +7,7 @@
* @returns Response from the server * @returns Response from the server
*/ */
export async function sendTestRequest(req: Request) { export async function sendTestRequest(req: Request) {
console.log(req); return server.fetch(req);
return fetch(req);
// return server.fetch(req);
} }
export function wrapRelativeUrl(url: string, base_url: string) { export function wrapRelativeUrl(url: string, base_url: string) {

View file

@ -1,13 +1,16 @@
import type { findManyStatuses, Status } from "~database/entities/Status"; import type { findManyStatuses, Status } from "~database/entities/Status";
import type { findManyUsers, User } from "~database/entities/User"; import type { findManyUsers, User } from "~database/entities/User";
import type { Notification } from "~database/entities/Notification"; import type {
findManyNotifications,
Notification,
} from "~database/entities/Notification";
import type { db } from "~drizzle/db"; import type { db } from "~drizzle/db";
export async function fetchTimeline<T extends User | Status | Notification>( export async function fetchTimeline<T extends User | Status | Notification>(
model: model:
| typeof findManyStatuses | typeof findManyStatuses
| typeof findManyUsers | typeof findManyUsers
| typeof db.query.notification.findMany, | typeof findManyNotifications,
args: args:
| Parameters<typeof findManyStatuses>[0] | Parameters<typeof findManyStatuses>[0]
| Parameters<typeof findManyUsers>[0] | Parameters<typeof findManyUsers>[0]