Skip to content

Commit 9255fcb

Browse files
authored
Merge pull request #799 from Lemoncode/feature/update-02-auth
Feature/update 02 auth
2 parents 13206ec + 662adc4 commit 9255fcb

157 files changed

Lines changed: 16034 additions & 1121 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

06-rest-api/02-auth/04-boilerplate/back/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import express from 'express';
22
import { createRestApiServer } from '#core/servers/index.js';
33
import { ENV, API_ROUTES } from '#core/constants/index.js';
4-
import { securityApi, jwtMiddleware } from '#pods/security/index.js';
4+
import { securityApi } from '#pods/security/index.js';
55
import { clientApi } from '#pods/client/index.js';
66
import { orderApi } from '#pods/order/index.js';
77

06-rest-api/02-auth/04-boilerplate/front/src/core/router/router.component.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@ import { HashRouter, Routes, Route, Navigate } from 'react-router-dom';
33
import { LoginScene, ListScene } from '#scenes';
44
import { switchRoutes } from './routes';
55

6-
export const RouterComponent: React.FunctionComponent = () => {
6+
export const RouterComponent: React.FC = () => {
77
return (
88
<HashRouter>
9-
<Routes>
10-
<Route path={switchRoutes.login} element={<LoginScene />} />
11-
<Route path={switchRoutes.list} element={<ListScene />} />
12-
<Route
13-
path={switchRoutes.root}
14-
element={<Navigate to={switchRoutes.login} />}
15-
/>
16-
</Routes>
9+
<AppRoutes />
1710
</HashRouter>
1811
);
1912
};
13+
14+
const AppRoutes: React.FC = () => {
15+
return (
16+
<Routes>
17+
<Route path={switchRoutes.login} element={<LoginScene />} />
18+
<Route path={switchRoutes.list} element={<ListScene />} />
19+
<Route
20+
path={switchRoutes.root}
21+
element={<Navigate to={switchRoutes.login} />}
22+
/>
23+
</Routes>
24+
);
25+
};

06-rest-api/02-auth/05-login-app-headers/README.md

Lines changed: 132 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -151,205 +151,222 @@ export const logout = async (): Promise<boolean> => {
151151

152152
## Redirect to login page
153153

154-
- Now, we will implement a redirect to login page on `401` Not Authorize responses:
154+
Now, we will implement a redirect to login page on `401` Not Authorize responses:
155155

156-
_./front/src/common-app/auth/auth.hooks.ts_
156+
_./front/src/core/api/api.hooks.ts_
157157

158158
```javascript
159-
import { useNavigate } from 'react-router-dom';
160-
import { useSnackbarContext } from 'common/components';
161-
import { linkRoutes } from 'core/router';
162-
import { AxiosError } from 'axios';
159+
import React from "react";
160+
import axios, { AxiosError } from "axios";
161+
import { useNavigate } from "react-router-dom";
162+
import { useSnackbarContext } from "#common/components";
163+
import { linkRoutes } from "#core/router";
163164

164-
type Request = (...params: any[]) => Promise<any>;
165-
166-
export const useAuthRequest = <T extends Request[]>(...requestList: T): T => {
165+
export const useApiConfig = () => {
167166
const navigate = useNavigate();
168167
const { showMessage } = useSnackbarContext();
169168

170-
const authRequestList = requestList.map((request) => async (...params) => {
171-
try {
172-
return await request(...params);
173-
} catch (errorResponse) {
174-
if (isAuthError(errorResponse)) {
175-
navigate(linkRoutes.login);
176-
showMessage('You should login', 'error');
177-
throw undefined;
169+
React.useEffect(() => {
170+
axios.interceptors.response.use(
171+
(response) => response,
172+
(error: AxiosError) => {
173+
if (error.response.status === 401) {
174+
showMessage("You should login", "error");
175+
navigate(linkRoutes.login);
176+
}
178177
}
179-
throw errorResponse;
180-
}
181-
}) as T;
182-
183-
return authRequestList;
184-
};
185-
186-
const isAuthError = (error: AxiosError): boolean => {
187-
const errorCode = error.response.status;
188-
189-
return errorCode === 401;
178+
);
179+
}, []);
190180
};
191-
192181
```
193182

194-
- Update barrel file:
183+
Update barrel file:
195184

196-
_./front/src/common-app/auth/index.ts_
185+
_./front/src/core/api/index.ts_
197186

198187
```diff
199-
export * from './auth.context';
200-
export * from './auth.vm';
201-
+ export * from './auth.hooks';
188+
export * from './api.helpers';
189+
export * from './api.constants';
190+
+ export * from './api.hooks';
202191

203192
```
204193

205-
- Use it on `list container`:
194+
Use it on `router.component`:
206195

207-
_./front/src/pods/list/list.container.tsx_
196+
_./front/src/core/router/router.component.tsx_
208197

209198
```diff
210199
import React from 'react';
211-
+ import { useAuthRequest } from 'common-app/auth';
212-
import * as api from './api';
213-
...
214-
215-
export const ListContainer: React.FunctionComponent<Props> = (props) => {
216-
const { className } = props;
217-
+ const [getClientList, getOrderList] = useAuthRequest(
218-
+ api.getClientList,
219-
+ api.getOrderList
220-
+ );
200+
import { HashRouter, Routes, Route, Navigate } from 'react-router-dom';
201+
+ import { useApiConfig } from '#core/api';
202+
import { LoginScene, ListScene } from '#scenes';
203+
import { switchRoutes } from './routes';
221204

222205
...
223-
const handleLoadClientList = async () => {
224-
try {
225-
- const apiClientList = await api.getClientList();
226-
+ const apiClientList = await getClientList();
227-
const vmClientList = mapItemListFromApiToVm(apiClientList);
228-
setClientList(vmClientList);
229-
} catch {
230-
setClientList(createNoTokenItemList());
231-
}
232-
};
233206

234-
const handleLoadOrderList = async () => {
235-
try {
236-
- const apiOrderList = await api.getOrderList();
237-
+ const apiOrderList = await getOrderList();
238-
const vmOrderList = mapItemListFromApiToVm(apiOrderList);
239-
setOrderList(vmOrderList);
240-
} catch {
241-
setOrderList(createNoTokenItemList());
242-
}
243-
};
207+
const AppRoutes: React.FC = () => {
208+
+ useApiConfig();
209+
return (
210+
<Routes>
211+
<Route path={switchRoutes.login} element={<LoginScene />} />
212+
<Route path={switchRoutes.list} element={<ListScene />} />
213+
<Route
214+
path={switchRoutes.root}
215+
element={<Navigate to={switchRoutes.login} />}
216+
/>
217+
</Routes>
218+
);
219+
};
244220

245-
...
246221
```
247222

248-
- Use it on `logout`:
223+
> Try to fetch clients or orders without login.
249224
250-
_./front/src/common-app/app-bar/app-bar.component.tsx_
225+
Create `AuthRoute`:
226+
227+
_./front/src/core/router/router.component.tsx_
251228

252229
```diff
253-
...
254-
- import { AuthContext, createEmptyUserSession } from '../auth';
255-
+ import { AuthContext, createEmptyUserSession, useAuthRequest } from '../auth';
256-
import * as api from './api';
257-
import * as classes from './app-bar.styles';
258-
259-
export const AppBarComponent: React.FunctionComponent = () => {
260-
const { userSession, onChangeUserSession } = React.useContext(AuthContext);
261-
const history = useHistory();
262-
+ const [logout] = useAuthRequest(api.logout);
263-
264-
const handleLogout = async () => {
265-
- await api.logout();
266-
+ await logout();
267-
onChangeUserSession(createEmptyUserSession());
268-
history.goBack();
269-
};
230+
import React from 'react';
231+
import {
232+
HashRouter,
233+
Routes,
234+
Route,
235+
Navigate,
236+
+ Outlet,
237+
} from 'react-router-dom';
238+
import { useApiConfig } from '#core/api';
239+
+ import { AuthContext } from '#core/auth';
240+
import { LoginScene, ListScene } from '#scenes';
241+
- import { switchRoutes } from './routes';
242+
+ import { switchRoutes, linkRoutes } from './routes';
243+
244+
export const RouterComponent: React.FC = () => {
245+
return (
246+
<HashRouter>
247+
<AppRoutes />
248+
</HashRouter>
249+
);
250+
};
251+
252+
+ const PrivateRoutes = () => {
253+
+ const { userSession } = React.useContext(AuthContext);
254+
+ return userSession?.userName ? (
255+
+ <Outlet />
256+
+ ) : (
257+
+ <Navigate to={linkRoutes.login} />
258+
+ );
259+
+ };
260+
261+
const AppRoutes: React.FC = () => {
262+
useApiConfig();
263+
return (
264+
<Routes>
265+
<Route path={switchRoutes.login} element={<LoginScene />} />
266+
+ <Route element={<PrivateRoutes />}>
267+
+ <Route path={switchRoutes.list} element={<ListScene />} />
268+
+ </Route>
269+
<Route
270+
path={switchRoutes.root}
271+
element={<Navigate to={switchRoutes.login} />}
272+
/>
273+
</Routes>
274+
);
275+
};
270276

271-
...
272277
```
273278

279+
> Try to navigate to /list without login.
280+
274281
# Using CORS
275282

276-
- Let's update example to check if it's working with cors:
283+
Let's update example to check if it's working with cors:
277284

278285
```bash
279286
npm install cors --save
287+
npm install @types/cors --save-dev
280288
```
281289

282-
> NOTE: we can install @types/cors for Typescript.
290+
Configure cors:
283291

284-
_./back/src/core/servers/express.server.ts_
292+
_./back/src/core/servers/rest-api.server.ts_
285293

286294
```diff
287295
import express from 'express';
288296
+ import cors from 'cors';
289-
import cookieParser from 'cookie-parser';
290297

291-
export const createApp = () => {
298+
export const createRestApiServer = () => {
292299
const app = express();
293-
294300
app.use(express.json());
295301
+ app.use(
296302
+ cors({
297303
+ credentials: true,
298304
+ origin: '*',
299305
+ })
300306
+ );
301-
app.use(cookieParser());
302307

303308
return app;
304309
};
305310

306311
```
307312

308-
- Update front:
313+
> In a real scenario we should set `origin` to the real domain, for example: `http://my-domain.com`.
309314
310-
_./front/config/webapck/dev.js_
315+
Update front:
316+
317+
_./front/vite.config.ts_
311318

312319
```diff
313320
...
314-
devServer: {
315-
inline: true,
316-
host: 'localhost',
317-
port: 8080,
318-
stats: 'minimal',
319-
hot: true,
320-
- proxy: {
321-
- '/api': 'http://localhost:8081',
322-
- },
323-
},
321+
plugins: [react()],
322+
- server: {
323+
- proxy: {
324+
- '/api': 'http://localhost:3000',
325+
- },
326+
- },
327+
});
328+
324329
```
325330

326-
- Update login request:
331+
Update login request:
327332

328333
_./front/src/pods/login/api/login.api.ts_
329334

330335
```diff
331-
import Axios from 'axios';
332-
import { UserSession } from './login.api-model';
336+
...
333337

334338
- const url = '/api/security/login';
335-
+ const url = 'http://localhost:8081/api/security/login';
339+
+ const url = 'http://localhost:3000/api/security/login';
340+
341+
...
342+
343+
```
344+
345+
Update logout request:
346+
347+
_./front/src/core/app-bar/api/app-bar.api.ts_
348+
349+
```diff
350+
...
351+
352+
- const url = '/api/security/logout';
353+
+ const url = 'http://localhost:3000/api/security/logout';
336354

337355
...
338356

339357
```
340358

341-
- Update list requests:
359+
Update list requests:
342360

343361
_./front/src/pods/list/api/list.api.ts_
344362

345363
```diff
346-
import Axios from 'axios';
347-
import { Item } from './list.api-model';
364+
...
348365

349366
- const clientUrl = '/api/clients';
350-
+ const clientUrl = 'http://localhost:8081/api/clients';
367+
+ const clientUrl = 'http://localhost:3000/api/clients';
351368
- const orderUrl = '/api/orders';
352-
+ const orderUrl = 'http://localhost:8081/api/orders';
369+
+ const orderUrl = 'http://localhost:3000/api/orders';
353370

354371
...
355372

0 commit comments

Comments
 (0)