improved archive handler
This commit is contained in:
parent
d66c784d3f
commit
644b827669
|
@ -10,8 +10,8 @@ export default function Announcement({ toggleAnnouncementBar }: Props) {
|
||||||
const announcementId = localStorage.getItem("announcementId");
|
const announcementId = localStorage.getItem("announcementId");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed left-0 right-0 bottom-20 sm:bottom-10 w-full p-5 z-30">
|
<div className="fixed mx-auto bottom-20 sm:bottom-10 w-full pointer-events-none p-5 z-30">
|
||||||
<div className="mx-auto w-full p-2 flex justify-between gap-2 items-center border border-primary shadow-xl rounded-xl bg-base-300 backdrop-blur-sm bg-opacity-80 max-w-md">
|
<div className="mx-auto pointer-events-auto p-2 flex justify-between gap-2 items-center border border-primary shadow-xl rounded-xl bg-base-300 backdrop-blur-sm bg-opacity-80 max-w-md">
|
||||||
<i className="bi-stars text-2xl text-yellow-600 dark:text-yellow-500"></i>
|
<i className="bi-stars text-2xl text-yellow-600 dark:text-yellow-500"></i>
|
||||||
<p className="w-4/5 text-center text-sm sm:text-base">
|
<p className="w-4/5 text-center text-sm sm:text-base">
|
||||||
<Trans
|
<Trans
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { LaunchOptions, chromium, devices } from "playwright";
|
||||||
import { prisma } from "./db";
|
import { prisma } from "./db";
|
||||||
import sendToWayback from "./preservationScheme/sendToWayback";
|
import sendToWayback from "./preservationScheme/sendToWayback";
|
||||||
import { Collection, Link, User } from "@prisma/client";
|
import { Collection, Link, User } from "@prisma/client";
|
||||||
import validateUrlSize from "./validateUrlSize";
|
import fetchHeaders from "./fetchHeaders";
|
||||||
import createFolder from "./storage/createFolder";
|
import createFolder from "./storage/createFolder";
|
||||||
import { removeFiles } from "./manageLinkFiles";
|
import { removeFiles } from "./manageLinkFiles";
|
||||||
import handleMonolith from "./preservationScheme/handleMonolith";
|
import handleMonolith from "./preservationScheme/handleMonolith";
|
||||||
|
@ -65,17 +65,9 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
|
||||||
(async () => {
|
(async () => {
|
||||||
const user = link.collection?.owner;
|
const user = link.collection?.owner;
|
||||||
|
|
||||||
const validatedUrl = link.url
|
const header = link.url ? await fetchHeaders(link.url) : undefined;
|
||||||
? await validateUrlSize(link.url)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (
|
const contentType = header?.get("content-type");
|
||||||
validatedUrl === null &&
|
|
||||||
process.env.IGNORE_URL_SIZE_LIMIT !== "true"
|
|
||||||
)
|
|
||||||
throw "Something went wrong while retrieving the file size.";
|
|
||||||
|
|
||||||
const contentType = validatedUrl?.get("content-type");
|
|
||||||
let linkType = "url";
|
let linkType = "url";
|
||||||
let imageExtension = "png";
|
let imageExtension = "png";
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { prisma } from "@/lib/api/db";
|
import { prisma } from "@/lib/api/db";
|
||||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||||
import getTitle from "@/lib/shared/getTitle";
|
import fetchTitleAndHeaders from "@/lib/shared/fetchTitleAndHeaders";
|
||||||
import createFolder from "@/lib/api/storage/createFolder";
|
import createFolder from "@/lib/api/storage/createFolder";
|
||||||
import validateUrlSize from "../../validateUrlSize";
|
|
||||||
import setLinkCollection from "../../setLinkCollection";
|
import setLinkCollection from "../../setLinkCollection";
|
||||||
|
|
||||||
const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER) || 30000;
|
const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER) || 30000;
|
||||||
|
@ -70,17 +69,12 @@ export default async function postLink(
|
||||||
status: 400,
|
status: 400,
|
||||||
};
|
};
|
||||||
|
|
||||||
const title =
|
const { title, headers } = await fetchTitleAndHeaders(link.url || "");
|
||||||
!(link.name && link.name !== "") && link.url
|
|
||||||
? await getTitle(link.url)
|
|
||||||
: "";
|
|
||||||
|
|
||||||
const name =
|
const name =
|
||||||
link.name && link.name !== "" ? link.name : link.url ? title : "";
|
link.name && link.name !== "" ? link.name : link.url ? title : "";
|
||||||
|
|
||||||
const validatedUrl = link.url ? await validateUrlSize(link.url) : undefined;
|
const contentType = headers?.get("content-type");
|
||||||
|
|
||||||
const contentType = validatedUrl?.get("content-type");
|
|
||||||
let linkType = "url";
|
let linkType = "url";
|
||||||
let imageExtension = "png";
|
let imageExtension = "png";
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import fetch from "node-fetch";
|
||||||
import https from "https";
|
import https from "https";
|
||||||
import { SocksProxyAgent } from "socks-proxy-agent";
|
import { SocksProxyAgent } from "socks-proxy-agent";
|
||||||
|
|
||||||
export default async function validateUrlSize(url: string) {
|
export default async function fetchHeaders(url: string) {
|
||||||
if (process.env.IGNORE_URL_SIZE_LIMIT === "true") return null;
|
if (process.env.IGNORE_URL_SIZE_LIMIT === "true") return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -29,13 +29,17 @@ export default async function validateUrlSize(url: string) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url, fetchOpts);
|
const responsePromise = fetch(url, fetchOpts);
|
||||||
|
|
||||||
const totalSizeMB =
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
Number(response.headers.get("content-length")) / Math.pow(1024, 2);
|
setTimeout(() => {
|
||||||
if (totalSizeMB > Number(process.env.NEXT_PUBLIC_MAX_FILE_BUFFER || 10))
|
reject(new Error("Fetch header timeout"));
|
||||||
return null;
|
}, 10 * 1000); // Stop after 10 seconds
|
||||||
else return response.headers;
|
});
|
||||||
|
|
||||||
|
const response = await Promise.race([responsePromise, timeoutPromise]);
|
||||||
|
|
||||||
|
return (response as Response)?.headers || null;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return null;
|
return null;
|
|
@ -1,6 +1,7 @@
|
||||||
import { Link } from "@prisma/client";
|
import { Link } from "@prisma/client";
|
||||||
import { prisma } from "../db";
|
import { prisma } from "../db";
|
||||||
import createFile from "../storage/createFile";
|
import createFile from "../storage/createFile";
|
||||||
|
import generatePreview from "../generatePreview";
|
||||||
|
|
||||||
const imageHandler = async ({ url, id }: Link, extension: string) => {
|
const imageHandler = async ({ url, id }: Link, extension: string) => {
|
||||||
const image = await fetch(url as string).then((res) => res.blob());
|
const image = await fetch(url as string).then((res) => res.blob());
|
||||||
|
@ -18,6 +19,8 @@ const imageHandler = async ({ url, id }: Link, extension: string) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (linkExists) {
|
if (linkExists) {
|
||||||
|
await generatePreview(buffer, linkExists.collectionId, id);
|
||||||
|
|
||||||
await createFile({
|
await createFile({
|
||||||
data: buffer,
|
data: buffer,
|
||||||
filePath: `archives/${linkExists.collectionId}/${id}.${extension}`,
|
filePath: `archives/${linkExists.collectionId}/${id}.${extension}`,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import fetch from "node-fetch";
|
||||||
import https from "https";
|
import https from "https";
|
||||||
import { SocksProxyAgent } from "socks-proxy-agent";
|
import { SocksProxyAgent } from "socks-proxy-agent";
|
||||||
|
|
||||||
export default async function getTitle(url: string) {
|
export default async function fetchTitleAndHeaders(url: string) {
|
||||||
try {
|
try {
|
||||||
const httpsAgent = new https.Agent({
|
const httpsAgent = new https.Agent({
|
||||||
rejectUnauthorized:
|
rejectUnauthorized:
|
||||||
|
@ -41,12 +41,16 @@ export default async function getTitle(url: string) {
|
||||||
|
|
||||||
// regular expression to find the <title> tag
|
// regular expression to find the <title> tag
|
||||||
let match = text.match(/<title.*>([^<]*)<\/title>/);
|
let match = text.match(/<title.*>([^<]*)<\/title>/);
|
||||||
if (match) return match[1];
|
|
||||||
else return "";
|
const title = match[1] || "";
|
||||||
|
const headers = (response as Response)?.headers || null;
|
||||||
|
|
||||||
|
return { title, headers };
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return { title: "", headers: null };
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
return { title: "", headers: null };
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -60,7 +60,7 @@
|
||||||
"next-i18next": "^15.3.0",
|
"next-i18next": "^15.3.0",
|
||||||
"node-fetch": "^2.7.0",
|
"node-fetch": "^2.7.0",
|
||||||
"nodemailer": "^6.9.3",
|
"nodemailer": "^6.9.3",
|
||||||
"playwright": "^1.43.1",
|
"playwright": "^1.45.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-colorful": "^5.6.1",
|
"react-colorful": "^5.6.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
"zustand": "^4.3.8"
|
"zustand": "^4.3.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.43.1",
|
"@playwright/test": "^1.45.0",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.0",
|
||||||
"@types/dompurify": "^3.0.4",
|
"@types/dompurify": "^3.0.4",
|
||||||
"@types/jsdom": "^21.1.3",
|
"@types/jsdom": "^21.1.3",
|
||||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -1293,12 +1293,12 @@
|
||||||
tiny-glob "^0.2.9"
|
tiny-glob "^0.2.9"
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@playwright/test@^1.43.1":
|
"@playwright/test@^1.45.0":
|
||||||
version "1.43.1"
|
version "1.45.0"
|
||||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.43.1.tgz#16728a59eb8ce0f60472f98d8886d6cab0fa3e42"
|
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.45.0.tgz#790a66165a46466c0d7099dd260881802f5aba7e"
|
||||||
integrity sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==
|
integrity sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==
|
||||||
dependencies:
|
dependencies:
|
||||||
playwright "1.43.1"
|
playwright "1.45.0"
|
||||||
|
|
||||||
"@prisma/client@^4.16.2":
|
"@prisma/client@^4.16.2":
|
||||||
version "4.16.2"
|
version "4.16.2"
|
||||||
|
@ -5005,17 +5005,17 @@ pixelmatch@^4.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
pngjs "^3.0.0"
|
pngjs "^3.0.0"
|
||||||
|
|
||||||
playwright-core@1.43.1:
|
playwright-core@1.45.0:
|
||||||
version "1.43.1"
|
version "1.45.0"
|
||||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.43.1.tgz#0eafef9994c69c02a1a3825a4343e56c99c03b02"
|
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.0.tgz#5741a670b7c9060ce06852c0051d84736fb94edc"
|
||||||
integrity sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==
|
integrity sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==
|
||||||
|
|
||||||
playwright@1.43.1, playwright@^1.43.1:
|
playwright@1.45.0, playwright@^1.45.0:
|
||||||
version "1.43.1"
|
version "1.45.0"
|
||||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.43.1.tgz#8ad08984ac66c9ef3d0db035be54dd7ec9f1c7d9"
|
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.0.tgz#400c709c64438690f13705cb9c88ef93089c5c27"
|
||||||
integrity sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==
|
integrity sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==
|
||||||
dependencies:
|
dependencies:
|
||||||
playwright-core "1.43.1"
|
playwright-core "1.45.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "2.3.2"
|
fsevents "2.3.2"
|
||||||
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue