use monolith instead of singlefile

This commit is contained in:
daniel31x13 2024-06-27 21:58:07 -04:00
parent 5b8e1d53cc
commit a71f42af6e
25 changed files with 90 additions and 88 deletions

View File

@ -39,7 +39,7 @@ export default function CollectionCard({ collection, className }: Props) {
username: "", username: "",
image: "", image: "",
archiveAsScreenshot: undefined as unknown as boolean, archiveAsScreenshot: undefined as unknown as boolean,
archiveAsSinglefile: undefined as unknown as boolean, archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean, archiveAsPDF: undefined as unknown as boolean,
}); });
@ -55,7 +55,7 @@ export default function CollectionCard({ collection, className }: Props) {
username: account.username as string, username: account.username as string,
image: account.image as string, image: account.image as string,
archiveAsScreenshot: account.archiveAsScreenshot as boolean, archiveAsScreenshot: account.archiveAsScreenshot as boolean,
archiveAsSinglefile: account.archiveAsSinglefile as boolean, archiveAsMonolith: account.archiveAsMonolith as boolean,
archiveAsPDF: account.archiveAsPDF as boolean, archiveAsPDF: account.archiveAsPDF as boolean,
}); });
} }

View File

@ -70,9 +70,14 @@ export default function LinkIcon({
size={size} size={size}
icon="bi-file-earmark-image" icon="bi-file-earmark-image"
/> />
) : link.type === "singlefile" ? ( ) : // : link.type === "monolith" ? (
<i className={`bi-filetype-html ${iconClasses}`}></i> // <LinkPlaceholderIcon
) : undefined} // iconClasses={iconClasses + dimension}
// size={size}
// icon="bi-filetype-html"
// />
// )
undefined}
</> </>
); );
} }

View File

@ -68,7 +68,7 @@ export default function EditCollectionSharingModal({
username: "", username: "",
image: "", image: "",
archiveAsScreenshot: undefined as unknown as boolean, archiveAsScreenshot: undefined as unknown as boolean,
archiveAsSinglefile: undefined as unknown as boolean, archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean, archiveAsPDF: undefined as unknown as boolean,
}); });

View File

