mirror of
https://github.com/Nezumi-2711/next-gdrive-index.git
synced 2026-09-23 04:10:03 +00:00
Add setup page
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { GetServerSideProps } from "next";
|
||||
import Link from "next/link";
|
||||
import useSWR from "swr";
|
||||
import fetcher from "@utils/swrFetch";
|
||||
import LoadingFeedback from "@components/APIFeedback/Loading";
|
||||
import MarkdownRender from "@components/utility/MarkdownRender";
|
||||
import useLocalStorage from "@hooks/useLocalStorage";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { MdContentCopy } from "react-icons/md";
|
||||
import useCopyText from "@hooks/useCopyText";
|
||||
|
||||
type ConfigProps = {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
refresh_token: string;
|
||||
};
|
||||
const defaultConfig: ConfigProps = {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
refresh_token: "",
|
||||
};
|
||||
|
||||
export default function SetupFinal() {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [tempKey] = useLocalStorage("tempEncryption", "");
|
||||
const [tempConfig] = useLocalStorage<ConfigProps>(
|
||||
"tempGoogleCloud",
|
||||
defaultConfig,
|
||||
);
|
||||
const [dataConfig, setDataConfig] = useState<ConfigProps>(defaultConfig);
|
||||
const [dataKey, setDataKey] = useState<string>("");
|
||||
const [mdContent, setMdContent] = useState<string>("");
|
||||
|
||||
const dataRef = useRef<HTMLElement>(null);
|
||||
const copyText = useCopyText();
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
if (tempConfig) {
|
||||
setDataConfig(tempConfig);
|
||||
}
|
||||
if (tempKey) {
|
||||
setDataKey(tempKey);
|
||||
}
|
||||
setIsLoading(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
const mdContent = `To finishing the setup, you need to do the following steps:
|
||||
|
||||
## API Config
|
||||
The API Config file are located at \`src/config/api.ts\`. You need to fill in the following information:
|
||||
\`\`\`js
|
||||
module.exports = {
|
||||
client_id: "${dataConfig.client_id}",
|
||||
client_secret: "${dataConfig.client_secret}",
|
||||
refresh_token: "${dataConfig.refresh_token}",
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Environment Config
|
||||
The environment variables are differ for each hosting service. If you are using Vercel, you can add the environment variables on the project settings page.
|
||||
\`\`\`
|
||||
ENCRYPTION_KEY="${dataKey}"
|
||||
\`\`\`
|
||||
|
||||
**NOTE:** Make sure to redeploy the site after adding the environment variables.
|
||||
|
||||
## Customizing the Site
|
||||
The site can be customized by editing the \`src/config/site.ts\` file. You can change the site title, description, and other information.
|
||||
All the explanation also included in the file.`;
|
||||
|
||||
setMdContent(mdContent);
|
||||
setIsLoading(false);
|
||||
|
||||
// Remove the temp data
|
||||
localStorage.removeItem("tempEncryption");
|
||||
localStorage.removeItem("tempGoogleCloud");
|
||||
}, [dataConfig, dataKey]);
|
||||
|
||||
return (
|
||||
<div className={"mx-auto flex max-w-screen-xl flex-col gap-4"}>
|
||||
<div className={"card"}>
|
||||
<div className='flex w-full items-center justify-between rounded-lg px-4'>
|
||||
<span className='font-bold'>Finishing setup</span>
|
||||
</div>
|
||||
|
||||
<div className={"divider-horizontal"} />
|
||||
|
||||
{isLoading ? (
|
||||
<LoadingFeedback message={"Loading data..."} />
|
||||
) : (
|
||||
<MarkdownRender content={mdContent} />
|
||||
)}
|
||||
|
||||
<div className={"divider-horizontal"} />
|
||||
|
||||
<div
|
||||
className={
|
||||
"mx-auto flex w-full flex-row items-center justify-end gap-2 tablet:gap-4"
|
||||
}
|
||||
>
|
||||
<Link href={"/"}>
|
||||
<button className={"primary"}>Finish</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { MdContentCopy, MdWarning } from "react-icons/md";
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { encrypt, generateRandomEncryptionKey } from "@utils/encryptionHelper";
|
||||
import useCopyText from "@hooks/useCopyText";
|
||||
import { toast } from "react-toastify";
|
||||
import Link from "next/link";
|
||||
import useLocalStorage from "@hooks/useLocalStorage";
|
||||
import MarkdownRender from "@components/utility/MarkdownRender";
|
||||
import useSWR from "swr";
|
||||
import fetcher from "@utils/swrFetch";
|
||||
import LoadingFeedback from "@components/APIFeedback/Loading";
|
||||
|
||||
type StepProps = {
|
||||
stepName: string;
|
||||
imgSrc?: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export default function SetupGoogleCloud() {
|
||||
const [encryptionKey] = useLocalStorage("tempEncryption", "");
|
||||
const [settingJson, setSettingJson] = useLocalStorage("tempGoogleCloud", {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
refresh_token: "",
|
||||
});
|
||||
const [client_id, setClientID] = useState<string>("");
|
||||
const [client_secret, setClientSecret] = useState<string>("");
|
||||
const [refresh_token, setRefreshToken] = useState<string>("");
|
||||
const [allowNext, setAllowNext] = useState<boolean>(false);
|
||||
|
||||
const { data, isLoading } = useSWR("/setup/GoogleCloudStep.md", fetcher);
|
||||
|
||||
useEffect(() => {
|
||||
if (client_id && client_secret && refresh_token) {
|
||||
setAllowNext(true);
|
||||
} else {
|
||||
setAllowNext(false);
|
||||
}
|
||||
}, [client_id, client_secret, refresh_token]);
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex max-w-screen-xl flex-col gap-4'>
|
||||
<div className={"card"}>
|
||||
<div className='flex w-full items-center justify-between rounded-lg px-4'>
|
||||
<span className='font-bold'>Setting up Google Cloud</span>
|
||||
</div>
|
||||
|
||||
<div className={"divider-horizontal"} />
|
||||
|
||||
<div className={"banner"}>
|
||||
<span>
|
||||
If you already have Client ID, Client Secret, and Refresh Token.
|
||||
Click <a href={"#skip"}>here</a> to skip this step.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={"mx-auto flex w-full max-w-screen-md flex-col gap-4 px-4"}
|
||||
>
|
||||
<div className={"flex flex-col gap-2"}>
|
||||
{isLoading ? (
|
||||
<LoadingFeedback message={"Loading file..."} />
|
||||
) : (
|
||||
<MarkdownRender content={data as string} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={"card"}
|
||||
id={"skip"}
|
||||
>
|
||||
<div className='flex w-full items-center justify-between rounded-lg px-4'>
|
||||
<span className='font-bold'>Store Google Cloud credentials</span>
|
||||
</div>
|
||||
<div className={"divider-horizontal"} />
|
||||
<div
|
||||
className={"mx-auto flex w-full max-w-screen-md flex-col gap-4 px-4"}
|
||||
>
|
||||
<div className={"flex flex-col gap-2"}>
|
||||
<span className='font-bold'>Client ID</span>
|
||||
<input
|
||||
type={"text"}
|
||||
className={"pr-4"}
|
||||
value={client_id}
|
||||
onChange={(e) => setClientID(e.target.value)}
|
||||
placeholder={"Enter your client id here..."}
|
||||
/>
|
||||
</div>
|
||||
<div className={"flex flex-col gap-2"}>
|
||||
<span className='font-bold'>Client Secret</span>
|
||||
<input
|
||||
type={"text"}
|
||||
className={"pr-4"}
|
||||
value={client_secret}
|
||||
onChange={(e) => setClientSecret(e.target.value)}
|
||||
placeholder={"Enter your client secret here..."}
|
||||
/>
|
||||
<span className={"text-xs tablet:text-sm"}>
|
||||
* Client secret will be encrypted using your encryption key
|
||||
</span>
|
||||
</div>
|
||||
<div className={"flex flex-col gap-2"}>
|
||||
<span className='font-bold'>Refresh Token</span>
|
||||
<input
|
||||
type={"text"}
|
||||
className={"pr-4"}
|
||||
value={refresh_token}
|
||||
onChange={(e) => setRefreshToken(e.target.value)}
|
||||
placeholder={"Enter your refresh token here..."}
|
||||
/>
|
||||
<span className={"text-xs tablet:text-sm"}>
|
||||
* Refresh token will be encrypted using your encryption key
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={"divider-horizontal"} />
|
||||
|
||||
<div
|
||||
className={
|
||||
"mx-auto flex w-full items-center justify-end gap-2 tablet:gap-4"
|
||||
}
|
||||
>
|
||||
<Link href={"/setup/encryption"}>
|
||||
<button className={"secondary"}>Previous Page</button>
|
||||
</Link>
|
||||
<Link
|
||||
href={allowNext ? "/setup/final" : ""}
|
||||
onClick={() => {
|
||||
if (!allowNext) return;
|
||||
setSettingJson({
|
||||
client_id,
|
||||
client_secret: encrypt(client_secret, encryptionKey),
|
||||
refresh_token: encrypt(refresh_token, encryptionKey),
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button
|
||||
className={"primary"}
|
||||
disabled={!allowNext}
|
||||
>
|
||||
Next Page
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { GetServerSideProps } from "next";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Setup() {
|
||||
return (
|
||||
<div className={"mx-auto flex max-w-screen-xl flex-col gap-4"}>
|
||||
<div className={"card"}>
|
||||
<div className='flex w-full items-center justify-between rounded-lg px-4'>
|
||||
<span className='font-bold'>Starting configuration</span>
|
||||
</div>
|
||||
|
||||
<div className={"divider-horizontal"} />
|
||||
|
||||
<p className={"mx-auto max-w-screen-md px-4 py-4 text-center"}>
|
||||
This page will guide you through the initial configuration for
|
||||
deploying guDora-index. It start from setting up your encryption key,
|
||||
Google cloud, and setting up the project.
|
||||
<br />
|
||||
<br />
|
||||
This step will take you couple of minutes. So please read the
|
||||
instructions and follow them carefully.
|
||||
</p>
|
||||
|
||||
<div className={"divider-horizontal"} />
|
||||
|
||||
<div
|
||||
className={
|
||||
"mx-auto flex w-full flex-row items-center justify-end gap-2 tablet:gap-4"
|
||||
}
|
||||
>
|
||||
<Link href={"/setup/encryption"}>
|
||||
<button className={"primary"}>Start</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+40
-6
@@ -71,11 +71,16 @@ html, body {
|
||||
}
|
||||
a {
|
||||
@apply hover:opacity-75 transition duration-150;
|
||||
@apply text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 underline;
|
||||
}
|
||||
a:has(div.items), a:has(span) {
|
||||
@apply text-zinc-900 dark:text-zinc-100 hover:text-zinc-800 dark:hover:text-zinc-200;
|
||||
@apply no-underline;
|
||||
}
|
||||
|
||||
|
||||
input, textarea, button, select {
|
||||
@apply rounded-full px-4 py-2 tablet:py-1;
|
||||
@apply rounded-lg px-4 py-2 tablet:py-1;
|
||||
@apply text-sm tablet:text-base;
|
||||
@apply outline-none border;
|
||||
@apply border-zinc-400 hover:border-blue-300 focus:border-blue-400;
|
||||
@@ -91,20 +96,33 @@ html, body {
|
||||
button {
|
||||
@apply cursor-pointer disabled:cursor-default;
|
||||
}
|
||||
button[disabled] {
|
||||
@apply cursor-not-allowed !important;
|
||||
@apply border-zinc-300 dark:border-zinc-700 !important;
|
||||
@apply bg-zinc-400 dark:bg-zinc-800 !important;
|
||||
@apply text-zinc-500 !important;
|
||||
}
|
||||
button.primary {
|
||||
@apply text-zinc-100 dark:text-zinc-300;
|
||||
@apply text-zinc-100 dark:text-zinc-100;
|
||||
@apply border-blue-400 hover:border-blue-500 active:border-blue-600;
|
||||
@apply bg-blue-500 hover:bg-blue-600 active:bg-blue-400;
|
||||
@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-400 dark:active:bg-blue-600;
|
||||
@apply dark:bg-blue-500 dark:hover:bg-blue-600 dark:active:bg-blue-600;
|
||||
}
|
||||
button.secondary{
|
||||
@apply text-zinc-100 dark:text-zinc-300;
|
||||
@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 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
table {
|
||||
@apply w-full border-collapse;
|
||||
@@ -118,6 +136,10 @@ html, body {
|
||||
td {
|
||||
@apply border-b border-zinc-400 dark:border-zinc-700 py-1.5;
|
||||
}
|
||||
|
||||
img.thumbnail {
|
||||
@apply rounded-lg w-full max-w-screen-tablet mt-2 mb-4;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
@@ -128,6 +150,18 @@ html, body {
|
||||
@apply bg-zinc-100 dark:bg-zinc-900;
|
||||
}
|
||||
|
||||
div.banner {
|
||||
@apply rounded-lg p-2 tablet:p-4 my-2 tablet:my-4;
|
||||
@apply border border-zinc-400 dark:border-zinc-700;
|
||||
@apply drop-shadow;
|
||||
@apply bg-zinc-100 dark:bg-zinc-900;
|
||||
@apply flex gap-4 items-center;
|
||||
}
|
||||
div.banner.warning {
|
||||
@apply border-yellow-400 dark:border-yellow-600;
|
||||
@apply bg-yellow-100 dark:bg-yellow-900;
|
||||
}
|
||||
|
||||
div.layoutDropdown {
|
||||
@apply rounded-lg pr-2 pl-4 py-1 cursor-pointer;
|
||||
@apply flex items-center justify-between gap-8;
|
||||
@@ -187,7 +221,7 @@ html, body {
|
||||
}
|
||||
|
||||
.navbar a {
|
||||
@apply text-inherit font-normal not-italic;
|
||||
@apply text-inherit font-normal not-italic no-underline;
|
||||
}
|
||||
|
||||
.interactive {
|
||||
|
||||
@@ -54,7 +54,7 @@ html.dark {
|
||||
}
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
pre[class*="language-"], pre {
|
||||
background: var(--syntax-bg);
|
||||
color: var(--syntax-fg);
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0);
|
||||
@@ -92,7 +92,7 @@ pre[class*="language-"] *::selection {
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
pre[class*="language-"], pre {
|
||||
padding: 1em;
|
||||
margin: 0.5em 0;
|
||||
overflow: auto;
|
||||
|
||||
Reference in New Issue
Block a user