mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 20:55:42 +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