@ -30,7 +30,7 @@ export default function NewLinkModal({ onClose }: Props) {
image: "", image: "",
pdf: "", pdf: "",
readable: "", readable: "",
singlefile: "", monolith: "",
textContent: "", textContent: "",
collection: { collection: {
name: "", name: "",

View File

@ -12,7 +12,7 @@ import { useSession } from "next-auth/react";
import { import {
pdfAvailable, pdfAvailable,
readabilityAvailable, readabilityAvailable,
singlefileAvailable, monolithAvailable,
screenshotAvailable, screenshotAvailable,
} from "@/lib/shared/getArchiveValidity"; } from "@/lib/shared/getArchiveValidity";
import PreservedFormatRow from "@/components/PreserverdFormatRow"; import PreservedFormatRow from "@/components/PreserverdFormatRow";
@ -43,7 +43,7 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
username: "", username: "",
image: "", image: "",
archiveAsScreenshot: undefined as unknown as boolean, archiveAsScreenshot: undefined as unknown as boolean,
archiveAsSinglefile: undefined as unknown as boolean, archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean, archiveAsPDF: undefined as unknown as boolean,
}); });
@ -61,7 +61,7 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
username: account.username as string, username: account.username as string,
image: account.image as string, image: account.image as string,
archiveAsScreenshot: account.archiveAsScreenshot as boolean, archiveAsScreenshot: account.archiveAsScreenshot as boolean,
archiveAsSinglefile: account.archiveAsScreenshot as boolean, archiveAsMonolith: account.archiveAsScreenshot as boolean,
archiveAsPDF: account.archiveAsPDF as boolean, archiveAsPDF: account.archiveAsPDF as boolean,
}); });
} }
@ -76,8 +76,8 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
(collectionOwner.archiveAsScreenshot === true (collectionOwner.archiveAsScreenshot === true
? link.pdf && link.pdf !== "pending" ? link.pdf && link.pdf !== "pending"
: true) && : true) &&
(collectionOwner.archiveAsSinglefile === true (collectionOwner.archiveAsMonolith === true
? link.singlefile && link.singlefile !== "pending" ? link.monolith && link.monolith !== "pending"
: true) && : true) &&
(collectionOwner.archiveAsPDF === true (collectionOwner.archiveAsPDF === true
? link.pdf && link.pdf !== "pending" ? link.pdf && link.pdf !== "pending"
@ -92,7 +92,7 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
screenshotAvailable(link) || screenshotAvailable(link) ||
pdfAvailable(link) || pdfAvailable(link) ||
readabilityAvailable(link) || readabilityAvailable(link) ||
singlefileAvailable(link) monolithAvailable(link)
); );
}; };
@ -124,7 +124,7 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
clearInterval(interval); clearInterval(interval);
} }
}; };
}, [link?.singlefile]); }, [link?.monolith]);
const updateArchive = async () => { const updateArchive = async () => {
const load = toast.loading(t("sending_request")); const load = toast.loading(t("sending_request"));
@ -152,18 +152,18 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {
{screenshotAvailable(link) || {screenshotAvailable(link) ||
pdfAvailable(link) || pdfAvailable(link) ||
readabilityAvailable(link) || readabilityAvailable(link) ||
singlefileAvailable(link) ? ( monolithAvailable(link) ? (
<p className="mb-3">{t("available_formats")}</p> <p className="mb-3">{t("available_formats")}</p>
) : ( ) : (
"" ""
)} )}
<div className={`flex flex-col gap-3`}> <div className={`flex flex-col gap-3`}>
{singlefileAvailable(link) ? ( {monolithAvailable(link) ? (
<PreservedFormatRow <PreservedFormatRow
name={t("webpage")} name={t("webpage")}
icon={"bi-filetype-html"} icon={"bi-filetype-html"}
format={ArchivedFormat.singlefile} format={ArchivedFormat.monolith}
activeLink={link} activeLink={link}
downloadable={true} downloadable={true}
/> />

View File

@ -33,7 +33,7 @@ export default function UploadFileModal({ onClose }: Props) {
image: "", image: "",
pdf: "", pdf: "",
readable: "", readable: "",
singlefile: "", monolith: "",
textContent: "", textContent: "",
collection: { collection: {
name: "", name: "",
@ -97,7 +97,7 @@ export default function UploadFileModal({ onClose }: Props) {
const submit = async () => { const submit = async () => {
if (!submitLoader && file) { if (!submitLoader && file) {
let fileType: ArchivedFormat | null = null; let fileType: ArchivedFormat | null = null;
let linkType: "url" | "image" | "singlefile" | "pdf" | null = null; let linkType: "url" | "image" | "monolith" | "pdf" | null = null;
if (file?.type === "image/jpg" || file.type === "image/jpeg") { if (file?.type === "image/jpg" || file.type === "image/jpeg") {
fileType = ArchivedFormat.jpeg; fileType = ArchivedFormat.jpeg;
@ -105,13 +105,14 @@ export default function UploadFileModal({ onClose }: Props) {
} else if (file.type === "image/png") { } else if (file.type === "image/png") {
fileType = ArchivedFormat.png; fileType = ArchivedFormat.png;
linkType = "image"; linkType = "image";
} else if (file.type === "text/html") {
fileType = ArchivedFormat.singlefile;
linkType = "singlefile";
} else if (file.type === "application/pdf") { } else if (file.type === "application/pdf") {
fileType = ArchivedFormat.pdf; fileType = ArchivedFormat.pdf;
linkType = "pdf"; linkType = "pdf";
} }
// else if (file.type === "text/html") {
// fileType = ArchivedFormat.monolith;
// linkType = "monolith";
// }
setSubmitLoader(true); setSubmitLoader(true);
const load = toast.loading(t("creating")); const load = toast.loading(t("creating"));

View File

@ -61,7 +61,7 @@ export default function PreservedFormatRow({
clearInterval(interval); clearInterval(interval);
} }
}; };
}, [link?.image, link?.pdf, link?.readable, link?.singlefile]); }, [link?.image, link?.pdf, link?.readable, link?.monolith]);
const handleDownload = () => { const handleDownload = () => {
const path = `/api/v1/archives/${link?.id}?format=${format}`; const path = `/api/v1/archives/${link?.id}?format=${format}`;
@ -72,7 +72,7 @@ export default function PreservedFormatRow({
const anchorElement = document.createElement("a"); const anchorElement = document.createElement("a");
anchorElement.href = path; anchorElement.href = path;
anchorElement.download = anchorElement.download =
format === ArchivedFormat.singlefile format === ArchivedFormat.monolith
? "Webpage" ? "Webpage"
: format === ArchivedFormat.pdf : format === ArchivedFormat.pdf
? "PDF" ? "PDF"

View File

@ -81,11 +81,11 @@ export default function ReadableView({ link }: Props) {
(link?.image === "pending" || (link?.image === "pending" ||
link?.pdf === "pending" || link?.pdf === "pending" ||
link?.readable === "pending" || link?.readable === "pending" ||
link?.singlefile === "pending" || link?.monolith === "pending" ||
!link?.image || !link?.image ||
!link?.pdf || !link?.pdf ||
!link?.readable || !link?.readable ||
!link?.singlefile) !link?.monolith)
) { ) {
interval = setInterval(() => getLink(link.id as number), 5000); interval = setInterval(() => getLink(link.id as number), 5000);
} else { } else {
@ -99,7 +99,7 @@ export default function ReadableView({ link }: Props) {
clearInterval(interval); clearInterval(interval);
} }
}; };
}, [link?.image, link?.pdf, link?.readable, link?.singlefile]); }, [link?.image, link?.pdf, link?.readable, link?.monolith]);
const rgbToHex = (r: number, g: number, b: number): string => const rgbToHex = (r: number, g: number, b: number): string =>
"#" + "#" +

View File

@ -102,7 +102,7 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
readable: !link.readable?.startsWith("archive") readable: !link.readable?.startsWith("archive")
? "pending" ? "pending"
: undefined, : undefined,
singlefile: !link.singlefile?.startsWith("archive") monolith: !link.monolith?.startsWith("archive")
? "pending" ? "pending"
: undefined, : undefined,
preview: !link.readable?.startsWith("archive") preview: !link.readable?.startsWith("archive")
@ -151,11 +151,11 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
) )
await handleScreenshotAndPdf(link, page, user); await handleScreenshotAndPdf(link, page, user);
// SingleFile // Monolith
if ( if (
!link.singlefile?.startsWith("archive") && !link.monolith?.startsWith("archive") &&
!link.singlefile?.startsWith("unavailable") && !link.monolith?.startsWith("unavailable") &&
user.archiveAsSinglefile && user.archiveAsMonolith &&
link.url link.url
) )
await handleMonolith(link, content); await handleMonolith(link, content);
@ -183,7 +183,7 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
image: !finalLink.image?.startsWith("archives") image: !finalLink.image?.startsWith("archives")
? "unavailable" ? "unavailable"
: undefined, : undefined,
singlefile: !finalLink.singlefile?.startsWith("archives") monolith: !finalLink.monolith?.startsWith("archives")
? "unavailable" ? "unavailable"
: undefined, : undefined,
pdf: !finalLink.pdf?.startsWith("archives") pdf: !finalLink.pdf?.startsWith("archives")

View File

@ -75,7 +75,7 @@ export default async function getPublicUser(
username: lessSensitiveInfo.username, username: lessSensitiveInfo.username,
image: lessSensitiveInfo.image, image: lessSensitiveInfo.image,
archiveAsScreenshot: lessSensitiveInfo.archiveAsScreenshot, archiveAsScreenshot: lessSensitiveInfo.archiveAsScreenshot,
archiveAsSinglefile: lessSensitiveInfo.archiveAsSinglefile, archiveAsMonolith: lessSensitiveInfo.archiveAsMonolith,
archiveAsPDF: lessSensitiveInfo.archiveAsPDF, archiveAsPDF: lessSensitiveInfo.archiveAsPDF,
}; };

View File

@ -207,7 +207,7 @@ export default async function updateUserById(
), ),
locale: i18n.locales.includes(data.locale) ? data.locale : "en", locale: i18n.locales.includes(data.locale) ? data.locale : "en",
archiveAsScreenshot: data.archiveAsScreenshot, archiveAsScreenshot: data.archiveAsScreenshot,
archiveAsSinglefile: data.archiveAsSinglefile, archiveAsMonolith: data.archiveAsMonolith,
archiveAsPDF: data.archiveAsPDF, archiveAsPDF: data.archiveAsPDF,
archiveAsWaybackMachine: data.archiveAsWaybackMachine, archiveAsWaybackMachine: data.archiveAsWaybackMachine,
linksRouteTo: data.linksRouteTo, linksRouteTo: data.linksRouteTo,

View File

@ -19,7 +19,7 @@ const handleMonolith = async (link: Link, content: string) => {
); );
if (!html?.length) { if (!html?.length) {
console.error("Error running SINGLEFILE_ARCHIVE_COMMAND: Empty buffer"); console.error("Error running MONOLITH: Empty buffer");
return; return;
} }
@ -30,12 +30,12 @@ const handleMonolith = async (link: Link, content: string) => {
await prisma.link.update({ await prisma.link.update({
where: { id: link.id }, where: { id: link.id },
data: { data: {
singlefile: `archives/${link.collectionId}/${link.id}.html`, monolith: `archives/${link.collectionId}/${link.id}.html`,
}, },
}); });
}); });
} catch (err) { } catch (err) {
console.error("Error running SINGLEFILE_ARCHIVE_COMMAND:", err); console.error("Error running MONOLITH:", err);
} }
}; };

