From 86abf78493aec3d9d923534f6d8d3af28eac0db9 Mon Sep 17 00:00:00 2001 From: SevicheCC <91365763+Sevichecc@users.noreply.github.com> Date: Tue, 6 Jun 2023 23:58:20 +0800 Subject: [PATCH] feat: useAuth hook --- components/FormContainer.tsx | 18 +++++-- components/InputForm.tsx | 21 +++++--- components/ResultTable.tsx | 52 ++++++++++++-------- components/TokenTable.tsx | 9 ++++ hooks/useAuth.ts | 92 ++++++++++++++++++++++++++++++++++++ hooks/useCreateApp.ts | 10 ++-- lib/types.ts | 23 +++++---- 7 files changed, 180 insertions(+), 45 deletions(-) create mode 100644 components/TokenTable.tsx create mode 100644 hooks/useAuth.ts diff --git a/components/FormContainer.tsx b/components/FormContainer.tsx index 99ff53a..3b33c55 100644 --- a/components/FormContainer.tsx +++ b/components/FormContainer.tsx @@ -1,16 +1,28 @@ "use client"; +import { useEffect, useState } from "react"; import InputForm from "@/components/InputForm"; -import DataDisplay from "@/components/ResultTable"; +import ResultTable from "@/components/ResultTable"; +import TokenTable from "@/components/TokenTable"; import useCreateApp from "@/hooks/useCreateApp"; +import useAuth from "@/hooks/useAuth"; +import { FormSchema } from "@/components/InputForm"; +export type AppInfo = Pick; const FormContainer = () => { const { appEntry, createApp } = useCreateApp(); + const [appInfo, setAppInfo] = useState({ + instanceUrl: "", + scopes: [""], + }); + const { token, getAccessToken } = useAuth(appInfo); return ( <> - - {appEntry && } + + {appEntry && } + {/* */} ); }; + export default FormContainer; diff --git a/components/InputForm.tsx b/components/InputForm.tsx index 73ee9c4..1a1da7b 100644 --- a/components/InputForm.tsx +++ b/components/InputForm.tsx @@ -17,9 +17,12 @@ import { import ScopeSection from "@/components/scopes/ScopeSection"; import { scopesInfo } from "@/lib/utils"; +import { Dispatch, SetStateAction } from "react"; +import { AppEntry } from "@/lib/types"; +import { AppInfo } from "./FormContainer"; const formSchema = z.object({ - instance: z.string().trim(), + instanceUrl: z.string().trim(), clientName: z.string().trim(), redirectUris: z.string().trim(), scopes: z.string().array().nonempty().optional(), @@ -28,29 +31,35 @@ const formSchema = z.object({ export type FormSchema = z.infer; interface InputFormProps { - createApp: ({}: FormSchema) => Promise; + createApp: ({ }: FormSchema) => Promise + setAppInfo: Dispatch> } -const InputForm: React.FC = ({ createApp }) => { +const InputForm: React.FC = ({ createApp, setAppInfo }) => { const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { - instance: "https://", + instanceUrl: "https://", clientName: "", redirectUris: "", scopes: ["read"], }, }); + const onSubmit = async (values: FormSchema) => { + setAppInfo(values) + await createApp(values) + } + return (
( Instance diff --git a/components/ResultTable.tsx b/components/ResultTable.tsx index 096a62c..4c69804 100644 --- a/components/ResultTable.tsx +++ b/components/ResultTable.tsx @@ -10,7 +10,13 @@ import { TableRow, } from "@/components/ui/table"; import { AppEntry } from "@/lib/types"; -import { CopyButton } from "./ui/copybutton"; +import { CopyButton } from "@/components/ui/copybutton"; +import { Button } from "@/components/ui/button"; + +interface ResultTableProps { + appEntry: AppEntry; + getAccessToken: (appEntry: AppEntry) => Promise; +} const renderTableRow = (label: string, value: string | undefined) => ( @@ -22,26 +28,32 @@ const renderTableRow = (label: string, value: string | undefined) => ( ); -const DataDisplay = ({ appEntry }: { appEntry: AppEntry }) => { +const ResultTable: React.FC = ({ + appEntry, + getAccessToken, +}) => { return ( - - - - Type - Value - - - - {renderTableRow("ID", appEntry?.id)} - {renderTableRow("Name", appEntry?.name)} - {renderTableRow("Website", appEntry?.website || "")} - {renderTableRow("Redirect URI", appEntry?.redirect_uri)} - {renderTableRow("Client ID", appEntry?.client_id)} - {renderTableRow("Client Secret", appEntry?.client_secret)} - {renderTableRow("Vapid Key", appEntry?.vapid_key)} - -
+ <> + + + + Type + Value + + + + {renderTableRow("ID", appEntry?.id)} + {renderTableRow("Name", appEntry?.name)} + {renderTableRow("Website", appEntry?.website || "")} + {renderTableRow("Redirect URI", appEntry?.redirect_uri)} + {renderTableRow("Client ID", appEntry?.client_id)} + {renderTableRow("Client Secret", appEntry?.client_secret)} + {renderTableRow("Vapid Key", appEntry?.vapid_key)} + +
+ + ); }; -export default DataDisplay; +export default ResultTable; diff --git a/components/TokenTable.tsx b/components/TokenTable.tsx new file mode 100644 index 0000000..cf08c16 --- /dev/null +++ b/components/TokenTable.tsx @@ -0,0 +1,9 @@ +'use client' + +const TokenTable = () => { + + return ( +
5
+ ); +}; +export default TokenTable; diff --git a/hooks/useAuth.ts b/hooks/useAuth.ts new file mode 100644 index 0000000..fe9a7f7 --- /dev/null +++ b/hooks/useAuth.ts @@ -0,0 +1,92 @@ +import { useCallback, useState, useMemo } from "react"; +import { AppEntry, MError } from "@/lib/types"; +import { FormSchema } from "@/components/InputForm"; +import { AppInfo } from "@/components/FormContainer"; + +const useAuth = (appInfo: AppInfo) => { + const { instanceUrl, scopes} = appInfo + const [token, setToken] = useState(""); + + const getCode = useCallback( + (client_id: string, redirect_uri: string) => { + const code = new URLSearchParams(window.location.search).get("code"); + + if (!code) { + const params = { + response_type: "code", + client_id, + redirect_uri, + }; + + if (scopes) { + const scope = 1 + } + + const queryString = new URLSearchParams(params).toString(); + window.location.href = `${instanceUrl}/oauth/authorize?${queryString}`; + } else { + return code; + } + }, + [instanceUrl, scopes] + ); + + const getAuth = useCallback( + async ( + code: string, + client_id: string, + redirect_uri: string, + client_secret: string + ) => { + const body = new URLSearchParams({ + grant_type: "authorization_code", + client_id, + client_secret, + redirect_uri, + code, + }); + + try { + const response = await fetch(`${instanceUrl}/oauth/token`, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: body.toString(), + }); + + const result = await response.json(); + if (!response.ok) throw new Error((result as MError).error); + + return result.access_token; + } catch (error) { + console.error(error); + } + }, + [instanceUrl] + ); + + const getAccessToken = useCallback( + async (appEntry: AppEntry) => { + const { client_id, redirect_uri, client_secret } = appEntry; + const code = getCode(client_id, redirect_uri); + if (code) { + const accessToken = await getAuth( + code, + client_id, + redirect_uri, + client_secret + ); + setToken(accessToken); + } + }, + [getCode, getAuth] + ); + + return { + token, + getAccessToken, + }; +}; + +export default useAuth; diff --git a/hooks/useCreateApp.ts b/hooks/useCreateApp.ts index a97c328..64654d8 100644 --- a/hooks/useCreateApp.ts +++ b/hooks/useCreateApp.ts @@ -1,17 +1,13 @@ import { FormSchema } from "@/components/InputForm"; import { useCallback, useState } from "react"; -import { AppEntry } from "@/lib/types"; - -type MError = { - error: string; -}; +import { AppEntry, MError } from "@/lib/types"; const useCreateApp = () => { const [appEntry, setAppEntry] = useState(); const createApp = useCallback( async ({ - instance, + instanceUrl, website, clientName, redirectUris, @@ -25,7 +21,7 @@ const useCreateApp = () => { }; try { - let request = await fetch(`${instance}/api/v1/apps`, { + let request = await fetch(`${instanceUrl}/api/v1/apps`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/lib/types.ts b/lib/types.ts index 2c2193b..8812dbd 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -14,13 +14,18 @@ export interface ScopeInfo { description: string; } - export interface AppEntry { - id: string; - name: string; - website: string | null; - redirect_uri: string; - client_id: string; - client_secret: string; - vapid_key: string; - } +export interface AppEntry { + id: string; + name: string; + website: string | null; + redirect_uri: string; + client_id: string; + client_secret: string; + vapid_key: string; +} + +export type MError = { + error: string; + error_description: string +};