improve link refresh logic + many changes and improvements
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import archiveHandler from "@/lib/api/archiveHandler";
|
||||
import { prisma } from "@/lib/api/db";
|
||||
import verifyUser from "@/lib/api/verifyUser";
|
||||
import isValidUrl from "@/lib/shared/isValidUrl";
|
||||
import removeFile from "@/lib/api/storage/removeFile";
|
||||
import { Collection, Link } from "@prisma/client";
|
||||
|
||||
const RE_ARCHIVE_LIMIT = Number(process.env.RE_ARCHIVE_LIMIT) || 5;
|
||||
|
||||
@@ -42,20 +43,17 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) {
|
||||
} minutes or create a new one.`,
|
||||
});
|
||||
|
||||
if (link.url && isValidUrl(link.url)) {
|
||||
archiveHandler(link);
|
||||
if (!link.url || !isValidUrl(link.url))
|
||||
return res.status(200).json({
|
||||
response: "Link is not a webpage to be archived.",
|
||||
response: "Invalid URL.",
|
||||
});
|
||||
}
|
||||
|
||||
await deleteArchivedFiles(link);
|
||||
|
||||
return res.status(200).json({
|
||||
response: "Link is being archived.",
|
||||
});
|
||||
}
|
||||
|
||||
// TODO - Later?
|
||||
// else if (req.method === "DELETE") {}
|
||||
}
|
||||
|
||||
const getTimezoneDifferenceInMinutes = (future: Date, past: Date) => {
|
||||
@@ -68,3 +66,26 @@ const getTimezoneDifferenceInMinutes = (future: Date, past: Date) => {
|
||||
|
||||
return diffInMinutes;
|
||||
};
|
||||
|
||||
const deleteArchivedFiles = async (link: Link & { collection: Collection }) => {
|
||||
await prisma.link.update({
|
||||
where: {
|
||||
id: link.id,
|
||||
},
|
||||
data: {
|
||||
screenshotPath: null,
|
||||
pdfPath: null,
|
||||
readabilityPath: null,
|
||||
},
|
||||
});
|
||||
|
||||
await removeFile({
|
||||
filePath: `archives/${link.collection.id}/${link.id}.pdf`,
|
||||
});
|
||||
await removeFile({
|
||||
filePath: `archives/${link.collection.id}/${link.id}.png`,
|
||||
});
|
||||
await removeFile({
|
||||
filePath: `archives/${link.collection.id}/${link.id}_readability.json`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -55,6 +55,8 @@ export default function Index() {
|
||||
name: "",
|
||||
username: "",
|
||||
image: "",
|
||||
archiveAsScreenshot: undefined as unknown as boolean,
|
||||
archiveAsPDF: undefined as unknown as boolean,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -70,6 +72,8 @@ export default function Index() {
|
||||
name: account.name,
|
||||
username: account.username as string,
|
||||
image: account.image as string,
|
||||
archiveAsScreenshot: account.archiveAsScreenshot as boolean,
|
||||
archiveAsPDF: account.archiveAsPDF as boolean,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -118,7 +122,7 @@ export default function Index() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="dropdown dropdown-bottom dropdown-end">
|
||||
<div className="dropdown dropdown-bottom dropdown-end mt-2">
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
|
||||
@@ -56,19 +56,11 @@ export default function Collections() {
|
||||
|
||||
{sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] ? (
|
||||
<>
|
||||
<div className="flex items-center gap-3 my-5">
|
||||
<i className="bi-folder text-3xl sm:text-2xl text-primary drop-shadow"></i>
|
||||
|
||||
<div>
|
||||
<p className="text-3xl capitalize font-thin">
|
||||
Other Collections
|
||||
</p>
|
||||
|
||||
<p className="sm:text-sm text-xs">
|
||||
Shared collections you're a member of
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<PageHeader
|
||||
icon={"bi-folder"}
|
||||
title={"Other Collections"}
|
||||
description={"Shared collections you're a member of"}
|
||||
/>
|
||||
|
||||
<div className="grid xl:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5">
|
||||
{sortedCollections
|
||||
|
||||
+15
-12
@@ -8,7 +8,6 @@ import useLinks from "@/hooks/useLinks";
|
||||
import Link from "next/link";
|
||||
import useWindowDimensions from "@/hooks/useWindowDimensions";
|
||||
import React from "react";
|
||||
import useModalStore from "@/store/modals";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { MigrationFormat, MigrationRequest } from "@/types/global";
|
||||
import DashboardItem from "@/components/DashboardItem";
|
||||
@@ -20,8 +19,6 @@ export default function Dashboard() {
|
||||
const { links } = useLinkStore();
|
||||
const { tags } = useTagStore();
|
||||
|
||||
const { setModal } = useModalStore();
|
||||
|
||||
const [numberOfLinks, setNumberOfLinks] = useState(0);
|
||||
|
||||
const [showLinks, setShowLinks] = useState(3);
|
||||
@@ -100,14 +97,14 @@ export default function Dashboard() {
|
||||
description={"A brief overview of your data"}
|
||||
/>
|
||||
<div>
|
||||
<div className="flex justify-evenly flex-col md:flex-row md:items-center gap-2 md:w-full h-full rounded-2xl p-8 border border-neutral-content bg-base-200">
|
||||
<div className="flex justify-evenly flex-col xl:flex-row xl:items-center gap-2 xl:w-full h-full rounded-2xl p-8 border border-neutral-content bg-base-200">
|
||||
<DashboardItem
|
||||
name={numberOfLinks === 1 ? "Link" : "Links"}
|
||||
value={numberOfLinks}
|
||||
icon={"bi-link-45deg"}
|
||||
/>
|
||||
|
||||
<div className="divider md:divider-horizontal"></div>
|
||||
<div className="divider xl:divider-horizontal"></div>
|
||||
|
||||
<DashboardItem
|
||||
name={collections.length === 1 ? "Collection" : "Collections"}
|
||||
@@ -115,7 +112,7 @@ export default function Dashboard() {
|
||||
icon={"bi-folder"}
|
||||
/>
|
||||
|
||||
<div className="divider md:divider-horizontal"></div>
|
||||
<div className="divider xl:divider-horizontal"></div>
|
||||
|
||||
<DashboardItem
|
||||
name={tags.length === 1 ? "Tag" : "Tags"}
|
||||
@@ -127,15 +124,18 @@ export default function Dashboard() {
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<i className="bi-clock-history text-primary text-2xl drop-shadow"></i>
|
||||
<p className="text-2xl">Recent</p>
|
||||
<PageHeader
|
||||
icon={"bi-clock-history"}
|
||||
title={"Recent"}
|
||||
description={"Recently added Links"}
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
href="/links"
|
||||
className="flex items-center text-sm text-black/75 dark:text-white/75 gap-2 cursor-pointer"
|
||||
>
|
||||
View All
|
||||
<i className="bi-chevron-right text-sm "></i>
|
||||
<i className="bi-chevron-right text-sm"></i>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -171,7 +171,7 @@ export default function Dashboard() {
|
||||
onClick={() => {
|
||||
setNewLinkModal(true);
|
||||
}}
|
||||
className="inline-flex items-center gap-2 text-sm btn btn-accent dark:border-accent text-white"
|
||||
className="inline-flex items-center gap-2 text-sm btn btn-accent dark:border-violet-400 text-white"
|
||||
>
|
||||
<i className="bi-plus-lg text-xl duration-100"></i>
|
||||
<span className="group-hover:opacity-0 text-right duration-100">
|
||||
@@ -239,8 +239,11 @@ export default function Dashboard() {
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<i className="bi-pin-angle text-primary text-2xl drop-shadow"></i>
|
||||
<p className="text-2xl">Pinned</p>
|
||||
<PageHeader
|
||||
icon={"bi-pin-angle"}
|
||||
title={"Pinned"}
|
||||
description={"Your pinned Links"}
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
href="/links/pinned"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import useLinkStore from "@/store/links";
|
||||
import { useRouter } from "next/router";
|
||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||
import {
|
||||
ArchivedFormat,
|
||||
LinkIncludingShortenedCollectionAndTags,
|
||||
} from "@/types/global";
|
||||
import ReadableView from "@/components/ReadableView";
|
||||
|
||||
export default function Index() {
|
||||
@@ -30,7 +33,22 @@ export default function Index() {
|
||||
{/* <div className="fixed left-1/2 transform -translate-x-1/2 w-fit py-1 px-3 bg-base-200 border border-neutral-content rounded-md">
|
||||
Readable
|
||||
</div> */}
|
||||
{link && <ReadableView link={link} />}
|
||||
{link && Number(router.query.format) === ArchivedFormat.readability && (
|
||||
<ReadableView link={link} />
|
||||
)}
|
||||
{link && Number(router.query.format) === ArchivedFormat.pdf && (
|
||||
<iframe
|
||||
src={`/api/v1/archives/${link.id}?format=${ArchivedFormat.pdf}`}
|
||||
className="w-full h-screen border-none"
|
||||
></iframe>
|
||||
)}
|
||||
{link && Number(router.query.format) === ArchivedFormat.png && (
|
||||
<img
|
||||
alt=""
|
||||
src={`/api/v1/archives/${link.id}?format=${ArchivedFormat.png}`}
|
||||
className="object-contain w-full h-screen"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import Head from "next/head";
|
||||
import useLinks from "@/hooks/useLinks";
|
||||
import useLinkStore from "@/store/links";
|
||||
import ProfilePhoto from "@/components/ProfilePhoto";
|
||||
import ModalManagement from "@/components/ModalManagement";
|
||||
import ToggleDarkMode from "@/components/ToggleDarkMode";
|
||||
import getPublicUserData from "@/lib/client/getPublicUserData";
|
||||
import Image from "next/image";
|
||||
@@ -42,10 +41,12 @@ export default function PublicCollections() {
|
||||
const router = useRouter();
|
||||
|
||||
const [collectionOwner, setCollectionOwner] = useState({
|
||||
id: null,
|
||||
id: null as unknown as number,
|
||||
name: "",
|
||||
username: "",
|
||||
image: "",
|
||||
archiveAsScreenshot: undefined as unknown as boolean,
|
||||
archiveAsPDF: undefined as unknown as boolean,
|
||||
});
|
||||
|
||||
const [searchFilter, setSearchFilter] = useState({
|
||||
@@ -102,8 +103,6 @@ export default function PublicCollections() {
|
||||
} 18rem, ${settings.theme === "dark" ? "#171717" : "#ffffff"} 100%)`,
|
||||
}}
|
||||
>
|
||||
<ModalManagement />
|
||||
|
||||
{collection ? (
|
||||
<Head>
|
||||
<title>{collection.name} | Linkwarden</title>
|
||||
|
||||
Reference in New Issue
Block a user