View File

@ -8,7 +8,7 @@ import {
pdfAvailable, pdfAvailable,
readabilityAvailable, readabilityAvailable,
screenshotAvailable, screenshotAvailable,
singlefileAvailable, monolithAvailable,
} from "../shared/getArchiveValidity"; } from "../shared/getArchiveValidity";
export const generateLinkHref = ( export const generateLinkHref = (
@ -39,10 +39,10 @@ export const generateLinkHref = (
return `/preserved/${link?.id}?format=${ return `/preserved/${link?.id}?format=${
link?.image?.endsWith("png") ? ArchivedFormat.png : ArchivedFormat.jpeg link?.image?.endsWith("png") ? ArchivedFormat.png : ArchivedFormat.jpeg
}`; }`;
} else if (account.linksRouteTo === LinksRouteTo.SINGLEFILE) { } else if (account.linksRouteTo === LinksRouteTo.MONOLITH) {
if (!singlefileAvailable(link)) return link.url || ""; if (!monolithAvailable(link)) return link.url || "";
return `/preserved/${link?.id}?format=${ArchivedFormat.singlefile}`; return `/preserved/${link?.id}?format=${ArchivedFormat.monolith}`;
} else { } else {
return link.url || ""; return link.url || "";
} }

View File

@ -28,14 +28,14 @@ export function readabilityAvailable(
); );
} }
export function singlefileAvailable( export function monolithAvailable(
link: LinkIncludingShortenedCollectionAndTags link: LinkIncludingShortenedCollectionAndTags
) { ) {
return ( return (
link && link &&
link.singlefile && link.monolith &&
link.singlefile !== "pending" && link.monolith !== "pending" &&
link.singlefile !== "unavailable" link.monolith !== "unavailable"
); );
} }

