mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-30 13:45:47 +00:00
feat: app context
This commit is contained in:
37
frontend/src/context/app-context.tsx
Normal file
37
frontend/src/context/app-context.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { AppContextSchema } from "@/schemas/app-context-schema";
|
||||
import { createContext, useContext } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
const AppContext = createContext<AppContextSchema | null>(null);
|
||||
|
||||
export const AppContextProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const { isPending, isError, data, error } = useQuery({
|
||||
queryKey: ["status"],
|
||||
queryFn: () => axios.get("/api/app").then((res) => res.data),
|
||||
});
|
||||
|
||||
if (isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return <AppContext.Provider value={data}>{children}</AppContext.Provider>;
|
||||
};
|
||||
|
||||
export const useAppContext = () => {
|
||||
const context = useContext(AppContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useAppContext must be used within an AppContextProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
@@ -12,4 +12,8 @@ export const isValidUrl = (url: string) => {
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const capitalize = (str: string) => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import { TotpPage } from "./pages/totp-page.tsx";
|
||||
import { ForgotPasswordPage } from "./pages/forgot-password-page.tsx";
|
||||
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";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@@ -56,10 +58,16 @@ const router = createBrowserRouter([
|
||||
},
|
||||
]);
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<Layout>
|
||||
<RouterProvider router={router} />
|
||||
</Layout>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AppContextProvider>
|
||||
<Layout>
|
||||
<RouterProvider router={router} />
|
||||
</Layout>
|
||||
</AppContextProvider>
|
||||
</QueryClientProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
|
||||
@@ -7,22 +7,21 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Code } from "@/components/ui/code";
|
||||
import { useAppContext } from "@/context/app-context";
|
||||
import { isValidUrl } from "@/lib/utils";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Navigate, useNavigate } from "react-router";
|
||||
|
||||
export const ContinuePage = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const redirectURI = params.get("redirect_uri");
|
||||
|
||||
const redirectURI = params.get("redirect_uri") ?? "";
|
||||
const { domain, disableContinue } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
//psuedo
|
||||
const domain = "127.0.0.1";
|
||||
const disableContinue = false;
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (redirectURI === "") {
|
||||
if (!redirectURI) {
|
||||
return <Navigate to="/" />;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { useAppContext } from "@/context/app-context";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
export const ForgotPasswordPage = () => {
|
||||
const { forgotPasswordMessage } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -15,10 +17,7 @@ export const ForgotPasswordPage = () => {
|
||||
<CardHeader>
|
||||
<CardTitle className="text-3xl">{t("forgotPasswordTitle")}</CardTitle>
|
||||
<CardDescription>
|
||||
<Markdown>
|
||||
You can reset your password by changing the `USERS` environment
|
||||
variable.
|
||||
</Markdown>
|
||||
<Markdown>{forgotPasswordMessage}</Markdown>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
@@ -11,12 +11,12 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { OAuthButton } from "@/components/ui/oauth-button";
|
||||
import { SeperatorWithChildren } from "@/components/ui/separator";
|
||||
import { useAppContext } from "@/context/app-context";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const LoginPage = () => {
|
||||
const { configuredProviders, title } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
const configuredProviders = ["google", "github", "generic", "username"];
|
||||
const title = "Tinyauth";
|
||||
|
||||
const oauthConfigured =
|
||||
configuredProviders.filter((provider) => provider !== "username").length >
|
||||
|
||||
@@ -7,14 +7,15 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Code } from "@/components/ui/code";
|
||||
import { capitalize } from "@/utils/utils";
|
||||
import { useAppContext } from "@/context/app-context";
|
||||
import { capitalize } from "@/lib/utils";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
export const LogoutPage = () => {
|
||||
const { genericName } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const provider = "google";
|
||||
const genericName = "generic";
|
||||
const username = "username";
|
||||
const email = "smbd@example.com";
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { Code } from "@/components/ui/code";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Navigate } from "react-router";
|
||||
import { Navigate, useNavigate } from "react-router";
|
||||
|
||||
export const UnauthorizedPage = () => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
@@ -17,6 +17,7 @@ export const UnauthorizedPage = () => {
|
||||
const groupErr = searchParams.get("groupErr");
|
||||
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!username) {
|
||||
return <Navigate to="/" />;
|
||||
@@ -51,7 +52,7 @@ export const UnauthorizedPage = () => {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex flex-col items-stretch">
|
||||
<Button onClick={() => window.location.replace("/")}>
|
||||
<Button onClick={() => navigate("/login")}>
|
||||
{t("unauthorizedButton")}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
|
||||
13
frontend/src/schemas/app-context-schema.ts
Normal file
13
frontend/src/schemas/app-context-schema.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const appContextSchema = z.object({
|
||||
configuredProviders: z.array(z.string()),
|
||||
disableContinue: z.boolean(),
|
||||
title: z.string(),
|
||||
genericName: z.string(),
|
||||
domain: z.string(),
|
||||
forgotPasswordMessage: z.string(),
|
||||
oauthAutoRedirect: z.string(),
|
||||
})
|
||||
|
||||
export type AppContextSchema = z.infer<typeof appContextSchema>;
|
||||
13
frontend/src/schemas/user-context-schema.ts
Normal file
13
frontend/src/schemas/user-context-schema.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const userContextSchema = z.object({
|
||||
isLoggedIn: z.boolean(),
|
||||
username: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
provider: z.string(),
|
||||
oauth: z.boolean(),
|
||||
totpPending: z.boolean(),
|
||||
})
|
||||
|
||||
export type UserContextSchema = z.infer<typeof userContextSchema>;
|
||||
@@ -1,3 +0,0 @@
|
||||
export const capitalize = (str: string) => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
Reference in New Issue
Block a user