added support for nested collection (backend)

This commit is contained in:
daniel31x13 2024-02-03 07:57:29 -05:00
parent 8534572662
commit dba2453453
9 changed files with 158 additions and 25 deletions

View File

@ -37,6 +37,8 @@ export default async function deleteCollection(
}
const deletedCollection = await prisma.$transaction(async () => {
await deleteSubCollections(collectionId);
await prisma.usersAndCollections.deleteMany({
where: {
collection: {
@ -53,7 +55,7 @@ export default async function deleteCollection(
},
});
removeFolder({ filePath: `archives/${collectionId}` });
await removeFolder({ filePath: `archives/${collectionId}` });
return await prisma.collection.delete({
where: {
@ -64,3 +66,35 @@ export default async function deleteCollection(
return { response: deletedCollection, status: 200 };
}
async function deleteSubCollections(collectionId: number) {
const subCollections = await prisma.collection.findMany({
where: { parentId: collectionId },
});
for (const subCollection of subCollections) {
await deleteSubCollections(subCollection.id);
await prisma.usersAndCollections.deleteMany({
where: {
collection: {
id: subCollection.id,
},
},
});
await prisma.link.deleteMany({
where: {
collection: {
id: subCollection.id,
},
},
});
await prisma.collection.delete({
where: { id: subCollection.id },
});
await removeFolder({ filePath: `archives/${subCollection.id}` });
}
}

View File

@ -0,0 +1,35 @@
import { prisma } from "@/lib/api/db";
export default async function getCollectionById(
userId: number,
collectionId: number
) {
const collections = await prisma.collection.findFirst({
where: {
id: collectionId,
OR: [
{ ownerId: userId },
{ members: { some: { user: { id: userId } } } },
],
},
include: {
_count: {
select: { links: true },
},
subCollections: true,
members: {
include: {
user: {
select: {
username: true,
name: true,
image: true,
},
},
},
},
},
});
return { response: collections, status: 200 };
}

View File

@ -1,7 +1,6 @@
import { prisma } from "@/lib/api/db";
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
import getPermission from "@/lib/api/getPermission";
import { Collection, UsersAndCollections } from "@prisma/client";
export default async function updateCollection(
userId: number,
@ -19,6 +18,26 @@ export default async function updateCollection(
if (!(collectionIsAccessible?.ownerId === userId))
return { response: "Collection is not accessible.", status: 401 };
if (data.parentId) {
const findParentCollection = await prisma.collection.findUnique({
where: {
id: data.parentId,
},
select: {
ownerId: true,
},
});
if (
findParentCollection?.ownerId !== userId ||
typeof data.parentId !== "number"
)
return {
response: "You are not authorized to create a sub-collection here.",
status: 403,
};
}
const updatedCollection = await prisma.$transaction(async () => {
await prisma.usersAndCollections.deleteMany({
where: {
@ -38,6 +57,11 @@ export default async function updateCollection(
description: data.description,
color: data.color,
isPublic: data.isPublic,
parent: {
connect: {
id: data.parentId || undefined,
},
},
members: {
create: data.members.map((e) => ({
user: { connect: { id: e.user.id || e.userId } },

View File

@ -12,6 +12,26 @@ export default async function postCollection(
status: 400,
};
if (collection.parentId) {
const findParentCollection = await prisma.collection.findUnique({
where: {
id: collection.parentId,
},
select: {
ownerId: true,
},
});
if (
findParentCollection?.ownerId !== userId ||
typeof collection.parentId !== "number"
)
return {
response: "You are not authorized to create a sub-collection here.",
status: 403,
};
}
const findCollection = await prisma.user.findUnique({
where: {
id: userId,
@ -40,6 +60,13 @@ export default async function postCollection(
name: collection.name.trim(),
description: collection.description,
color: collection.color,
parent: collection.parentId
? {
connect: {
id: collection.parentId,
},
}
: undefined,
},
include: {
_count: {

View File

@ -1,4 +1,5 @@
import type { NextApiRequest, NextApiResponse } from "next";
import getCollectionById from "@/lib/api/controllers/collections/collectionId/getCollectionById";
import updateCollectionById from "@/lib/api/controllers/collections/collectionId/updateCollectionById";
import deleteCollectionById from "@/lib/api/controllers/collections/collectionId/deleteCollectionById";
import verifyUser from "@/lib/api/verifyUser";
@ -10,18 +11,18 @@ export default async function collections(
const user = await verifyUser({ req, res });
if (!user) return;
if (req.method === "PUT") {
const updated = await updateCollectionById(
user.id,
Number(req.query.id) as number,
req.body
);
const collectionId = Number(req.query.id);
if (req.method === "GET") {
const collections = await getCollectionById(user.id, collectionId);
return res
.status(collections.status)
.json({ response: collections.response });
} else if (req.method === "PUT") {
const updated = await updateCollectionById(user.id, collectionId, req.body);
return res.status(updated.status).json({ response: updated.response });
} else if (req.method === "DELETE") {
const deleted = await deleteCollectionById(
user.id,
Number(req.query.id) as number
);
const deleted = await deleteCollectionById(user.id, collectionId);
return res.status(deleted.status).json({ response: deleted.response });
}
}

View File

@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Collection" ADD COLUMN "parentId" INTEGER;
-- AddForeignKey
ALTER TABLE "Collection" ADD CONSTRAINT "Collection_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Collection"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@ -73,6 +73,9 @@ model Collection {
name String
description String @default("")
color String @default("#0ea5e9")
parentId Int?
parent Collection? @relation("SubCollections", fields: [parentId], references: [id])
subCollections Collection[] @relation("SubCollections")
isPublic Boolean @default(false)
owner User @relation(fields: [ownerId], references: [id])
ownerId Int

View File

@ -1,4 +1,4 @@
import 'dotenv/config';
import "dotenv/config";
import { Collection, Link, User } from "@prisma/client";
import { prisma } from "../lib/api/db";
import archiveHandler from "../lib/api/archiveHandler";

View File

@ -78,7 +78,11 @@ const useCollectionStore = create<CollectionStore>()((set) => ({
if (response.ok) {
set((state) => ({
collections: state.collections.filter((e) => e.id !== collectionId),
collections: state.collections.filter(
(collection) =>
collection.id !== collectionId &&
collection.parentId !== collectionId
),
}));
useTagStore.getState().setTags();
}