Skip to content

Commit

Permalink
feat: finalized auth
Browse files Browse the repository at this point in the history
Signed-off-by: rajput-hemant <[email protected]>
  • Loading branch information
rajput-hemant committed Dec 11, 2023
1 parent e8ae837 commit d19dfad
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 74 deletions.
55 changes: 32 additions & 23 deletions app/(auth)/components/auth-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,23 @@ import {
type FormData = z.infer<typeof authSchema>;
type AuthFormProps = { mode: "login" | "signup" | "reset" };

const defaultValues: FormData = {
email: "",
password: "",
confirmPassword: "",
};

export function AuthForm({ mode }: AuthFormProps) {
const [isEmailMode, setIsEmailMode] = React.useState(true);
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [isPassVisible, setIsPassVisible] = React.useState(false);
const [isConfirmPassVisible, setIsConfirmPassVisible] = React.useState(false);
const [oauthLoading, setOauthLoading] = React.useState<"google" | "github">();

const form = useForm<FormData>({ resolver: zodResolver(authSchema) });
const form = useForm<FormData>({
resolver: zodResolver(authSchema),
defaultValues,
mode: "onChange",
});

function signInToaster() {
toast({
Expand All @@ -55,35 +65,32 @@ export function AuthForm({ mode }: AuthFormProps) {
});
}

function onSubmit({ username, email, password }: FormData) {
setIsSubmitting(true);
async function onSubmit({ email, username, password }: FormData) {
try {
if (mode === "login") {
signInToaster();
signIn("credentials", { username, email, password });
await signIn("credentials", { username, email, password });
} else if (mode === "signup") {
toast({
title: "Creating account...",
description: "Please wait while we create your account",
});
createNewAccount({ mode: "email", email, password });
await createNewAccount({ mode: "email", email: email!, password });
} else {
toast({
title: "Resetting password...",
description: "Please wait while we reset your password",
});
resetPassword({ mode: "email", email, password });
await resetPassword({ mode: "email", email: email!, password });
}
} catch (error) {
const err = error as Error;
console.error(err.message);
}

setIsSubmitting(false);
}

async function googleSignInHandler() {
setIsSubmitting(true);
setOauthLoading("google");

try {
signInToaster();
Expand All @@ -93,11 +100,11 @@ export function AuthForm({ mode }: AuthFormProps) {
console.error(err.message);
}

setIsSubmitting(false);
setOauthLoading(undefined);
}

async function githubSignInHandler() {
setIsSubmitting(true);
setOauthLoading("github");

try {
signInToaster();
Expand All @@ -107,14 +114,16 @@ export function AuthForm({ mode }: AuthFormProps) {
console.error(err.message);
}

setIsSubmitting(false);
setOauthLoading(undefined);
}

const toggleCredentialMode = () => setIsEmailMode(!isEmailMode);
const togglePassVisibility = () => setIsPassVisible(!isPassVisible);
const toggleConfirmPassVisibility = () =>
setIsConfirmPassVisible(!isConfirmPassVisible);

const isFormDisabled = !!oauthLoading || form.formState.isSubmitting;

return (
<Form {...form}>
<Link
Expand All @@ -140,7 +149,7 @@ export function AuthForm({ mode }: AuthFormProps) {
<div className="relative">
<Input
type={isEmailMode ? "email" : "text"}
disabled={isSubmitting}
disabled={isFormDisabled}
placeholder={isEmailMode ? "[email protected]" : "@username"}
className={cn("shadow-sm", mode === "login" && "pr-8")}
{...field}
Expand Down Expand Up @@ -184,7 +193,7 @@ export function AuthForm({ mode }: AuthFormProps) {
<div className="relative">
<Input
type={isPassVisible ? "text" : "password"}
disabled={isSubmitting}
disabled={isFormDisabled}
placeholder="••••••••••"
className="pr-8 shadow-sm"
{...field}
Expand Down Expand Up @@ -227,7 +236,7 @@ export function AuthForm({ mode }: AuthFormProps) {
<div className="relative">
<Input
type={isConfirmPassVisible ? "text" : "password"}
disabled={isSubmitting}
disabled={isFormDisabled}
placeholder="••••••••••"
className="pr-8 shadow-sm"
{...field}
Expand Down Expand Up @@ -264,10 +273,10 @@ export function AuthForm({ mode }: AuthFormProps) {

<Button
type="submit"
disabled={isSubmitting}
disabled={isFormDisabled}
className="w-full shadow-md"
>
{isSubmitting ? (
{form.formState.isSubmitting ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<>
Expand All @@ -285,7 +294,7 @@ export function AuthForm({ mode }: AuthFormProps) {
</>
)}
{mode === "reset" && "Reset Password"}
{mode === "login" && isEmailMode ? "Login with Email" : "Login"}
{mode === "login" && (isEmailMode ? "Login with Email" : "Login")}
{mode === "signup" && "Sign Up with Email"}
</Button>
</form>
Expand All @@ -307,10 +316,10 @@ export function AuthForm({ mode }: AuthFormProps) {
<div className="mt-6 flex w-full flex-col space-y-2 text-white">
<Button
onClick={googleSignInHandler}
disabled={isSubmitting}
disabled={isFormDisabled}
className="w-full shadow-md"
>
{isSubmitting ? (
{oauthLoading === "google" ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Google className="mr-2 h-4 w-4" />
Expand All @@ -320,10 +329,10 @@ export function AuthForm({ mode }: AuthFormProps) {

<Button
onClick={githubSignInHandler}
disabled={isSubmitting}
disabled={isFormDisabled}
className="w-full shadow-md"
>
{isSubmitting ? (
{oauthLoading === "github" ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<GitHub className="mr-2 h-4 w-4" />
Expand Down
28 changes: 0 additions & 28 deletions app/(auth)/components/auth-mode-toggle.tsx

This file was deleted.

1 change: 1 addition & 0 deletions app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default function AuthLayout({ children }: React.PropsWithChildren) {

<div className="m-auto">
<Image
priority
src="/illustrations/success.svg"
alt="Get started"
width={500}
Expand Down
1 change: 1 addition & 0 deletions app/(lobby)/components/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export function Hero() {
</div>

<Image
priority
src="/illustrations/home-office.svg"
alt="Home Office"
width={500}
Expand Down
9 changes: 9 additions & 0 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function DashBoardPage() {
return (
<div className="flex min-h-screen items-center justify-center">
<h1 className="mt-4 text-center font-heading text-4xl font-bold [text-shadow:_0_4px_0_#e1e1e1] dark:bg-gradient-to-br dark:from-foreground dark:to-gray-500 dark:bg-clip-text dark:text-transparent dark:[text-shadow:none] md:text-7xl">
Dashboard Page
</h1>
</div>
);
}
22 changes: 15 additions & 7 deletions lib/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ export const authSchema = z
.string()
.min(1, "Username is Required")
.regex(/^(?=.{8,15}$)/, "Username must be 8-15 characters long.")
.regex(/^[a-zA-Z0-9]+$/, "Username must be alphanumeric."),
.regex(/^[a-zA-Z0-9]+$/, "Username must be alphanumeric.")
// TODO: conditionally add optional if email is present
.optional(),
email: z
.string()
.min(1, "Email is Required")
.email("Please enter a valid email"),
.email("Please enter a valid email")
// TODO: conditionally add optional if username is present
.optional(),
password: z
.string()
.min(1, "Password is Required")
Expand All @@ -29,9 +33,13 @@ export const authSchema = z
"Password must contain at least one special character."
)
.min(8, "Password must be at least 8 characters long."),
confirmPassword: z.string(),
confirmPassword: z.string().optional(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords do not match",
path: ["confirmPassword"],
});
.refine(
({ password, confirmPassword }) =>
!confirmPassword || password === confirmPassword,
{
message: "Passwords do not match.",
path: ["confirmPassword"],
}
);
34 changes: 18 additions & 16 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,7 @@ export default withAuth(
* Rate limiting middleware
* -----------------------------------------------------------------------------------------------*/

if (
env.ENABLE_RATE_LIMITING &&
env.NODE_ENV === "production" &&
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
!req.nextUrl.pathname.match(
/^\/(api|_next\/static|_next\/image|favicon\.ico)/
)
) {
if (env.ENABLE_RATE_LIMITING && env.NODE_ENV === "production") {
const id = req.ip ?? "anonymous";
const { limit, pending, remaining, reset, success } =
await ratelimit.limit(id);
Expand Down Expand Up @@ -69,8 +56,9 @@ export default withAuth(
const isAuthPage = (
["/login", "/signup", "/reset-password"] as Route[]
).some((route) => req.nextUrl.pathname.startsWith(route));
const isHomePage = req.nextUrl.pathname === "/";

if (isAuthPage) {
if (isAuthPage || isHomePage) {
if (isAuth) {
return NextResponse.redirect(new URL("/dashboard", req.url));
}
Expand Down Expand Up @@ -102,5 +90,19 @@ export default withAuth(
);

export const config = {
matcher: ["/login", "/signup", "/reset-password", "/dashboard/:path*"],
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
"/((?!api|_next/static|_next/image|favicon.ico).*)",

"/login",
"/signup",
"/reset-password",
"/dashboard/:path*",
],
};

0 comments on commit d19dfad

Please sign in to comment.