From 4a321e36092b3576c20cffe666ed2540233ca03e Mon Sep 17 00:00:00 2001 From: subnub <44621867+subnub@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:05:37 -0500 Subject: [PATCH 1/3] added access token refresher, renamed files --- .gitignore | 1 + backend/config/.env.test.example | 23 +++++ backend/key/get-key.ts | 10 +- backend/middleware/authRefresh.ts | 5 - backend/webUI/startSetupServer.ts | 95 ------------------- src/api/filesAPI.ts | 2 +- src/api/foldersAPI.ts | 2 +- src/api/{user.ts => userAPI.ts} | 9 ++ src/components/LoginPage/LoginPage.tsx | 2 +- .../ResetPasswordPage/ResetPasswordPage.tsx | 2 +- .../SettingsPage/SettingsAccountSection.tsx | 2 +- .../SettingsChangePasswordPopup.tsx | 2 +- src/components/SettingsPage/SettingsPage.tsx | 2 +- .../VerifyEmailPage/VerifyEmailPage.tsx | 2 +- src/hooks/user.ts | 26 +++++ src/reducers/user.ts | 7 +- src/routers/AppRouter.jsx | 2 + 17 files changed, 82 insertions(+), 112 deletions(-) create mode 100644 backend/config/.env.test.example delete mode 100644 backend/webUI/startSetupServer.ts rename src/api/{user.ts => userAPI.ts} (91%) create mode 100644 src/hooks/user.ts diff --git a/.gitignore b/.gitignore index 27c291b..54d93e6 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ config/test.env config/.env.development config/.env.test config/.env.production +config/.env.test .env.development .env.test .env.production diff --git a/backend/config/.env.test.example b/backend/config/.env.test.example new file mode 100644 index 0000000..c3ef179 --- /dev/null +++ b/backend/config/.env.test.example @@ -0,0 +1,23 @@ +MONGODB_URL= +DB_TYPE= +HTTP_PORT= +HTTPS_PORT= +PASSWORD_ACCESS= +PASSWORD_REFRESH= +PASSWORD_COOKIE= +_SECURE_COOKIES= +FS_DIRECTORY= +KEY= +EMAIL_VERIFICATION= +EMAIL_DOMAIN= +EMAIL_ADDRESS= +EMAIL_API_KEY= +EMAIL_HOST= +REMOTE_URL= +S3_ID= +S3_KEY= +S3_BUCKET= +URL= +TEMP_DIRECTORY= +VIDEO_THUMBNAILS_ENABLED= +TEMP_VIDEO_THUMBNAIL_LIMIT= \ No newline at end of file diff --git a/backend/key/get-key.ts b/backend/key/get-key.ts index a4cb1cc..68fffbe 100644 --- a/backend/key/get-key.ts +++ b/backend/key/get-key.ts @@ -3,11 +3,15 @@ import crypto from "crypto"; import getKeyFromTerminal from "../utils/getKeyFromTerminal"; const getKey = async () => { - if (process.env.KEY || process.env.NODE_ENV === "development") { + if ( + process.env.KEY || + process.env.NODE_ENV === "development" || + process.env.NODE_ENV === "test" + ) { const password = process.env.KEY; if (!password) { - console.log("Key is required for development server"); - throw new Error("Key is required for development server"); + console.log(`Key is required for ${process.env.NODE_ENV} server`); + throw new Error(`Key is required for ${process.env.NODE_ENV} server`); } env.key = crypto.createHash("md5").update(password).digest("hex"); diff --git a/backend/middleware/authRefresh.ts b/backend/middleware/authRefresh.ts index 9857eb6..8e2e74a 100644 --- a/backend/middleware/authRefresh.ts +++ b/backend/middleware/authRefresh.ts @@ -77,11 +77,6 @@ const authRefresh = async ( } } - if (!tokenFound) { - console.log("token not found", encryptedToken); - console.log("token list", user.tokens); - } - if (!tokenFound) throw new Error("Refresh Token Not Found"); req.user = user; diff --git a/backend/webUI/startSetupServer.ts b/backend/webUI/startSetupServer.ts deleted file mode 100644 index 1160239..0000000 --- a/backend/webUI/startSetupServer.ts +++ /dev/null @@ -1,95 +0,0 @@ -import express, { Request, Response } from "express"; -import http from "http"; -import path from "path"; -import bodyParser from "body-parser"; -import fs from "fs"; -const app = express(); - -const getWebUIKey = () => { - console.log("Starting Server...\n"); - - const publicPath = path.join(__dirname, "..", "..", "webUISetup"); - - return new Promise((resolve, reject) => { - app.use(express.static(publicPath)); - app.use(express.json()); - app.use(bodyParser.json({ limit: "50mb" })); - app.use( - bodyParser.urlencoded({ - limit: "50mb", - extended: true, - parameterLimit: 50000, - }) - ); - - app.post("/submit", async (req: Request, res: Response) => { - console.log("Response"); - await createConfigFiles(req.body.clientObj, req.body.serverObj); - - res.send(); - }); - - app.get("*", (req: Request, res: Response) => { - res.sendFile(path.join(publicPath, "index.html")); - }); - - const port = process.env.HTTP_PORT || process.env.PORT || "3000"; - const url = process.env.DOCKER ? undefined : "localhost"; - - const server = http.createServer(app) as any; - - server.listen(port, () => { - console.log( - `\nPlease navigate to http://localhost:${port} to enter setup details\n` - ); - }); - }); -}; - -const awaitcreateDir = (path: any) => { - return new Promise((resolve, reject) => { - fs.mkdir(path, () => { - resolve(); - }); - }); -}; - -const awaitWriteFile = (path: string, data: string) => { - return new Promise((resolve, reject) => { - fs.writeFile(path, data, async (err: any) => { - if (err) { - console.log("file write error", err); - reject(); - } - - resolve(); - }); - }); -}; - -const createConfigFiles = async (clientObj: any, serverObj: any) => { - await awaitcreateDir("./config"); - - let totalClientString = ""; - let totalServerString = ""; - - for (let currentKey in clientObj) { - totalClientString += `${currentKey}=${clientObj[currentKey]}\n`; - } - - for (let currentKey in serverObj) { - totalServerString += `${currentKey}=${serverObj[currentKey]}\n`; - } - - if (serverObj.DOCKER) { - const combinedStrings = totalClientString + totalServerString; - await awaitWriteFile("./docker-variables.env", combinedStrings); - } else { - await awaitWriteFile("./.env.production", totalClientString); - await awaitWriteFile("./config/prod.env", totalServerString); - } - - console.log("File(s) Created"); -}; - -getWebUIKey(); diff --git a/src/api/filesAPI.ts b/src/api/filesAPI.ts index d56d0c4..31b3fc5 100644 --- a/src/api/filesAPI.ts +++ b/src/api/filesAPI.ts @@ -1,6 +1,6 @@ import { QueryFunctionContext } from "react-query"; import axios from "../axiosInterceptor"; -import { getUserToken } from "./user"; +import { getUserToken } from "./userAPI"; import getBackendURL from "../utils/getBackendURL"; interface QueryKeyParams { diff --git a/src/api/foldersAPI.ts b/src/api/foldersAPI.ts index 4be3e1f..c1ef715 100644 --- a/src/api/foldersAPI.ts +++ b/src/api/foldersAPI.ts @@ -1,6 +1,6 @@ import { QueryFunctionContext } from "react-query"; import axios from "../axiosInterceptor"; -import { getUserToken } from "./user"; +import { getUserToken } from "./userAPI"; import getBackendURL from "../utils/getBackendURL"; interface QueryKeyParams { diff --git a/src/api/user.ts b/src/api/userAPI.ts similarity index 91% rename from src/api/user.ts rename to src/api/userAPI.ts index 0c6e9a8..6b8e876 100644 --- a/src/api/user.ts +++ b/src/api/userAPI.ts @@ -45,6 +45,15 @@ export const logoutAllAPI = async () => { return response.data; }; +export const getAccessToken = async (uuid: string) => { + const response = await axios.post("/user-service/get-token", undefined, { + headers: { + uuid, + }, + }); + return response.data; +}; + // PATCH export const changePasswordAPI = async ( diff --git a/src/components/LoginPage/LoginPage.tsx b/src/components/LoginPage/LoginPage.tsx index 590a2d3..26804c7 100755 --- a/src/components/LoginPage/LoginPage.tsx +++ b/src/components/LoginPage/LoginPage.tsx @@ -4,7 +4,7 @@ import { getUserAPI, loginAPI, sendPasswordResetAPI, -} from "../../api/user"; +} from "../../api/userAPI"; import { useLocation, useNavigate } from "react-router-dom"; import { setUser } from "../../reducers/user"; import { useAppDispatch } from "../../hooks/store"; diff --git a/src/components/ResetPasswordPage/ResetPasswordPage.tsx b/src/components/ResetPasswordPage/ResetPasswordPage.tsx index c119055..a00576b 100644 --- a/src/components/ResetPasswordPage/ResetPasswordPage.tsx +++ b/src/components/ResetPasswordPage/ResetPasswordPage.tsx @@ -2,7 +2,7 @@ import { useNavigate, useParams } from "react-router-dom"; import Spinner from "../Spinner/Spinner"; import { ToastContainer, toast } from "react-toastify"; import { useState } from "react"; -import { resetPasswordAPI } from "../../api/user"; +import { resetPasswordAPI } from "../../api/userAPI"; import AlertIcon from "../../icons/AlertIcon"; const ResetPasswordPage = () => { diff --git a/src/components/SettingsPage/SettingsAccountSection.tsx b/src/components/SettingsPage/SettingsAccountSection.tsx index 11e53ef..ca8c92f 100644 --- a/src/components/SettingsPage/SettingsAccountSection.tsx +++ b/src/components/SettingsPage/SettingsAccountSection.tsx @@ -1,7 +1,7 @@ import React, { useRef, useState } from "react"; import SettingsChangePasswordPopup from "./SettingsChangePasswordPopup"; import { toast } from "react-toastify"; -import { logoutAPI, resendVerifyEmailAPI } from "../../api/user"; +import { logoutAPI, resendVerifyEmailAPI } from "../../api/userAPI"; import Swal from "sweetalert2"; import { useNavigate } from "react-router-dom"; diff --git a/src/components/SettingsPage/SettingsChangePasswordPopup.tsx b/src/components/SettingsPage/SettingsChangePasswordPopup.tsx index b829186..7352d01 100644 --- a/src/components/SettingsPage/SettingsChangePasswordPopup.tsx +++ b/src/components/SettingsPage/SettingsChangePasswordPopup.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from "react"; import CloseIcon from "../../icons/CloseIcon"; import classNames from "classnames"; import { toast } from "react-toastify"; -import { changePasswordAPI } from "../../api/user"; +import { changePasswordAPI } from "../../api/userAPI"; interface SettingsChangePasswordPopupProps { closePopup: () => void; diff --git a/src/components/SettingsPage/SettingsPage.tsx b/src/components/SettingsPage/SettingsPage.tsx index 3b27fd0..443bc3d 100644 --- a/src/components/SettingsPage/SettingsPage.tsx +++ b/src/components/SettingsPage/SettingsPage.tsx @@ -6,7 +6,7 @@ import AccountIcon from "../../icons/AccountIcon"; import TuneIcon from "../../icons/TuneIcon"; import { useNavigate } from "react-router-dom"; import SettingsAccountSection from "./SettingsAccountSection"; -import { getUserDetailedAPI, logoutAPI } from "../../api/user"; +import { getUserDetailedAPI, logoutAPI } from "../../api/userAPI"; import Spinner from "../Spinner/Spinner"; import Swal from "sweetalert2"; import SettingsGeneralSection from "./SettingsGeneralSection"; diff --git a/src/components/VerifyEmailPage/VerifyEmailPage.tsx b/src/components/VerifyEmailPage/VerifyEmailPage.tsx index 5913079..144cc07 100644 --- a/src/components/VerifyEmailPage/VerifyEmailPage.tsx +++ b/src/components/VerifyEmailPage/VerifyEmailPage.tsx @@ -1,5 +1,5 @@ import { useParams } from "react-router-dom"; -import { verifyEmailAPI } from "../../api/user"; +import { verifyEmailAPI } from "../../api/userAPI"; import { toast, ToastContainer } from "react-toastify"; import { useEffect } from "react"; diff --git a/src/hooks/user.ts b/src/hooks/user.ts new file mode 100644 index 0000000..b56563e --- /dev/null +++ b/src/hooks/user.ts @@ -0,0 +1,26 @@ +import { useCallback, useEffect } from "react"; +import { getAccessToken } from "../api/userAPI"; +import uuid from "uuid"; + +const useAccessTokenHandler = () => { + const refreshAccessToken = useCallback(async () => { + try { + const randomID = uuid.v4(); + localStorage.setItem("browser-id", randomID); + await getAccessToken(randomID); + } catch (e) { + console.log("Error refreshing access token", e); + } + }, []); + + useEffect(() => { + refreshAccessToken(); + const timer = setInterval(refreshAccessToken, 60 * 1000 * 20); + + return () => { + clearInterval(timer); + }; + }, []); +}; + +export default useAccessTokenHandler; diff --git a/src/reducers/user.ts b/src/reducers/user.ts index eb68dcb..f149a6d 100644 --- a/src/reducers/user.ts +++ b/src/reducers/user.ts @@ -4,11 +4,13 @@ import { UserType } from "../types/user"; interface UserStateType { user?: null | UserType; loggedIn: boolean; + lastRefreshed: number; } const initialState: UserStateType = { user: null, loggedIn: false, + lastRefreshed: 0, }; const userSlice = createSlice({ @@ -19,9 +21,12 @@ const userSlice = createSlice({ state.user = action.payload; state.loggedIn = true; }, + setLastRefreshed: (state) => { + state.lastRefreshed = Date.now(); + }, }, }); -export const { setUser } = userSlice.actions; +export const { setUser, setLastRefreshed } = userSlice.actions; export default userSlice.reducer; diff --git a/src/routers/AppRouter.jsx b/src/routers/AppRouter.jsx index 293ace9..7749891 100755 --- a/src/routers/AppRouter.jsx +++ b/src/routers/AppRouter.jsx @@ -17,11 +17,13 @@ import ResetPasswordPage from "../components/ResetPasswordPage/ResetPasswordPage import SettingsPage from "../components/SettingsPage/SettingsPage"; import Homepage from "../components/Homepage/Homepage"; import { usePreferenceSetter } from "../hooks/preferenceSetter"; +import useAccessTokenHandler from "../hooks/user"; // export const history = createHistory(); const AppRouter = () => { usePreferenceSetter(); + useAccessTokenHandler(); return ( From d5988803fcaf79a6e0bffa166d822604ce438451 Mon Sep 17 00:00:00 2001 From: subnub <44621867+subnub@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:16:27 -0500 Subject: [PATCH 2/3] improved access token refresher --- src/hooks/user.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/hooks/user.ts b/src/hooks/user.ts index b56563e..fadde08 100644 --- a/src/hooks/user.ts +++ b/src/hooks/user.ts @@ -13,14 +13,23 @@ const useAccessTokenHandler = () => { } }, []); + const visibilityChange = useCallback(() => { + if (document.visibilityState === "visible") { + refreshAccessToken(); + } + }, [refreshAccessToken]); + useEffect(() => { refreshAccessToken(); + const timer = setInterval(refreshAccessToken, 60 * 1000 * 20); + document.addEventListener("visibilitychange", visibilityChange); return () => { clearInterval(timer); + document.removeEventListener("visibilitychange", visibilityChange); }; - }, []); + }, [refreshAccessToken, visibilityChange]); }; export default useAccessTokenHandler; From b1454185d2e5404e4e9033a1fe762ecde754fe38 Mon Sep 17 00:00:00 2001 From: subnub <44621867+subnub@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:20:28 -0500 Subject: [PATCH 3/3] more fixes --- src/hooks/user.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hooks/user.ts b/src/hooks/user.ts index fadde08..f2a94d1 100644 --- a/src/hooks/user.ts +++ b/src/hooks/user.ts @@ -5,9 +5,11 @@ import uuid from "uuid"; const useAccessTokenHandler = () => { const refreshAccessToken = useCallback(async () => { try { - const randomID = uuid.v4(); - localStorage.setItem("browser-id", randomID); - await getAccessToken(randomID); + const browserID = localStorage.getItem("browser-id") || uuid.v4(); + if (!localStorage.getItem("browser-id")) { + localStorage.setItem("browser-id", browserID); + } + await getAccessToken(browserID); } catch (e) { console.log("Error refreshing access token", e); }