zezung27 / api-mymeostore

1 stars 0 forks source link

axios #18

Open zezung27 opened 1 year ago

zezung27 commented 1 year ago

import axios from "axios"; import { ENDPOINTS } from "constants/endpoints"; import { LOCAL_STORAGE } from "constants/store"; import { get } from "lodash";

const headersDefault = { "Content-Type": "application/json", Accept: "application/json", }; const api = axios.create({ baseURL: ENDPOINTS.BASE_BACK_END_URL, timeout: LOCAL_STORAGE.TIMEOUT, headers: headersDefault, });

// Add a request interceptor api.interceptors.request.use( async (config) => { const token = localStorage.getItem(LOCAL_STORAGE.ACCESS_TOKEN); if (token) config.headers["Authorization"] = Bearer ${token}; return config; }, (error) => { Promise.reject(error); } );

// Add a response interceptor api.interceptors.response.use( (response) => { return response; }, async (error) => { const originalRequest = error.response.config; if ( error.response.status === 401 && originalRequest.url === ${ENDPOINTS.BASE_BACK_END_URL}${ENDPOINTS.REFRESH_TOKEN} ) { localStorage.removeItem(LOCAL_STORAGE.ACCESS_TOKEN); localStorage.removeItem(LOCAL_STORAGE.REFRESH_TOKEN); localStorage.removeItem(LOCAL_STORAGE.ACCOUNT_ID); // navagate to login page

  return Promise.reject(error);
}
const accessToken = await localStorage.getItem(LOCAL_STORAGE.ACCESS_TOKEN);
if (
  accessToken &&
  error.response.status === 401 &&
  !originalRequest._retry
) {
  originalRequest._retry = true;

  const refreshToken =
    localStorage.getItem(LOCAL_STORAGE.REFRESH_TOKEN) || "_";
  const oldAccessToken =
    localStorage.getItem(LOCAL_STORAGE.ACCESS_TOKEN) || "_";

  return axios({
    method: "POST",
    url: `${ENDPOINTS.BASE_BACK_END_URL}${ENDPOINTS.REFRESH_TOKEN}`,
    headers: {
      Authorization: `Bear ${oldAccessToken}`,
    },
    data: {
      refreshToken,
    },
  })
    .then(async (res) => {
      if (get(res, "data.accessToken", "")) {
        await localStorage.setItem(
          LOCAL_STORAGE.ACCESS_TOKEN,
          get(res, "data.accessToken")
        );
        await localStorage.setItem(
          LOCAL_STORAGE.REFRESH_TOKEN,
          get(res, "data.refreshToken")
        );
        api.defaults.headers.common["Authorization"] = `Bearer ${get(
          res,
          "data.accessToken"
        )}`;
        return api(originalRequest);
      } else {
        localStorage.removeItem(LOCAL_STORAGE.ACCESS_TOKEN);
        localStorage.removeItem(LOCAL_STORAGE.REFRESH_TOKEN);
        localStorage.removeItem(LOCAL_STORAGE.ACCOUNT_ID);
        // navagate to login page
      }
    })
    .catch((err) => {
      if (
        get(err, "response.status", 0) == 499 ||
        get(err, "response.status", 0) == 500
      ) {
        localStorage.removeItem(LOCAL_STORAGE.ACCESS_TOKEN);
        localStorage.removeItem(LOCAL_STORAGE.REFRESH_TOKEN);
        localStorage.removeItem(LOCAL_STORAGE.ACCOUNT_ID);
        // navagate to login page
      }
    })
    .finally(() => {
      return 1;
    });
}
return Promise.reject(error);

} );

