+23
-20
@@ -1,22 +1,21 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router-dom';
|
||||
import AuthContext from './context/AuthContext';
|
||||
|
||||
// Styles
|
||||
import './styles/custom.scss';
|
||||
|
||||
// Interfaces
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||
import Landing from './views/Landing';
|
||||
import BrowserRoutes from './routes';
|
||||
import ProtectedRoute from './routes/ProtectedRoute';
|
||||
import Layout from './layout/PrivateLayout';
|
||||
import browserRoutes from './routes';
|
||||
import NotFound from './views/NotFound';
|
||||
import AuthContext from './context/AuthContext';
|
||||
|
||||
import Landing from './views/Landing';
|
||||
import Login from './views/Login';
|
||||
import NotFound from './views/NotFound';
|
||||
|
||||
import './styles/custom.scss';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const { signed, checkCurrentAuthUser } = useContext(AuthContext);
|
||||
|
||||
const notSignedRouter = createBrowserRouter([
|
||||
const notSignedRouter: RouteObject[] = [
|
||||
{
|
||||
path: '/',
|
||||
element: <Landing />
|
||||
@@ -29,38 +28,42 @@ const App: React.FC = () => {
|
||||
path: '*',
|
||||
element: <NotFound />
|
||||
}
|
||||
]);
|
||||
];
|
||||
|
||||
const signedRouter = createBrowserRouter([
|
||||
const signedRouter: RouteObject[] = [
|
||||
{
|
||||
path: '/',
|
||||
path: '/', // ROUTES.ROOT='/'
|
||||
element: <ProtectedRoute />,
|
||||
children: [
|
||||
{
|
||||
element: <Layout />,
|
||||
children: browserRoutes
|
||||
children: BrowserRoutes
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
path: '*', // ROUTES.ALL_ROUTES='*'
|
||||
element: <NotFound />
|
||||
}
|
||||
]);
|
||||
];
|
||||
|
||||
const getBrowserRoutes = () => {
|
||||
const getBrowserRouter = () => {
|
||||
if (signed) {
|
||||
return signedRouter;
|
||||
console.log('app signed');
|
||||
return createBrowserRouter(signedRouter);
|
||||
}
|
||||
return notSignedRouter;
|
||||
console.log('app not signed');
|
||||
return createBrowserRouter(notSignedRouter);
|
||||
};
|
||||
|
||||
const browserRouter = getBrowserRouter();
|
||||
|
||||
useEffect(() => {
|
||||
checkCurrentAuthUser(window.location.pathname);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<RouterProvider router={getBrowserRoutes()} />
|
||||
<RouterProvider router={browserRouter} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,36 +1,74 @@
|
||||
import { API_TOKEN } from '../app-constants/app-constants';
|
||||
import { env } from '../env';
|
||||
import { User } from '../types/User';
|
||||
import { SigninResponse } from '../types/SigninResponse';
|
||||
|
||||
const server = env.VITE_BACKEND_SERVER;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function privateSessionFake(signed: boolean): Promise<User | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (signed) {
|
||||
resolve({
|
||||
name: 'User',
|
||||
email: 'email@domain.com'
|
||||
});
|
||||
async function privateSessionState(signed: boolean): Promise<SigninResponse | undefined> {
|
||||
const tokenState = localStorage.getItem(API_TOKEN);
|
||||
|
||||
if (signed && tokenState) {
|
||||
const response = await fetch(ApiConfig.refreshTokenUrl, {
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${tokenState}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
localStorage.setItem(API_TOKEN, data.token);
|
||||
return {
|
||||
token: data.token
|
||||
};
|
||||
} else {
|
||||
reject();
|
||||
console.error(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
const ApiConfig = {
|
||||
login: `${server}/login`,
|
||||
signinUrl: `${server}/auth/signin`,
|
||||
|
||||
loginFake: async (): Promise<void> => new Promise((resolve) => {
|
||||
resolve();
|
||||
}),
|
||||
refreshTokenUrl: `${server}/rest/user-sessions/refresh`,
|
||||
|
||||
signin: async (email: string, password: string): Promise<SigninResponse | undefined> => {
|
||||
try {
|
||||
const response = await fetch(ApiConfig.signinUrl, {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ email, password })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
localStorage.setItem(API_TOKEN, data.token);
|
||||
return {
|
||||
token: data.token
|
||||
};
|
||||
} else {
|
||||
console.error('signin error', response);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
logoutFake: async (): Promise<void> => new Promise((resolve) => {
|
||||
resolve();
|
||||
}),
|
||||
|
||||
currentSessionFake: privateSessionFake
|
||||
currentSessionState: privateSessionState
|
||||
};
|
||||
|
||||
export default ApiConfig;
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export const REDIRECT_PATH = 'TASKNOTE-REDIRECT';
|
||||
export const API_TOKEN = 'TASKNOTE-TOKEN';
|
||||
export const USER_DATA = 'TASKNOTE-USER';
|
||||
|
||||
@@ -5,7 +5,7 @@ export interface AuthContextData {
|
||||
signed: boolean;
|
||||
user: User | undefined;
|
||||
checkCurrentAuthUser: (pathname: string) => Promise<void>;
|
||||
signIn: () => void;
|
||||
signIn: (email: string, password: string) => void;
|
||||
signOut: () => void;
|
||||
isAdmin: boolean;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { User } from '../types/User';
|
||||
import { env } from '../env';
|
||||
import ApiConfig from '../api-service/apiConfig';
|
||||
import AuthContext, { AuthContextData } from './AuthContext';
|
||||
import { REDIRECT_PATH } from '../app-constants/app-constants';
|
||||
import { API_TOKEN, REDIRECT_PATH, USER_DATA } from '../app-constants/app-constants';
|
||||
import { SigninResponse } from '../types/SigninResponse';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
@@ -16,13 +17,13 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }: Pro
|
||||
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||
const [intervalInstance, setIntervalInstance] = useState<NodeJS.Timeout | null>(null);
|
||||
|
||||
const fetchCurrentSession = async (pathname: string): Promise<User | undefined> => {
|
||||
const fetchCurrentSession = async (pathname: string): Promise<SigninResponse | undefined> => {
|
||||
try {
|
||||
const currentUser = await ApiConfig.currentSessionFake(signed);
|
||||
if (currentUser) {
|
||||
const bearerToken: SigninResponse | undefined = await ApiConfig.currentSessionState(signed);
|
||||
if (bearerToken && bearerToken.token) {
|
||||
setSigned(true);
|
||||
}
|
||||
return currentUser;
|
||||
return bearerToken;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
console.warn(e.message);
|
||||
@@ -38,28 +39,35 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }: Pro
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const updateUserSession = (userPriv: User) => {
|
||||
localStorage.setItem('TaskNote-token', userPriv.email);
|
||||
const updateUserSession = (userPriv: User | null, bearerToken: string) => {
|
||||
if (userPriv) {
|
||||
localStorage.setItem(USER_DATA, JSON.stringify(userPriv));
|
||||
}
|
||||
localStorage.setItem(API_TOKEN, bearerToken);
|
||||
};
|
||||
|
||||
const checkCurrentAuthUser = async (pathname: string): Promise<void> => {
|
||||
const currentUser = await fetchCurrentSession(pathname);
|
||||
if (currentUser) {
|
||||
updateUserSession(currentUser);
|
||||
setUser(currentUser);
|
||||
console.log('checkCurrentAuthUser', pathname);
|
||||
const bearerToken: SigninResponse | undefined = await fetchCurrentSession(pathname);
|
||||
if (bearerToken && bearerToken.token) {
|
||||
console.log('checkCurrentAuthUser token', bearerToken);
|
||||
updateUserSession(null, bearerToken.token);
|
||||
}
|
||||
};
|
||||
|
||||
const signIn = async (): Promise<void> => {
|
||||
const appEnv = env.VITE_ENV || 'dev';
|
||||
await ApiConfig.loginFake();
|
||||
setSigned(true);
|
||||
const currentUser: User = {
|
||||
name: `Ricardo ${appEnv}`,
|
||||
email: 'ricardompcampos@gmail.com'
|
||||
};
|
||||
const signIn = async (email: string, password: string): Promise<void> => {
|
||||
const bearerToken: SigninResponse | undefined = await ApiConfig.signin(email, password);
|
||||
if (bearerToken && bearerToken.token) {
|
||||
const currentUser: User = {
|
||||
email
|
||||
};
|
||||
|
||||
setUser(currentUser);
|
||||
setSigned(true);
|
||||
setUser(currentUser);
|
||||
updateUserSession(currentUser, bearerToken.token);
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject();
|
||||
};
|
||||
|
||||
const signOut = async (): Promise<void> => {
|
||||
@@ -74,11 +82,12 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }: Pro
|
||||
localStorage.clear();
|
||||
};
|
||||
|
||||
const refreshTokenPvt = async () => {
|
||||
const currentUser = await fetchCurrentSession('/');
|
||||
if (currentUser) {
|
||||
updateUserSession(currentUser);
|
||||
const refreshTokenPvt = async (): Promise<void> => {
|
||||
const bearerToken: SigninResponse | undefined = await fetchCurrentSession('/');
|
||||
if (bearerToken) {
|
||||
updateUserSession(null, bearerToken.token);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// 2 minutes
|
||||
|
||||
@@ -8,6 +8,7 @@ import Footer from '../../components/Footer';
|
||||
*
|
||||
*/
|
||||
function Layout() {
|
||||
console.log('Layout');
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
|
||||
@@ -5,7 +5,7 @@ import Home from '../views/Home';
|
||||
import About from '../views/About';
|
||||
import NotFound from '../views/NotFound';
|
||||
|
||||
const browserRoutes: RouteObject[] = [
|
||||
const BrowserRoutes: RouteObject[] = [
|
||||
{
|
||||
path: '/',
|
||||
element: (
|
||||
@@ -38,4 +38,4 @@ const browserRoutes: RouteObject[] = [
|
||||
}
|
||||
];
|
||||
|
||||
export default browserRoutes;
|
||||
export default BrowserRoutes;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export type SigninResponse = {
|
||||
token: string
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
export type User = {
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import React from 'react';
|
||||
*
|
||||
*/
|
||||
function Home() {
|
||||
console.log('This is home!');
|
||||
|
||||
return (
|
||||
<h1>This is home!</h1>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
import React from 'react';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import './styles.scss';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import AuthContext from '../../context/AuthContext';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function Landing() {
|
||||
const { signed, checkCurrentAuthUser } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogin = () => {
|
||||
console.log('Landing signed', signed);
|
||||
if (signed) {
|
||||
navigate('/home');
|
||||
} else {
|
||||
navigate('/signin');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkCurrentAuthUser(window.location.pathname);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>This is landing page</h1>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => navigate('/signin')}
|
||||
onClick={handleLogin}
|
||||
>
|
||||
SignIn
|
||||
</Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import { FeedbackType } from 'react-bootstrap/esm/Feedback';
|
||||
import { Alert, Button, Col, Form } from 'react-bootstrap';
|
||||
import AuthContext from '../../context/AuthContext';
|
||||
import { redirect } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* Created the Login component.
|
||||
@@ -9,98 +9,63 @@ import AuthContext from '../../context/AuthContext';
|
||||
function Login() {
|
||||
const { signIn } = useContext(AuthContext);
|
||||
const [validated, setValidated] = useState<boolean>(true);
|
||||
const [email, setEmail] = useState<string>('');
|
||||
const [password, setPassword] = useState<string>('');
|
||||
const [emailValidType, setEmailValidType] = useState<FeedbackType | undefined>('valid');
|
||||
const [emailValidMsg, setEmailValidMsg] = useState<string>('Looks good!');
|
||||
const [passwordValidType, setPasswordValidType] = useState<FeedbackType | undefined>('valid');
|
||||
const [passwordValidMsg, setPasswordValidMsg] = useState<string>('Looks good!');
|
||||
const [formInvalid, setFormInvalid] = useState<boolean>(false);
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setValidated(true);
|
||||
|
||||
const form = event.currentTarget;
|
||||
if (form.checkValidity() === false) {
|
||||
if (email.length === 0) {
|
||||
setEmailValidType('invalid');
|
||||
setEmailValidMsg('Please type your email!');
|
||||
} else {
|
||||
setEmailValidType('valid');
|
||||
setEmailValidMsg('Looks good!');
|
||||
}
|
||||
|
||||
if (password.length === 0) {
|
||||
setPasswordValidType('invalid');
|
||||
setPasswordValidMsg('Please type your password!');
|
||||
} else {
|
||||
setPasswordValidType('valid');
|
||||
setPasswordValidMsg('Looks good!');
|
||||
}
|
||||
|
||||
console.log('something is invalid!');
|
||||
setFormInvalid(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setEmailValidType('valid');
|
||||
setEmailValidMsg('Looks good!');
|
||||
|
||||
setPasswordValidType('valid');
|
||||
setPasswordValidMsg('Looks good!');
|
||||
signIn();
|
||||
setFormInvalid(false);
|
||||
console.log(`email=${form.email.value}, password=${form.password.value}`);
|
||||
await signIn(form.email.value, form.password.value);
|
||||
console.log('finished!? login component!?');
|
||||
redirect("/home");
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setValidated(false);
|
||||
setEmail('');
|
||||
setEmailValidType('valid');
|
||||
setPassword('');
|
||||
setPasswordValidType('valid');
|
||||
};
|
||||
|
||||
useEffect(() => {}, [validated]);
|
||||
useEffect(() => {}, [formInvalid]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>This is login page</h1>
|
||||
<Form noValidate validated={validated} onSubmit={handleSubmit}>
|
||||
<Form.Group className="mb-3" controlId="formBasicEmail">
|
||||
<Form.Group as={Col} md="4" className="mb-3" controlId="formBasicEmail">
|
||||
<Form.Label>Email address</Form.Label>
|
||||
<Form.Control
|
||||
required
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="Enter email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
isInvalid={emailValidType === 'invalid'}
|
||||
/>
|
||||
<Form.Control.Feedback type={emailValidType}>
|
||||
{emailValidMsg}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3" controlId="formBasicPassword">
|
||||
<Form.Group as={Col} md="4" className="mb-3" controlId="formBasicPassword">
|
||||
<Form.Label>Password</Form.Label>
|
||||
<Form.Control
|
||||
required
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
isInvalid={passwordValidType === 'invalid'}
|
||||
/>
|
||||
<Form.Control.Feedback type={passwordValidType}>
|
||||
{passwordValidMsg}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Login
|
||||
</Button>
|
||||
<Button variant="secondary" type="button" onClick={() => resetForm()}>
|
||||
Reset
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
{formInvalid ? (
|
||||
<Alert variant={"danger"}>
|
||||
Something is wrong! Check your email and password!
|
||||
</Alert>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user