2023-10-03 06:04:13 -05:00
|
|
|
import { prisma } from "@/lib/api/db";
|
|
|
|
import createFolder from "@/lib/api/storage/createFolder";
|
|
|
|
import { JSDOM } from "jsdom";
|
|
|
|
|
2023-12-19 16:20:09 -06:00
|
|
|
const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER) || 30000;
|
|
|
|
|
2023-10-16 17:27:04 -05:00
|
|
|
export default async function importFromHTMLFile(
|
|
|
|
userId: number,
|
|
|
|
rawData: string
|
|
|
|
) {
|
2023-11-07 07:03:35 -06:00
|
|
|
const dom = new JSDOM(rawData);
|
|
|
|
const document = dom.window.document;
|
2023-10-03 06:04:13 -05:00
|
|
|
|
2023-12-19 16:20:09 -06:00
|
|
|
const bookmarks = document.querySelectorAll("A");
|
|
|
|
const totalImports = bookmarks.length;
|
|
|
|
|
|
|
|
const numberOfLinksTheUserHas = await prisma.link.count({
|
|
|
|
where: {
|
|
|
|
collection: {
|
|
|
|
ownerId: userId,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (totalImports + numberOfLinksTheUserHas > MAX_LINKS_PER_USER)
|
|
|
|
return {
|
|
|
|
response: `Error: Each user can only have a maximum of ${MAX_LINKS_PER_USER} Links.`,
|
|
|
|
status: 400,
|
|
|
|
};
|
|
|
|
|
2023-11-07 07:03:35 -06:00
|
|
|
const folders = document.querySelectorAll("H3");
|
2024-02-10 01:47:58 -06:00
|
|
|
let unorganizedCollectionId: number | null = null;
|
2023-10-03 06:04:13 -05:00
|
|
|
|
2024-02-10 01:47:58 -06:00
|
|
|
if (folders.length === 0) {
|
|
|
|
const unorganizedCollection = await prisma.collection.findFirst({
|
|
|
|
where: {
|
|
|
|
name: "Imported",
|
|
|
|
ownerId: userId,
|
|
|
|
},
|
|
|
|
});
|
2023-10-03 06:04:13 -05:00
|
|
|
|
2024-02-10 01:47:58 -06:00
|
|
|
if (!unorganizedCollection) {
|
|
|
|
const newUnorganizedCollection = await prisma.collection.create({
|
|
|
|
data: {
|
|
|
|
name: "Imported",
|
|
|
|
description:
|
|
|
|
"Automatically created collection for imported bookmarks.",
|
|
|
|
ownerId: userId,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
unorganizedCollectionId = newUnorganizedCollection.id;
|
|
|
|
} else {
|
|
|
|
unorganizedCollectionId = unorganizedCollection.id;
|
|
|
|
}
|
2023-10-03 06:04:13 -05:00
|
|
|
|
2024-02-10 01:47:58 -06:00
|
|
|
createFolder({ filePath: `archives/${unorganizedCollectionId}` });
|
|
|
|
}
|
2023-10-03 06:04:13 -05:00
|
|
|
|
2024-02-10 01:47:58 -06:00
|
|
|
await prisma
|
|
|
|
.$transaction(
|
|
|
|
async () => {
|
|
|
|
if (unorganizedCollectionId) {
|
|
|
|
// @ts-ignore
|
|
|
|
for (const bookmark of bookmarks) {
|
2024-02-14 09:35:59 -06:00
|
|
|
createBookmark(userId, bookmark, unorganizedCollectionId);
|
2024-02-10 01:47:58 -06:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// @ts-ignore
|
|
|
|
for (const folder of folders) {
|
2024-02-15 11:26:42 -06:00
|
|
|
await createCollectionAndBookmarks(
|
|
|
|
userId,
|
|
|
|
folder,
|
|
|
|
folder.nextElementSibling,
|
|
|
|
null
|
|
|
|
);
|
2023-11-07 07:03:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ timeout: 30000 }
|
|
|
|
)
|
|
|
|
.catch((err) => console.log(err));
|
2023-10-03 06:04:13 -05:00
|
|
|
|
|
|
|
return { response: "Success.", status: 200 };
|
|
|
|
}
|
2024-02-14 09:35:59 -06:00
|
|
|
|
2024-02-15 11:26:42 -06:00
|
|
|
const createCollectionAndBookmarks = async (
|
|
|
|
userId: number,
|
|
|
|
folder: any,
|
|
|
|
folderContent: any,
|
|
|
|
parentId: number | null
|
|
|
|
) => {
|
|
|
|
const findCollection = await prisma.collection.findFirst({
|
|
|
|
where: {
|
|
|
|
name: folder.textContent.trim(),
|
|
|
|
ownerId: userId,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const checkIfCollectionExists = findCollection;
|
|
|
|
let collectionId = findCollection?.id;
|
|
|
|
|
|
|
|
if (!checkIfCollectionExists || !collectionId) {
|
|
|
|
const newCollection = await prisma.collection.create({
|
|
|
|
data: {
|
|
|
|
name: folder.textContent.trim(),
|
|
|
|
description: "",
|
|
|
|
color: "#0ea5e9",
|
|
|
|
isPublic: false,
|
|
|
|
ownerId: userId,
|
|
|
|
parentId
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
createFolder({ filePath: `archives/${newCollection.id}` });
|
|
|
|
|
|
|
|
collectionId = newCollection.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
createFolder({ filePath: `archives/${collectionId}` });
|
|
|
|
|
|
|
|
const bookmarks = folderContent.querySelectorAll("A");
|
|
|
|
for (const bookmark of bookmarks) {
|
|
|
|
createBookmark(userId, bookmark, collectionId);
|
|
|
|
}
|
|
|
|
|
|
|
|
const subfolders = folderContent.querySelectorAll("H3");
|
|
|
|
for (const subfolder of subfolders) {
|
|
|
|
await createCollectionAndBookmarks(userId, subfolder, subfolder.nextElementSibling, collectionId);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-02-14 09:35:59 -06:00
|
|
|
const createBookmark = async (
|
|
|
|
userId: number,
|
|
|
|
bookmark: any,
|
|
|
|
collectionId: number
|
|
|
|
) => {
|
|
|
|
// Move up to the parent node (<DT>) and then find the next sibling
|
|
|
|
let parentDT = bookmark.parentNode;
|
|
|
|
let nextSibling = parentDT ? parentDT.nextSibling : null;
|
|
|
|
let description = "";
|
|
|
|
|
|
|
|
// Loop through siblings to skip any potential text nodes or whitespace
|
|
|
|
while (nextSibling && nextSibling.nodeType !== 1) {
|
|
|
|
nextSibling = nextSibling.nextSibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the next sibling element is a <DD> tag and use its content as the description
|
|
|
|
if (nextSibling && nextSibling.tagName === "DD") {
|
|
|
|
description = nextSibling.textContent.trim();
|
|
|
|
}
|
|
|
|
|
2024-02-15 11:26:42 -06:00
|
|
|
const linkName = bookmark.textContent.trim();
|
|
|
|
const linkURL = bookmark.getAttribute("HREF");
|
|
|
|
|
|
|
|
const existingLink = await prisma.link.findFirst({
|
|
|
|
where: {
|
|
|
|
url: linkURL,
|
|
|
|
collectionId
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Create the link only if it doesn't already exist
|
|
|
|
if (!existingLink) {
|
|
|
|
await prisma.link.create({
|
|
|
|
data: {
|
|
|
|
name: linkName,
|
|
|
|
url: linkURL,
|
|
|
|
tags: bookmark.getAttribute("TAGS")
|
|
|
|
? {
|
2024-02-14 09:35:59 -06:00
|
|
|
connectOrCreate: bookmark
|
|
|
|
.getAttribute("TAGS")
|
|
|
|
.split(",")
|
|
|
|
.map((tag: string) =>
|
|
|
|
tag
|
|
|
|
? {
|
2024-02-15 11:26:42 -06:00
|
|
|
where: {
|
|
|
|
data: {
|
2024-02-14 09:35:59 -06:00
|
|
|
name: tag.trim(),
|
2024-02-15 11:26:42 -06:00
|
|
|
ownerId: userId,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
create: {
|
|
|
|
name: tag.trim(),
|
|
|
|
owner: {
|
|
|
|
connect: {
|
|
|
|
id: userId,
|
2024-02-14 09:35:59 -06:00
|
|
|
},
|
|
|
|
},
|
2024-02-15 11:26:42 -06:00
|
|
|
},
|
|
|
|
}
|
2024-02-14 09:35:59 -06:00
|
|
|
: undefined
|
|
|
|
),
|
|
|
|
}
|
2024-02-15 11:26:42 -06:00
|
|
|
: undefined,
|
|
|
|
description,
|
|
|
|
collectionId,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|