️ Query optimizations (#1829)

This commit is contained in:
Luke Vella 2025-07-15 17:08:47 +01:00 committed by GitHub
parent c50016f11a
commit e06e106d3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 92 deletions

View file

@ -1,7 +1,7 @@
import { notFound, redirect } from "next/navigation";
import { cache } from "react";
import { defineAbilityFor } from "@/features/ability-manager";
import { getDefaultSpace, getSpace } from "@/features/spaces/queries";
import { loadDefaultSpace, loadSpace } from "@/features/spaces/queries";
import { getUser } from "@/features/user/queries";
import { auth } from "@/next-auth";
@ -44,18 +44,16 @@ export const getActiveSpace = cache(async () => {
const { user } = await requireUserAbility();
if (user.activeSpaceId) {
const activeSpace = await getSpace({ id: user.activeSpaceId });
if (activeSpace) {
return activeSpace;
}
try {
return await loadSpace({ id: user.activeSpaceId });
} catch {
console.warn(
`User ${user.id} has an active space ID ${user.activeSpaceId} that does not exist or is no longer accessible`,
);
}
}
return await getDefaultSpace();
return await loadDefaultSpace();
});
export const requireUserAbility = cache(async () => {

View file

@ -1,7 +1,6 @@
import { accessibleBy } from "@casl/prisma";
import type { SpaceMemberRole } from "@rallly/database";
import { prisma } from "@rallly/database";
import { redirect } from "next/navigation";
import { cache } from "react";
import { requireUserAbility } from "@/auth/queries";
@ -19,89 +18,26 @@ export const loadSpaces = cache(async () => {
where: accessibleBy(ability).Space,
include: {
subscription: true,
members: true,
},
});
const availableSpaces: SpaceDTO[] = [];
for (const space of spaces) {
const role = space.members.find(
(member) => member.userId === user.id,
)?.role;
if (!role) {
console.warn(`User ${user.id} does not have access to space ${space.id}`);
continue;
}
availableSpaces.push({
id: space.id,
name: space.name,
ownerId: space.ownerId,
isPro: Boolean(space.subscription?.active),
role,
});
}
return availableSpaces;
});
export const getDefaultSpace = cache(async () => {
const { user } = await requireUserAbility();
const space = await prisma.space.findFirst({
where: {
ownerId: user.id,
},
orderBy: {
createdAt: "asc",
},
include: {
subscription: true,
},
});
if (!space) {
redirect("/setup");
}
return {
id: space.id,
name: space.name,
ownerId: space.ownerId,
isPro: Boolean(space.subscription?.active),
role: "OWNER",
} satisfies SpaceDTO;
});
export const getSpace = cache(async ({ id }: { id: string }) => {
const { user, ability } = await requireUserAbility();
const space = await prisma.space.findFirst({
where: {
AND: [accessibleBy(ability).Space, { id }],
},
include: {
subscription: {
where: {
active: true,
},
},
members: {
where: {
userId: user.id,
},
select: {
role: true,
},
},
},
});
if (!space) {
throw new Error(`User ${user.id} does not have access to space ${id}`);
}
const role = space.members.find((member) => member.userId === user.id)?.role;
return spaces
.map((space) => {
const role = space.members[0]?.role;
if (!role) {
throw new Error(`User ${user.id} is not a member of space ${id}`);
console.warn(
`User ${user.id} does not have access to space ${space.id}`,
);
return null;
}
return {
@ -111,4 +47,30 @@ export const getSpace = cache(async ({ id }: { id: string }) => {
isPro: Boolean(space.subscription?.active),
role,
} satisfies SpaceDTO;
})
.filter(Boolean) as SpaceDTO[];
});
export const loadDefaultSpace = cache(async () => {
const { user } = await requireUserAbility();
const spaces = await loadSpaces();
const defaultSpace = spaces.find((space) => space.ownerId === user.id);
if (!defaultSpace) {
throw new Error(`User ${user.id} does not have access to any spaces`);
}
return defaultSpace;
});
export const loadSpace = cache(async ({ id }: { id: string }) => {
const spaces = await loadSpaces();
const space = spaces.find((space) => space.id === id);
if (!space) {
const { user } = await requireUserAbility();
throw new Error(`User ${user.id} does not have access to space ${id}`);
}
return space;
});

View file

@ -0,0 +1,8 @@
-- DropIndex
DROP INDEX "space_members_space_id_idx";
-- CreateIndex
CREATE INDEX "space_members_space_id_idx" ON "space_members" USING HASH ("space_id");
-- CreateIndex
CREATE INDEX "space_members_user_id_idx" ON "space_members" USING HASH ("user_id");

View file

@ -35,6 +35,7 @@ model SpaceMember {
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([spaceId, userId])
@@index([spaceId])
@@index([spaceId], type: Hash, map: "space_members_space_id_idx")
@@index([userId], type: Hash, map: "space_members_user_id_idx")
@@map("space_members")
}