mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 22:25:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			223 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   Button,
 | |
|   Paper,
 | |
|   PasswordInput,
 | |
|   TextInput,
 | |
|   Title,
 | |
|   Text,
 | |
|   Divider,
 | |
|   Grid,
 | |
| } from "@mantine/core";
 | |
| import { useForm, zodResolver } from "@mantine/form";
 | |
| import { notifications } from "@mantine/notifications";
 | |
| import { useMutation } from "@tanstack/react-query";
 | |
| import axios from "axios";
 | |
| import { z } from "zod";
 | |
| import { useUserContext } from "../context/user-context";
 | |
| import { Navigate } from "react-router";
 | |
| import { Layout } from "../components/layouts/layout";
 | |
| import { GoogleIcon } from "../icons/google";
 | |
| import { GithubIcon } from "../icons/github";
 | |
| import { OAuthIcon } from "../icons/oauth";
 | |
| import { TailscaleIcon } from "../icons/tailscale";
 | |
| 
 | |
| export const LoginPage = () => {
 | |
|   const queryString = window.location.search;
 | |
|   const params = new URLSearchParams(queryString);
 | |
|   const redirectUri = params.get("redirect_uri");
 | |
| 
 | |
|   const { isLoggedIn, configuredProviders } = useUserContext();
 | |
|   const oauthProviders = configuredProviders.filter(
 | |
|     (value) => value !== "username",
 | |
|   );
 | |
| 
 | |
|   if (isLoggedIn) {
 | |
|     return <Navigate to="/logout" />;
 | |
|   }
 | |
| 
 | |
|   const schema = z.object({
 | |
|     username: z.string(),
 | |
|     password: z.string(),
 | |
|   });
 | |
| 
 | |
|   type FormValues = z.infer<typeof schema>;
 | |
| 
 | |
|   const form = useForm({
 | |
|     mode: "uncontrolled",
 | |
|     initialValues: {
 | |
|       username: "",
 | |
|       password: "",
 | |
|     },
 | |
|     validate: zodResolver(schema),
 | |
|   });
 | |
| 
 | |
|   const loginMutation = useMutation({
 | |
|     mutationFn: (login: FormValues) => {
 | |
|       return axios.post("/api/login", login);
 | |
|     },
 | |
|     onError: () => {
 | |
|       notifications.show({
 | |
|         title: "Failed to login",
 | |
|         message: "Check your username and password",
 | |
|         color: "red",
 | |
|       });
 | |
|     },
 | |
|     onSuccess: () => {
 | |
|       notifications.show({
 | |
|         title: "Logged in",
 | |
|         message: "Welcome back!",
 | |
|         color: "green",
 | |
|       });
 | |
|       setTimeout(() => {
 | |
|         if (redirectUri === "null") {
 | |
|           window.location.replace("/");
 | |
|         } else {
 | |
|           window.location.replace(`/continue?redirect_uri=${redirectUri}`);
 | |
|         }
 | |
|       }, 500);
 | |
|     },
 | |
|   });
 | |
| 
 | |
|   const loginOAuthMutation = useMutation({
 | |
|     mutationFn: (provider: string) => {
 | |
|       return axios.get(
 | |
|         `/api/oauth/url/${provider}?redirect_uri=${redirectUri}`,
 | |
|       );
 | |
|     },
 | |
|     onError: () => {
 | |
|       notifications.show({
 | |
|         title: "Internal error",
 | |
|         message: "Failed to get OAuth URL",
 | |
|         color: "red",
 | |
|       });
 | |
|     },
 | |
|     onSuccess: (data) => {
 | |
|       notifications.show({
 | |
|         title: "Redirecting",
 | |
|         message: "Redirecting to your OAuth provider",
 | |
|         color: "blue",
 | |
|       });
 | |
|       setTimeout(() => {
 | |
|         window.location.href = data.data.url;
 | |
|       }, 500);
 | |
|     },
 | |
|   });
 | |
| 
 | |
|   const handleSubmit = (values: FormValues) => {
 | |
|     loginMutation.mutate(values);
 | |
|   };
 | |
| 
 | |
|   return (
 | |
|     <Layout>
 | |
|       <Title ta="center">Tinyauth</Title>
 | |
|       <Paper shadow="md" p="xl" mt={30} radius="md" withBorder>
 | |
|         {oauthProviders.length > 0 && (
 | |
|           <>
 | |
|             <Text size="lg" fw={500} ta="center">
 | |
|               Welcome back, login with
 | |
|             </Text>
 | |
|             <Grid mb="md" mt="md" align="center" justify="center">
 | |
|               {oauthProviders.includes("google") && (
 | |
|                 <Grid.Col span="content">
 | |
|                   <Button
 | |
|                     radius="xl"
 | |
|                     leftSection={
 | |
|                       <GoogleIcon style={{ width: 14, height: 14 }} />
 | |
|                     }
 | |
|                     variant="default"
 | |
|                     onClick={() => loginOAuthMutation.mutate("google")}
 | |
|                     loading={loginOAuthMutation.isLoading}
 | |
|                   >
 | |
|                     Google
 | |
|                   </Button>
 | |
|                 </Grid.Col>
 | |
|               )}
 | |
|               {oauthProviders.includes("github") && (
 | |
|                 <Grid.Col span="content">
 | |
|                   <Button
 | |
|                     radius="xl"
 | |
|                     leftSection={
 | |
|                       <GithubIcon style={{ width: 14, height: 14 }} />
 | |
|                     }
 | |
|                     variant="default"
 | |
|                     onClick={() => loginOAuthMutation.mutate("github")}
 | |
|                     loading={loginOAuthMutation.isLoading}
 | |
|                   >
 | |
|                     Github
 | |
|                   </Button>
 | |
|                 </Grid.Col>
 | |
|               )}
 | |
|               {oauthProviders.includes("tailscale") && (
 | |
|                 <Grid.Col span="content">
 | |
|                   <Button
 | |
|                     radius="xl"
 | |
|                     leftSection={
 | |
|                       <TailscaleIcon style={{ width: 14, height: 14 }} />
 | |
|                     }
 | |
|                     variant="default"
 | |
|                     onClick={() => loginOAuthMutation.mutate("tailscale")}
 | |
|                     loading={loginOAuthMutation.isLoading}
 | |
|                   >
 | |
|                     Tailscale
 | |
|                   </Button>
 | |
|                 </Grid.Col>
 | |
|               )}
 | |
|               {oauthProviders.includes("generic") && (
 | |
|                 <Grid.Col span="content">
 | |
|                   <Button
 | |
|                     radius="xl"
 | |
|                     leftSection={
 | |
|                       <OAuthIcon style={{ width: 14, height: 14 }} />
 | |
|                     }
 | |
|                     variant="default"
 | |
|                     onClick={() => loginOAuthMutation.mutate("generic")}
 | |
|                     loading={loginOAuthMutation.isLoading}
 | |
|                   >
 | |
|                     Generic
 | |
|                   </Button>
 | |
|                 </Grid.Col>
 | |
|               )}
 | |
|             </Grid>
 | |
|             {configuredProviders.includes("username") && (
 | |
|               <Divider
 | |
|                 label="Or continue with password"
 | |
|                 labelPosition="center"
 | |
|                 my="lg"
 | |
|               />
 | |
|             )}
 | |
|           </>
 | |
|         )}
 | |
|         {configuredProviders.includes("username") && (
 | |
|           <form onSubmit={form.onSubmit(handleSubmit)}>
 | |
|             <TextInput
 | |
|               label="Username"
 | |
|               placeholder="user@example.com"
 | |
|               required
 | |
|               disabled={loginMutation.isLoading}
 | |
|               key={form.key("username")}
 | |
|               {...form.getInputProps("username")}
 | |
|             />
 | |
|             <PasswordInput
 | |
|               label="Password"
 | |
|               placeholder="password"
 | |
|               required
 | |
|               mt="md"
 | |
|               disabled={loginMutation.isLoading}
 | |
|               key={form.key("password")}
 | |
|               {...form.getInputProps("password")}
 | |
|             />
 | |
|             <Button
 | |
|               fullWidth
 | |
|               mt="xl"
 | |
|               type="submit"
 | |
|               loading={loginMutation.isLoading}
 | |
|             >
 | |
|               Login
 | |
|             </Button>
 | |
|           </form>
 | |
|         )}
 | |
|       </Paper>
 | |
|     </Layout>
 | |
|   );
 | |
| };
 | 