const baseApi = { get: (url, params) => { return api({ method: "get", url: url, params, headers: { ...headersDefault, Authorization: Bearer ${localStorage.getItem( LOCAL_STORAGE.ACCESS_TOKEN )}, }, }) .then((response) => { return response; }) .catch((err) => { throw err; }); }, post: (url, data, header = headersDefault) => { return api({ method: "post", url: url, data: data, headers: { ...headersDefault, Authorization: Bearer ${localStorage.getItem( LOCAL_STORAGE.ACCESS_TOKEN )}, ...header, }, }) .then((response) => { return response; }) .catch((err) => { throw err; }); }, delete: (url, data) => { return api({ method: "delete", url: url, data: data, headers: { ...headersDefault, Authorization: Bearer ${localStorage.getItem( LOCAL_STORAGE.ACCESS_TOKEN )}, }, }) .then((response) => { return response; }) .catch((err) => { throw err; }); }, put: (url, data) => { return api({ method: "put", url: url, data: data, headers: { ...headersDefault, Authorization: Bearer ${localStorage.getItem( LOCAL_STORAGE.ACCESS_TOKEN )}, }, }) .then((response) => { return response; }) .catch((err) => { throw err; }); }, };

export default baseApi;

zezung27 commented 1 year ago

import { ENDPOINTS } from "constants/endpoints"; import baseApi from "./base-api";

const apiAuth = { register: (data) => baseApi.post(${ENDPOINTS.BASE_BACK_END_URL}${ENDPOINTS.REGISTER}, data), login: (data) => baseApi.post(${ENDPOINTS.BASE_BACK_END_URL}${ENDPOINTS.LOGIN}, data), fetchAlluser: () => baseApi.get(${ENDPOINTS.BASE_BACK_END_URL}${ENDPOINTS.ALL_USER}), }; export default apiAuth;

zezung27 commented 1 year ago

import apiAuth from "api/auth"; import { ACTION_CONST } from "constants/actions"; import { LOCAL_STORAGE } from "constants/store"; import { takeLatest, put, call } from "redux-saga/effects";

const saveToken = async (resData) => { await localStorage.setItem( LOCAL_STORAGE.ACCESS_TOKEN, resData.data.accessToken ); await localStorage.setItem( LOCAL_STORAGE.REFRESH_TOKEN, resData.data.refreshToken ); await localStorage.setItem(LOCAL_STORAGE.ACCOUNT_ID, resData.data.id); };

function* handleLogin(action) { try { const res = yield call(apiAuth.login, action.payload); if (res.status === 200) { saveToken(res); yield put({ type: ACTION_CONST.LOGIN_SUCCESS, data: res.data.data, }); } } catch (error) { console.log(error); } }

function* handleRegister(action) { try { const res = yield call(apiAuth.register, action.payload); console.log(res); if (res?.status === 200) { yield put({ type: ACTION_CONST.REGISTER_SUCCESS, data: res.data.data.id, }); } } catch (error) { console.log(error); } }

function* handleFetchAllUser() { try { const res = yield call(apiAuth.fetchAlluser); console.log(res); } catch (error) { console.log(error); } }

export function watchSubmitLogin() { yield takeLatest(ACTION_CONST.LOGIN_REQUEST, handleLogin); } export function watchSubmitRegister() { yield takeLatest(ACTION_CONST.REGISTER_REQUEST, handleRegister); } export function* watchFetchAllUser() { yield takeLatest(ACTION_CONST.FETCH_ALL_USER, handleFetchAllUser); }

zezung27 commented 1 year ago

import { all, call } from "redux-saga/effects"; import { watchSubmitLogin, watchSubmitRegister, watchFetchAllUser, } from "./userSaga";

export default function* rootSaga() { yield all([ call(watchSubmitLogin), call(watchSubmitRegister), call(watchFetchAllUser), ]); }

zezung27 commented 1 year ago

import { createStore, applyMiddleware } from "redux";

import reducers from "./reducers";

//Redux saga import createSagaMiddleware from "redux-saga"; import rootSaga from "./saga/rootSaga"; import { configureStore } from "@reduxjs/toolkit";

const middleware = []; const sagaMiddleware = createSagaMiddleware();

middleware.push(sagaMiddleware);

// const appMiddleWare = applyMiddleware(...middleware);

const store = configureStore({ reducer: reducers, middleware: middleware, });

sagaMiddleware.run(rootSaga);

export default store;

zezung27 commented 1 year ago

import { ACTION_CONST } from "constants/actions";

