add sign-up page

This commit is contained in:
2024-08-09 16:53:37 +02:00
parent 750578b787
commit da0cccfb0e
6 changed files with 87 additions and 18 deletions

View File

@@ -10,7 +10,8 @@ import { useMeQuery } from "@/services";
export const AuthRedirect: FC<{ children: ReactNode }> = ({ children }) => {
const router = useRouter();
const { data: user, isLoading } = useMeQuery();
const isAuthPage = router.pathname === "/login";
const isAuthPage =
router.pathname === "/login" || router.pathname === "/sign-up";
useEffect(() => {
if (!isLoading && !user && !isAuthPage) {

49
src/pages/sign-up.tsx Normal file
View File

@@ -0,0 +1,49 @@
import type { FormEvent } from "react";
import React from "react";
import type { NextPage } from "next";
import { Button, Input } from "@/components";
import { useSignUpMutation } from "@/services";
const Login: NextPage = () => {
const { mutate: signUp } = useSignUpMutation();
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const values = Object.fromEntries(formData) as any;
signUp(values);
};
return (
<div className={"flex h-screen items-center justify-center"}>
<form
className={"flex w-96 flex-col gap-3 rounded-md border p-6"}
onSubmit={handleSubmit}
>
<h1 className={"text-2xl font-bold"}>Sign up</h1>
<label className={"flex flex-col gap-1"}>
Username (optional)
<Input name={"username"} type="text" />
</label>
<label className={"flex flex-col gap-1"}>
Email
<Input name={"email"} type="email" />
</label>
<label className={"flex flex-col gap-1"}>
Password
<Input type="password" name={"password"} />
</label>
<Button className={"w-full"} type={"submit"}>
Login
</Button>
</form>
</div>
);
};
export default Login;

View File

@@ -3,6 +3,7 @@ import type {
LogoutResponse,
MeResponse,
PostLoginArgs,
PostSignUpArgs,
} from "../todolists";
import { handleError } from "@/helpers";
@@ -12,7 +13,7 @@ export const AuthApi = {
async login(args: PostLoginArgs) {
const res = await todolistApiInstance.post<LoginResponse>(
"/auth/login",
args
args,
);
localStorage.setItem("accessToken", res.data.accessToken);
@@ -20,7 +21,11 @@ export const AuthApi = {
return res.data;
},
async signUp(args: PostSignUpArgs) {
const res = await todolistApiInstance.post<any>("/auth/sign-up", args);
return res.data;
},
async logout() {
const res = await todolistApiInstance.delete<LogoutResponse>("/auth/login");

View File

@@ -38,3 +38,9 @@ export const useLogoutMutation = () => {
},
});
};
export const useSignUpMutation = () => {
return useMutation({
mutationFn: AuthApi.signUp,
});
};

View File

@@ -7,7 +7,7 @@ const mutex = new Mutex();
let refreshedAt: number | null = null;
export const todolistApiInstance = axios.create({
baseURL: "http://localhost:3000"
baseURL: "http://localhost:3000",
});
async function refreshAccessToken(): Promise<string> {
@@ -19,9 +19,9 @@ async function refreshAccessToken(): Promise<string> {
{},
{
headers: {
Authorization: `Bearer ${localStorage.getItem("refreshToken")}`
}
}
Authorization: `Bearer ${localStorage.getItem("refreshToken")}`,
},
},
);
localStorage.setItem("accessToken", res.data.accessToken);
@@ -42,14 +42,15 @@ todolistApiInstance.interceptors.request.use(
return config;
},
(error) => Promise.reject(error)
(error) => Promise.reject(error),
);
todolistApiInstance.interceptors.response.use(
(response) => response,
async (error) => {
if(error?.response?.request?.responseURL?.includes("refresh-token")){
return }
if (error?.response?.request?.responseURL?.includes("refresh-token")) {
return;
}
await mutex.waitForUnlock();
const originalRequest = error.config;
// Check for a 401 response and if this request hasn't been retried yet
@@ -61,9 +62,7 @@ todolistApiInstance.interceptors.response.use(
if (refreshedAt && refreshedAt + 60000 > new Date().getTime()) {
// If the token has been refreshed within the last minute, use the refreshed token
originalRequest.headers[
"Authorization"
] = `Bearer ${localStorage.getItem("accessToken")}`;
originalRequest.headers.Authorization = `Bearer ${localStorage.getItem("accessToken")}`;
release();
return todolistApiInstance(originalRequest);
@@ -72,11 +71,14 @@ todolistApiInstance.interceptors.response.use(
try {
const newAccessToken = await refreshAccessToken();
originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return todolistApiInstance(originalRequest); // Retry the original request with the new token
} catch (error) {
router.push("/login");
console.log(window.location.pathname);
if (!["/login", "/sign-up"].includes(window.location.pathname)) {
router.push("/login");
}
} finally {
release();
}
@@ -84,12 +86,12 @@ todolistApiInstance.interceptors.response.use(
await mutex.waitForUnlock();
const newAccessToken = localStorage.getItem("accessToken");
originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return todolistApiInstance(originalRequest); // Retry the original request with the new token
}
}
return Promise.reject(error);
}
},
);

View File

@@ -29,6 +29,12 @@ export type PostLoginArgs = {
rememberMe: boolean;
};
export type PostSignUpArgs = {
email: string;
password: string;
username?: string;
};
export type LoginResponseData = {
accessToken: string;
refreshToken: string;
@@ -51,8 +57,8 @@ export type CreateTodolistResponseData = {
};
export type CreateTaskResponse = Task;
export type DeleteTaskResponse = void;
export type UpdateTaskResponse = void;
export type DeleteTaskResponse = undefined;
export type UpdateTaskResponse = undefined;
export enum TaskStatus {
OPEN = "OPEN",