mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 06:05:43 +00:00 
			
		
		
		
	feat: user context
This commit is contained in:
		| @@ -1,5 +1,12 @@ | ||||
| import { Navigate } from "react-router"; | ||||
| import { useUserContext } from "./context/user-context"; | ||||
|  | ||||
| export const App = () => { | ||||
|   const { isLoggedIn } = useUserContext(); | ||||
|  | ||||
|   if (isLoggedIn) { | ||||
|     return <Navigate to="/logout" />; | ||||
|   } | ||||
|  | ||||
|   return <Navigate to="/login" />; | ||||
| }; | ||||
|   | ||||
| @@ -1,22 +0,0 @@ | ||||
| import { twMerge } from "tailwind-merge"; | ||||
|  | ||||
| interface CodeProps extends React.ComponentPropsWithoutRef<"code"> { | ||||
|   children?: React.ReactNode; | ||||
|   className?: string; | ||||
| } | ||||
|  | ||||
| function Code({ children, className, ...props }: CodeProps) { | ||||
|   return ( | ||||
|     <code | ||||
|       className={twMerge( | ||||
|         "relative rounded bg-muted px-[0.2rem] py-[0.1rem] font-mono text-sm font-semibold", | ||||
|         className, | ||||
|       )} | ||||
|       {...props} | ||||
|     > | ||||
|       {children} | ||||
|     </code> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export { Code }; | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { AppContextSchema } from "@/schemas/app-context-schema"; | ||||
| import { createContext, useContext } from "react"; | ||||
| import { useQuery } from "@tanstack/react-query"; | ||||
| import { useSuspenseQuery } from "@tanstack/react-query"; | ||||
| import axios from "axios"; | ||||
|  | ||||
| const AppContext = createContext<AppContextSchema | null>(null); | ||||
| @@ -10,16 +10,12 @@ export const AppContextProvider = ({ | ||||
| }: { | ||||
|   children: React.ReactNode; | ||||
| }) => { | ||||
|   const { isPending, isError, data, error } = useQuery({ | ||||
|     queryKey: ["status"], | ||||
|   const { isFetching, data, error } = useSuspenseQuery({ | ||||
|     queryKey: ["app"], | ||||
|     queryFn: () => axios.get("/api/app").then((res) => res.data), | ||||
|   }); | ||||
|  | ||||
|   if (isPending) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (isError) { | ||||
|   if (error && !isFetching) { | ||||
|     throw error; | ||||
|   } | ||||
|  | ||||
|   | ||||
							
								
								
									
										35
									
								
								frontend/src/context/user-context.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								frontend/src/context/user-context.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import { UserContextSchema } from "@/schemas/user-context-schema"; | ||||
| import { createContext, useContext } from "react"; | ||||
| import { useSuspenseQuery } from "@tanstack/react-query"; | ||||
| import axios from "axios"; | ||||
|  | ||||
| const UserContext = createContext<UserContextSchema | null>(null); | ||||
|  | ||||
| export const UserContextProvider = ({ | ||||
|   children, | ||||
| }: { | ||||
|   children: React.ReactNode; | ||||
| }) => { | ||||
|   const { isFetching, data, error } = useSuspenseQuery({ | ||||
|     queryKey: ["user"], | ||||
|     queryFn: () => axios.get("/api/user").then((res) => res.data), | ||||
|   }); | ||||
|  | ||||
|   if (error && !isFetching) { | ||||
|     throw error; | ||||
|   } | ||||
|  | ||||
|   return <UserContext.Provider value={data}>{children}</UserContext.Provider>; | ||||
| }; | ||||
|  | ||||
| export const useUserContext = () => { | ||||
|   const context = useContext(UserContext); | ||||
|  | ||||
|   if (!context) { | ||||
|     throw new Error( | ||||
|       "useUserContext must be used within an UserContextProvider", | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   return context; | ||||
| }; | ||||
| @@ -118,3 +118,59 @@ | ||||
|     @apply bg-background text-foreground; | ||||
|   } | ||||
| } | ||||
|  | ||||
| h1 { | ||||
|   @apply scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl; | ||||
| } | ||||
|  | ||||
| h2 { | ||||
|   @apply scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0; | ||||
| } | ||||
|  | ||||
| h3 { | ||||
|   @apply scroll-m-20 text-2xl font-semibold tracking-tight; | ||||
| } | ||||
|  | ||||
| h4 { | ||||
|   @apply scroll-m-20 text-xl font-semibold tracking-tight; | ||||
| } | ||||
|  | ||||
| p { | ||||
|   @apply leading-7 [&:not(:first-child)]:mt-6; | ||||
| } | ||||
|  | ||||
| blockquote { | ||||
|   @apply mt-6 border-l-2 pl-6 italic; | ||||
| } | ||||
|  | ||||
| tr { | ||||
|   @apply m-0 border-t p-0 even:bg-muted; | ||||
| } | ||||
|  | ||||
| th { | ||||
|   @apply border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right; | ||||
| } | ||||
|  | ||||
| ul { | ||||
|   @apply my-6 ml-6 list-disc [&>li]:mt-2; | ||||
| } | ||||
|  | ||||
| code { | ||||
|   @apply relative rounded bg-muted px-[0.2rem] py-[0.1rem] font-mono text-sm font-semibold; | ||||
| } | ||||
|  | ||||
| .lead { | ||||
|   @apply text-xl text-muted-foreground; | ||||
| } | ||||
|  | ||||
| .large { | ||||
|   @apply text-lg font-semibold; | ||||
| } | ||||
|  | ||||
| small { | ||||
|   @apply text-sm font-medium leading-none; | ||||
| } | ||||
|  | ||||
| .muted { | ||||
|   @apply text-sm text-muted-foreground; | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import { LogoutPage } from "./pages/logout-page.tsx"; | ||||
| import { UnauthorizedPage } from "./pages/unauthorized-page.tsx"; | ||||
| import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; | ||||
| import { AppContextProvider } from "./context/app-context.tsx"; | ||||
| import { UserContextProvider } from "./context/user-context.tsx"; | ||||
|  | ||||
| const router = createBrowserRouter([ | ||||
|   { | ||||
| @@ -64,9 +65,11 @@ createRoot(document.getElementById("root")!).render( | ||||
|   <StrictMode> | ||||
|     <QueryClientProvider client={queryClient}> | ||||
|       <AppContextProvider> | ||||
|         <Layout> | ||||
|           <RouterProvider router={router} /> | ||||
|         </Layout> | ||||
|         <UserContextProvider> | ||||
|           <Layout> | ||||
|             <RouterProvider router={router} /> | ||||
|           </Layout> | ||||
|         </UserContextProvider> | ||||
|       </AppContextProvider> | ||||
|     </QueryClientProvider> | ||||
|   </StrictMode>, | ||||
|   | ||||
| @@ -6,8 +6,8 @@ import { | ||||
|   CardHeader, | ||||
|   CardTitle, | ||||
| } from "@/components/ui/card"; | ||||
| import { Code } from "@/components/ui/code"; | ||||
| import { useAppContext } from "@/context/app-context"; | ||||
| import { useUserContext } from "@/context/user-context"; | ||||
| import { isValidUrl } from "@/lib/utils"; | ||||
| import { Trans, useTranslation } from "react-i18next"; | ||||
| import { Navigate, useNavigate } from "react-router"; | ||||
| @@ -16,11 +16,16 @@ export const ContinuePage = () => { | ||||
|   const params = new URLSearchParams(window.location.search); | ||||
|   const redirectURI = params.get("redirect_uri"); | ||||
|  | ||||
|   const { isLoggedIn } = useUserContext(); | ||||
|   const { domain, disableContinue } = useAppContext(); | ||||
|   const { t } = useTranslation(); | ||||
|  | ||||
|   const navigate = useNavigate(); | ||||
|  | ||||
|   if (!isLoggedIn) { | ||||
|     return <Navigate to="/login" />; | ||||
|   } | ||||
|  | ||||
|   if (!redirectURI) { | ||||
|     return <Navigate to="/" />; | ||||
|   } | ||||
| @@ -37,7 +42,7 @@ export const ContinuePage = () => { | ||||
|  | ||||
|   if (!url.hostname.includes(domain)) { | ||||
|     return ( | ||||
|       <Card className="min-w-xs md:max-w-sm"> | ||||
|       <Card className="min-w-xs sm:min-w-sm"> | ||||
|         <CardHeader> | ||||
|           <CardTitle className="text-3xl"> | ||||
|             {t("untrustedRedirectTitle")} | ||||
| @@ -47,7 +52,7 @@ export const ContinuePage = () => { | ||||
|               i18nKey="untrustedRedirectSubtitle" | ||||
|               t={t} | ||||
|               components={{ | ||||
|                 code: <Code />, | ||||
|                 code: <code />, | ||||
|               }} | ||||
|               values={{ domain }} | ||||
|             /> | ||||
| @@ -70,7 +75,7 @@ export const ContinuePage = () => { | ||||
|  | ||||
|   if (url.protocol === "http:" && window.location.protocol === "https:") { | ||||
|     return ( | ||||
|       <Card className="min-w-xs md:max-w-sm"> | ||||
|       <Card className="min-w-xs sm:min-w-sm"> | ||||
|         <CardHeader> | ||||
|           <CardTitle className="text-3xl"> | ||||
|             {t("continueInsecureRedirectTitle")} | ||||
| @@ -80,7 +85,7 @@ export const ContinuePage = () => { | ||||
|               i18nKey="continueInsecureRedirectSubtitle" | ||||
|               t={t} | ||||
|               components={{ | ||||
|                 code: <Code />, | ||||
|                 code: <code />, | ||||
|               }} | ||||
|             /> | ||||
|           </CardDescription> | ||||
|   | ||||
| @@ -16,6 +16,8 @@ import { useTranslation } from "react-i18next"; | ||||
|  | ||||
| export const LoginPage = () => { | ||||
|   const { configuredProviders, title } = useAppContext(); | ||||
|  | ||||
|   console.log("Configured providers:", configuredProviders); | ||||
|   const { t } = useTranslation(); | ||||
|  | ||||
|   const oauthConfigured = | ||||
|   | ||||
| @@ -6,18 +6,20 @@ import { | ||||
|   CardHeader, | ||||
|   CardTitle, | ||||
| } from "@/components/ui/card"; | ||||
| import { Code } from "@/components/ui/code"; | ||||
| import { useAppContext } from "@/context/app-context"; | ||||
| import { useUserContext } from "@/context/user-context"; | ||||
| import { capitalize } from "@/lib/utils"; | ||||
| import { Trans, useTranslation } from "react-i18next"; | ||||
| import { Navigate } from "react-router"; | ||||
|  | ||||
| export const LogoutPage = () => { | ||||
|   const { provider, username, email, isLoggedIn } = useUserContext(); | ||||
|   const { genericName } = useAppContext(); | ||||
|   const { t } = useTranslation(); | ||||
|  | ||||
|   const provider = "google"; | ||||
|   const username = "username"; | ||||
|   const email = "smbd@example.com"; | ||||
|   if (!isLoggedIn) { | ||||
|     return <Navigate to="/login" />; | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <Card className="min-w-xs sm:min-w-sm"> | ||||
| @@ -29,7 +31,7 @@ export const LogoutPage = () => { | ||||
|               i18nKey="logoutOauthSubtitle" | ||||
|               t={t} | ||||
|               components={{ | ||||
|                 code: <Code />, | ||||
|                 code: <code />, | ||||
|               }} | ||||
|               values={{ | ||||
|                 username: email, | ||||
| @@ -42,7 +44,7 @@ export const LogoutPage = () => { | ||||
|               i18nKey="logoutUsernameSubtitle" | ||||
|               t={t} | ||||
|               components={{ | ||||
|                 code: <Code />, | ||||
|                 code: <code />, | ||||
|               }} | ||||
|               values={{ | ||||
|                 username: username, | ||||
|   | ||||
| @@ -6,7 +6,6 @@ import { | ||||
|   CardHeader, | ||||
|   CardTitle, | ||||
| } from "@/components/ui/card"; | ||||
| import { Code } from "@/components/ui/code"; | ||||
| import { Trans, useTranslation } from "react-i18next"; | ||||
| import { Navigate, useNavigate } from "react-router"; | ||||
|  | ||||
| @@ -42,7 +41,7 @@ export const UnauthorizedPage = () => { | ||||
|             i18nKey={i18nKey} | ||||
|             t={t} | ||||
|             components={{ | ||||
|               code: <Code />, | ||||
|               code: <code />, | ||||
|             }} | ||||
|             values={{ | ||||
|               username: username, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Stavros
					Stavros