View File

@ -29,7 +29,7 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) {
else if (format === ArchivedFormat.jpeg) suffix = ".jpeg"; else if (format === ArchivedFormat.jpeg) suffix = ".jpeg";
else if (format === ArchivedFormat.pdf) suffix = ".pdf"; else if (format === ArchivedFormat.pdf) suffix = ".pdf";
else if (format === ArchivedFormat.readability) suffix = "_readability.json"; else if (format === ArchivedFormat.readability) suffix = "_readability.json";
else if (format === ArchivedFormat.singlefile) suffix = ".html"; else if (format === ArchivedFormat.monolith) suffix = ".html";
//@ts-ignore //@ts-ignore
if (!linkId || !suffix) if (!linkId || !suffix)

View File

@ -76,7 +76,7 @@ const deleteArchivedFiles = async (link: Link & { collection: Collection }) => {
image: null, image: null,
pdf: null, pdf: null,
readable: null, readable: null,
singlefile: null, monolith: null,
preview: null, preview: null,
}, },
}); });

View File

@ -59,7 +59,7 @@ export default function Index() {
username: "", username: "",
image: "", image: "",
archiveAsScreenshot: undefined as unknown as boolean, archiveAsScreenshot: undefined as unknown as boolean,
archiveAsSinglefile: undefined as unknown as boolean, archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean, archiveAsPDF: undefined as unknown as boolean,
}); });
@ -77,7 +77,7 @@ export default function Index() {
username: account.username as string, username: account.username as string,
image: account.image as string, image: account.image as string,
archiveAsScreenshot: account.archiveAsScreenshot as boolean, archiveAsScreenshot: account.archiveAsScreenshot as boolean,
archiveAsSinglefile: account.archiveAsScreenshot as boolean, archiveAsMonolith: account.archiveAsScreenshot as boolean,
archiveAsPDF: account.archiveAsPDF as boolean, archiveAsPDF: account.archiveAsPDF as boolean,
}); });
} }

