implemented basic support for pdf, png and jpg
This commit is contained in:
parent
33be9e5d83
commit
9c65e3e215
|
@ -13,6 +13,7 @@ STORAGE_FOLDER=
|
|||
AUTOSCROLL_TIMEOUT=
|
||||
NEXT_PUBLIC_DISABLE_REGISTRATION=
|
||||
RE_ARCHIVE_LIMIT=
|
||||
NEXT_PUBLIC_MAX_UPLOAD_SIZE=
|
||||
|
||||
# AWS S3 Settings
|
||||
SPACES_KEY=
|
||||
|
|
|
@ -13,14 +13,9 @@ type Props = {
|
|||
value?: number;
|
||||
}
|
||||
| undefined;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export default function CollectionSelection({
|
||||
onChange,
|
||||
defaultValue,
|
||||
id,
|
||||
}: Props) {
|
||||
export default function CollectionSelection({ onChange, defaultValue }: Props) {
|
||||
const { collections } = useCollectionStore();
|
||||
const router = useRouter();
|
||||
|
||||
|
@ -49,7 +44,6 @@ export default function CollectionSelection({
|
|||
|
||||
return (
|
||||
<CreatableSelect
|
||||
key={id || "key"}
|
||||
isClearable={false}
|
||||
className="react-select-container"
|
||||
classNamePrefix="react-select"
|
||||
|
|
|
@ -64,10 +64,12 @@ export default function DeleteCollectionModal({
|
|||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<p className="text-xl mb-5 font-thin text-red-500">
|
||||
<p className="text-xl font-thin text-red-500">
|
||||
{permissions === true ? "Delete" : "Leave"} Collection
|
||||
</p>
|
||||
|
||||
<div className="divider my-3"></div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
{permissions === true ? (
|
||||
<>
|
||||
|
|
|
@ -21,14 +21,6 @@ export default function DeleteLinkModal({ onClose, activeLink }: Props) {
|
|||
const [link, setLink] =
|
||||
useState<LinkIncludingShortenedCollectionAndTags>(activeLink);
|
||||
|
||||
let shortendURL;
|
||||
|
||||
try {
|
||||
shortendURL = new URL(link.url).host.toLowerCase();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
const { removeLink } = useLinkStore();
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
|
||||
|
@ -50,7 +42,10 @@ export default function DeleteLinkModal({ onClose, activeLink }: Props) {
|
|||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<p className="text-xl mb-5 font-thin text-red-500">Delete Link</p>
|
||||
<p className="text-xl font-thin text-red-500">Delete Link</p>
|
||||
|
||||
<div className="divider my-3"></div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<p>Are you sure you want to delete this Link?</p>
|
||||
|
||||
|
|
|
@ -49,7 +49,9 @@ export default function EditCollectionModal({
|
|||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<p className="text-xl mb-5 font-thin">Edit Collection Info</p>
|
||||
<p className="text-xl font-thin">Edit Collection Info</p>
|
||||
|
||||
<div className="divider my-3"></div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
|
|
|
@ -95,10 +95,12 @@ export default function EditCollectionSharingModal({
|
|||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<p className="text-xl font-thin mb-5">
|
||||
<p className="text-xl font-thin">
|
||||
{permissions === true ? "Share and Collaborate" : "Team"}
|
||||
</p>
|
||||
|
||||
<div className="divider my-3"></div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
{permissions === true && (
|
||||
<div>
|
||||
|
@ -178,7 +180,7 @@ export default function EditCollectionSharingModal({
|
|||
setMemberState
|
||||
)
|
||||
}
|
||||
className="btn btn-primary text-white btn-square"
|
||||
className="btn btn-primary btn-square btn-sm h-10 w-10"
|
||||
>
|
||||
<FontAwesomeIcon icon={faUserPlus} className="w-5 h-5" />
|
||||
</div>
|
||||
|
@ -323,7 +325,7 @@ export default function EditCollectionSharingModal({
|
|||
}}
|
||||
/>
|
||||
<span
|
||||
className={`peer-checked:bg-primary text-sm ${
|
||||
className={`peer-checked:bg-primary peer-checked:text-primary-content text-sm ${
|
||||
permissions === true
|
||||
? "hover:bg-neutral-content duration-100"
|
||||
: ""
|
||||
|
@ -368,7 +370,7 @@ export default function EditCollectionSharingModal({
|
|||
}}
|
||||
/>
|
||||
<span
|
||||
className={`peer-checked:bg-primary text-sm ${
|
||||
className={`peer-checked:bg-primary peer-checked:text-primary-content text-sm ${
|
||||
permissions === true
|
||||
? "hover:bg-neutral-content duration-100"
|
||||
: ""
|
||||
|
@ -413,7 +415,7 @@ export default function EditCollectionSharingModal({
|
|||
}}
|
||||
/>
|
||||
<span
|
||||
className={`peer-checked:bg-primary text-sm ${
|
||||
className={`peer-checked:bg-primary peer-checked:text-primary-content text-sm ${
|
||||
permissions === true
|
||||
? "hover:bg-neutral-content duration-100"
|
||||
: ""
|
||||
|
|
|
@ -24,7 +24,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
|
|||
let shortendURL;
|
||||
|
||||
try {
|
||||
shortendURL = new URL(link.url).host.toLowerCase();
|
||||
shortendURL = new URL(link.url || "").host.toLowerCase();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
@ -78,8 +78,11 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
|
|||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<p className="text-xl mb-5 font-thin">Edit Link</p>
|
||||
<p className="text-xl font-thin">Edit Link</p>
|
||||
|
||||
<div className="divider my-3"></div>
|
||||
|
||||
{link.url ? (
|
||||
<Link
|
||||
href={link.url}
|
||||
className="truncate text-neutral flex gap-2 mb-5 w-fit max-w-full"
|
||||
|
@ -92,6 +95,7 @@ export default function EditLinkModal({ onClose, activeLink }: Props) {
|
|||
/>
|
||||
<p>{shortendURL}</p>
|
||||
</Link>
|
||||
) : undefined}
|
||||
|
||||
<div className="w-full">
|
||||
<p className="mb-2">Name</p>
|
||||
|
|
|
@ -54,7 +54,9 @@ export default function NewCollectionModal({ onClose }: Props) {
|
|||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<p className="text-xl mb-5 font-thin">Create a New Collection</p>
|
||||
<p className="text-xl font-thin">Create a New Collection</p>
|
||||
|
||||
<div className="divider my-3"></div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
|
|
|
@ -41,8 +41,6 @@ export default function NewLinkModal({ onClose }: Props) {
|
|||
const { addLink } = useLinkStore();
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
|
||||
const [resetCollectionSelection, setResetCollectionSelection] = useState("");
|
||||
|
||||
const [optionsExpanded, setOptionsExpanded] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -66,9 +64,6 @@ export default function NewLinkModal({ onClose }: Props) {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
setResetCollectionSelection(Date.now().toString());
|
||||
console.log(link);
|
||||
|
||||
setOptionsExpanded(false);
|
||||
if (router.query.id) {
|
||||
const currentCollection = collections.find(
|
||||
|
@ -123,7 +118,10 @@ export default function NewLinkModal({ onClose }: Props) {
|
|||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<p className="text-xl mb-5 font-thin">Create a New Link</p>
|
||||
<p className="text-xl font-thin">Create a New Link</p>
|
||||
|
||||
<div className="divider my-3"></div>
|
||||
|
||||
<div className="grid grid-flow-row-dense sm:grid-cols-5 gap-3">
|
||||
<div className="sm:col-span-3 col-span-5">
|
||||
<p className="mb-2">Link</p>
|
||||
|
@ -143,7 +141,6 @@ export default function NewLinkModal({ onClose }: Props) {
|
|||
label: link.collection.name,
|
||||
value: link.collection.id,
|
||||
}}
|
||||
id={resetCollectionSelection}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import CollectionSelection from "@/components/InputSelect/CollectionSelection";
|
||||
import TagSelection from "@/components/InputSelect/TagSelection";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import unescapeString from "@/lib/client/unescapeString";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import useLinkStore from "@/store/links";
|
||||
import {
|
||||
ArchivedFormat,
|
||||
LinkIncludingShortenedCollectionAndTags,
|
||||
} from "@/types/global";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/router";
|
||||
import toast from "react-hot-toast";
|
||||
import Modal from "../Modal";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faQuestion } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
|
||||
type Props = {
|
||||
onClose: Function;
|
||||
};
|
||||
|
||||
export default function UploadFileModal({ onClose }: Props) {
|
||||
const { data } = useSession();
|
||||
|
||||
const initial = {
|
||||
name: "",
|
||||
url: "",
|
||||
description: "",
|
||||
type: "url",
|
||||
tags: [],
|
||||
screenshotPath: "",
|
||||
pdfPath: "",
|
||||
readabilityPath: "",
|
||||
textContent: "",
|
||||
collection: {
|
||||
name: "",
|
||||
ownerId: data?.user.id as number,
|
||||
},
|
||||
} as LinkIncludingShortenedCollectionAndTags;
|
||||
|
||||
const [link, setLink] =
|
||||
useState<LinkIncludingShortenedCollectionAndTags>(initial);
|
||||
|
||||
const [file, setFile] = useState<File>();
|
||||
|
||||
const { addLink } = useLinkStore();
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
|
||||
const [optionsExpanded, setOptionsExpanded] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const { collections } = useCollectionStore();
|
||||
|
||||
const setCollection = (e: any) => {
|
||||
if (e?.__isNew__) e.value = null;
|
||||
|
||||
setLink({
|
||||
...link,
|
||||
collection: { id: e?.value, name: e?.label, ownerId: e?.ownerId },
|
||||
});
|
||||
};
|
||||
|
||||
const setTags = (e: any) => {
|
||||
const tagNames = e.map((e: any) => {
|
||||
return { name: e.label };
|
||||
});
|
||||
|
||||
setLink({ ...link, tags: tagNames });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setOptionsExpanded(false);
|
||||
if (router.query.id) {
|
||||
const currentCollection = collections.find(
|
||||
(e) => e.id == Number(router.query.id)
|
||||
);
|
||||
|
||||
if (
|
||||
currentCollection &&
|
||||
currentCollection.ownerId &&
|
||||
router.asPath.startsWith("/collections/")
|
||||
)
|
||||
setLink({
|
||||
...initial,
|
||||
collection: {
|
||||
id: currentCollection.id,
|
||||
name: currentCollection.name,
|
||||
ownerId: currentCollection.ownerId,
|
||||
},
|
||||
});
|
||||
} else
|
||||
setLink({
|
||||
...initial,
|
||||
collection: {
|
||||
name: "Unorganized",
|
||||
ownerId: data?.user.id as number,
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const submit = async () => {
|
||||
if (!submitLoader && file) {
|
||||
let fileType: ArchivedFormat | null = null;
|
||||
|
||||
if (file?.type === "image/jpg" || file.type === "image/jpeg")
|
||||
fileType = ArchivedFormat.jpeg;
|
||||
else if (file.type === "image/png") fileType = ArchivedFormat.png;
|
||||
else if (file.type === "application/pdf") fileType = ArchivedFormat.pdf;
|
||||
|
||||
console.log(fileType);
|
||||
if (fileType !== null) {
|
||||
setSubmitLoader(true);
|
||||
|
||||
let response;
|
||||
|
||||
const load = toast.loading("Creating...");
|
||||
|
||||
response = await addLink(link);
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
if (response.ok) {
|
||||
const formBody = new FormData();
|
||||
file && formBody.append("file", file);
|
||||
|
||||
console.log(formBody.get("file"));
|
||||
await fetch(
|
||||
`/api/v1/archives/${
|
||||
(response.data as LinkIncludingShortenedCollectionAndTags).id
|
||||
}?format=${fileType}`,
|
||||
{
|
||||
body: formBody,
|
||||
method: "POST",
|
||||
}
|
||||
);
|
||||
toast.success(`Created!`);
|
||||
onClose();
|
||||
} else toast.error(response.data as string);
|
||||
|
||||
setSubmitLoader(false);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal toggleModal={onClose}>
|
||||
<div className="flex gap-2 items-start">
|
||||
<p className="text-xl font-thin">Upload File</p>
|
||||
</div>
|
||||
<div className="divider my-3"></div>
|
||||
<div className="grid grid-flow-row-dense sm:grid-cols-5 gap-3">
|
||||
<div className="sm:col-span-3 col-span-5">
|
||||
<p className="mb-2">File</p>
|
||||
<label className="btn h-10 btn-sm w-full border border-neutral-content hover:border-neutral-content flex justify-between">
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf,.png,.jpg,.jpeg"
|
||||
className="cursor-pointer custom-file-input"
|
||||
onChange={(e) => e.target.files && setFile(e.target.files[0])}
|
||||
/>
|
||||
</label>
|
||||
<p className="text-xs font-semibold mt-2">
|
||||
PDF, PNG, JPG (Up to {process.env.NEXT_PUBLIC_MAX_UPLOAD_SIZE || 30}
|
||||
MB)
|
||||
</p>
|
||||
</div>
|
||||
<div className="sm:col-span-2 col-span-5">
|
||||
<p className="mb-2">Collection</p>
|
||||
{link.collection.name ? (
|
||||
<CollectionSelection
|
||||
onChange={setCollection}
|
||||
defaultValue={{
|
||||
label: link.collection.name,
|
||||
value: link.collection.id,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{optionsExpanded ? (
|
||||
<div className="mt-5">
|
||||
{/* <hr className="mb-3 border border-neutral-content" /> */}
|
||||
<div className="grid sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<p className="mb-2">Name</p>
|
||||
<TextInput
|
||||
value={link.name}
|
||||
onChange={(e) => setLink({ ...link, name: e.target.value })}
|
||||
placeholder="e.g. Example Link"
|
||||
className="bg-base-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-2">Tags</p>
|
||||
<TagSelection
|
||||
onChange={setTags}
|
||||
defaultValue={link.tags.map((e) => {
|
||||
return { label: e.name, value: e.id };
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="sm:col-span-2">
|
||||
<p className="mb-2">Description</p>
|
||||
<textarea
|
||||
value={unescapeString(link.description) as string}
|
||||
onChange={(e) =>
|
||||
setLink({ ...link, description: e.target.value })
|
||||
}
|
||||
placeholder="Will be auto generated if nothing is provided."
|
||||
className="resize-none w-full rounded-md p-2 border-neutral-content bg-base-200 focus:border-sky-300 dark:focus:border-sky-600 border-solid border outline-none duration-100"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : undefined}
|
||||
<div className="flex justify-between items-center mt-5">
|
||||
<div
|
||||
onClick={() => setOptionsExpanded(!optionsExpanded)}
|
||||
className={`rounded-md cursor-pointer btn btn-sm btn-ghost duration-100 flex items-center px-2 w-fit text-sm`}
|
||||
>
|
||||
<p>{optionsExpanded ? "Hide" : "More"} Options</p>
|
||||
</div>
|
||||
|
||||
<button className="btn btn-accent" onClick={submit}>
|
||||
Create Link
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -14,6 +14,7 @@ import useLocalSettingsStore from "@/store/localSettings";
|
|||
import NewLinkModal from "./ModalContent/NewLinkModal";
|
||||
import NewCollectionModal from "./ModalContent/NewCollectionModal";
|
||||
import Link from "next/link";
|
||||
import UploadFileModal from "./ModalContent/UploadFileModal";
|
||||
|
||||
export default function Navbar() {
|
||||
const { settings, updateSettings } = useLocalSettingsStore();
|
||||
|
@ -48,6 +49,7 @@ export default function Navbar() {
|
|||
|
||||
const [newLinkModal, setNewLinkModal] = useState(false);
|
||||
const [newCollectionModal, setNewCollectionModal] = useState(false);
|
||||
const [uploadFileModal, setUploadFileModal] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex justify-between gap-2 items-center px-4 py-2 border-solid border-b-neutral-content border-b">
|
||||
|
@ -88,6 +90,18 @@ export default function Navbar() {
|
|||
New Link
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
onClick={() => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
setUploadFileModal(true);
|
||||
}}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
>
|
||||
Upload File
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
onClick={() => {
|
||||
|
@ -163,6 +177,9 @@ export default function Navbar() {
|
|||
{newCollectionModal ? (
|
||||
<NewCollectionModal onClose={() => setNewCollectionModal(false)} />
|
||||
) : undefined}
|
||||
{uploadFileModal ? (
|
||||
<UploadFileModal onClose={() => setUploadFileModal(false)} />
|
||||
) : undefined}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"@prisma/client": "^4.16.2",
|
||||
"@stripe/stripe-js": "^1.54.1",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/formidable": "^3.4.5",
|
||||
"@types/node": "20.4.4",
|
||||
"@types/nodemailer": "^6.4.8",
|
||||
"@types/react": "18.2.14",
|
||||
|
@ -37,6 +38,7 @@
|
|||
"dompurify": "^3.0.6",
|
||||
"eslint": "8.46.0",
|
||||
"eslint-config-next": "13.4.9",
|
||||
"formidable": "^3.5.1",
|
||||
"framer-motion": "^10.16.4",
|
||||
"jsdom": "^22.1.0",
|
||||
"lottie-web": "^5.12.2",
|
||||
|
|
|
@ -3,21 +3,35 @@ import readFile from "@/lib/api/storage/readFile";
|
|||
import { getToken } from "next-auth/jwt";
|
||||
import { prisma } from "@/lib/api/db";
|
||||
import { ArchivedFormat } from "@/types/global";
|
||||
import verifyUser from "@/lib/api/verifyUser";
|
||||
import getPermission from "@/lib/api/getPermission";
|
||||
import { UsersAndCollections } from "@prisma/client";
|
||||
import formidable from "formidable";
|
||||
import createFile from "@/lib/api/storage/createFile";
|
||||
import fs from "fs";
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default async function Index(req: NextApiRequest, res: NextApiResponse) {
|
||||
const linkId = Number(req.query.linkId);
|
||||
const format = Number(req.query.format);
|
||||
|
||||
let suffix;
|
||||
let suffix: string;
|
||||
|
||||
if (format === ArchivedFormat.png) suffix = ".png";
|
||||
else if (format === ArchivedFormat.jpeg) suffix = ".jpeg";
|
||||
else if (format === ArchivedFormat.pdf) suffix = ".pdf";
|
||||
else if (format === ArchivedFormat.readability) suffix = "_readability.json";
|
||||
|
||||
//@ts-ignore
|
||||
if (!linkId || !suffix)
|
||||
return res.status(401).json({ response: "Invalid parameters." });
|
||||
|
||||
if (req.method === "GET") {
|
||||
const token = await getToken({ req });
|
||||
const userId = token?.id;
|
||||
|
||||
|
@ -48,4 +62,70 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) {
|
|||
res.setHeader("Content-Type", contentType).status(status as number);
|
||||
|
||||
return res.send(file);
|
||||
} else if (req.method === "POST") {
|
||||
const user = await verifyUser({ req, res });
|
||||
if (!user) return;
|
||||
|
||||
const collectionPermissions = await getPermission({
|
||||
userId: user.id,
|
||||
linkId,
|
||||
});
|
||||
|
||||
const memberHasAccess = collectionPermissions?.members.some(
|
||||
(e: UsersAndCollections) => e.userId === user.id && e.canCreate
|
||||
);
|
||||
|
||||
if (!(collectionPermissions?.ownerId === user.id || memberHasAccess))
|
||||
return { response: "Collection is not accessible.", status: 401 };
|
||||
|
||||
// await uploadHandler(linkId, )
|
||||
|
||||
const MAX_UPLOAD_SIZE = Number(process.env.NEXT_PUBLIC_MAX_UPLOAD_SIZE);
|
||||
|
||||
const form = formidable({
|
||||
maxFields: 1,
|
||||
maxFiles: 1,
|
||||
maxFileSize: MAX_UPLOAD_SIZE || 30 * 1048576,
|
||||
});
|
||||
|
||||
form.parse(req, async (err, fields, files) => {
|
||||
console.log(files);
|
||||
|
||||
const allowedMIMETypes = [
|
||||
"application/pdf",
|
||||
"image/png",
|
||||
"image/jpg",
|
||||
"image/jpeg",
|
||||
];
|
||||
|
||||
if (
|
||||
err ||
|
||||
!files.file ||
|
||||
!files.file[0] ||
|
||||
!allowedMIMETypes.includes(files.file[0].mimetype || "")
|
||||
) {
|
||||
// Handle parsing error
|
||||
return res.status(500).json({
|
||||
response: `Sorry, we couldn't process your file. Please ensure it's a PDF, PNG, or JPG format and doesn't exceed ${MAX_UPLOAD_SIZE}MB.`,
|
||||
});
|
||||
} else {
|
||||
console.log(files.file[0].mimetype);
|
||||
|
||||
const fileBuffer = fs.readFileSync(files.file[0].filepath);
|
||||
|
||||
console.log(fileBuffer);
|
||||
|
||||
await createFile({
|
||||
filePath: `archives/${collectionPermissions?.id}/${linkId + suffix}`,
|
||||
data: fileBuffer,
|
||||
});
|
||||
|
||||
fs.unlinkSync(files.file[0].filepath);
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
response: files,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,3 +286,7 @@ body {
|
|||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-file-input::file-selector-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ declare global {
|
|||
STORAGE_FOLDER?: string;
|
||||
AUTOSCROLL_TIMEOUT?: string;
|
||||
RE_ARCHIVE_LIMIT?: string;
|
||||
NEXT_PUBLIC_MAX_UPLOAD_SIZE?: string;
|
||||
|
||||
SPACES_KEY?: string;
|
||||
SPACES_SECRET?: string;
|
||||
|
|
34
yarn.lock
34
yarn.lock
|
@ -1497,6 +1497,13 @@
|
|||
dependencies:
|
||||
"@types/trusted-types" "*"
|
||||
|
||||
"@types/formidable@^3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-3.4.5.tgz#8e45c053cac5868e2b71cc7410e2bd92872f6b9c"
|
||||
integrity sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/jsdom@^21.1.3":
|
||||
version "21.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.3.tgz#a88c5dc65703e1b10b2a7839c12db49662b43ff0"
|
||||
|
@ -1766,6 +1773,11 @@ array.prototype.tosorted@^1.1.1:
|
|||
es-shim-unscopables "^1.0.0"
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
asap@^2.0.0:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
|
||||
|
||||
asn1@~0.2.3:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
|
||||
|
@ -2299,6 +2311,14 @@ detect-libc@^2.0.0, detect-libc@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
|
||||
integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
|
||||
|
||||
dezalgo@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81"
|
||||
integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==
|
||||
dependencies:
|
||||
asap "^2.0.0"
|
||||
wrappy "1"
|
||||
|
||||
didyoumean@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
||||
|
@ -2836,6 +2856,15 @@ form-data@~2.3.2:
|
|||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formidable@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.1.tgz#9360a23a656f261207868b1484624c4c8d06ee1a"
|
||||
integrity sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==
|
||||
dependencies:
|
||||
dezalgo "^1.0.4"
|
||||
hexoid "^1.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
fraction.js@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
||||
|
@ -3151,6 +3180,11 @@ has@^1.0.3:
|
|||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hexoid@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
||||
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
||||
|
||||
hoist-non-react-statics@^3.3.1:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
|
|
Ŝarĝante…
Reference in New Issue