updated route + bug fixed
This commit is contained in:
parent
bd16136946
commit
0c6911aaf0
|
@ -1,4 +1,7 @@
|
||||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
import {
|
||||||
|
ArchivedFormat,
|
||||||
|
LinkIncludingShortenedCollectionAndTags,
|
||||||
|
} from "@/types/global";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
@ -65,15 +68,16 @@ export default function PreservedFormats() {
|
||||||
} else toast.error(data.response);
|
} else toast.error(data.response);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownload = (format: "png" | "pdf") => {
|
const handleDownload = (format: ArchivedFormat) => {
|
||||||
const path = `/api/v1/archives/${link?.collection.id}/${link?.id}.${format}`;
|
const path = `/api/v1/archives/${link?.id}?format=${format}`;
|
||||||
fetch(path)
|
fetch(path)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// Create a temporary link and click it to trigger the download
|
// Create a temporary link and click it to trigger the download
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = path;
|
link.href = path;
|
||||||
link.download = format === "pdf" ? "PDF" : "Screenshot";
|
link.download =
|
||||||
|
format === ArchivedFormat.screenshot ? "Screenshot" : "PDF";
|
||||||
link.click();
|
link.click();
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to download file");
|
console.error("Failed to download file");
|
||||||
|
@ -98,7 +102,7 @@ export default function PreservedFormats() {
|
||||||
|
|
||||||
<div className="flex text-black dark:text-white gap-1">
|
<div className="flex text-black dark:text-white gap-1">
|
||||||
<div
|
<div
|
||||||
onClick={() => handleDownload("png")}
|
onClick={() => handleDownload(ArchivedFormat.screenshot)}
|
||||||
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
@ -108,7 +112,7 @@ export default function PreservedFormats() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/api/v1/archives/${link.collectionId}/${link.id}.png`}
|
href={`/api/v1/archives/${link?.id}?format=${ArchivedFormat.screenshot}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
||||||
>
|
>
|
||||||
|
@ -133,7 +137,7 @@ export default function PreservedFormats() {
|
||||||
|
|
||||||
<div className="flex text-black dark:text-white gap-1">
|
<div className="flex text-black dark:text-white gap-1">
|
||||||
<div
|
<div
|
||||||
onClick={() => handleDownload("pdf")}
|
onClick={() => handleDownload(ArchivedFormat.pdf)}
|
||||||
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
@ -143,7 +147,7 @@ export default function PreservedFormats() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/api/v1/archives/${link.collectionId}/${link.id}.pdf`}
|
href={`/api/v1/archives/${link?.id}?format=${ArchivedFormat.pdf}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
className="cursor-pointer hover:opacity-60 duration-100 p-2 rounded-md"
|
||||||
>
|
>
|
||||||
|
|
|
@ -10,14 +10,10 @@ export default async function deleteCollection(
|
||||||
if (!collectionId)
|
if (!collectionId)
|
||||||
return { response: "Please choose a valid collection.", status: 401 };
|
return { response: "Please choose a valid collection.", status: 401 };
|
||||||
|
|
||||||
const collectionIsAccessible = (await getPermission({
|
const collectionIsAccessible = await getPermission({
|
||||||
userId,
|
userId,
|
||||||
collectionId,
|
collectionId,
|
||||||
})) as
|
});
|
||||||
| (Collection & {
|
|
||||||
members: UsersAndCollections[];
|
|
||||||
})
|
|
||||||
| null;
|
|
||||||
|
|
||||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||||
(e: UsersAndCollections) => e.userId === userId
|
(e: UsersAndCollections) => e.userId === userId
|
||||||
|
|
|
@ -11,14 +11,10 @@ export default async function updateCollection(
|
||||||
if (!collectionId)
|
if (!collectionId)
|
||||||
return { response: "Please choose a valid collection.", status: 401 };
|
return { response: "Please choose a valid collection.", status: 401 };
|
||||||
|
|
||||||
const collectionIsAccessible = (await getPermission({
|
const collectionIsAccessible = await getPermission({
|
||||||
userId,
|
userId,
|
||||||
collectionId,
|
collectionId,
|
||||||
})) as
|
});
|
||||||
| (Collection & {
|
|
||||||
members: UsersAndCollections[];
|
|
||||||
})
|
|
||||||
| null;
|
|
||||||
|
|
||||||
if (!(collectionIsAccessible?.ownerId === userId))
|
if (!(collectionIsAccessible?.ownerId === userId))
|
||||||
return { response: "Collection is not accessible.", status: 401 };
|
return { response: "Collection is not accessible.", status: 401 };
|
||||||
|
|
|
@ -6,11 +6,7 @@ import removeFile from "@/lib/api/storage/removeFile";
|
||||||
export default async function deleteLink(userId: number, linkId: number) {
|
export default async function deleteLink(userId: number, linkId: number) {
|
||||||
if (!linkId) return { response: "Please choose a valid link.", status: 401 };
|
if (!linkId) return { response: "Please choose a valid link.", status: 401 };
|
||||||
|
|
||||||
const collectionIsAccessible = (await getPermission({ userId, linkId })) as
|
const collectionIsAccessible = await getPermission({ userId, linkId });
|
||||||
| (Collection & {
|
|
||||||
members: UsersAndCollections[];
|
|
||||||
})
|
|
||||||
| null;
|
|
||||||
|
|
||||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||||
(e: UsersAndCollections) => e.userId === userId && e.canDelete
|
(e: UsersAndCollections) => e.userId === userId && e.canDelete
|
||||||
|
|
|
@ -9,11 +9,7 @@ export default async function getLinkById(userId: number, linkId: number) {
|
||||||
status: 401,
|
status: 401,
|
||||||
};
|
};
|
||||||
|
|
||||||
const collectionIsAccessible = (await getPermission({ userId, linkId })) as
|
const collectionIsAccessible = await getPermission({ userId, linkId });
|
||||||
| (Collection & {
|
|
||||||
members: UsersAndCollections[];
|
|
||||||
})
|
|
||||||
| null;
|
|
||||||
|
|
||||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||||
(e: UsersAndCollections) => e.userId === userId
|
(e: UsersAndCollections) => e.userId === userId
|
||||||
|
|
|
@ -15,11 +15,7 @@ export default async function updateLinkById(
|
||||||
status: 401,
|
status: 401,
|
||||||
};
|
};
|
||||||
|
|
||||||
const collectionIsAccessible = (await getPermission({ userId, linkId })) as
|
const collectionIsAccessible = await getPermission({ userId, linkId });
|
||||||
| (Collection & {
|
|
||||||
members: UsersAndCollections[];
|
|
||||||
})
|
|
||||||
| null;
|
|
||||||
|
|
||||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||||
(e: UsersAndCollections) => e.userId === userId && e.canUpdate
|
(e: UsersAndCollections) => e.userId === userId && e.canUpdate
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { prisma } from "@/lib/api/db";
|
||||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
||||||
import getTitle from "@/lib/api/getTitle";
|
import getTitle from "@/lib/api/getTitle";
|
||||||
import archive from "@/lib/api/archive";
|
import archive from "@/lib/api/archive";
|
||||||
import { Collection, UsersAndCollections } from "@prisma/client";
|
import { UsersAndCollections } from "@prisma/client";
|
||||||
import getPermission from "@/lib/api/getPermission";
|
import getPermission from "@/lib/api/getPermission";
|
||||||
import createFolder from "@/lib/api/storage/createFolder";
|
import createFolder from "@/lib/api/storage/createFolder";
|
||||||
|
|
||||||
|
@ -27,14 +27,10 @@ export default async function postLink(
|
||||||
link.collection.name = link.collection.name.trim();
|
link.collection.name = link.collection.name.trim();
|
||||||
|
|
||||||
if (link.collection.id) {
|
if (link.collection.id) {
|
||||||
const collectionIsAccessible = (await getPermission({
|
const collectionIsAccessible = await getPermission({
|
||||||
userId,
|
userId,
|
||||||
collectionId: link.collection.id,
|
collectionId: link.collection.id,
|
||||||
})) as
|
});
|
||||||
| (Collection & {
|
|
||||||
members: UsersAndCollections[];
|
|
||||||
})
|
|
||||||
| null;
|
|
||||||
|
|
||||||
const memberHasAccess = collectionIsAccessible?.members.some(
|
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||||
(e: UsersAndCollections) => e.userId === userId && e.canCreate
|
(e: UsersAndCollections) => e.userId === userId && e.canCreate
|
||||||
|
|
|
@ -1,35 +1,24 @@
|
||||||
import { prisma } from "@/lib/api/db";
|
import { prisma } from "@/lib/api/db";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
userId?: number;
|
userId: number;
|
||||||
collectionId?: number;
|
collectionId?: number;
|
||||||
linkId?: number;
|
linkId?: number;
|
||||||
isPublic?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function getPermission({
|
export default async function getPermission({
|
||||||
userId,
|
userId,
|
||||||
collectionId,
|
collectionId,
|
||||||
linkId,
|
linkId,
|
||||||
isPublic,
|
|
||||||
}: Props) {
|
}: Props) {
|
||||||
if (linkId) {
|
if (linkId) {
|
||||||
const check = await prisma.collection.findFirst({
|
const check = await prisma.collection.findFirst({
|
||||||
where: {
|
where: {
|
||||||
[isPublic ? "OR" : "AND"]: [
|
links: {
|
||||||
{
|
some: {
|
||||||
id: collectionId,
|
id: linkId,
|
||||||
OR: [{ ownerId: userId }, { members: { some: { userId } } }],
|
|
||||||
links: {
|
|
||||||
some: {
|
|
||||||
id: linkId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
isPublic: isPublic ? true : undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
include: { members: true },
|
include: { members: true },
|
||||||
});
|
});
|
||||||
|
@ -38,15 +27,8 @@ export default async function getPermission({
|
||||||
} else if (collectionId) {
|
} else if (collectionId) {
|
||||||
const check = await prisma.collection.findFirst({
|
const check = await prisma.collection.findFirst({
|
||||||
where: {
|
where: {
|
||||||
[isPublic ? "OR" : "AND"]: [
|
id: collectionId,
|
||||||
{
|
OR: [{ ownerId: userId }, { members: { some: { userId } } }],
|
||||||
id: collectionId,
|
|
||||||
OR: [{ ownerId: userId }, { members: { some: { userId } } }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isPublic: isPublic ? true : undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
include: { members: true },
|
include: { members: true },
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
|
||||||
import getPermission from "@/lib/api/getPermission";
|
|
||||||
import readFile from "@/lib/api/storage/readFile";
|
|
||||||
import { getToken } from "next-auth/jwt";
|
|
||||||
|
|
||||||
export default async function Index(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
if (!req.query.params)
|
|
||||||
return res.status(401).json({ response: "Invalid parameters." });
|
|
||||||
|
|
||||||
const token = await getToken({ req });
|
|
||||||
const userId = token?.id;
|
|
||||||
|
|
||||||
const collectionId = req.query.params[0];
|
|
||||||
const linkId = req.query.params[1];
|
|
||||||
|
|
||||||
const collectionIsAccessible = await getPermission({
|
|
||||||
userId,
|
|
||||||
collectionId: Number(collectionId),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!collectionIsAccessible)
|
|
||||||
return res
|
|
||||||
.status(401)
|
|
||||||
.json({ response: "You don't have access to this collection." });
|
|
||||||
|
|
||||||
const { file, contentType, status } = await readFile(
|
|
||||||
`archives/${collectionId}/${linkId}`
|
|
||||||
);
|
|
||||||
res.setHeader("Content-Type", contentType).status(status as number);
|
|
||||||
|
|
||||||
return res.send(file);
|
|
||||||
}
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import readFile from "@/lib/api/storage/readFile";
|
||||||
|
import { getToken } from "next-auth/jwt";
|
||||||
|
import { prisma } from "@/lib/api/db";
|
||||||
|
import { ArchivedFormat } from "@/types/global";
|
||||||
|
|
||||||
|
export default async function Index(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const linkId = Number(req.query.linkId);
|
||||||
|
const format = Number(req.query.format);
|
||||||
|
|
||||||
|
let suffix;
|
||||||
|
|
||||||
|
if (format === ArchivedFormat.screenshot) suffix = ".png";
|
||||||
|
else if (format === ArchivedFormat.pdf) suffix = ".pdf";
|
||||||
|
else if (format === ArchivedFormat.readability) suffix = "_readability.json";
|
||||||
|
|
||||||
|
if (!linkId || !suffix)
|
||||||
|
return res.status(401).json({ response: "Invalid parameters." });
|
||||||
|
|
||||||
|
const token = await getToken({ req });
|
||||||
|
const userId = token?.id;
|
||||||
|
|
||||||
|
const collectionIsAccessible = await prisma.collection.findFirst({
|
||||||
|
where: {
|
||||||
|
links: {
|
||||||
|
some: {
|
||||||
|
id: linkId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OR: [
|
||||||
|
{ ownerId: userId || -1 },
|
||||||
|
{ members: { some: { userId: userId || -1 } } },
|
||||||
|
{ isPublic: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!collectionIsAccessible)
|
||||||
|
return res
|
||||||
|
.status(401)
|
||||||
|
.json({ response: "You don't have access to this collection." });
|
||||||
|
|
||||||
|
const { file, contentType, status } = await readFile(
|
||||||
|
`archives/${collectionIsAccessible.id}/${linkId + suffix}`
|
||||||
|
);
|
||||||
|
res.setHeader("Content-Type", contentType).status(status as number);
|
||||||
|
|
||||||
|
return res.send(file);
|
||||||
|
}
|
|
@ -3,7 +3,10 @@ import React, { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import useLinkStore from "@/store/links";
|
import useLinkStore from "@/store/links";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
import {
|
||||||
|
ArchivedFormat,
|
||||||
|
LinkIncludingShortenedCollectionAndTags,
|
||||||
|
} from "@/types/global";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import ColorThief, { RGBColor } from "colorthief";
|
import ColorThief, { RGBColor } from "colorthief";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
@ -63,7 +66,9 @@ export default function Index() {
|
||||||
link?.readabilityPath &&
|
link?.readabilityPath &&
|
||||||
link?.readabilityPath !== "pending"
|
link?.readabilityPath !== "pending"
|
||||||
) {
|
) {
|
||||||
const response = await fetch(`/api/v1/${link?.readabilityPath}`);
|
const response = await fetch(
|
||||||
|
`/api/v1/archives/${link?.id}?format=${ArchivedFormat.readability}`
|
||||||
|
);
|
||||||
|
|
||||||
const data = await response?.json();
|
const data = await response?.json();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,10 @@ import React, { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import useLinkStore from "@/store/links";
|
import useLinkStore from "@/store/links";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
|
import {
|
||||||
|
ArchivedFormat,
|
||||||
|
LinkIncludingShortenedCollectionAndTags,
|
||||||
|
} from "@/types/global";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import ColorThief, { RGBColor } from "colorthief";
|
import ColorThief, { RGBColor } from "colorthief";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
@ -64,7 +67,9 @@ export default function Index() {
|
||||||
link?.readabilityPath &&
|
link?.readabilityPath &&
|
||||||
link?.readabilityPath !== "pending"
|
link?.readabilityPath !== "pending"
|
||||||
) {
|
) {
|
||||||
const response = await fetch(`/api/v1/${link?.readabilityPath}`);
|
const response = await fetch(
|
||||||
|
`/api/v1/archives/${link?.id}?format=${ArchivedFormat.readability}`
|
||||||
|
);
|
||||||
|
|
||||||
const data = await response?.json();
|
const data = await response?.json();
|
||||||
|
|
||||||
|
|
|
@ -115,3 +115,9 @@ export type DeleteUserBody = {
|
||||||
feedback?: Stripe.SubscriptionCancelParams.CancellationDetails.Feedback;
|
feedback?: Stripe.SubscriptionCancelParams.CancellationDetails.Feedback;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum ArchivedFormat {
|
||||||
|
screenshot,
|
||||||
|
pdf,
|
||||||
|
readability,
|
||||||
|
}
|
||||||
|
|
Ŝarĝante…
Reference in New Issue