diff --git a/.env.sample b/.env.sample index 14f74df..bafdc9f 100644 --- a/.env.sample +++ b/.env.sample @@ -75,6 +75,13 @@ AUTH0_ISSUER= AUTH0_CLIENT_SECRET= AUTH0_CLIENT_ID= +# Authelia +NEXT_PUBLIC_AUTHELIA_ENABLED="" +AUTHELIA_CLIENT_ID="" +AUTHELIA_CLIENT_SECRET="" +AUTHELIA_WELLKNOWN_URL="" + + # Authentik NEXT_PUBLIC_AUTHENTIK_ENABLED= AUTHENTIK_CUSTOM_NAME= diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..651e877 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,45 @@ +# Architecture + +This is a summary of the architecture of Linkwarden. It's intended as a primer for collaborators to get a high-level understanding of the project. + +When you start Linkwarden, there are mainly two components that run: + +- The NextJS app, This is the main app and it's responsible for serving the frontend and handling the API routes. +- [The Background Worker](https://github.com/linkwarden/linkwarden/blob/main/scripts/worker.ts), This is a separate `ts-node` process that runs in the background and is responsible for archiving links. + +## Main Tech Stack + +- [NextJS](https://github.com/vercel/next.js) +- [TypeScript](https://github.com/microsoft/TypeScript) +- [Tailwind](https://github.com/tailwindlabs/tailwindcss) +- [DaisyUI](https://github.com/saadeghi/daisyui) +- [Prisma](https://github.com/prisma/prisma) +- [Playwright](https://github.com/microsoft/playwright) +- [Zustand](https://github.com/pmndrs/zustand) + +## Folder Structure + +Here's a summary of the main files and folders in the project: + +``` +linkwarden +├── components # React components +├── hooks # React reusable hooks +├── layouts # Layouts for pages +├── lib +│ ├── api # Server-side functions (controllers, etc.) +│ ├── client # Client-side functions +│ └── shared # Shared functions between client and server +├── pages # Pages and API routes +├── prisma # Prisma schema and migrations +├── scripts +│ ├── migration # Scripts for breaking changes +│ └── worker.ts # Background worker for archiving links +├── store # Zustand stores +├── styles # Styles +└── types # TypeScript types +``` + +## Versioning + +We use semantic versioning for the project. You can track the changes from the [Releases](https://github.com/linkwarden/linkwarden/releases). diff --git a/Dockerfile b/Dockerfile index a6d6129..9de1541 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,4 +20,4 @@ COPY . . RUN yarn prisma generate && \ yarn build -CMD yarn prisma migrate deploy && yarn start +CMD yarn prisma migrate deploy && yarn start \ No newline at end of file diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/components/CollectionListing.tsx b/components/CollectionListing.tsx index eb43386..171117b 100644 --- a/components/CollectionListing.tsx +++ b/components/CollectionListing.tsx @@ -47,7 +47,10 @@ const CollectionListing = () => { useEffect(() => { if (account.username) { - if (!account.collectionOrder || account.collectionOrder.length === 0) + if ( + (!account.collectionOrder || account.collectionOrder.length === 0) && + collections.length > 0 + ) updateAccount({ ...account, collectionOrder: collections diff --git a/components/FilterSearchDropdown.tsx b/components/FilterSearchDropdown.tsx index 57a0ea4..01b8906 100644 --- a/components/FilterSearchDropdown.tsx +++ b/components/FilterSearchDropdown.tsx @@ -26,7 +26,7 @@ export default function FilterSearchDropdown({ > -
{shortendURL}
-{collection?.name}
-{shortendURL}
+ + ) : ( +{shortendURL}
-
- {link?.createdAt
- ? new Date(link?.createdAt).toLocaleString("en-US", {
+ {date
+ ? new Date(date).toLocaleString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
diff --git a/components/SettingsSidebar.tsx b/components/SettingsSidebar.tsx
index eeb2e7d..9e169df 100644
--- a/components/SettingsSidebar.tsx
+++ b/components/SettingsSidebar.tsx
@@ -4,7 +4,7 @@ import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
export default function SettingsSidebar({ className }: { className?: string }) {
- const LINKWARDEN_VERSION = "v2.5.0";
+ const LINKWARDEN_VERSION = "v2.5.1";
const { collections } = useCollectionStore();
diff --git a/hooks/useLinks.tsx b/hooks/useLinks.tsx
index 6a06c1b..bec2d49 100644
--- a/hooks/useLinks.tsx
+++ b/hooks/useLinks.tsx
@@ -1,5 +1,5 @@
import { LinkRequestQuery } from "@/types/global";
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
import useDetectPageBottom from "./useDetectPageBottom";
import { useRouter } from "next/router";
import useLinkStore from "@/store/links";
@@ -22,6 +22,8 @@ export default function useLinks(
useLinkStore();
const router = useRouter();
+ const [isLoading, setIsLoading] = useState(true);
+
const { reachedBottom, setReachedBottom } = useDetectPageBottom();
const getLinks = async (isInitialCall: boolean, cursor?: number) => {
@@ -61,10 +63,14 @@ export default function useLinks(
basePath = "/api/v1/public/collections/links";
} else basePath = "/api/v1/links";
+ setIsLoading(true);
+
const response = await fetch(`${basePath}?${queryString}`);
const data = await response.json();
+ setIsLoading(false);
+
if (response.ok) setLinks(data.response, isInitialCall);
};
@@ -92,4 +98,6 @@ export default function useLinks(
setReachedBottom(false);
}, [reachedBottom]);
+
+ return { isLoading };
}
diff --git a/lib/api/archiveHandler.ts b/lib/api/archiveHandler.ts
index 08a35b5..a32498f 100644
--- a/lib/api/archiveHandler.ts
+++ b/lib/api/archiveHandler.ts
@@ -7,9 +7,9 @@ import { JSDOM } from "jsdom";
import DOMPurify from "dompurify";
import { Collection, Link, User } from "@prisma/client";
import validateUrlSize from "./validateUrlSize";
-import removeFile from "./storage/removeFile";
-import Jimp from "jimp";
import createFolder from "./storage/createFolder";
+import generatePreview from "./generatePreview";
+import { removeFiles } from "./manageLinkFiles";
type LinksAndCollectionAndOwner = Link & {
collection: Collection & {
@@ -51,6 +51,14 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
);
});
+ createFolder({
+ filePath: `archives/preview/${link.collectionId}`,
+ });
+
+ createFolder({
+ filePath: `archives/${link.collectionId}`,
+ });
+
try {
await Promise.race([
(async () => {
@@ -162,10 +170,6 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
return metaTag ? (metaTag as any).content : null;
});
- createFolder({
- filePath: `archives/preview/${link.collectionId}`,
- });
-
if (ogImageUrl) {
console.log("Found og:image URL:", ogImageUrl);
@@ -175,35 +179,7 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
// Check if imageResponse is not null
if (imageResponse && !link.preview?.startsWith("archive")) {
const buffer = await imageResponse.body();
-
- // Check if buffer is not null
- if (buffer) {
- // Load the image using Jimp
- Jimp.read(buffer, async (err, image) => {
- if (image && !err) {
- image?.resize(1280, Jimp.AUTO).quality(20);
- const processedBuffer = await image?.getBufferAsync(
- Jimp.MIME_JPEG
- );
-
- createFile({
- data: processedBuffer,
- filePath: `archives/preview/${link.collectionId}/${link.id}.jpeg`,
- }).then(() => {
- return prisma.link.update({
- where: { id: link.id },
- data: {
- preview: `archives/preview/${link.collectionId}/${link.id}.jpeg`,
- },
- });
- });
- }
- }).catch((err) => {
- console.error("Error processing the image:", err);
- });
- } else {
- console.log("No image data found.");
- }
+ await generatePreview(buffer, link.collectionId, link.id);
}
await page.goBack();
@@ -323,14 +299,7 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) {
},
});
else {
- removeFile({ filePath: `archives/${link.collectionId}/${link.id}.png` });
- removeFile({ filePath: `archives/${link.collectionId}/${link.id}.pdf` });
- removeFile({
- filePath: `archives/${link.collectionId}/${link.id}_readability.json`,
- });
- removeFile({
- filePath: `archives/preview/${link.collectionId}/${link.id}.jpeg`,
- });
+ await removeFiles(link.id, link.collectionId);
}
await browser.close();
diff --git a/lib/api/checkSubscriptionByEmail.ts b/lib/api/checkSubscriptionByEmail.ts
index 460a168..62a2320 100644
--- a/lib/api/checkSubscriptionByEmail.ts
+++ b/lib/api/checkSubscriptionByEmail.ts
@@ -32,11 +32,12 @@ export default async function checkSubscriptionByEmail(email: string) {
customer.subscriptions?.data.some((subscription) => {
subscription.current_period_end;
- active = subscription.items.data.some(
- (e) =>
- (e.price.id === MONTHLY_PRICE_ID && e.price.active === true) ||
- (e.price.id === YEARLY_PRICE_ID && e.price.active === true)
- );
+ active =
+ subscription.items.data.some(
+ (e) =>
+ (e.price.id === MONTHLY_PRICE_ID && e.price.active === true) ||
+ (e.price.id === YEARLY_PRICE_ID && e.price.active === true)
+ ) || false;
stripeSubscriptionId = subscription.id;
currentPeriodStart = subscription.current_period_start * 1000;
currentPeriodEnd = subscription.current_period_end * 1000;
@@ -44,7 +45,7 @@ export default async function checkSubscriptionByEmail(email: string) {
});
return {
- active,
+ active: active || false,
stripeSubscriptionId,
currentPeriodStart,
currentPeriodEnd,
diff --git a/lib/api/controllers/links/bulk/deleteLinksById.ts b/lib/api/controllers/links/bulk/deleteLinksById.ts
index 466db98..2db3896 100644
--- a/lib/api/controllers/links/bulk/deleteLinksById.ts
+++ b/lib/api/controllers/links/bulk/deleteLinksById.ts
@@ -2,6 +2,7 @@ import { prisma } from "@/lib/api/db";
import { UsersAndCollections } from "@prisma/client";
import getPermission from "@/lib/api/getPermission";
import removeFile from "@/lib/api/storage/removeFile";
+import { removeFiles } from "@/lib/api/manageLinkFiles";
export default async function deleteLinksById(
userId: number,
@@ -43,15 +44,7 @@ export default async function deleteLinksById(
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`,
- });
+ if (collectionIsAccessible) removeFiles(linkId, collectionIsAccessible.id);
}
return { response: deletedLinks, status: 200 };
diff --git a/lib/api/controllers/links/linkId/deleteLinkById.ts b/lib/api/controllers/links/linkId/deleteLinkById.ts
index db68ee7..dba90cb 100644
--- a/lib/api/controllers/links/linkId/deleteLinkById.ts
+++ b/lib/api/controllers/links/linkId/deleteLinkById.ts
@@ -2,6 +2,7 @@ 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";
+import { removeFiles } from "@/lib/api/manageLinkFiles";
export default async function deleteLink(userId: number, linkId: number) {
if (!linkId) return { response: "Please choose a valid link.", status: 401 };
@@ -12,7 +13,10 @@ export default async function deleteLink(userId: number, linkId: number) {
(e: UsersAndCollections) => e.userId === userId && e.canDelete
);
- if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess))
+ if (
+ !collectionIsAccessible ||
+ !(collectionIsAccessible?.ownerId === userId || memberHasAccess)
+ )
return { response: "Collection is not accessible.", status: 401 };
const deleteLink: Link = await prisma.link.delete({
@@ -21,15 +25,7 @@ export default async function deleteLink(userId: number, linkId: number) {
},
});
- removeFile({
- filePath: `archives/${collectionIsAccessible?.id}/${linkId}.pdf`,
- });
- removeFile({
- filePath: `archives/${collectionIsAccessible?.id}/${linkId}.png`,
- });
- removeFile({
- filePath: `archives/${collectionIsAccessible?.id}/${linkId}_readability.json`,
- });
+ removeFiles(linkId, collectionIsAccessible.id);
return { response: deleteLink, status: 200 };
}
diff --git a/lib/api/controllers/links/linkId/updateLinkById.ts b/lib/api/controllers/links/linkId/updateLinkById.ts
index e6f7f0d..4a24f4a 100644
--- a/lib/api/controllers/links/linkId/updateLinkById.ts
+++ b/lib/api/controllers/links/linkId/updateLinkById.ts
@@ -2,7 +2,7 @@ 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";
+import { moveFiles } from "@/lib/api/manageLinkFiles";
export default async function updateLinkById(
userId: number,
@@ -146,20 +146,7 @@ export default async function updateLinkById(
});
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`
- );
+ await moveFiles(linkId, collectionIsAccessible?.id, data.collection.id);
}
return { response: updatedLink, status: 200 };
diff --git a/lib/api/controllers/links/postLink.ts b/lib/api/controllers/links/postLink.ts
index 7746dd6..4c42f6c 100644
--- a/lib/api/controllers/links/postLink.ts
+++ b/lib/api/controllers/links/postLink.ts
@@ -12,14 +12,16 @@ 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.url || link.type === "url") {
+ 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) {
@@ -48,6 +50,7 @@ export default async function postLink(
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: {
@@ -180,7 +183,7 @@ export default async function postLink(
const newLink = await prisma.link.create({
data: {
- url: link.url?.trim().replace(/\/+$/, ""),
+ url: link.url?.trim().replace(/\/+$/, "") || null,
name: link.name,
description,
type: linkType,
diff --git a/lib/api/controllers/migration/importFromHTMLFile.ts b/lib/api/controllers/migration/importFromHTMLFile.ts
index 4398600..21fb3c4 100644
--- a/lib/api/controllers/migration/importFromHTMLFile.ts
+++ b/lib/api/controllers/migration/importFromHTMLFile.ts
@@ -2,6 +2,7 @@ import { prisma } from "@/lib/api/db";
import createFolder from "@/lib/api/storage/createFolder";
import { JSDOM } from "jsdom";
import { parse, Node, Element, TextNode } from "himalaya";
+import { writeFileSync } from "fs";
const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER) || 30000;
@@ -36,7 +37,9 @@ export default async function importFromHTMLFile(
const jsonData = parse(document.documentElement.outerHTML);
- for (const item of jsonData) {
+ const processedArray = processNodes(jsonData);
+
+ for (const item of processedArray) {
console.log(item);
await processBookmarks(userId, item as Element);
}
@@ -74,7 +77,9 @@ async function processBookmarks(
} else if (item.type === "element" && item.tagName === "a") {
// process link
- const linkUrl = item?.attributes.find((e) => e.key === "href")?.value;
+ const linkUrl = item?.attributes.find(
+ (e) => e.key.toLowerCase() === "href"
+ )?.value;
const linkName = (
item?.children.find((e) => e.type === "text") as TextNode
)?.content;
@@ -82,14 +87,33 @@ async function processBookmarks(
.find((e) => e.key === "tags")
?.value.split(",");
+ // set date if available
+ const linkDateValue = item?.attributes.find(
+ (e) => e.key.toLowerCase() === "add_date"
+ )?.value;
+
+ const linkDate = linkDateValue
+ ? new Date(Number(linkDateValue) * 1000)
+ : undefined;
+
+ let linkDesc =
+ (
+ (
+ item?.children?.find(
+ (e) => e.type === "element" && e.tagName === "dd"
+ ) as Element
+ )?.children[0] as TextNode
+ )?.content || "";
+
if (linkUrl && parentCollectionId) {
await createLink(
userId,
linkUrl,
parentCollectionId,
linkName,
- "",
- linkTags
+ linkDesc,
+ linkTags,
+ linkDate
);
} else if (linkUrl) {
// create a collection named "Imported Bookmarks" and add the link to it
@@ -100,8 +124,9 @@ async function processBookmarks(
linkUrl,
collectionId,
linkName,
- "",
- linkTags
+ linkDesc,
+ linkTags,
+ linkDate
);
}
@@ -160,7 +185,8 @@ const createLink = async (
collectionId: number,
name?: string,
description?: string,
- tags?: string[]
+ tags?: string[],
+ importDate?: Date
) => {
await prisma.link.create({
data: {
@@ -193,6 +219,48 @@ const createLink = async (
}),
}
: undefined,
+ importDate: importDate || undefined,
},
});
};
+
+function processNodes(nodes: Node[]) {
+ const findAndProcessDL = (node: Node) => {
+ if (node.type === "element" && node.tagName === "dl") {
+ processDLChildren(node);
+ } else if (
+ node.type === "element" &&
+ node.children &&
+ node.children.length
+ ) {
+ node.children.forEach((child) => findAndProcessDL(child));
+ }
+ };
+
+ const processDLChildren = (dlNode: Element) => {
+ dlNode.children.forEach((child, i) => {
+ if (child.type === "element" && child.tagName === "dt") {
+ const nextSibling = dlNode.children[i + 1];
+ if (
+ nextSibling &&
+ nextSibling.type === "element" &&
+ nextSibling.tagName === "dd"
+ ) {
+ const aElement = child.children.find(
+ (el) => el.type === "element" && el.tagName === "a"
+ );
+ if (aElement && aElement.type === "element") {
+ // Add the 'dd' element as a child of the 'a' element
+ aElement.children.push(nextSibling);
+ // Remove the 'dd' from the parent 'dl' to avoid duplicate processing
+ dlNode.children.splice(i + 1, 1);
+ // Adjust the loop counter due to the removal
+ }
+ }
+ }
+ });
+ };
+
+ nodes.forEach(findAndProcessDL);
+ return nodes;
+}
diff --git a/lib/api/controllers/users/userId/deleteUserById.ts b/lib/api/controllers/users/userId/deleteUserById.ts
index 2a5a083..976bd71 100644
--- a/lib/api/controllers/users/userId/deleteUserById.ts
+++ b/lib/api/controllers/users/userId/deleteUserById.ts
@@ -71,6 +71,10 @@ export default async function deleteUserById(
// Delete archive folders
removeFolder({ filePath: `archives/${collection.id}` });
+
+ await removeFolder({
+ filePath: `archives/preview/${collection.id}`,
+ });
}
// Delete collections after cleaning up related data
diff --git a/lib/api/generatePreview.ts b/lib/api/generatePreview.ts
new file mode 100644
index 0000000..6e81630
--- /dev/null
+++ b/lib/api/generatePreview.ts
@@ -0,0 +1,36 @@
+import Jimp from "jimp";
+import { prisma } from "./db";
+import createFile from "./storage/createFile";
+import createFolder from "./storage/createFolder";
+
+const generatePreview = async (
+ buffer: Buffer,
+ collectionId: number,
+ linkId: number
+) => {
+ if (buffer && collectionId && linkId) {
+ // Load the image using Jimp
+ await Jimp.read(buffer, async (err, image) => {
+ if (image && !err) {
+ image?.resize(1280, Jimp.AUTO).quality(20);
+ const processedBuffer = await image?.getBufferAsync(Jimp.MIME_JPEG);
+
+ createFile({
+ data: processedBuffer,
+ filePath: `archives/preview/${collectionId}/${linkId}.jpeg`,
+ }).then(() => {
+ return prisma.link.update({
+ where: { id: linkId },
+ data: {
+ preview: `archives/preview/${collectionId}/${linkId}.jpeg`,
+ },
+ });
+ });
+ }
+ }).catch((err) => {
+ console.error("Error processing the image:", err);
+ });
+ }
+};
+
+export default generatePreview;
diff --git a/lib/api/manageLinkFiles.ts b/lib/api/manageLinkFiles.ts
new file mode 100644
index 0000000..7bacdab
--- /dev/null
+++ b/lib/api/manageLinkFiles.ts
@@ -0,0 +1,61 @@
+import moveFile from "./storage/moveFile";
+import removeFile from "./storage/removeFile";
+
+const removeFiles = async (linkId: number, collectionId: number) => {
+ // PDF
+ await removeFile({
+ filePath: `archives/${collectionId}/${linkId}.pdf`,
+ });
+ // Images
+ await removeFile({
+ filePath: `archives/${collectionId}/${linkId}.png`,
+ });
+ await removeFile({
+ filePath: `archives/${collectionId}/${linkId}.jpeg`,
+ });
+ await removeFile({
+ filePath: `archives/${collectionId}/${linkId}.jpg`,
+ });
+ // Preview
+ await removeFile({
+ filePath: `archives/preview/${collectionId}/${linkId}.jpeg`,
+ });
+ // Readability
+ await removeFile({
+ filePath: `archives/${collectionId}/${linkId}_readability.json`,
+ });
+};
+
+const moveFiles = async (linkId: number, from: number, to: number) => {
+ await moveFile(
+ `archives/${from}/${linkId}.pdf`,
+ `archives/${to}/${linkId}.pdf`
+ );
+
+ await moveFile(
+ `archives/${from}/${linkId}.png`,
+ `archives/${to}/${linkId}.png`
+ );
+
+ await moveFile(
+ `archives/${from}/${linkId}.jpeg`,
+ `archives/${to}/${linkId}.jpeg`
+ );
+
+ await moveFile(
+ `archives/${from}/${linkId}.jpg`,
+ `archives/${to}/${linkId}.jpg`
+ );
+
+ await moveFile(
+ `archives/preview/${from}/${linkId}.jpeg`,
+ `archives/preview/${to}/${linkId}.jpeg`
+ );
+
+ await moveFile(
+ `archives/${from}/${linkId}_readability.json`,
+ `archives/${to}/${linkId}_readability.json`
+ );
+};
+
+export { removeFiles, moveFiles };
diff --git a/lib/api/verifySubscription.ts b/lib/api/verifySubscription.ts
index fd32307..b153cc6 100644
--- a/lib/api/verifySubscription.ts
+++ b/lib/api/verifySubscription.ts
@@ -17,15 +17,7 @@ export default async function verifySubscription(
const currentDate = new Date();
- if (
- subscription &&
- currentDate > subscription.currentPeriodEnd &&
- !subscription.active
- ) {
- return null;
- }
-
- if (!subscription || currentDate > subscription.currentPeriodEnd) {
+ if (!subscription?.active || currentDate > subscription.currentPeriodEnd) {
const {
active,
stripeSubscriptionId,
@@ -59,15 +51,21 @@ export default async function verifySubscription(
},
})
.catch((err) => console.log(err));
- }
+ } else if (!active) {
+ const subscription = await prisma.subscription.findFirst({
+ where: {
+ userId: user.id,
+ },
+ });
- if (!active) {
- if (user.username)
- // await prisma.user.update({
- // where: { id: user.id },
- // data: { username: null },
- // });
- return null;
+ if (subscription)
+ await prisma.subscription.delete({
+ where: {
+ userId: user.id,
+ },
+ });
+
+ return null;
}
}
diff --git a/lib/api/verifyUser.ts b/lib/api/verifyUser.ts
index 847bdaf..6e5fdf5 100644
--- a/lib/api/verifyUser.ts
+++ b/lib/api/verifyUser.ts
@@ -52,7 +52,7 @@ export default async function verifyUser({
}
if (STRIPE_SECRET_KEY) {
- const subscribedUser = verifySubscription(user);
+ const subscribedUser = await verifySubscription(user);
if (!subscribedUser) {
res.status(401).json({
diff --git a/lib/client/generateLinkHref.ts b/lib/client/generateLinkHref.ts
index 47c1888..f012ab9 100644
--- a/lib/client/generateLinkHref.ts
+++ b/lib/client/generateLinkHref.ts
@@ -16,24 +16,30 @@ export const generateLinkHref = (
): string => {
// Return the links href based on the account's preference
// If the user's preference is not available, return the original link
- switch (account.linksRouteTo) {
- case LinksRouteTo.ORIGINAL:
- return link.url || "";
- case LinksRouteTo.PDF:
- if (!pdfAvailable(link)) return link.url || "";
+ if (account.linksRouteTo === LinksRouteTo.ORIGINAL && link.type === "url") {
+ return link.url || "";
+ } else if (account.linksRouteTo === LinksRouteTo.PDF || link.type === "pdf") {
+ if (!pdfAvailable(link)) return link.url || "";
- return `/preserved/${link?.id}?format=${ArchivedFormat.pdf}`;
- case LinksRouteTo.READABLE:
- if (!readabilityAvailable(link)) return link.url || "";
+ return `/preserved/${link?.id}?format=${ArchivedFormat.pdf}`;
+ } else if (
+ account.linksRouteTo === LinksRouteTo.READABLE &&
+ link.type === "url"
+ ) {
+ if (!readabilityAvailable(link)) return link.url || "";
- return `/preserved/${link?.id}?format=${ArchivedFormat.readability}`;
- case LinksRouteTo.SCREENSHOT:
- if (!screenshotAvailable(link)) return link.url || "";
+ return `/preserved/${link?.id}?format=${ArchivedFormat.readability}`;
+ } else if (
+ account.linksRouteTo === LinksRouteTo.SCREENSHOT ||
+ link.type === "image"
+ ) {
+ console.log(link);
+ if (!screenshotAvailable(link)) return link.url || "";
- return `/preserved/${link?.id}?format=${
- link?.image?.endsWith("png") ? ArchivedFormat.png : ArchivedFormat.jpeg
- }`;
- default:
- return link.url || "";
+ return `/preserved/${link?.id}?format=${
+ link?.image?.endsWith("png") ? ArchivedFormat.png : ArchivedFormat.jpeg
+ }`;
+ } else {
+ return link.url || "";
}
};
diff --git a/package.json b/package.json
index 1f17a6b..6b03cf3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "linkwarden",
- "version": "2.5.0",
+ "version": "2.5.1",
"main": "index.js",
"repository": "https://github.com/linkwarden/linkwarden.git",
"author": "Daniel31X13
Nothing found.{" "}
¯\_(ツ)_/¯