diff --git a/apps/client/src/scenes/Account/Login/Login.tsx b/apps/client/src/scenes/Account/Login/Login.tsx index 7850f2e8..da72fa24 100644 --- a/apps/client/src/scenes/Account/Login/Login.tsx +++ b/apps/client/src/scenes/Account/Login/Login.tsx @@ -1,21 +1,39 @@ -import { useEffect } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; +import { Checkbox } from "@mui/material"; +import clsx from "clsx"; import Text from "../../../components/Text"; import { selectUser } from "../../../services/redux/modules/user/selector"; import { getSpotifyLogUrl } from "../../../services/tools"; import s from "../index.module.css"; +import { LocalStorage, REMEMBER_ME_KEY } from "../../../services/storage"; export default function Login() { const navigate = useNavigate(); const user = useSelector(selectUser); + const [rememberMe, setRememberMe] = useState( + LocalStorage.get(REMEMBER_ME_KEY) === "true", + ); useEffect(() => { if (user) { navigate("/"); + } else if (LocalStorage.get(REMEMBER_ME_KEY) === "true") { + window.location.href = getSpotifyLogUrl(); } }, [navigate, user]); + const handleRememberMeClick = useCallback(async () => { + const newRememberMe = !rememberMe; + setRememberMe(newRememberMe); + if (newRememberMe) { + LocalStorage.set(REMEMBER_ME_KEY, "true"); + } else { + LocalStorage.delete(REMEMBER_ME_KEY); + } + }, [rememberMe]); + return (
@@ -29,6 +47,21 @@ export default function Login() { Login
+
+ +
); } diff --git a/apps/client/src/scenes/Account/index.module.css b/apps/client/src/scenes/Account/index.module.css index 493d7096..8599b0b9 100644 --- a/apps/client/src/scenes/Account/index.module.css +++ b/apps/client/src/scenes/Account/index.module.css @@ -27,6 +27,18 @@ color: white; height: 40px; border-radius: 20px; - padding: 0px 24px; + padding: 0px 58px; font-weight: bold; } + +.rememberMe { + margin-top: 8px; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; +} + +.check { + padding: 0px !important; +} diff --git a/apps/client/src/scenes/Logout/Logout.tsx b/apps/client/src/scenes/Logout/Logout.tsx index b6c6c959..3df0cdc9 100644 --- a/apps/client/src/scenes/Logout/Logout.tsx +++ b/apps/client/src/scenes/Logout/Logout.tsx @@ -1,10 +1,11 @@ import { CircularProgress } from "@mui/material"; -import React, { useEffect } from "react"; +import { useEffect } from "react"; import { useNavigate } from "react-router-dom"; import Text from "../../components/Text"; import { api } from "../../services/apis/api"; import { logout } from "../../services/redux/modules/user/reducer"; import { useAppDispatch } from "../../services/redux/tools"; +import { LocalStorage, REMEMBER_ME_KEY } from "../../services/storage"; export default function Logout() { const navigate = useNavigate(); @@ -18,9 +19,10 @@ export default function Logout() { } catch (e) { console.error(e); } + LocalStorage.delete(REMEMBER_ME_KEY); navigate("/login"); } - dologout(); + dologout().catch(console.error); }, [navigate, dispatch]); return ( diff --git a/apps/client/src/services/apis/api.ts b/apps/client/src/services/apis/api.ts index 0d196081..3d1f7084 100644 --- a/apps/client/src/services/apis/api.ts +++ b/apps/client/src/services/apis/api.ts @@ -26,6 +26,17 @@ const axios = Axios.create({ withCredentials: true, }); +// Add a response interceptor +axios.interceptors.response.use( + response => response, + error => { + if (error.response && error.response.status === 401) { + window.location.pathname = "/login"; + } + return Promise.reject(error); + } +); + // Adds latency to requests without having to use chrome latency // const get = (url: string, params: Record = {}): Promise<{ data: T }> => // new Promise((res, rej) => { diff --git a/apps/client/src/services/storage.ts b/apps/client/src/services/storage.ts new file mode 100644 index 00000000..7609e2ad --- /dev/null +++ b/apps/client/src/services/storage.ts @@ -0,0 +1,19 @@ +interface KeyValueStorage { + get(key: string): string | undefined; + set(key: string, value: string): void; + delete(key: string): void; +} + +export const LocalStorage: KeyValueStorage = { + get(key: string) { + return localStorage.getItem(key) ?? undefined; + }, + set(key: string, value: string) { + localStorage.setItem(key, value); + }, + delete(key: string) { + localStorage.removeItem(key); + }, +}; + +export const REMEMBER_ME_KEY = "remember-me"; diff --git a/apps/server/src/tools/middleware.ts b/apps/server/src/tools/middleware.ts index 6493d0ab..5bac48c8 100644 --- a/apps/server/src/tools/middleware.ts +++ b/apps/server/src/tools/middleware.ts @@ -133,10 +133,15 @@ export const optionalLogged = async ( export const admin = (req: Request, res: Response, next: NextFunction) => { const { user } = req as LoggedRequest; - if (!user || !user.admin) { + if (!user) { res.status(401).end(); return; } + + if (!user.admin) { + res.status(403).end(); + return; + } next(); };