View File

@ -37,9 +37,9 @@ export default function Index() {
{link && Number(router.query.format) === ArchivedFormat.readability && ( {link && Number(router.query.format) === ArchivedFormat.readability && (
<ReadableView link={link} /> <ReadableView link={link} />
)} )}
{link && Number(router.query.format) === ArchivedFormat.singlefile && ( {link && Number(router.query.format) === ArchivedFormat.monolith && (
<iframe <iframe
src={`/api/v1/archives/${link.id}?format=${ArchivedFormat.singlefile}`} src={`/api/v1/archives/${link.id}?format=${ArchivedFormat.monolith}`}
className="w-full h-screen border-none" className="w-full h-screen border-none"
></iframe> ></iframe>
)} )}

View File

@ -42,7 +42,7 @@ export default function PublicCollections() {
username: "", username: "",
image: "", image: "",
archiveAsScreenshot: undefined as unknown as boolean, archiveAsScreenshot: undefined as unknown as boolean,
archiveAsSinglefile: undefined as unknown as boolean, archiveAsMonolith: undefined as unknown as boolean,
archiveAsPDF: undefined as unknown as boolean, archiveAsPDF: undefined as unknown as boolean,
}); });

View File

@ -26,8 +26,8 @@ export default function Appearance() {
account.archiveAsPDF account.archiveAsPDF
); );
const [archiveAsSinglefile, setArchiveAsSinglefile] = useState<boolean>( const [archiveAsMonolith, setArchiveAsMonolith] = useState<boolean>(
account.archiveAsSinglefile account.archiveAsMonolith
); );
const [archiveAsWaybackMachine, setArchiveAsWaybackMachine] = const [archiveAsWaybackMachine, setArchiveAsWaybackMachine] =
@ -39,7 +39,7 @@ export default function Appearance() {
setUser({ setUser({
...account, ...account,
archiveAsScreenshot, archiveAsScreenshot,
archiveAsSinglefile, archiveAsMonolith,
archiveAsPDF, archiveAsPDF,
archiveAsWaybackMachine, archiveAsWaybackMachine,
linksRouteTo, linksRouteTo,
@ -48,7 +48,7 @@ export default function Appearance() {
}, [ }, [
account, account,
archiveAsScreenshot, archiveAsScreenshot,
archiveAsSinglefile, archiveAsMonolith,
archiveAsPDF, archiveAsPDF,
archiveAsWaybackMachine, archiveAsWaybackMachine,
linksRouteTo, linksRouteTo,
@ -62,7 +62,7 @@ export default function Appearance() {
useEffect(() => { useEffect(() => {
if (!objectIsEmpty(account)) { if (!objectIsEmpty(account)) {
setArchiveAsScreenshot(account.archiveAsScreenshot); setArchiveAsScreenshot(account.archiveAsScreenshot);
setArchiveAsSinglefile(account.archiveAsSinglefile); setArchiveAsMonolith(account.archiveAsMonolith);
setArchiveAsPDF(account.archiveAsPDF); setArchiveAsPDF(account.archiveAsPDF);
setArchiveAsWaybackMachine(account.archiveAsWaybackMachine); setArchiveAsWaybackMachine(account.archiveAsWaybackMachine);
setLinksRouteTo(account.linksRouteTo); setLinksRouteTo(account.linksRouteTo);
@ -136,9 +136,9 @@ export default function Appearance() {
/> />
<Checkbox <Checkbox
label="Singlefile" label={t("webpage")}
state={archiveAsSinglefile} state={archiveAsMonolith}
onClick={() => setArchiveAsSinglefile(!archiveAsSinglefile)} onClick={() => setArchiveAsMonolith(!archiveAsMonolith)}
/> />
<Checkbox <Checkbox
@ -229,27 +229,13 @@ export default function Appearance() {
type="radio" type="radio"
name="link-preference-radio" name="link-preference-radio"
className="radio checked:bg-primary" className="radio checked:bg-primary"
value="Singlefile" value="Monolith"
checked={linksRouteTo === LinksRouteTo.SINGLEFILE} checked={linksRouteTo === LinksRouteTo.MONOLITH}
onChange={() => setLinksRouteTo(LinksRouteTo.SINGLEFILE)} onChange={() => setLinksRouteTo(LinksRouteTo.MONOLITH)}
/> />
<span className="label-text">Open Singlefile, if available</span> <span className="label-text">
</label> {t("open_webpage_if_available")}
</span>
<label
className="label cursor-pointer flex gap-2 justify-start w-fit"
tabIndex={0}
role="button"
>
<input
type="radio"
name="link-preference-radio"
className="radio checked:bg-primary"
value="Singlefile"
checked={linksRouteTo === LinksRouteTo.SINGLEFILE}
onChange={() => setLinksRouteTo(LinksRouteTo.SINGLEFILE)}
/>
<span className="label-text">Open Singlefile, if available</span>
</label> </label>
<label <label

View File

@ -0,0 +1,9 @@
/*
Warnings:
- You are about to drop the column `singlefile` on the `Link` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Link" DROP COLUMN "singlefile",
ADD COLUMN "monolith" TEXT;

View File

@ -139,7 +139,7 @@ model Link {
image String? image String?
pdf String? pdf String?
readable String? readable String?
singlefile String? monolith String?
lastPreserved DateTime? lastPreserved DateTime?
importDate DateTime? importDate DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())

View File

@ -166,6 +166,7 @@
"open_pdf_if_available": "Open PDF, if available", "open_pdf_if_available": "Open PDF, if available",
"open_readable_if_available": "Open Readable, if available", "open_readable_if_available": "Open Readable, if available",
"open_screenshot_if_available": "Open Screenshot, if available", "open_screenshot_if_available": "Open Screenshot, if available",
"open_webpage_if_available": "Open Webpage copy, if available",
"tag_renamed": "Tag renamed!", "tag_renamed": "Tag renamed!",
"tag_deleted": "Tag deleted!", "tag_deleted": "Tag deleted!",
"rename_tag": "Rename Tag", "rename_tag": "Rename Tag",

View File

@ -40,10 +40,10 @@ async function processBatch() {
}, },
/////////////////////// ///////////////////////
{ {
singlefile: null, monolith: null,
}, },
{ {
singlefile: "pending", monolith: "pending",
}, },
], ],
}, },
@ -84,10 +84,10 @@ async function processBatch() {
}, },
/////////////////////// ///////////////////////
{ {
singlefile: null, monolith: null,
}, },
{ {
singlefile: "pending", monolith: "pending",
}, },
], ],
}, },

View File

@ -137,14 +137,14 @@ export enum ArchivedFormat {
jpeg, jpeg,
pdf, pdf,
readability, readability,
singlefile, monolith,
} }
export enum LinkType { export enum LinkType {
url, url,
pdf, pdf,
image, image,
singlefile, monolith,
} }
export enum TokenExpiry { export enum TokenExpiry {