diff --git a/package.json b/package.json
index 729e7e8..a4b7199 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"jsonwebtoken": "^9.0.0",
"mime-types": "^2.1.35",
"next": "13.3.0",
+ "next-seo": "^6.0.0",
"nextjs-progressbar": "^0.0.16",
"postcss": "8.4.22",
"react": "18.2.0",
diff --git a/src/components/Breadcrumb/index.tsx b/src/components/Breadcrumb/index.tsx
index 1a00be3..7d67b8a 100644
--- a/src/components/Breadcrumb/index.tsx
+++ b/src/components/Breadcrumb/index.tsx
@@ -2,7 +2,6 @@ import { TFileParent } from "@/types/googleapis";
import Link from "next/link";
import { Fragment, useEffect, useState } from "react";
import { MdHome } from "react-icons/md";
-import useLocalStorage from "@hooks/useLocalStorage";
import ReactLoading from "react-loading";
import config from "@config/site.config";
@@ -21,6 +20,10 @@ export default function Breadcrumb({ data, isLoading }: Props) {
useEffect(() => {
// setIsLoading(true);
if (data.length > 0) {
+ const findRoot = data.find((item) => item.id === config.files.rootFolder);
+ if (findRoot) {
+ data = data.filter((item) => item.id !== config.files.rootFolder);
+ }
setLimitedPath(data.slice(0, limitItem).reverse());
setSlicedPath(data.slice(limitItem)[0]);
setIsLimited(data.length > limitItem);
@@ -30,51 +33,52 @@ export default function Breadcrumb({ data, isLoading }: Props) {
return (
+
setIsSearching(true)}
@@ -117,8 +121,10 @@ export default function Navbar() {
{/* Dark mode */}
void;
};
-export default function Password({ folderId }: Props) {
+export default function Password({ folderId, inputCallback }: Props) {
const router = useRouter();
+
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [passwordStorage, setPasswordStorage] = useLocalStorage<{
@@ -17,12 +20,16 @@ export default function Password({ folderId }: Props) {
}>("passwordStorage", {});
const handleSubmit = () => {
- setPasswordStorage({
+ inputCallback({
...passwordStorage,
[folderId]: hashToken(password),
});
-
- router.reload();
+ // setPasswordStorage({
+ // ...passwordStorage,
+ // [folderId]: hashToken(password),
+ // });
+ //
+ // callback();
};
return (
@@ -89,7 +96,16 @@ export default function Password({ folderId }: Props) {
className={"primary w-full whitespace-nowrap tablet:w-fit"}
onClick={handleSubmit}
>
+ {/*{isLoading ? (*/}
+ {/* */}
+ {/*) : (*/}
Submit
+ {/*)}*/}
diff --git a/src/config/site.config.js b/src/config/site.config.js
index ad32f7f..4d9fd25 100644
--- a/src/config/site.config.js
+++ b/src/config/site.config.js
@@ -52,7 +52,7 @@ const config = {
// If this set to true, any user can download or view protected files.
// If this set to false, only authorized users can download or view protected files.
// The authorized users URL will have a token in it that valid for 1 hour.
- allowDownloadProtectedFiles: false, // If this set to true, any user can download protected files, but can't see the details of the file or folder.
+ allowDownloadProtectedWithoutAccess: false, // If this set to true, any user can download protected files, but can't see the details of the file or folder.
},
/* Config for readme file render */
readme: {
diff --git a/src/pages/api/files/[id]/download.ts b/src/pages/api/files/[id]/download.ts
index 87df4dc..546878e 100644
--- a/src/pages/api/files/[id]/download.ts
+++ b/src/pages/api/files/[id]/download.ts
@@ -4,6 +4,7 @@ import { NextApiRequest, NextApiResponse } from "next";
import config from "@config/site.config";
import { validateProtected } from "@utils/driveHelper";
import { ExtendedError } from "@/types/default";
+import { reverseString } from "@utils/hashHelper";
export default async function handler(
request: NextApiRequest,
@@ -19,9 +20,13 @@ export default async function handler(
fields: "id, name, mimeType, size, exportLinks, parents",
});
- if (!config.files.allowDownloadProtectedFiles) {
+ if (!config.files.allowDownloadProtectedWithoutAccess) {
const parentsArray: TFileParent[] = [];
+ let validHash = headerHash as string;
+ if (hash) {
+ validHash = reverseString(hash as string);
+ }
// Fetch parents
if (
fetchFileMetadata.data.mimeType === "application/vnd.google-apps.folder"
@@ -54,10 +59,7 @@ export default async function handler(
}
// Check for password file
- const validatePassword = await validateProtected(
- parentsArray,
- (headerHash as string) || (hash as string),
- );
+ const validatePassword = await validateProtected(parentsArray, validHash);
if (validatePassword.isProtected && !validatePassword.valid) {
return response.status(200).json({
success: true,
diff --git a/src/pages/api/files/[id]/view.ts b/src/pages/api/files/[id]/view.ts
index bce72a7..7114d80 100644
--- a/src/pages/api/files/[id]/view.ts
+++ b/src/pages/api/files/[id]/view.ts
@@ -4,6 +4,9 @@ import { NextApiRequest, NextApiResponse } from "next";
import config from "@config/site.config";
import { validateProtected } from "@utils/driveHelper";
import { ExtendedError } from "@/types/default";
+import { decrypt } from "@utils/encryptionHelper";
+import { verify } from "jsonwebtoken";
+import { reverseString } from "@utils/hashHelper";
export default async function handler(
request: NextApiRequest,
@@ -11,6 +14,7 @@ export default async function handler(
) {
try {
const { id, hash } = request.query;
+ const { vector, data } = request.query;
const { authorization } = request.headers;
const headerHash = authorization?.split(" ")[1] || null;
@@ -19,9 +23,14 @@ export default async function handler(
fields: "id, name, mimeType, size, exportLinks, parents",
});
- if (!config.files.allowDownloadProtectedFiles) {
+ if (!config.files.allowDownloadProtectedWithoutAccess) {
const parentsArray: TFileParent[] = [];
+ let validHash = headerHash as string;
+ if (hash) {
+ validHash = reverseString(hash as string);
+ }
+
// Fetch parents
if (
fetchFileMetadata.data.mimeType === "application/vnd.google-apps.folder"
@@ -54,10 +63,7 @@ export default async function handler(
}
// Check for password file
- const validatePassword = await validateProtected(
- parentsArray,
- (headerHash as string) || (hash as string),
- );
+ const validatePassword = await validateProtected(parentsArray, validHash);
if (validatePassword.isProtected && !validatePassword.valid) {
return response.status(200).json({
success: true,
diff --git a/src/pages/file/[id].tsx b/src/pages/file/[id].tsx
index 3fa172c..d484452 100644
--- a/src/pages/file/[id].tsx
+++ b/src/pages/file/[id].tsx
@@ -1,13 +1,7 @@
import useSWR from "swr";
-import fetcher from "@utils/swrFetch";
-import {
- ErrorResponse,
- FileResponse,
- FilesResponse,
- TFileParent,
-} from "@/types/googleapis";
+import { ErrorResponse, FileResponse, TFileParent } from "@/types/googleapis";
import Breadcrumb from "@/components/Breadcrumb";
-import { useEffect, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import LoadingFeedback from "@components/APIFeedback/Loading";
import ErrorFeedback from "@components/APIFeedback/Error";
import { useRouter } from "next/router";
@@ -15,6 +9,7 @@ import FileDetails from "@components/layout/FileDetails";
import useLocalStorage from "@hooks/useLocalStorage";
import axios from "axios";
import { GetServerSidePropsContext } from "next";
+import Password from "@components/layout/Password";
type Props = {
passwordParent?: string;
@@ -24,33 +19,51 @@ export default function File({ passwordParent }: Props) {
const { id } = router.query;
const [data, setData] = useState
();
- const [dataLoading, setDataLoading] = useState(true);
+ const [globalLoading, setGlobalLoading] = useState(true);
- const [passwordStorage] = useLocalStorage<{
+ const [passwordStorage, setPasswordStorage] = useLocalStorage<{
[key: string]: string;
}>("passwordStorage", {});
+ const [password, setPassword] = useState<{ [p: string]: string }>(
+ passwordStorage,
+ );
const {
data: swrData,
error,
isLoading,
- } = useSWR(`/api/files/${id}`, (url, headers) =>
- axios
- .get(url, {
- headers: {
- Authorization: `Bearer ${
- passwordStorage?.[passwordParent as string] ||
- passwordStorage?.[id as string] ||
- ""
- }`,
- ...headers,
- },
- })
- .then((res) => res.data),
+ isValidating,
+ mutate,
+ } = useSWR(
+ `/api/files/${id}`,
+ (url, headers) =>
+ axios
+ .get(url, {
+ headers: {
+ Authorization: `Bearer ${
+ password?.[passwordParent as string] ||
+ password?.[id as string] ||
+ passwordStorage?.[passwordParent as string] ||
+ passwordStorage?.[id as string] ||
+ ""
+ }`,
+ ...headers,
+ },
+ })
+ .then((res) => res.data),
+ {
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ refreshWhenHidden: false,
+ refreshInterval: 0,
+ shouldRetryOnError: false,
+ revalidateIfStale: true,
+ },
);
useEffect(() => {
- setDataLoading(true);
+ setGlobalLoading(true);
if (swrData) {
const parentsArray: TFileParent[] | undefined = swrData.parents;
parentsArray?.unshift({
@@ -62,28 +75,67 @@ export default function File({ passwordParent }: Props) {
...swrData,
};
setData(payload);
- setDataLoading(false);
+ setGlobalLoading(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [swrData, error, isLoading]);
+ }, [swrData, error, isLoading, isValidating, password]);
+
+ useEffect(() => {
+ if (!isLoading && !isValidating) {
+ setGlobalLoading(false);
+ } else {
+ setGlobalLoading(true);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading, isValidating]);
+
+ useEffect(() => {
+ mutate(swrData, {
+ revalidate: true,
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [password]);
+
+ const inputPassCallback = useCallback(
+ (data: { [p: string]: string }) => {
+ setGlobalLoading(true);
+ setPasswordStorage(data);
+ setPassword(data);
+ },
+ [setPasswordStorage],
+ );
return (
-
-
-
+ {globalLoading &&
}
+ {!globalLoading && error && (
+
+ )}
+ {!globalLoading && !error && data && (
+ <>
+ {data.passwordRequired && !data.passwordValidated && (
+
+ )}
+ {(data.passwordValidated || !data.passwordRequired) && (
+ <>
+
+
+
- {isLoading &&
}
- {!isLoading && error &&
}
- {!isLoading && !error && data && (
-
+
+ >
+ )}
+ >
)}
);
@@ -95,6 +147,11 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
`http://localhost:5000/api/files/${id}`,
);
+ context.res.setHeader(
+ "Cache-Control",
+ "public, s-maxage=10, stale-while-revalidate=59",
+ );
+
if (passwordParent) {
return {
props: {
diff --git a/src/pages/folder/[id].tsx b/src/pages/folder/[id].tsx
index 2bc52aa..3345737 100644
--- a/src/pages/folder/[id].tsx
+++ b/src/pages/folder/[id].tsx
@@ -1,10 +1,16 @@
-import useSWR from "swr";
+import useSWR, { mutate } from "swr";
import useSWRInfinite from "swr/infinite";
import fetcher, { buildNextKey } from "@utils/swrFetch";
import { ErrorResponse, FilesResponse, TFile } from "@/types/googleapis";
import Breadcrumb from "@/components/Breadcrumb";
import { drive_v3 } from "googleapis";
-import { useEffect, useState } from "react";
+import {
+ Dispatch,
+ SetStateAction,
+ useCallback,
+ useEffect,
+ useState,
+} from "react";
import MarkdownRender from "@/components/utility/MarkdownRender";
import config from "@config/site.config";
import GridLayout from "@components/layout/Files/GridLayout";
@@ -26,15 +32,19 @@ export default function Folder({ passwordParent }: Props) {
const { id } = router.query;
const [data, setData] = useState();
- const [dataLoading, setDataLoading] = useState(true);
const [isReadmeExists, setIsReadmeExists] = useState(false);
const [renderStyle] = useLocalStorage<"grid" | "list">("renderStyle", "grid");
const [layoutStyle, setLayoutStyle] = useState<"grid" | "list">(renderStyle);
+ const [globalLoading, setGlobalLoading] = useState(true);
- const [passwordStorage] = useLocalStorage<{
+ const [passwordStorage, setPasswordStorage] = useLocalStorage<{
[key: string]: string;
}>("passwordStorage", {});
+ const [password, setPassword] = useState<{ [p: string]: string }>(
+ passwordStorage,
+ );
+
const getNextKey = buildNextKey(`/api/files/${id}`);
const {
data: swrData,
@@ -42,25 +52,47 @@ export default function Folder({ passwordParent }: Props) {
isLoading,
size,
setSize,
- } = useSWRInfinite(getNextKey, (url, headers) =>
- axios
- .get(url, {
- headers: {
- Authorization: `Bearer ${
- passwordStorage?.[passwordParent as string] ||
- passwordStorage?.[id as string] ||
- ""
- }`,
- ...headers,
- },
- })
- .then((res) => res.data),
+ isValidating,
+ mutate,
+ } = useSWRInfinite(
+ getNextKey,
+ (url, headers) =>
+ axios
+ .get(url, {
+ headers: {
+ Authorization: `Bearer ${
+ password?.[passwordParent as string] ||
+ password?.[id as string] ||
+ passwordStorage?.[passwordParent as string] ||
+ passwordStorage?.[id as string] ||
+ ""
+ }`,
+ ...headers,
+ },
+ })
+ .then((res) => res.data),
+ {
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ refreshWhenHidden: false,
+ refreshInterval: 0,
+ shouldRetryOnError: false,
+ revalidateIfStale: true,
+ },
);
const {
data: readmeData,
error: readmeError,
isLoading: readmeLoading,
- } = useSWR(`/api/readme/${id}`, fetcher);
+ } = useSWR(`/api/readme/${id}`, fetcher, {
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ refreshWhenHidden: false,
+ refreshInterval: 0,
+ shouldRetryOnError: false,
+ });
const isLoadingInitialData = !swrData && !error;
const isLoadingMore =
@@ -74,7 +106,7 @@ export default function Folder({ passwordParent }: Props) {
typeof swrData[swrData.length - 1]?.nextPageToken === "undefined");
useEffect(() => {
- setDataLoading(true);
+ setGlobalLoading(true);
const files: (TFile | drive_v3.Schema$File)[] | undefined =
swrData?.flatMap((item: FilesResponse) => item.files);
const folders: (TFile | drive_v3.Schema$File)[] | undefined =
@@ -85,28 +117,58 @@ export default function Folder({ passwordParent }: Props) {
folders: folders || [],
};
setData(newData);
- setDataLoading(false);
if (newData.readmeExists) setIsReadmeExists(true);
+ setGlobalLoading(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [swrData, error, isLoading, size]);
+ }, [swrData, error, isLoading, size, isValidating, password]);
+
+ useEffect(() => {
+ if (!isLoading && !isValidating) {
+ setGlobalLoading(false);
+ } else {
+ setGlobalLoading(true);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading, isValidating]);
+
+ useEffect(() => {
+ mutate(swrData, {
+ revalidate: true,
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [password]);
+
+ const inputPassCallback = useCallback(
+ (data: { [p: string]: string }) => {
+ setGlobalLoading(true);
+ setPasswordStorage(data);
+ setPassword(data);
+ },
+ [setPasswordStorage],
+ );
return (
- {isLoading &&
}
- {!isLoading && error &&
}
- {!isLoading && !error && data && (
+ {globalLoading &&
}
+ {!globalLoading && error && (
+
+ )}
+ {!globalLoading && !error && data && (
<>
{data.passwordRequired && !data.passwordValidated && (
-
+
)}
{(data.passwordValidated || !data.passwordRequired) && (
<>
@@ -178,6 +240,11 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
`http://localhost:5000/api/files/${id}`,
);
+ context.res.setHeader(
+ "Cache-Control",
+ "public, s-maxage=10, stale-while-revalidate=59",
+ );
+
if (passwordParent) {
return {
props: {
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 7dc6c11..db4cb3b 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -4,7 +4,7 @@ import fetcher, { buildNextKey } from "@utils/swrFetch";
import { ErrorResponse, FilesResponse, TFile } from "@/types/googleapis";
import Breadcrumb from "@/components/Breadcrumb";
import { drive_v3 } from "googleapis";
-import { useEffect, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import MarkdownRender from "@/components/utility/MarkdownRender";
import config from "@config/site.config";
import GridLayout from "@components/layout/Files/GridLayout";
@@ -13,20 +13,22 @@ import SwitchLayout from "@components/utility/SwitchLayout";
import ListLayout from "@components/layout/Files/ListLayout";
import LoadingFeedback from "@components/APIFeedback/Loading";
import ErrorFeedback from "@components/APIFeedback/Error";
-import { hashToken } from "@utils/hashHelper";
import axios from "axios";
import Password from "@components/layout/Password";
export default function Home() {
const [data, setData] = useState
();
- const [dataLoading, setDataLoading] = useState(true);
const [isReadmeExists, setIsReadmeExists] = useState(false);
const [renderStyle] = useLocalStorage<"grid" | "list">("renderStyle", "grid");
const [layoutStyle, setLayoutStyle] = useState<"grid" | "list">(renderStyle);
+ const [globalLoading, setGlobalLoading] = useState(true);
const [passwordStorage, setPasswordStorage] = useLocalStorage<{
[key: string]: string;
}>("passwordStorage", {});
+ const [password, setPassword] = useState<{ [p: string]: string }>(
+ passwordStorage,
+ );
const getNextKey = buildNextKey("/api/files/");
const {
@@ -35,24 +37,41 @@ export default function Home() {
isLoading,
size,
setSize,
+ isValidating,
mutate,
- } = useSWRInfinite(getNextKey, (url, headers) =>
- axios
- .get(url, {
- headers: {
- Authorization: `Bearer ${
- passwordStorage?.[config.files.rootFolder] || ""
- }`,
- ...headers,
- },
- })
- .then((res) => res.data),
+ } = useSWRInfinite(
+ getNextKey,
+ (url, headers) =>
+ axios
+ .get(url, {
+ headers: {
+ Authorization: `Bearer ${
+ passwordStorage?.[config.files.rootFolder] || ""
+ }`,
+ ...headers,
+ },
+ })
+ .then((res) => res.data),
+ {
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ refreshWhenHidden: false,
+ refreshInterval: 0,
+ shouldRetryOnError: false,
+ revalidateIfStale: true,
+ },
);
const {
data: readmeData,
error: readmeError,
isLoading: readmeLoading,
} = useSWR("/api/readme/", fetcher, {
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ refreshWhenHidden: false,
+ refreshInterval: 0,
shouldRetryOnError: false,
});
@@ -68,7 +87,7 @@ export default function Home() {
typeof swrData[swrData.length - 1]?.nextPageToken === "undefined");
useEffect(() => {
- setDataLoading(true);
+ setGlobalLoading(true);
const files: (TFile | drive_v3.Schema$File)[] | undefined =
swrData?.flatMap((item: FilesResponse) => item.files);
const folders: (TFile | drive_v3.Schema$File)[] | undefined =
@@ -79,31 +98,61 @@ export default function Home() {
folders: folders || [],
};
setData(newData);
- setDataLoading(false);
if (newData.readmeExists) setIsReadmeExists(true);
+ setGlobalLoading(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [swrData, error, isLoading, size]);
+ }, [swrData, error, isLoading, size, isValidating, password]);
+
+ useEffect(() => {
+ if (!isLoading && !isValidating) {
+ setGlobalLoading(false);
+ } else {
+ setGlobalLoading(true);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading, isValidating]);
+
+ useEffect(() => {
+ mutate(swrData, {
+ revalidate: true,
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [password]);
+
+ const inputPassCallback = useCallback(
+ (data: { [p: string]: string }) => {
+ setGlobalLoading(true);
+ setPasswordStorage(data);
+ setPassword(data);
+ },
+ [setPasswordStorage],
+ );
return (
- {isLoading &&
}
- {!isLoading && error && (
+ {globalLoading &&
}
+ {!globalLoading && error && (
)}
- {!isLoading && !error && data && (
+ {!globalLoading && !error && data && (
<>
+ {/* If the root folder have password and the password isn't validated, show password input */}
{data.passwordRequired && !data.passwordValidated && (
-
+
)}
+ {/* If password is validated or the root folder doesn't require password, show the files */}
{(data.passwordValidated || !data.passwordRequired) && (
<>
{isReadmeExists && config.readme.position === "start" && (
diff --git a/src/styles/globals.css b/src/styles/globals.css
index 65d4210..7d8e520 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -106,21 +106,21 @@ html, body {
@apply border-blue-400 hover:border-blue-500 active:border-blue-600;
@apply bg-blue-500 hover:bg-blue-700 active:bg-blue-400;
@apply dark:border-blue-500 dark:hover:border-blue-600 dark:active:border-blue-700;
- @apply dark:bg-blue-500 dark:hover:bg-blue-600 dark:active:bg-blue-600;
+ @apply dark:bg-blue-500 dark:hover:bg-blue-600 dark:active:bg-blue-700;
}
button.secondary{
@apply text-zinc-100 dark:text-zinc-100;
@apply border-zinc-400 hover:border-zinc-500 active:border-zinc-600;
- @apply bg-zinc-500 hover:bg-zinc-600 active:bg-zinc-400;
+ @apply bg-zinc-500 hover:bg-zinc-700 active:bg-zinc-400;
@apply dark:border-zinc-500 dark:hover:border-zinc-600 dark:active:border-zinc-700;
- @apply dark:bg-zinc-500 dark:hover:bg-zinc-400 dark:active:bg-zinc-600;
+ @apply dark:bg-zinc-500 dark:hover:bg-zinc-600 dark:active:bg-zinc-700;
}
button.danger {
@apply text-zinc-100 dark:text-zinc-100;
@apply border-red-400 hover:border-red-500 active:border-red-600;
@apply bg-red-500 hover:bg-red-700 active:bg-red-400;
@apply dark:border-red-500 dark:hover:border-red-600 dark:active:border-red-700;
- @apply dark:bg-red-500 dark:hover:bg-red-600 dark:active:bg-red-600;
+ @apply dark:bg-red-500 dark:hover:bg-red-600 dark:active:bg-red-700;
}
table {
@@ -150,7 +150,7 @@ html, body {
}
div.banner {
- @apply rounded-lg p-2 tablet:p-4 my-2 tablet:my-4;
+ @apply rounded-lg p-2 tablet:p-4 my-2;
@apply border border-zinc-400 dark:border-zinc-700;
@apply drop-shadow;
@apply bg-zinc-100 dark:bg-zinc-900;
diff --git a/src/utils/driveHelper.ts b/src/utils/driveHelper.ts
index a31bcc0..07ba168 100644
--- a/src/utils/driveHelper.ts
+++ b/src/utils/driveHelper.ts
@@ -73,7 +73,7 @@ export async function _validateFolderPassword(
export async function validateProtected(
fileId: string | TFileParent[],
- passwordHash: string,
+ passwordHash?: string,
): Promise<{ isProtected: boolean; valid?: boolean; protectedId?: string }> {
const fetchPassword = await drive.files.list({
q: `name = '.password' and 'me' in owners and trashed = false`,
diff --git a/src/utils/hashHelper.ts b/src/utils/hashHelper.ts
index 0e804a9..06495fc 100644
--- a/src/utils/hashHelper.ts
+++ b/src/utils/hashHelper.ts
@@ -7,3 +7,9 @@ export function hashToken(text: string): string {
export function verifyHash(text: string, hash: string): boolean {
return hashToken(text) === hash;
}
+
+// It's not a good idea to use this.
+// Change this to jwt or something else.
+export function reverseString(str: string): string {
+ return str.split("").reverse().join("");
+}
diff --git a/yarn.lock b/yarn.lock
index 8fc0770..ae72404 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3239,6 +3239,11 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+next-seo@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-6.0.0.tgz#4568dc61a44dbdf5fe5ff44156cd0ff8804889a2"
+ integrity sha512-jKKt1p1z4otMA28AyeoAONixVjdYmgFCWwpEFtu+DwRHQDllVX3RjtyXbuCQiUZEfQ9rFPBpAI90vDeLZlMBdg==
+
next@13.3.0:
version "13.3.0"
resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1"