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();
};