mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-23 11:17:26 +02:00
⚡️ Query optimizations (#1829)
This commit is contained in:
parent
c50016f11a
commit
e06e106d3c
4 changed files with 61 additions and 92 deletions
|
@ -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 () => {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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");
|
|
@ -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")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue