diff --git a/lib/api/controllers/collections/collectionId/deleteCollectionById.ts b/lib/api/controllers/collections/collectionId/deleteCollectionById.ts index e8fe51a..1ac1bf6 100644 --- a/lib/api/controllers/collections/collectionId/deleteCollectionById.ts +++ b/lib/api/controllers/collections/collectionId/deleteCollectionById.ts @@ -1,6 +1,6 @@ import { prisma } from "@/lib/api/db"; import getPermission from "@/lib/api/getPermission"; -import { Collection, UsersAndCollections } from "@prisma/client"; +import { UsersAndCollections } from "@prisma/client"; import removeFolder from "@/lib/api/storage/removeFolder"; export default async function deleteCollection( diff --git a/lib/api/controllers/links/postLink.ts b/lib/api/controllers/links/postLink.ts index d046f54..8693f65 100644 --- a/lib/api/controllers/links/postLink.ts +++ b/lib/api/controllers/links/postLink.ts @@ -1,10 +1,9 @@ import { prisma } from "@/lib/api/db"; import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; import getTitle from "@/lib/shared/getTitle"; -import { UsersAndCollections } from "@prisma/client"; -import getPermission from "@/lib/api/getPermission"; import createFolder from "@/lib/api/storage/createFolder"; import validateUrlSize from "../../validateUrlSize"; +import setLinkCollection from "../../setLinkCollection"; const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER) || 30000; @@ -24,93 +23,10 @@ export default async function postLink( } } - if (!link.collection.id && link.collection.name) { - link.collection.name = link.collection.name.trim(); + const linkCollection = await setLinkCollection(link, userId); - // find the collection with the name and the user's id - const findCollection = await prisma.collection.findFirst({ - where: { - name: link.collection.name, - ownerId: userId, - parentId: link.collection.parentId, - }, - }); - - if (findCollection) { - const collectionIsAccessible = await getPermission({ - userId, - collectionId: findCollection.id, - }); - - const memberHasAccess = collectionIsAccessible?.members.some( - (e: UsersAndCollections) => e.userId === userId && e.canCreate - ); - - if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) - return { response: "Collection is not accessible.", status: 401 }; - - link.collection.id = findCollection.id; - link.collection.ownerId = findCollection.ownerId; - } else { - const collection = await prisma.collection.create({ - data: { - name: link.collection.name, - ownerId: userId, - }, - }); - - link.collection.id = collection.id; - - await prisma.user.update({ - where: { - id: userId, - }, - data: { - collectionOrder: { - push: link.collection.id, - }, - }, - }); - } - } else if (link.collection.id) { - const collectionIsAccessible = await getPermission({ - userId, - collectionId: link.collection.id, - }); - - const memberHasAccess = collectionIsAccessible?.members.some( - (e: UsersAndCollections) => e.userId === userId && e.canCreate - ); - - if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) - return { response: "Collection is not accessible.", status: 401 }; - } else if (!link.collection.id) { - link.collection.name = "Unorganized"; - link.collection.parentId = null; - - // find the collection with the name "Unorganized" and the user's id - const unorganizedCollection = await prisma.collection.findFirst({ - where: { - name: "Unorganized", - ownerId: userId, - }, - }); - - link.collection.id = unorganizedCollection?.id; - - await prisma.user.update({ - where: { - id: userId, - }, - data: { - collectionOrder: { - push: link.collection.id, - }, - }, - }); - } else { - return { response: "Uncaught error.", status: 500 }; - } + if (!linkCollection) + return { response: "Collection is not accessible.", status: 400 }; const user = await prisma.user.findUnique({ where: { @@ -124,8 +40,6 @@ export default async function postLink( const urlWithoutWww = hasWwwPrefix ? url?.replace(`://www.`, "://") : url; const urlWithWww = hasWwwPrefix ? url : url?.replace("://", `://www.`); - console.log(url, urlWithoutWww, urlWithWww); - const existingLink = await prisma.link.findFirst({ where: { OR: [{ url: urlWithWww }, { url: urlWithoutWww }], @@ -135,8 +49,6 @@ export default async function postLink( }, }); - console.log(url, urlWithoutWww, urlWithWww, "DONE!"); - if (existingLink) return { response: "Link already exists", @@ -147,19 +59,17 @@ export default async function postLink( const numberOfLinksTheUserHas = await prisma.link.count({ where: { collection: { - ownerId: userId, + ownerId: linkCollection.ownerId, }, }, }); if (numberOfLinksTheUserHas > MAX_LINKS_PER_USER) return { - response: `Error: Each user can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, + response: `Each collection owner can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, status: 400, }; - link.collection.name = link.collection.name.trim(); - const title = !(link.name && link.name !== "") && link.url ? await getTitle(link.url) @@ -190,7 +100,7 @@ export default async function postLink( type: linkType, collection: { connect: { - id: link.collection.id, + id: linkCollection.id, }, }, tags: { @@ -198,14 +108,14 @@ export default async function postLink( where: { name_ownerId: { name: tag.name.trim(), - ownerId: link.collection.ownerId, + ownerId: linkCollection.ownerId, }, }, create: { name: tag.name.trim(), owner: { connect: { - id: link.collection.ownerId, + id: linkCollection.ownerId, }, }, }, diff --git a/lib/api/controllers/migration/importFromHTMLFile.ts b/lib/api/controllers/migration/importFromHTMLFile.ts index 21fb3c4..6e6cf71 100644 --- a/lib/api/controllers/migration/importFromHTMLFile.ts +++ b/lib/api/controllers/migration/importFromHTMLFile.ts @@ -31,7 +31,7 @@ export default async function importFromHTMLFile( if (totalImports + numberOfLinksTheUserHas > MAX_LINKS_PER_USER) return { - response: `Error: Each user can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, + response: `Each collection owner can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, status: 400, }; diff --git a/lib/api/controllers/migration/importFromLinkwarden.ts b/lib/api/controllers/migration/importFromLinkwarden.ts index fb486cb..5e872e8 100644 --- a/lib/api/controllers/migration/importFromLinkwarden.ts +++ b/lib/api/controllers/migration/importFromLinkwarden.ts @@ -26,7 +26,7 @@ export default async function importFromLinkwarden( if (totalImports + numberOfLinksTheUserHas > MAX_LINKS_PER_USER) return { - response: `Error: Each user can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, + response: `Each collection owner can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, status: 400, }; diff --git a/lib/api/controllers/migration/importFromWallabag.ts b/lib/api/controllers/migration/importFromWallabag.ts index 6f9f404..568952a 100644 --- a/lib/api/controllers/migration/importFromWallabag.ts +++ b/lib/api/controllers/migration/importFromWallabag.ts @@ -47,7 +47,7 @@ export default async function importFromWallabag( if (totalImports + numberOfLinksTheUserHas > MAX_LINKS_PER_USER) return { - response: `Error: Each user can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, + response: `Each collection owner can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, status: 400, }; diff --git a/lib/api/getPermission.ts b/lib/api/getPermission.ts index 93dd04c..61dc5c5 100644 --- a/lib/api/getPermission.ts +++ b/lib/api/getPermission.ts @@ -3,14 +3,12 @@ import { prisma } from "@/lib/api/db"; type Props = { userId: number; collectionId?: number; - collectionName?: string; linkId?: number; }; export default async function getPermission({ userId, collectionId, - collectionName, linkId, }: Props) { if (linkId) { @@ -26,11 +24,10 @@ export default async function getPermission({ }); return check; - } else if (collectionId || collectionName) { + } else if (collectionId) { const check = await prisma.collection.findFirst({ where: { - id: collectionId || undefined, - name: collectionName || undefined, + id: collectionId, OR: [{ ownerId: userId }, { members: { some: { userId } } }], }, include: { members: true }, diff --git a/lib/api/setLinkCollection.ts b/lib/api/setLinkCollection.ts new file mode 100644 index 0000000..901e585 --- /dev/null +++ b/lib/api/setLinkCollection.ts @@ -0,0 +1,89 @@ +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; +import { prisma } from "./db"; +import getPermission from "./getPermission"; +import { UsersAndCollections } from "@prisma/client"; + +const setLinkCollection = async ( + link: LinkIncludingShortenedCollectionAndTags, + userId: number +) => { + if (link?.collection?.id && typeof link?.collection?.id === "number") { + const existingCollection = await prisma.collection.findUnique({ + where: { + id: link.collection.id, + }, + }); + + if (!existingCollection) return null; + + const collectionIsAccessible = await getPermission({ + userId, + collectionId: existingCollection.id, + }); + + const memberHasAccess = collectionIsAccessible?.members.some( + (e: UsersAndCollections) => e.userId === userId && e.canCreate + ); + + if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) + return null; + + return existingCollection; + } else if (link?.collection?.name) { + if (link.collection.name === "Unorganized") { + const firstTopLevelUnorganizedCollection = + await prisma.collection.findFirst({ + where: { + name: "Unorganized", + ownerId: userId, + parentId: null, + }, + }); + + if (firstTopLevelUnorganizedCollection) + return firstTopLevelUnorganizedCollection; + } + + const newCollection = await prisma.collection.create({ + data: { + name: link.collection.name.trim(), + ownerId: userId, + }, + }); + + await prisma.user.update({ + where: { + id: userId, + }, + data: { + collectionOrder: { + push: newCollection.id, + }, + }, + }); + + return newCollection; + } else { + const firstTopLevelUnorganizedCollection = + await prisma.collection.findFirst({ + where: { + name: "Unorganized", + ownerId: userId, + parentId: null, + }, + }); + + if (firstTopLevelUnorganizedCollection) + return firstTopLevelUnorganizedCollection; + else + return await prisma.collection.create({ + data: { + name: "Unorganized", + ownerId: userId, + parentId: null, + }, + }); + } +}; + +export default setLinkCollection; diff --git a/pages/api/v1/archives/[linkId].ts b/pages/api/v1/archives/[linkId].ts index 4a56e90..7b0a981 100644 --- a/pages/api/v1/archives/[linkId].ts +++ b/pages/api/v1/archives/[linkId].ts @@ -85,12 +85,15 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { linkId, }); - const memberHasAccess = collectionPermissions?.members.some( + if (!collectionPermissions) + return { response: "Collection is not accessible.", status: 400 }; + + const memberHasAccess = collectionPermissions.members.some( (e: UsersAndCollections) => e.userId === user.id && e.canCreate ); - if (!(collectionPermissions?.ownerId === user.id || memberHasAccess)) - return { response: "Collection is not accessible.", status: 401 }; + if (!(collectionPermissions.ownerId === user.id || memberHasAccess)) + return { response: "Collection is not accessible.", status: 400 }; // await uploadHandler(linkId, ) @@ -106,18 +109,18 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { if (numberOfLinksTheUserHas > MAX_LINKS_PER_USER) return { - response: `Error: Each user can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, + response: `Each collection owner can only have a maximum of ${MAX_LINKS_PER_USER} Links.`, status: 400, }; - const MAX_UPLOAD_SIZE = Number( + const NEXT_PUBLIC_MAX_FILE_BUFFER = Number( process.env.NEXT_PUBLIC_MAX_FILE_BUFFER || 10 ); const form = formidable({ maxFields: 1, maxFiles: 1, - maxFileSize: MAX_UPLOAD_SIZE * 1048576, + maxFileSize: NEXT_PUBLIC_MAX_FILE_BUFFER * 1048576, }); form.parse(req, async (err, fields, files) => { @@ -136,7 +139,7 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { ) { // Handle parsing error return res.status(500).json({ - response: `Sorry, we couldn't process your file. Please ensure it's a PDF, PNG, or JPG format and doesn't exceed ${MAX_UPLOAD_SIZE}MB.`, + response: `Sorry, we couldn't process your file. Please ensure it's a PDF, PNG, or JPG format and doesn't exceed ${NEXT_PUBLIC_MAX_FILE_BUFFER}MB.`, }); } else { const fileBuffer = fs.readFileSync(files.file[0].filepath); @@ -146,7 +149,7 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { }); if (linkStillExists && files.file[0].mimetype?.includes("image")) { - const collectionId = collectionPermissions?.id as number; + const collectionId = collectionPermissions.id as number; createFolder({ filePath: `archives/preview/${collectionId}`, }); @@ -156,9 +159,7 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { if (linkStillExists) { await createFile({ - filePath: `archives/${collectionPermissions?.id}/${ - linkId + suffix - }`, + filePath: `archives/${collectionPermissions.id}/${linkId + suffix}`, data: fileBuffer, }); @@ -169,10 +170,10 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { ? "unavailable" : undefined, image: files.file[0].mimetype?.includes("image") - ? `archives/${collectionPermissions?.id}/${linkId + suffix}` + ? `archives/${collectionPermissions.id}/${linkId + suffix}` : null, pdf: files.file[0].mimetype?.includes("pdf") - ? `archives/${collectionPermissions?.id}/${linkId + suffix}` + ? `archives/${collectionPermissions.id}/${linkId + suffix}` : null, lastPreserved: new Date().toISOString(), },