feat: add oauth logic

This commit is contained in:
Stavros
2025-05-12 23:03:14 +03:00
parent 4e91e567b2
commit a488b70bbe
4 changed files with 54 additions and 5 deletions

View File

@@ -0,0 +1,15 @@
import { useCallback, useEffect, useRef } from 'react'
export function useIsMounted(): () => boolean {
const isMounted = useRef(false)
useEffect(() => {
isMounted.current = true
return () => {
isMounted.current = false
}
}, [])
return useCallback(() => isMounted.current, [])
}

View File

@@ -13,9 +13,11 @@ import { OAuthButton } from "@/components/ui/oauth-button";
import { SeperatorWithChildren } from "@/components/ui/separator"; import { SeperatorWithChildren } from "@/components/ui/separator";
import { useAppContext } from "@/context/app-context"; import { useAppContext } from "@/context/app-context";
import { useUserContext } from "@/context/user-context"; import { useUserContext } from "@/context/user-context";
import { useIsMounted } from "@/lib/hooks/use-is-mounted";
import { LoginSchema } from "@/schemas/login-schema"; import { LoginSchema } from "@/schemas/login-schema";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import axios from "axios"; import axios from "axios";
import { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Navigate } from "react-router"; import { Navigate } from "react-router";
import { toast } from "sonner"; import { toast } from "sonner";
@@ -25,7 +27,7 @@ export const LoginPage = () => {
const redirectUri = searchParams.get("redirect_uri"); const redirectUri = searchParams.get("redirect_uri");
const { isLoggedIn } = useUserContext(); const { isLoggedIn } = useUserContext();
const { configuredProviders, title } = useAppContext(); const { configuredProviders, title, oauthAutoRedirect } = useAppContext();
const { t } = useTranslation(); const { t } = useTranslation();
if (isLoggedIn) { if (isLoggedIn) {
@@ -37,6 +39,27 @@ export const LoginPage = () => {
0; 0;
const userAuthConfigured = configuredProviders.includes("username"); const userAuthConfigured = configuredProviders.includes("username");
const isMounted = useIsMounted();
const oauthMutation = useMutation({
mutationFn: (provider: string) => axios.get(`/api/oauth/url/${provider}`),
mutationKey: ["oauth"],
onSuccess: (data) => {
toast.info(t("loginOauthSuccessTitle"), {
description: t("loginOauthSuccessSubtitle"),
});
setTimeout(() => {
window.location.href = data.data.url;
}, 500);
},
onError: () => {
toast.error(t("loginOauthFailTitle"), {
description: t("loginOauthFailSubtitle"),
});
},
});
const loginMutation = useMutation({ const loginMutation = useMutation({
mutationFn: (values: LoginSchema) => axios.post("/api/login", values), mutationFn: (values: LoginSchema) => axios.post("/api/login", values),
mutationKey: ["login"], mutationKey: ["login"],
@@ -63,6 +86,14 @@ export const LoginPage = () => {
}, },
}); });
useEffect(() => {
if (isMounted()) {
if (oauthConfigured && configuredProviders.includes(oauthAutoRedirect)) {
oauthMutation.mutate(oauthAutoRedirect);
}
}
});
return ( return (
<Card className="min-w-xs sm:min-w-sm"> <Card className="min-w-xs sm:min-w-sm">
<CardHeader> <CardHeader>
@@ -81,6 +112,7 @@ export const LoginPage = () => {
title="Google" title="Google"
icon={<GoogleIcon />} icon={<GoogleIcon />}
className="w-full" className="w-full"
onClick={() => oauthMutation.mutate("google")}
/> />
)} )}
{configuredProviders.includes("github") && ( {configuredProviders.includes("github") && (
@@ -88,6 +120,7 @@ export const LoginPage = () => {
title="Github" title="Github"
icon={<GithubIcon />} icon={<GithubIcon />}
className="w-full" className="w-full"
onClick={() => oauthMutation.mutate("github")}
/> />
)} )}
{configuredProviders.includes("generic") && ( {configuredProviders.includes("generic") && (
@@ -95,6 +128,7 @@ export const LoginPage = () => {
title="Generic" title="Generic"
icon={<GenericIcon />} icon={<GenericIcon />}
className="w-full" className="w-full"
onClick={() => oauthMutation.mutate("generic")}
/> />
)} )}
</div> </div>

View File

@@ -16,7 +16,7 @@ import { Navigate } from "react-router";
import { toast } from "sonner"; import { toast } from "sonner";
export const LogoutPage = () => { export const LogoutPage = () => {
const { provider, username, email, isLoggedIn } = useUserContext(); const { provider, username, isLoggedIn } = useUserContext();
const { genericName } = useAppContext(); const { genericName } = useAppContext();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -56,7 +56,7 @@ export const LogoutPage = () => {
code: <code />, code: <code />,
}} }}
values={{ values={{
username: email, username: username,
provider: provider:
provider === "generic" ? genericName : capitalize(provider), provider === "generic" ? genericName : capitalize(provider),
}} }}

View File

@@ -3,8 +3,8 @@ import { z } from "zod";
export const userContextSchema = z.object({ export const userContextSchema = z.object({
isLoggedIn: z.boolean(), isLoggedIn: z.boolean(),
username: z.string(), username: z.string(),
name: z.string(), // name: z.string(), not yet implemented
email: z.string(), // email: z.string(),
provider: z.string(), provider: z.string(),
oauth: z.boolean(), oauth: z.boolean(),
totpPending: z.boolean(), totpPending: z.boolean(),