Skip to content

Commit

Permalink
Create sign out button
Browse files Browse the repository at this point in the history
Consume sign out auth api and fix cookie token not showing in the application tab of the browser
  • Loading branch information
Fingertips18 committed Sep 13, 2024
1 parent 86bb5ca commit 84b6981
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 32 deletions.
33 changes: 33 additions & 0 deletions client/src/components/icon-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Loader2, LucideProps } from "lucide-react";

interface IconButtonProps {
icon: React.ForwardRefExoticComponent<
Omit<LucideProps, "ref"> & React.RefAttributes<SVGSVGElement>
>;
size?: number;
onClick: () => void;
disabled?: boolean;
loading?: boolean;
}

const IconButton = ({
icon: Icon,
size = 22,
onClick,
disabled,
loading,
}: IconButtonProps) => {
return (
<button
className="w-10 h-10 rounded-full flex-center transition-colors bg-accent/40
hover:bg-accent hover:drop-shadow-accent-glow disabled:pointer-events-none
disabled:bg-accent/50 disabled:text-foreground/50"
disabled={disabled || loading}
onClick={onClick}
>
{loading ? <Loader2 size={size} /> : <Icon size={size} />}
</button>
);
};

export default IconButton;
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Loader2 } from "lucide-react";

interface ButtonProps {
interface TextButtonProps {
label: string;
onClick?: () => void;
type: "submit" | "reset" | "button" | undefined;
disabled?: boolean;
loading?: boolean;
}

const Button = ({ label, onClick, type, disabled, loading }: ButtonProps) => {
const TextButton = ({
label,
onClick,
type,
disabled,
loading,
}: TextButtonProps) => {
return (
<button
type={type}
Expand All @@ -22,4 +28,4 @@ const Button = ({ label, onClick, type, disabled, loading }: ButtonProps) => {
);
};

export { Button };
export { TextButton as Button };
1 change: 1 addition & 0 deletions client/src/constants/keys.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const SIGNUPKEY = "sign-up";
export const SIGNINKEY = "sign-in";
export const SIGNOUTKEY = "sign-out";
1 change: 1 addition & 0 deletions client/src/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum AppRoutes {
Root = "/",
SignUp = "/sign-up",
SignIn = "/sign-in",
SignOut = "/sign-out",
ForgotPassword = "/forgot-password",
ResetPassword = "/reset-password",
VerifyEmail = "/verify-email",
Expand Down
10 changes: 9 additions & 1 deletion client/src/lib/services/auth-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ export const AuthService = {
signIn: async (signIn: SignInDTO) => {
return await fetch(`${baseURL}${AppRoutes.SignIn}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(signIn),
});
},
signOut: async () => {
return await fetch(`${baseURL}${AppRoutes.SignOut}`, {
method: "POST",
});
},
};
6 changes: 5 additions & 1 deletion client/src/pages/root/_components/header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SignOutButton } from "./sign-out-button";
import { ToggleMode } from "./toggle-mode";
import { Logo } from "./logo";

Expand All @@ -6,7 +7,10 @@ const Header = () => {
<header className="h-14 w-full fixed top-0 bg-secondary/10 backdrop-blur-lg border-b border-secondary/25 shadow-sm px-4 lg:px-0">
<nav className="max-w-screen-lg mx-auto h-full flex-between">
<Logo />
<ToggleMode />
<div className="flex items-center justify-end gap-x-4">
<ToggleMode />
<SignOutButton />
</div>
</nav>
</header>
);
Expand Down
27 changes: 27 additions & 0 deletions client/src/pages/root/_components/header/sign-out-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useMutation } from "@tanstack/react-query";
import { useNavigate } from "react-router-dom";
import { DoorOpen } from "lucide-react";
import { toast } from "sonner";

import { AuthService } from "@/lib/services/auth-service";
import IconButton from "@/components/icon-button";
import { AppRoutes } from "@/constants/routes";
import { SIGNOUTKEY } from "@/constants/keys";

const SignOutButton = () => {
const navigate = useNavigate();

const { mutate, isPending } = useMutation({
mutationKey: [SIGNOUTKEY],
mutationFn: AuthService.signOut,
onSuccess: () => {
toast.success("Signed out successfully");
navigate(AppRoutes.SignIn);
},
onError: ({ message }) => toast.error(message || "Unable to sign out"),
});

return <IconButton onClick={mutate} icon={DoorOpen} loading={isPending} />;
};

export { SignOutButton };
8 changes: 2 additions & 6 deletions client/src/pages/root/_components/header/toggle-mode.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Moon, Sun } from "lucide-react";

import { Theme, useTheme } from "@/lib/hooks/use-theme";
import IconButton from "@/components/icon-button";

const ToggleMode = () => {
const { theme, setTheme } = useTheme();
Expand All @@ -14,12 +15,7 @@ const ToggleMode = () => {
};

return (
<button
className="w-10 h-10 rounded-full flex-center transition-colors bg-accent/40 hover:bg-accent hover:drop-shadow-accent-glow"
onClick={onClick}
>
{theme === Theme.Light ? <Sun size={22} /> : <Moon size={22} />}
</button>
<IconButton onClick={onClick} icon={theme === Theme.Light ? Sun : Moon} />
);
};

Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/sign-in/_components/sign-in-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { SIGNININPUTS } from "@/constants/collections";
import { SignInDTO } from "@/lib/DTO/sign-in.dto";
import { AppRoutes } from "@/constants/routes";
import { SIGNINKEY } from "@/constants/keys";
import { Button } from "@/components/button";
import { Button } from "@/components/text-button";
import { Input } from "@/components/input";

const SignInForm = () => {
Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/sign-up/_components/sign-up-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { SIGNUPINPUTS } from "@/constants/collections";
import { SignUpDTO } from "@/lib/DTO/sign-up.dto";
import { AppRoutes } from "@/constants/routes";
import { SIGNUPKEY } from "@/constants/keys";
import { Button } from "@/components/button";
import { Button } from "@/components/text-button";
import { Input } from "@/components/input";

const SignUpForm = () => {
Expand Down
32 changes: 13 additions & 19 deletions controllers/auth_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,31 +76,25 @@ func SignIn(c fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid credentials"})
}

token, err := utils.GenerateJWTToken(user.ID.String(), user.Username)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}

utils.SetCookieToken(c, token)
if user.IsVerified {
token, err := utils.GenerateJWTToken(user.ID.String(), user.Username)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}

user.LastSignedIn = time.Now()
utils.SetCookieToken(c, token)

if time.Now().After(*user.VerificationTokenExpiration) {
verificationToken := rand.Intn(9000) + 1000
tokenString := strconv.Itoa(verificationToken)
user.VerificationToken = &tokenString
user.LastSignedIn = time.Now()

if err := utils.SendEmailVerification(user.Email, user.Username, *user.VerificationToken); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err})
res = database.Instance.Save(&user)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Unable to save sign in credentials"})
}
}

res = database.Instance.Save(&user)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Unable to save sign in credentials"})
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Sign in successful", "user": user})
} else {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "User is not verified"})
}

return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Sign in successful", "user": user})
}

func SignOut(c fiber.Ctx) error {
Expand Down

0 comments on commit 84b6981

Please sign in to comment.