Skip to content

Commit c58c837

Browse files
committed
feat: add authentication
1 parent 8c6677a commit c58c837

File tree

24 files changed

+369
-55
lines changed

24 files changed

+369
-55
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"Servlet",
99
"springboot",
1010
"springframework",
11-
"throyer"
11+
"throyer",
12+
"zustand"
1213
],
1314
"java.configuration.updateBuildConfiguration": "automatic",
1415

web/package-lock.json

Lines changed: 74 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
"@emotion/styled": "^11.10.4",
1515
"axios": "^0.27.2",
1616
"framer-motion": "^6.5.1",
17+
"luxon": "^3.0.3",
1718
"react": "^18.2.0",
1819
"react-dom": "^18.2.0",
1920
"react-icons": "^4.4.0",
20-
"react-router-dom": "^6.4.0"
21+
"react-router-dom": "^6.4.0",
22+
"zustand": "^4.1.1"
2123
},
2224
"devDependencies": {
25+
"@types/luxon": "^3.0.1",
2326
"@types/react": "^18.0.17",
2427
"@types/react-dom": "^18.0.6",
2528
"@vitejs/plugin-react": "^2.1.0",
Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
import { Button, Divider, Stack, Text } from "@chakra-ui/react";
2-
import { Link } from "react-router-dom";
2+
import { Link, useNavigate } from "react-router-dom";
3+
import { useAuthentication } from "../../hooks/use-authentication/use-authentication";
34

4-
export const Navbar = () => (
5-
<>
6-
<Stack mt="10" direction='row'>
7-
<Link to="/">
8-
<Button
9-
p="3"
10-
variant="link">
11-
Home
12-
</Button>
13-
</Link>
14-
<Link to="/users">
15-
<Button
5+
export const Navbar = () => {
6+
7+
const { logout } = useAuthentication()
8+
const navigate = useNavigate()
9+
10+
return (
11+
<>
12+
<Stack mt="10" direction='row'>
13+
<Link to="/">
14+
<Button
15+
p="3"
16+
variant="link">
17+
Home
18+
</Button>
19+
</Link>
20+
<Link to="/users">
21+
<Button
22+
p="3"
23+
variant="link">
24+
Users
25+
</Button>
26+
</Link>
27+
<Button
28+
onClick={() => {
29+
logout();
30+
navigate('/');
31+
}}
1632
p="3"
1733
variant="link">
18-
Users
34+
Logout
1935
</Button>
20-
</Link>
21-
</Stack>
22-
<Divider />
23-
</>
24-
)
36+
</Stack>
37+
<Divider />
38+
</>
39+
)
40+
}

web/src/components/table/index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { Box, Center, Skeleton } from "@chakra-ui/react";
2-
import { Table as ChakraUiTable, TableContainer, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/table";
3-
import { Random } from "../../utils/random";
1+
import { Box, Center } from "@chakra-ui/react";
2+
import { Table as ChakraUiTable, TableContainer } from "@chakra-ui/table";
43
import { Pagination, PaginationProps } from "../pagination";
54
import { Body } from "./body";
65
import { Head } from "./head";

web/src/hooks/use-authentication/index.ts

Whitespace-only changes.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { useCallback } from "react";
2+
import { Navigate, useNavigate } from "react-router-dom";
3+
import { api } from "../../http/api";
4+
import { useSession } from "../../providers/session";
5+
import { useUser } from "../../providers/user";
6+
import { Credentials, LoginResult, SessionsApiResponse } from "./use-authentication.types";
7+
8+
export const useAuthentication = () => {
9+
const { set: setSession } = useSession();
10+
const { set: setUser } = useUser();
11+
12+
const navigate = useNavigate()
13+
14+
const login = useCallback(async ({ email, password }: Credentials, redirectTo = '/home'): Promise<LoginResult> => {
15+
try {
16+
const { data } = await api.post<SessionsApiResponse>('sessions', { email, password });
17+
const { user, ...session } = data;
18+
19+
setSession(session);
20+
setUser(user);
21+
navigate(redirectTo);
22+
23+
return {
24+
isAuthenticated: true
25+
}
26+
} catch (error: any) {
27+
28+
console.log(error);
29+
error.response && console.log(error.response);
30+
31+
return {
32+
isAuthenticated: false
33+
}
34+
}
35+
}, [])
36+
37+
const logout = useCallback(() => {
38+
setSession(null);
39+
setUser(null);
40+
}, [])
41+
42+
return {
43+
login,
44+
logout
45+
}
46+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { User } from "../../providers/user/user-store.types";
2+
3+
export type SessionsApiResponse = {
4+
user: User;
5+
accessToken: string;
6+
expiresIn: string;
7+
refreshToken: string;
8+
tokenType: string;
9+
}
10+
11+
export type Credentials = {
12+
email: string;
13+
password: string;
14+
}
15+
16+
export type LoginResult = {
17+
isAuthenticated: boolean;
18+
}
19+
20+
export type UseAuthentication = {
21+
login: (credentials: Credentials) => Promise<LoginResult>;
22+
}

web/src/hooks/use-dialog.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useContext } from "react";
2+
import { DialogContext, DialogContextData } from "../providers/alert";
3+
4+
export function useDialog(): DialogContextData {
5+
const context = useContext(DialogContext);
6+
7+
if (!context) {
8+
throw new Error("o useDialog deve ser utilizado dentro de um DialogProvider");
9+
}
10+
11+
return context;
12+
}

web/src/http/api.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import axios from "axios";
2+
3+
import { useSession } from '../providers/session';
4+
5+
const ENV = {
6+
BASE_URL: "http://localhost:8080/api"
7+
}
8+
9+
export const api = axios.create({
10+
baseURL: ENV.BASE_URL
11+
});
12+
13+
api.interceptors.request.use(async (configs) => {
14+
const { session, set: setSession } = useSession.getState()
15+
16+
if (session) {
17+
18+
if (session.isExpired()) {
19+
try {
20+
const { data: sessionResponse } = await axios.post(`${ENV.BASE_URL}/sessions/refresh`, {
21+
refreshToken: session.refreshToken
22+
});
23+
setSession(sessionResponse);
24+
} catch (error) {
25+
setSession(null);
26+
}
27+
}
28+
29+
configs.headers = {
30+
...configs.headers,
31+
'Authorization': `Bearer ${session.accessToken}`,
32+
}
33+
}
34+
35+
return configs;
36+
})

0 commit comments

Comments
 (0)