diff --git a/public/images/setup/google-cloud-1.png b/public/images/setup/google-cloud-1.png new file mode 100644 index 0000000..f603793 Binary files /dev/null and b/public/images/setup/google-cloud-1.png differ diff --git a/public/images/setup/google-cloud-10.png b/public/images/setup/google-cloud-10.png new file mode 100644 index 0000000..dd12cf8 Binary files /dev/null and b/public/images/setup/google-cloud-10.png differ diff --git a/public/images/setup/google-cloud-2.png b/public/images/setup/google-cloud-2.png new file mode 100644 index 0000000..11c6b9d Binary files /dev/null and b/public/images/setup/google-cloud-2.png differ diff --git a/public/images/setup/google-cloud-3.png b/public/images/setup/google-cloud-3.png new file mode 100644 index 0000000..d49f1b0 Binary files /dev/null and b/public/images/setup/google-cloud-3.png differ diff --git a/public/images/setup/google-cloud-4.png b/public/images/setup/google-cloud-4.png new file mode 100644 index 0000000..8bf2b81 Binary files /dev/null and b/public/images/setup/google-cloud-4.png differ diff --git a/public/images/setup/google-cloud-5.png b/public/images/setup/google-cloud-5.png new file mode 100644 index 0000000..d1a8249 Binary files /dev/null and b/public/images/setup/google-cloud-5.png differ diff --git a/public/images/setup/google-cloud-6.png b/public/images/setup/google-cloud-6.png new file mode 100644 index 0000000..a9bfe03 Binary files /dev/null and b/public/images/setup/google-cloud-6.png differ diff --git a/public/images/setup/google-cloud-7.png b/public/images/setup/google-cloud-7.png new file mode 100644 index 0000000..62f5d38 Binary files /dev/null and b/public/images/setup/google-cloud-7.png differ diff --git a/public/images/setup/google-cloud-8.png b/public/images/setup/google-cloud-8.png new file mode 100644 index 0000000..57b7b96 Binary files /dev/null and b/public/images/setup/google-cloud-8.png differ diff --git a/public/images/setup/google-cloud-9.png b/public/images/setup/google-cloud-9.png new file mode 100644 index 0000000..284f479 Binary files /dev/null and b/public/images/setup/google-cloud-9.png differ diff --git a/public/setup/FinalStep.md b/public/setup/FinalStep.md new file mode 100644 index 0000000..405dcec --- /dev/null +++ b/public/setup/FinalStep.md @@ -0,0 +1,26 @@ +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: "lorem", + client_secret: "ipsum", + refresh_token: "dolor", +} +``` + +## 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. +```dotenv +ENCRYPTION_KEY="Encryption" +``` + +**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. + + +And that's it, you're done with the setup. You can now start using the site. \ No newline at end of file diff --git a/public/setup/GoogleCloudStep.md b/public/setup/GoogleCloudStep.md new file mode 100644 index 0000000..5425a23 --- /dev/null +++ b/public/setup/GoogleCloudStep.md @@ -0,0 +1,47 @@ +# Google Cloud Setup + +Table of Contents: +- [Making a Google Cloud Project and enabling Google Drive API](#making-a-google-cloud-project-and-enabling-google-drive-api) +- [Creating Oauth Consent Screen](#creating-oauth-consent-screen) +- [Creating Oauth Client ID](#creating-oauth-client-id) +- [Getting Refresh Token](#getting-refresh-token) + +## Making a Google Cloud Project and enabling Google Drive API +1. Open [Google Cloud Console](https://console.cloud.google.com/). +2. Click on the project selector dropdown in the top left corner and select or create a new project. +![google-cloud-1.png](../images/setup/google-cloud-1.png) +3. Activate the [Google Drive API](https://console.cloud.google.com/apis/library/drive.googleapis.com) for your project. +![google-cloud-3.png](../images/setup/google-cloud-3.png) +4. After activating the API, you will be redirected to the API overview page. For creating Oauth Client ID, you need to create Oauth consent first, you can do this by clicking on the "Oauth Consent Screen" menu. + +## Creating Oauth Consent Screen +1. On the Oauth consent screen page, select "External" as the user type and click on the "Create" button. +![google-cloud-5.png](../images/setup/google-cloud-5.png) +2. On the next page, fill in the required information and click on the "Save" button. +(In my case I'm only filling `App name`, `User support email`, and `Developer contact information`.) +3. On the **Scopes** Page, select the following scopes and click on the "Save" button, then click on the "Continue" button. +![google-cloud-6.png](../images/setup/google-cloud-6.png) +4. On the **Test Users** Page, click on the "Add Users" button and add your email address or any other email address that you want to use. Then click on the "Save and Continue" button. +5. And you're done with the Oauth consent screen. Now you can create the Oauth Client ID by clicking on the "Credentials" menu. +**NOTE: We don't need to publish the app, since we're only using it for our own purpose.** + +## Creating Oauth Client ID +1. On the **Credentials** page, click on the "Create Credentials" button and select "Oauth Client ID". +![google-cloud-4.png](../images/setup/google-cloud-4.png) +2. On the next page, select "Web Application" as the application type, and fill in the required information. +3. In the **Authorized redirect URIs** section, add the following redirect URI: `https://developers.google.com/oauthplayground`, then click on the "Create" button. +4. After creating the Oauth Client ID, you will be redirected to the **Credentials** page. Then copy the Client ID, and Client Secret. (You will need them later on the bottom of this page, and to fetch refresh token.) + +## Getting Refresh Token +1. Open [Google OAuth Playground](https://developers.google.com/oauthplayground/). +2. Click on the gear icon on the upper right corner and check the box next to "Use your own OAuth credentials" and paste the Client ID and Client Secret that you copied earlier. +![google-cloud-7.png](../images/setup/google-cloud-7.png) +3. Select the following scopes and click on the "Authorize APIs" button. +![google-cloud-8.png](../images/setup/google-cloud-8.png) +4. Google will ask you to log in to your Google account, you need to log in using the account that specified on Test User (Creating Oauth Consent Screen - step 4). +5. It will show you a warning that the app is not verified, simply click on "Continue" button, and it will redirect you back to the Playground. +![google-cloud-9.png](../images/setup/google-cloud-9.png) +6. Click on the "Exchange authorization code for tokens" button, and it will show you the refresh token. Copy the refresh token and paste it on the bottom of this page. +![google-cloud-10.png](../images/setup/google-cloud-10.png) +7. You can close the Playground tab now. +8. You're done with the Google Cloud Setup. \ No newline at end of file diff --git a/src/pages/setup/final.tsx b/src/pages/setup/final.tsx new file mode 100644 index 0000000..bf68b85 --- /dev/null +++ b/src/pages/setup/final.tsx @@ -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(true); + const [tempKey] = useLocalStorage("tempEncryption", ""); + const [tempConfig] = useLocalStorage( + "tempGoogleCloud", + defaultConfig, + ); + const [dataConfig, setDataConfig] = useState(defaultConfig); + const [dataKey, setDataKey] = useState(""); + const [mdContent, setMdContent] = useState(""); + + const dataRef = useRef(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 ( +
+
+
+ Finishing setup +
+ +
+ + {isLoading ? ( + + ) : ( + + )} + +
+ +
+ + + +
+
+
+ ); +} diff --git a/src/pages/setup/google-cloud.tsx b/src/pages/setup/google-cloud.tsx new file mode 100644 index 0000000..9e56768 --- /dev/null +++ b/src/pages/setup/google-cloud.tsx @@ -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(""); + const [client_secret, setClientSecret] = useState(""); + const [refresh_token, setRefreshToken] = useState(""); + const [allowNext, setAllowNext] = useState(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 ( +
+
+
+ Setting up Google Cloud +
+ +
+ +
+ + If you already have Client ID, Client Secret, and Refresh Token. + Click here to skip this step. + +
+ +
+
+ {isLoading ? ( + + ) : ( + + )} +
+
+
+ +
+
+ Store Google Cloud credentials +
+
+
+
+ Client ID + setClientID(e.target.value)} + placeholder={"Enter your client id here..."} + /> +
+
+ Client Secret + setClientSecret(e.target.value)} + placeholder={"Enter your client secret here..."} + /> + + * Client secret will be encrypted using your encryption key + +
+
+ Refresh Token + setRefreshToken(e.target.value)} + placeholder={"Enter your refresh token here..."} + /> + + * Refresh token will be encrypted using your encryption key + +
+
+ +
+ +
+ + + + { + if (!allowNext) return; + setSettingJson({ + client_id, + client_secret: encrypt(client_secret, encryptionKey), + refresh_token: encrypt(refresh_token, encryptionKey), + }); + }} + > + + +
+
+
+ ); +} diff --git a/src/pages/setup/index.tsx b/src/pages/setup/index.tsx new file mode 100644 index 0000000..4d39156 --- /dev/null +++ b/src/pages/setup/index.tsx @@ -0,0 +1,38 @@ +import { GetServerSideProps } from "next"; +import Link from "next/link"; + +export default function Setup() { + return ( +
+
+
+ Starting configuration +
+ +
+ +

+ 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. +
+
+ This step will take you couple of minutes. So please read the + instructions and follow them carefully. +

+ +
+ +
+ + + +
+
+
+ ); +} diff --git a/src/styles/globals.css b/src/styles/globals.css index b4f4c54..989f01d 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -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 { diff --git a/src/styles/highlight.css b/src/styles/highlight.css index 0a176c1..cee7e95 100644 --- a/src/styles/highlight.css +++ b/src/styles/highlight.css @@ -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;