const initState = { userInfo: null, tokens: [], uuid: null, listUser: null, };

const reducer = (state = initState, action) => { switch (action.type) { case ACTION_CONST.LOGIN_SUCCESS: return { ...state, tokens: action.data, }; case ACTION_CONST.REGISTER_SUCCESS: return { ...state, uuid: action.data, };

case ACTION_CONST.GET_ALL_USER:
  return {
    ...state,
    listUser: action.data,
  };
default:
  return state;

} };

export default reducer;

zezung27 commented 1 year ago

import { combineReducers } from "redux"; import user from "redux/reducers/userReducer";

const reducers = combineReducers({ user, });

export default reducers;

zezung27 commented 1 year ago

import AboutPage from "page/About/AboutPage";

import LoginPage from "page/Auth/LoginPage";

import RegisterPage from "page/Auth/RegisterPage";

import ResetAccount from "page/Auth/ResetAccount";

import PageNotFound from "page/error/PageNotFound";

import HomePage from "page/Home/HomePage";

import React, { useState } from "react";

import { createBrowserRouter, RouterProvider } from "react-router-dom";

const router = createBrowserRouter([

{

path: "/",

element: <HomePage />,

errorElement: <PageNotFound />,

},

{

path: "/about",

element: <AboutPage />,

},

{

path: "/register",

element: <RegisterPage />,

},

{

path: "/login",

element: <LoginPage />,

},

{

path: "/reset",

element: <ResetAccount />,

},

]);

const App = () => {

return ;

};

export default App;

zezung27 commented 1 year ago

import { Button, Form, Input } from "antd"; import React from "react"; import "./auth.scss"; import { Link } from "react-router-dom"; import { ROUTES } from "constants/routers";

const LoginPage = () => { const onFinish = (values) => { console.log(values); };

return ( <div className="container background-container" style={{ backgroundImage: "url(asset/bg5.jpg)" }}

Login to your account

          <Form layout="vertical" name="register form" onFinish={onFinish}>
            <Form.Item
              name="email"
              label="Email"
              rules={[
                { required: true, message: "Please enter your email" },
              ]}
              hasFeedback
            >
              <Input placeholder="Please enter your email" />
            </Form.Item>

            <Form.Item
              name="password"
              label="Password"
              rules={[
                { required: true, message: "Please enter you password" },
              ]}
              hasFeedback
            >
              <Input placeholder="Please enter your password" />
            </Form.Item>
            <div style={{ marginTop: 5, marginBottom: 5 }}>
              <Link to={ROUTES.FORGET_PASSWORD}>Forgot your password?</Link>
            </div>
            <Form.Item>
              <Button block type="primary" htmlType="submit">
                Login
              </Button>
            </Form.Item>

            <div className="text-center">
              <small>Don't have an account?</small>{" "}
              <Link to={ROUTES.REGISTER}>Register</Link>
            </div>
          </Form>
        </div>
      </div>
    </div>
  </div>
</div>

); };

export default LoginPage;

zezung27 commented 1 year ago

import { Button, Checkbox, Form, Input } from "antd"; import React from "react"; import "./auth.scss"; import { Link } from "react-router-dom"; import { ROUTES } from "constants/routers";

