add user authentication
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
import { Providers } from "@/app/providers";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "archive.local",
|
||||
@@ -9,11 +11,13 @@ export const metadata: Metadata = {
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className="h-full">
|
||||
<body className="h-full flex flex-col">{children}</body>
|
||||
</html>
|
||||
<Providers>
|
||||
<html lang="en" className="h-full">
|
||||
<body className="h-full flex flex-col">{children}</body>
|
||||
</html>
|
||||
</Providers>
|
||||
);
|
||||
}
|
||||
|
||||
63
web/src/app/login/LoginForm.tsx
Normal file
63
web/src/app/login/LoginForm.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { LoginData, useLoginMutation } from "@/api/auth/useLoginMutation";
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { Input } from "@/components/ui/Input";
|
||||
import dayjs from "dayjs";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
export const LoginForm = () => {
|
||||
const router = useRouter();
|
||||
const mutation = useLoginMutation();
|
||||
|
||||
const form = useForm<LoginData>({
|
||||
defaultValues: {
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = form.handleSubmit((data) => {
|
||||
mutation.mutate(data, {
|
||||
onSuccess(resp) {
|
||||
const expDate = dayjs().add(7, "days").toString();
|
||||
document.cookie = `access_token=${resp.access_token}; SameSite=None; Secure; Expires=${expDate}; Path=/`;
|
||||
router.push("/");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
className="max-w-[500px] w-full bg-white rounded-lg p-4 shadow-2xl"
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="johndoe@gmail.com"
|
||||
label="Email"
|
||||
{...form.register("email", { required: true })}
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="******"
|
||||
label="Password"
|
||||
{...form.register("password", { required: true })}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full mt-6">
|
||||
Sign in
|
||||
</Button>
|
||||
<p className="mt-2 text-sm">
|
||||
Don't have an account? Create one here{" "}
|
||||
<Link href="/register" className="underline hover:no-underline">
|
||||
here
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
18
web/src/app/login/page.tsx
Normal file
18
web/src/app/login/page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { LoginForm } from "@/app/login/LoginForm";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function Login() {
|
||||
const c = await cookies();
|
||||
const token = c.get("access_token");
|
||||
if (token) {
|
||||
throw redirect("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col justify-center items-center bg-neutral-100">
|
||||
<h2 className="mb-10 text-5xl font-medium">archive.local</h2>
|
||||
<LoginForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,14 @@
|
||||
export default function Home() {
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function Home() {
|
||||
const c = await cookies();
|
||||
const token = c.get("access_token");
|
||||
|
||||
if (!token) {
|
||||
throw redirect("/login");
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>archive.local</h1>
|
||||
|
||||
21
web/src/app/providers.tsx
Normal file
21
web/src/app/providers.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactNode, useState } from "react";
|
||||
|
||||
export const Providers = ({ children }: { children: ReactNode }) => {
|
||||
const [queryClient] = useState(
|
||||
() =>
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
66
web/src/app/register/RegisterForm.tsx
Normal file
66
web/src/app/register/RegisterForm.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
RegisterData,
|
||||
useRegisterMutation,
|
||||
} from "@/api/auth/useRegisterMutation";
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { Input } from "@/components/ui/Input";
|
||||
import dayjs from "dayjs";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
export const RegisterForm = () => {
|
||||
const router = useRouter();
|
||||
const mutation = useRegisterMutation();
|
||||
|
||||
const form = useForm<RegisterData>({
|
||||
defaultValues: {
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = form.handleSubmit((data) => {
|
||||
mutation.mutate(data, {
|
||||
onSuccess(resp) {
|
||||
const expDate = dayjs().add(7, "days").toString();
|
||||
document.cookie = `access_token=${resp.access_token}; SameSite=None; Secure; Expires=${expDate}; Path=/`;
|
||||
router.push("/");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
className="max-w-[500px] w-full bg-white rounded-lg p-4 shadow-2xl"
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="johndoe@gmail.com"
|
||||
label="Email"
|
||||
{...form.register("email", { required: true })}
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="******"
|
||||
label="Password"
|
||||
{...form.register("password", { required: true })}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full mt-6">
|
||||
Sign up
|
||||
</Button>
|
||||
<p className="mt-2 text-sm">
|
||||
Already have an account? Login{" "}
|
||||
<Link href="/login" className="underline hover:no-underline">
|
||||
here
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
18
web/src/app/register/page.tsx
Normal file
18
web/src/app/register/page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { RegisterForm } from "@/app/register/RegisterForm";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function Register() {
|
||||
const c = await cookies();
|
||||
const token = c.get("access_token");
|
||||
if (token) {
|
||||
throw redirect("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col justify-center items-center bg-neutral-100">
|
||||
<h2 className="mb-10 text-5xl font-medium">archive.local</h2>
|
||||
<RegisterForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user