all
This commit is contained in:
@ -0,0 +1,58 @@
|
||||
import { prisma } from "@/lib/api/db";
|
||||
import { UsersAndCollections } from "@prisma/client";
|
||||
import getPermission from "@/lib/api/getPermission";
|
||||
import removeFile from "@/lib/api/storage/removeFile";
|
||||
|
||||
export default async function deleteLinksById(
|
||||
userId: number,
|
||||
linkIds: number[]
|
||||
) {
|
||||
if (!linkIds || linkIds.length === 0) {
|
||||
return { response: "Please choose valid links.", status: 401 };
|
||||
}
|
||||
|
||||
const collectionIsAccessibleArray = [];
|
||||
|
||||
// Check if the user has access to the collection of each link
|
||||
// if any of the links are not accessible, return an error
|
||||
// if all links are accessible, continue with the deletion
|
||||
// and add the collection to the collectionIsAccessibleArray
|
||||
for (const linkId of linkIds) {
|
||||
const collectionIsAccessible = await getPermission({ userId, linkId });
|
||||
|
||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||
(e: UsersAndCollections) => e.userId === userId && e.canDelete
|
||||
);
|
||||
|
||||
if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) {
|
||||
return { response: "Collection is not accessible.", status: 401 };
|
||||
}
|
||||
|
||||
collectionIsAccessibleArray.push(collectionIsAccessible);
|
||||
}
|
||||
|
||||
const deletedLinks = await prisma.link.deleteMany({
|
||||
where: {
|
||||
id: { in: linkIds },
|
||||
},
|
||||
});
|
||||
|
||||
// Loop through each link and delete the associated files
|
||||
// if the user has access to the collection
|
||||
for (let i = 0; i < linkIds.length; i++) {
|
||||
const linkId = linkIds[i];
|
||||
const collectionIsAccessible = collectionIsAccessibleArray[i];
|
||||
|
||||
removeFile({
|
||||
filePath: `archives/${collectionIsAccessible?.id}/${linkId}.pdf`,
|
||||
});
|
||||
removeFile({
|
||||
filePath: `archives/${collectionIsAccessible?.id}/${linkId}.png`,
|
||||
});
|
||||
removeFile({
|
||||
filePath: `archives/${collectionIsAccessible?.id}/${linkId}_readability.json`,
|
||||
});
|
||||
}
|
||||
|
||||
return { response: deletedLinks, status: 200 };
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||
import updateLinkById from "../linkId/updateLinkById";
|
||||
|
||||
export default async function updateLinks(
|
||||
userId: number,
|
||||
links: LinkIncludingShortenedCollectionAndTags[],
|
||||
removePreviousTags: boolean,
|
||||
newData: Pick<
|
||||
LinkIncludingShortenedCollectionAndTags,
|
||||
"tags" | "collectionId"
|
||||
>
|
||||
) {
|
||||
let allUpdatesSuccessful = true;
|
||||
|
||||
// Have to use a loop here rather than updateMany, see the following:
|
||||
// https://github.com/prisma/prisma/issues/3143
|
||||
for (const link of links) {
|
||||
let updatedTags = [...link.tags, ...(newData.tags ?? [])];
|
||||
|
||||
if (removePreviousTags) {
|
||||
// If removePreviousTags is true, replace the existing tags with new tags
|
||||
updatedTags = [...(newData.tags ?? [])];
|
||||
}
|
||||
|
||||
const updatedData: LinkIncludingShortenedCollectionAndTags = {
|
||||
...link,
|
||||
tags: updatedTags,
|
||||
collection: {
|
||||
...link.collection,
|
||||
id: newData.collectionId ?? link.collection.id,
|
||||
},
|
||||
};
|
||||
|
||||
const updatedLink = await updateLinkById(
|
||||
userId,
|
||||
link.id as number,
|
||||
updatedData
|
||||
);
|
||||
|
||||
if (updatedLink.status !== 200) {
|
||||
allUpdatesSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allUpdatesSuccessful) {
|
||||
return { response: "All links updated successfully", status: 200 };
|
||||
} else {
|
||||
return { response: "Some links failed to update", status: 400 };
|
||||
}
|
||||
}
|
152
Securite/Linkwarden/lib/api/controllers/links/getLinks.ts
Normal file
152
Securite/Linkwarden/lib/api/controllers/links/getLinks.ts
Normal file
@ -0,0 +1,152 @@
|
||||
import { prisma } from "@/lib/api/db";
|
||||
import { LinkRequestQuery, Sort } from "@/types/global";
|
||||
|
||||
export default async function getLink(userId: number, query: LinkRequestQuery) {
|
||||
const POSTGRES_IS_ENABLED = process.env.DATABASE_URL.startsWith("postgresql");
|
||||
|
||||
let order: any;
|
||||
if (query.sort === Sort.DateNewestFirst) order = { id: "desc" };
|
||||
else if (query.sort === Sort.DateOldestFirst) order = { id: "asc" };
|
||||
else if (query.sort === Sort.NameAZ) order = { name: "asc" };
|
||||
else if (query.sort === Sort.NameZA) order = { name: "desc" };
|
||||
else if (query.sort === Sort.DescriptionAZ) order = { description: "asc" };
|
||||
else if (query.sort === Sort.DescriptionZA) order = { description: "desc" };
|
||||
|
||||
const searchConditions = [];
|
||||
|
||||
if (query.searchQueryString) {
|
||||
if (query.searchByName) {
|
||||
searchConditions.push({
|
||||
name: {
|
||||
contains: query.searchQueryString,
|
||||
mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (query.searchByUrl) {
|
||||
searchConditions.push({
|
||||
url: {
|
||||
contains: query.searchQueryString,
|
||||
mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (query.searchByDescription) {
|
||||
searchConditions.push({
|
||||
description: {
|
||||
contains: query.searchQueryString,
|
||||
mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (query.searchByTextContent) {
|
||||
searchConditions.push({
|
||||
textContent: {
|
||||
contains: query.searchQueryString,
|
||||
mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (query.searchByTags) {
|
||||
searchConditions.push({
|
||||
tags: {
|
||||
some: {
|
||||
name: {
|
||||
contains: query.searchQueryString,
|
||||
mode: POSTGRES_IS_ENABLED ? "insensitive" : undefined,
|
||||
},
|
||||
OR: [
|
||||
{ ownerId: userId },
|
||||
{
|
||||
links: {
|
||||
some: {
|
||||
collection: {
|
||||
members: {
|
||||
some: { userId },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const tagCondition = [];
|
||||
|
||||
if (query.tagId) {
|
||||
tagCondition.push({
|
||||
tags: {
|
||||
some: {
|
||||
id: query.tagId,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const collectionCondition = [];
|
||||
|
||||
if (query.collectionId) {
|
||||
collectionCondition.push({
|
||||
collection: {
|
||||
id: query.collectionId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const links = await prisma.link.findMany({
|
||||
take: Number(process.env.PAGINATION_TAKE_COUNT) || 20,
|
||||
skip: query.cursor ? 1 : undefined,
|
||||
cursor: query.cursor ? { id: query.cursor } : undefined,
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
collection: {
|
||||
OR: [
|
||||
{ ownerId: userId },
|
||||
{
|
||||
members: {
|
||||
some: { userId },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...collectionCondition,
|
||||
{
|
||||
OR: [
|
||||
...tagCondition,
|
||||
{
|
||||
[query.searchQueryString ? "OR" : "AND"]: [
|
||||
{
|
||||
pinnedBy: query.pinnedOnly
|
||||
? { some: { id: userId } }
|
||||
: undefined,
|
||||
},
|
||||
...searchConditions,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
include: {
|
||||
tags: true,
|
||||
collection: true,
|
||||
pinnedBy: {
|
||||
where: { id: userId },
|
||||
select: { id: true },
|
||||
},
|
||||
},
|
||||
orderBy: order || { id: "desc" },
|
||||
});
|
||||
|
||||
return { response: links, status: 200 };
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import { prisma } from "@/lib/api/db";
|
||||
import { Link, UsersAndCollections } from "@prisma/client";
|
||||
import getPermission from "@/lib/api/getPermission";
|
||||
import removeFile from "@/lib/api/storage/removeFile";
|
||||
|
||||
export default async function deleteLink(userId: number, linkId: number) {
|
||||
if (!linkId) return { response: "Please choose a valid link.", status: 401 };
|
||||
|
||||
const collectionIsAccessible = await getPermission({ userId, linkId });
|
||||
|
||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||
(e: UsersAndCollections) => e.userId === userId && e.canDelete
|
||||
);
|
||||
|
||||
if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess))
|
||||
return { response: "Collection is not accessible.", status: 401 };
|
||||
|
||||
const deleteLink: Link = await prisma.link.delete({
|
||||
where: {
|
||||
id: linkId,
|
||||
},
|
||||
});
|
||||
|
||||
removeFile({
|
||||
filePath: `archives/${collectionIsAccessible?.id}/${linkId}.pdf`,
|
||||
});
|
||||
removeFile({
|
||||
filePath: `archives/${collectionIsAccessible?.id}/${linkId}.png`,
|
||||
});
|
||||
removeFile({
|
||||
filePath: `archives/${collectionIsAccessible?.id}/${linkId}_readability.json`,
|
||||
});
|
||||
|
||||
return { response: deleteLink, status: 200 };
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import { prisma } from "@/lib/api/db";
|
||||
import { Collection, UsersAndCollections } from "@prisma/client";
|
||||
import getPermission from "@/lib/api/getPermission";
|
||||
|
||||
export default async function getLinkById(userId: number, linkId: number) {
|
||||
if (!linkId)
|
||||
return {
|
||||
response: "Please choose a valid link.",
|
||||
status: 401,
|
||||
};
|
||||
|
||||
const collectionIsAccessible = await getPermission({ userId, linkId });
|
||||
|
||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||
(e: UsersAndCollections) => e.userId === userId
|
||||
);
|
||||
|
||||
const isCollectionOwner = collectionIsAccessible?.ownerId === userId;
|
||||
|
||||
if (collectionIsAccessible?.ownerId !== userId && !memberHasAccess)
|
||||
return {
|
||||
response: "Collection is not accessible.",
|
||||
status: 401,
|
||||
};
|
||||
else {
|
||||
const link = await prisma.link.findUnique({
|
||||
where: {
|
||||
id: linkId,
|
||||
},
|
||||
include: {
|
||||
tags: true,
|
||||
collection: true,
|
||||
pinnedBy: isCollectionOwner
|
||||
? {
|
||||
where: { id: userId },
|
||||
select: { id: true },
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
return { response: link, status: 200 };
|
||||
}
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
import { prisma } from "@/lib/api/db";
|
||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||
import { UsersAndCollections } from "@prisma/client";
|
||||
import getPermission from "@/lib/api/getPermission";
|
||||
import moveFile from "@/lib/api/storage/moveFile";
|
||||
|
||||
export default async function updateLinkById(
|
||||
userId: number,
|
||||
linkId: number,
|
||||
data: LinkIncludingShortenedCollectionAndTags
|
||||
) {
|
||||
if (!data || !data.collection.id)
|
||||
return {
|
||||
response: "Please choose a valid link and collection.",
|
||||
status: 401,
|
||||
};
|
||||
|
||||
const collectionIsAccessible = await getPermission({ userId, linkId });
|
||||
|
||||
const isCollectionOwner =
|
||||
collectionIsAccessible?.ownerId === data.collection.ownerId &&
|
||||
data.collection.ownerId === userId;
|
||||
|
||||
const canPinPermission = collectionIsAccessible?.members.some(
|
||||
(e: UsersAndCollections) => e.userId === userId
|
||||
);
|
||||
|
||||
// If the user is able to create a link, they can pin it to their dashboard only.
|
||||
if (canPinPermission) {
|
||||
const updatedLink = await prisma.link.update({
|
||||
where: {
|
||||
id: linkId,
|
||||
},
|
||||
data: {
|
||||
pinnedBy:
|
||||
data?.pinnedBy && data.pinnedBy[0]
|
||||
? { connect: { id: userId } }
|
||||
: { disconnect: { id: userId } },
|
||||
},
|
||||
include: {
|
||||
collection: true,
|
||||
pinnedBy: isCollectionOwner
|
||||
? {
|
||||
where: { id: userId },
|
||||
select: { id: true },
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
return { response: updatedLink, status: 200 };
|
||||
}
|
||||
|
||||
const targetCollectionIsAccessible = await getPermission({
|
||||
userId,
|
||||
collectionId: data.collection.id,
|
||||
});
|
||||
|
||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||
(e: UsersAndCollections) => e.userId === userId && e.canUpdate
|
||||
);
|
||||
|
||||
const targetCollectionsAccessible =
|
||||
targetCollectionIsAccessible?.ownerId === userId;
|
||||
|
||||
const targetCollectionMatchesData = data.collection.id
|
||||
? data.collection.id === targetCollectionIsAccessible?.id
|
||||
: true && data.collection.name
|
||||
? data.collection.name === targetCollectionIsAccessible?.name
|
||||
: true && data.collection.ownerId
|
||||
? data.collection.ownerId === targetCollectionIsAccessible?.ownerId
|
||||
: true;
|
||||
|
||||
if (!targetCollectionsAccessible)
|
||||
return {
|
||||
response: "Target collection is not accessible.",
|
||||
status: 401,
|
||||
};
|
||||
else if (!targetCollectionMatchesData)
|
||||
return {
|
||||
response: "Target collection does not match the data.",
|
||||
status: 401,
|
||||
};
|
||||
|
||||
const unauthorizedSwitchCollection =
|
||||
!isCollectionOwner && collectionIsAccessible?.id !== data.collection.id;
|
||||
|
||||
// Makes sure collection members (non-owners) cannot move a link to/from a collection.
|
||||
if (unauthorizedSwitchCollection)
|
||||
return {
|
||||
response: "You can't move a link to/from a collection you don't own.",
|
||||
status: 401,
|
||||
};
|
||||
else if (collectionIsAccessible?.ownerId !== userId && !memberHasAccess)
|
||||
return {
|
||||
response: "Collection is not accessible.",
|
||||
status: 401,
|
||||
};
|
||||
else {
|
||||
const updatedLink = await prisma.link.update({
|
||||
where: {
|
||||
id: linkId,
|
||||
},
|
||||
data: {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
collection: {
|
||||
connect: {
|
||||
id: data.collection.id,
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
set: [],
|
||||
connectOrCreate: data.tags.map((tag) => ({
|
||||
where: {
|
||||
name_ownerId: {
|
||||
name: tag.name,
|
||||
ownerId: data.collection.ownerId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
name: tag.name,
|
||||
owner: {
|
||||
connect: {
|
||||
id: data.collection.ownerId,
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
pinnedBy:
|
||||
data?.pinnedBy && data.pinnedBy[0]
|
||||
? { connect: { id: userId } }
|
||||
: { disconnect: { id: userId } },
|
||||
},
|
||||
include: {
|
||||
tags: true,
|
||||
collection: true,
|
||||
pinnedBy: isCollectionOwner
|
||||
? {
|
||||
where: { id: userId },
|
||||
select: { id: true },
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
if (collectionIsAccessible?.id !== data.collection.id) {
|
||||
await moveFile(
|
||||
`archives/${collectionIsAccessible?.id}/${linkId}.pdf`,
|
||||
`archives/${data.collection.id}/${linkId}.pdf`
|
||||
);
|
||||
|
||||
await moveFile(
|
||||
`archives/${collectionIsAccessible?.id}/${linkId}.png`,
|
||||
`archives/${data.collection.id}/${linkId}.png`
|
||||
);
|
||||
|
||||
await moveFile(
|
||||
`archives/${collectionIsAccessible?.id}/${linkId}_readability.json`,
|
||||
`archives/${data.collection.id}/${linkId}_readability.json`
|
||||
);
|
||||
}
|
||||
|
||||
return { response: updatedLink, status: 200 };
|
||||
}
|
||||
}
|
209
Securite/Linkwarden/lib/api/controllers/links/postLink.ts
Normal file
209
Securite/Linkwarden/lib/api/controllers/links/postLink.ts
Normal file
@ -0,0 +1,209 @@
|
||||
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";
|
||||
|
||||
const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER) || 30000;
|
||||
|
||||
export default async function postLink(
|
||||
link: LinkIncludingShortenedCollectionAndTags,
|
||||
userId: number
|
||||
) {
|
||||
try {
|
||||
new URL(link.url || "");
|
||||
} catch (error) {
|
||||
return {
|
||||
response:
|
||||
"Please enter a valid Address for the Link. (It should start with http/https)",
|
||||
status: 400,
|
||||
};
|
||||
}
|
||||
|
||||
if (!link.collection.id && link.collection.name) {
|
||||
link.collection.name = link.collection.name.trim();
|
||||
|
||||
// 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 };
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (user?.preventDuplicateLinks) {
|
||||
const existingLink = await prisma.link.findFirst({
|
||||
where: {
|
||||
url: link.url?.trim(),
|
||||
collection: {
|
||||
ownerId: userId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existingLink)
|
||||
return {
|
||||
response: "Link already exists",
|
||||
status: 409,
|
||||
};
|
||||
}
|
||||
|
||||
const numberOfLinksTheUserHas = await prisma.link.count({
|
||||
where: {
|
||||
collection: {
|
||||
ownerId: userId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (numberOfLinksTheUserHas + 1 > MAX_LINKS_PER_USER)
|
||||
return {
|
||||
response: `Error: Each user can only have a maximum of ${MAX_LINKS_PER_USER} Links.`,
|
||||
status: 400,
|
||||
};
|
||||
|
||||
link.collection.name = link.collection.name.trim();
|
||||
|
||||
const description =
|
||||
link.description && link.description !== ""
|
||||
? link.description
|
||||
: link.url
|
||||
? await getTitle(link.url)
|
||||
: undefined;
|
||||
|
||||
const validatedUrl = link.url ? await validateUrlSize(link.url) : undefined;
|
||||
|
||||
const contentType = validatedUrl?.get("content-type");
|
||||
let linkType = "url";
|
||||
let imageExtension = "png";
|
||||
|
||||
if (!link.url) linkType = link.type;
|
||||
else if (contentType === "application/pdf") linkType = "pdf";
|
||||
else if (contentType?.startsWith("image")) {
|
||||
linkType = "image";
|
||||
if (contentType === "image/jpeg") imageExtension = "jpeg";
|
||||
else if (contentType === "image/png") imageExtension = "png";
|
||||
}
|
||||
|
||||
const newLink = await prisma.link.create({
|
||||
data: {
|
||||
url: link.url?.trim(),
|
||||
name: link.name,
|
||||
description,
|
||||
type: linkType,
|
||||
collection: {
|
||||
connect: {
|
||||
id: link.collection.id,
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
connectOrCreate: link.tags.map((tag) => ({
|
||||
where: {
|
||||
name_ownerId: {
|
||||
name: tag.name.trim(),
|
||||
ownerId: link.collection.ownerId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
name: tag.name.trim(),
|
||||
owner: {
|
||||
connect: {
|
||||
id: link.collection.ownerId,
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
},
|
||||
include: { tags: true, collection: true },
|
||||
});
|
||||
|
||||
createFolder({ filePath: `archives/${newLink.collectionId}` });
|
||||
|
||||
return { response: newLink, status: 200 };
|
||||
}
|
Reference in New Issue
Block a user