Skip to content

Commit

Permalink
Merge pull request #470 from bounswe/feature/FE-user-service
Browse files Browse the repository at this point in the history
Add user service management
  • Loading branch information
danzio19 authored Dec 12, 2024
2 parents 0866afe + 6955de0 commit 231f9df
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 48 deletions.
20 changes: 6 additions & 14 deletions frontend/src/components/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,26 @@ import { FaUserCircle } from "react-icons/fa";
import bullBearIcon from "../assets/icon-bare-700.png";
import { toast } from "react-toastify";
import { FaMoon, FaSun } from "react-icons/fa";
import UserService from "../service/userService";

const Dashboard = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userName, setUserName] = useState("");
const navigate = useNavigate();
const [darkMode, setDarkMode] = useState(false);



useEffect(() => {
// Check if access token exists in localStorage
const accessToken = localStorage.getItem("accessToken");

if (accessToken) {
if (UserService.isLoggedIn()) {
setIsLoggedIn(true);
const storedUserName = localStorage.getItem("userName");
const storedUserName = UserService.getUsername();
console.log(storedUserName);
if (storedUserName) {
setUserName(storedUserName);
}
} else {
setIsLoggedIn(false); // User is not logged in
}

const storedDarkMode = localStorage.getItem("darkMode");
const isDarkMode = storedDarkMode === "true";
setDarkMode(isDarkMode);
Expand All @@ -44,13 +41,8 @@ const Dashboard = () => {
};

const handleSignOut = () => {
// Clear the tokens from localStorage
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
localStorage.removeItem("userName");

UserService.logout(); // Clear tokens from localStorage
setIsLoggedIn(false); // Update state
// navigate("/login"); // Navigate to login? i am not sure
toast.success("Signed out successfully!");
};

Expand Down
48 changes: 14 additions & 34 deletions frontend/src/components/login/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { jwtDecode } from "jwt-decode";
import CircleAnimation from "../CircleAnimation";
import UserService from "../../service/userService";

function Login() {
const [username, setUsername] = useState("");
Expand All @@ -21,52 +22,31 @@ function Login() {
setError("");
setLoading(false);

const loginData = {
username: username,
password: password,
};


loadingTimeout.current = setTimeout(() => {
setLoading(true);
}, 1000);

try {

const response = await axios.post(
`${process.env.REACT_APP_API_BASE_URL}/login/`,
loginData,
{
headers: {
"Content-Type": "application/json" },
}
);

const response = await UserService.login(username, password);
console.log(response);
if (response.success) {
toast.success("Login successful!");

navigate("/home");
} else {
setError(response.error || "Login failed! Please ensure that your username and password are correct.");
toast.error(response.error || "Login failed! Please ensure that your username and password are correct.");
}

clearTimeout(loadingTimeout.current);
setLoading(false);

const { access, refresh } = response.data;
// console.log("token expiration", getTokenExpiration(access));

// Storing tokens in localStorage
localStorage.setItem("accessToken", access);
localStorage.setItem("refreshToken", refresh);
localStorage.setItem("userName", username);

// Setting up token refresh timer
setRefreshTimer(access);

// Display success toast
toast.success("Login successful!");

// Navigating to the home page if login successful
navigate("/home");
} catch (error) {
clearTimeout(loadingTimeout.current);
setLoading(false);
console.error("Login failed!", error);
toast.error(
"Login failed! Please ensure that your username and password are correct."
);
toast.error("Login failed! Please ensure that your username and password are correct.");
}
};

Expand Down
124 changes: 124 additions & 0 deletions frontend/src/service/userService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import axios from "axios";
import { toast } from "react-toastify";
import { jwtDecode } from "jwt-decode";
import log from '../utils/logger';

const API_BASE_URL = process.env.REACT_APP_API_BASE_URL;

class UserService {
static async login(username, password) {
try {
const loginData = {
username: username,
password: password,
};

const response = await axios.post(
`${API_BASE_URL}/login/`,
loginData,
{ headers: { "Content-Type": "application/json" } }
);

const { access, refresh } = response.data;

const decodedToken = jwtDecode(access);
const userId = decodedToken.user_id;

localStorage.setItem("accessToken", access);
localStorage.setItem("refreshToken", refresh);
localStorage.setItem("userName", username);
localStorage.setItem("userId", userId);

UserService.setRefreshTimer(access);

return { success: true };
} catch (error) {
console.error("Login failed!", error);
toast.error("Login failed! Please ensure that your username and password are correct.");
return { success: false, error: error.response?.data?.message || "Login failed" };
}
}

static logout() {
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
localStorage.removeItem("userName");
localStorage.removeItem("userId");
}

static isLoggedIn() {
const tokenExpired = UserService.isTokenExpired();
if (tokenExpired) {
UserService.logout();
}
const accessToken = localStorage.getItem("accessToken");
return accessToken ? true : false;
}

static getUsername() {
return localStorage.getItem("userName");
}

static getUserId() {
return localStorage.getItem("userId");
}

static getTokenExpiration(token) {
const decodedToken = jwtDecode(token);
log.debug("Decoded token:", decodedToken);
return decodedToken.exp * 1000;
}

static setRefreshTimer(accessToken) {
const expirationTime = UserService.getTokenExpiration(accessToken);
const currentTime = new Date().getTime();
const timeToRefresh = expirationTime - currentTime - 60 * 1000;

if (timeToRefresh > 0) {
setTimeout(() => {
UserService.refreshAccessToken();
}, timeToRefresh);
} else {
UserService.refreshAccessToken();
}
}

static async refreshAccessToken() {
const refreshToken = localStorage.getItem("refreshToken");
if (!refreshToken) {
console.error("No refresh token found");
toast.error("Session expired. Please log in again.");
return;
}

try {
const response = await axios.post(
`${API_BASE_URL}/refresh/`,
{ refresh: refreshToken },
{ headers: { "Content-Type": "application/json" } }
);

const { access } = response.data;
localStorage.setItem("accessToken", access);
UserService.setRefreshTimer(access);
toast.success("Access token refreshed!");
} catch (error) {
console.error("Failed to refresh token", error);
toast.error("Failed to refresh token. Please log in again.");
UserService.logout();
}
}

static isTokenExpired() {
const accessToken = localStorage.getItem("accessToken");
if (!accessToken) {
return true;
}

const expirationTime = UserService.getTokenExpiration(accessToken);
const currentTime = new Date().getTime();
return expirationTime < currentTime;
}
}

export default UserService;

0 comments on commit 231f9df

Please sign in to comment.