const RegisterPage = () => { const regex = /^(?=.?[A-Z])(?=.?[a-z])(?=.*?[0-9]).{8,}$/;

const onFinish = (values) => { console.log(values); };

return ( <div className="container background-container" style={{ backgroundImage: "url(asset/bg5.jpg)" }}

Register Account

          <Form layout="vertical" name="register form" onFinish={onFinish}>
            <Form.Item
              name="email"
              label="Email"
              rules={[
                { required: true, message: "Please enter your email" },
                { type: "email", message: "Please enter a valid mail" },
              ]}
              hasFeedback
            >
              <Input placeholder="Please enter your email" />
            </Form.Item>
            <Form.Item
              name="username"
              label="User Name"
              rules={[
                { required: true, message: "Please enter your user name" },
                { whitespace: true },
                { min: 6 },
                { max: 12 },
              ]}
              hasFeedback
            >
              <Input placeholder="Please enter your user name" />
            </Form.Item>
            <Form.Item
              name="password"
              label="Password"
              rules={[
                { required: true },
                { min: 8 },
                {
                  validator: (_, value) =>
                    value && regex.test(value)
                      ? Promise.resolve()
                      : Promise.reject(
                          "Minimum 8 characters, at least one uppercase English letter, one lowercase English letter and one number."
                        ),
                },
              ]}
              hasFeedback
            >
              <Input.Password
                placeholder="Please enter your password"
                value=""
              />
            </Form.Item>
            <Form.Item
              name="confirmPassword"
              label="Comfirm Password"
              rules={[
                { required: true },
                ({ getFieldValue }) => ({
                  validator(_, value) {
                    if (!value || getFieldValue("password") === value) {
                      return Promise.resolve();
                    }
                    return Promise.reject(
                      new Error(
                        "The two passwords that you entered do not match!"
                      )
                    );
                  },
                }),
              ]}
              hasFeedback
            >
              <Input.Password placeholder="Please comfirm your password" />
            </Form.Item>

            <Form.Item
              name="agreement"
              valuePropName="checked"
              rules={[
                {
                  validator: (_, value) =>
                    value
                      ? Promise.resolve()
                      : Promise.reject(
                          "To proceed, you need to agree with our terms and conditions"
                        ),
                },
              ]}
            >
              <Checkbox>
                {" "}
                Agree to our{" "}
                <Link to={ROUTES.TERMS}>
                  <span style={{ color: "blue" }}>
                    Terms and Conditions
                  </span>
                </Link>
              </Checkbox>
            </Form.Item>

            <Form.Item>
              <Button block type="primary" htmlType="submit">
                Register
              </Button>
            </Form.Item>

            <div className="text-center">
              <small>Already have an account?</small>{" "}
              <Link to={ROUTES.LOGIN}>Login</Link>
            </div>
          </Form>
        </div>
      </div>
    </div>
  </div>
</div>

); };

export default RegisterPage;

zezung27 commented 1 year ago

import { Button, Form, Input } from "antd"; import React from "react"; import "./auth.scss"; import { Link } from "react-router-dom"; import { ROUTES } from "constants/routers";

const ResetAccount = () => { const onFinish = (values) => { console.log(values); };

return ( <div className="container background-container" style={{ backgroundImage: "url(asset/bg5.jpg)" }}

Reset your password

Enter the email address associated with your account and we'll send you a link to reset your password.

          <Form layout="vertical" name="register form" onFinish={onFinish}>
            <Form.Item
              name="email"
              label="Email"
              rules={[
                { required: true, message: "Please enter your email" },
                { type: "email", message: "Please enter a valid mail" },
              ]}
              hasFeedback
            >
              <Input placeholder="Please enter your email" />
            </Form.Item>

            <Form.Item>
              <Button block type="primary" htmlType="submit">
                Reset Account
              </Button>
            </Form.Item>

            <div className="text-center">
              <Link to={ROUTES.REGISTER}>Return to login</Link>
            </div>
          </Form>
        </div>
      </div>
    </div>
  </div>
</div>

); };

export default ResetAccount;

zezung27 commented 1 year ago

"dependencies": {

"@reduxjs/toolkit": "^1.9.3",

"@testing-library/jest-dom": "^5.16.5",

"@testing-library/react": "^13.4.0",

"@testing-library/user-event": "^13.5.0",

"antd": "^5.3.2",

"axios": "^1.3.4",

"lodash": "^4.17.21",

"react": "^18.2.0",

"react-bootstrap": "^2.7.2",

"react-dom": "^18.2.0",

"react-icons": "^4.8.0",

"react-redux": "^8.0.5",

"react-router-dom": "^6.9.0",

"react-scripts": "5.0.1",

"redux": "^4.2.1",

"redux-logger": "^3.0.6",

"redux-saga": "^1.2.2",

"sass": "^1.59.3",

"web-vitals": "^2.1.4"

},

zezung27 commented 1 year ago

{ "compilerOptions": { "baseUrl": "src" } }