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 StatusWithRelations, statusToAPI } from "./Status";
import { type UserWithRelations, userToAPI } from "./User";
import {
type StatusWithRelations,
statusToAPI,
findFirstStatuses,
} from "./Status";
import {
type UserWithRelations,
userToAPI,
userRelations,
userExtrasTemplate,
transformOutputToUserWithRelations,
} from "./User";
import type { InferSelectModel } from "drizzle-orm";
import type { notification } from "~drizzle/schema";
import { db } from "~drizzle/db";
export type Notification = InferSelectModel<typeof notification>;
@ -11,6 +22,39 @@ export type NotificationWithRelations = Notification & {
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 (
notification: NotificationWithRelations,
): 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.
* @param user The user to check.
@ -138,6 +153,15 @@ export const findManyStatuses = async (
where: (attachment, { eq }) =>
eq(attachment.statusId, sql`"status"."id"`),
},
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
author: {
with: {
...userRelations,

View file

@ -536,7 +536,12 @@ export const userRelations = relations(user, ({ many, one }) => ({
relationshipSubjects: many(relationship, {
relationName: "RelationshipToSubject",
}),
notifications: many(notification),
notificationsMade: many(notification, {
relationName: "NotificationToAccount",
}),
notificationsReceived: many(notification, {
relationName: "NotificationToNotified",
}),
openIdAccounts: many(openIdAccount),
flags: many(flag),
modNotes: many(modNote),
@ -637,6 +642,24 @@ export const statusRelations = relations(status, ({ many, one }) => ({
reblogs: many(status, {
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 }) => ({

View file

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

View file

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

View file

@ -65,9 +65,9 @@ export default apiRoute<{
status.authorId,
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 (... = ...)
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
// WHERE format (... = ...)
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
@ -7,9 +7,7 @@
* @returns Response from the server
*/
export async function sendTestRequest(req: Request) {
console.log(req);
return fetch(req);
// return server.fetch(req);
return server.fetch(req);
}
export function wrapRelativeUrl(url: string, base_url: string) {

View file

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