mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-25 20:27:44 +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 { notFound, redirect } from "next/navigation";
|
||||||
import { cache } from "react";
|
import { cache } from "react";
|
||||||
import { defineAbilityFor } from "@/features/ability-manager";
|
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 { getUser } from "@/features/user/queries";
|
||||||
import { auth } from "@/next-auth";
|
import { auth } from "@/next-auth";
|
||||||
|
|
||||||
|
@ -44,18 +44,16 @@ export const getActiveSpace = cache(async () => {
|
||||||
const { user } = await requireUserAbility();
|
const { user } = await requireUserAbility();
|
||||||
|
|
||||||
if (user.activeSpaceId) {
|
if (user.activeSpaceId) {
|
||||||
const activeSpace = await getSpace({ id: user.activeSpaceId });
|
try {
|
||||||
|
return await loadSpace({ id: user.activeSpaceId });
|
||||||
if (activeSpace) {
|
} catch {
|
||||||
return activeSpace;
|
console.warn(
|
||||||
|
`User ${user.id} has an active space ID ${user.activeSpaceId} that does not exist or is no longer accessible`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 () => {
|
export const requireUserAbility = cache(async () => {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { accessibleBy } from "@casl/prisma";
|
import { accessibleBy } from "@casl/prisma";
|
||||||
import type { SpaceMemberRole } from "@rallly/database";
|
import type { SpaceMemberRole } from "@rallly/database";
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { cache } from "react";
|
import { cache } from "react";
|
||||||
import { requireUserAbility } from "@/auth/queries";
|
import { requireUserAbility } from "@/auth/queries";
|
||||||
|
|
||||||
|
@ -19,96 +18,59 @@ export const loadSpaces = cache(async () => {
|
||||||
where: accessibleBy(ability).Space,
|
where: accessibleBy(ability).Space,
|
||||||
include: {
|
include: {
|
||||||
subscription: true,
|
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: {
|
members: {
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
},
|
},
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return spaces
|
||||||
|
.map((space) => {
|
||||||
|
const role = space.members[0]?.role;
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
console.warn(
|
||||||
|
`User ${user.id} does not have access to space ${space.id}`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: space.id,
|
||||||
|
name: space.name,
|
||||||
|
ownerId: space.ownerId,
|
||||||
|
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) {
|
if (!space) {
|
||||||
|
const { user } = await requireUserAbility();
|
||||||
throw new Error(`User ${user.id} does not have access to space ${id}`);
|
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 space;
|
||||||
|
|
||||||
if (!role) {
|
|
||||||
throw new Error(`User ${user.id} is not a member of space ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: space.id,
|
|
||||||
name: space.name,
|
|
||||||
ownerId: space.ownerId,
|
|
||||||
isPro: Boolean(space.subscription?.active),
|
|
||||||
role,
|
|
||||||
} satisfies SpaceDTO;
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -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)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@unique([spaceId, userId])
|
@@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")
|
@@map("space_members")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue