feat: add basic api service infrastructure and user api
This commit is contained in:
parent
e1b74e41b1
commit
08621df30b
@ -38,7 +38,7 @@ module.exports = {
|
||||
{
|
||||
name: "react-redux",
|
||||
importNames: ["useSelector", "useStore", "useDispatch"],
|
||||
message: "Please use pre-typed versions from `src/app/hooks.ts` instead.",
|
||||
message: "Please use pre-typed versions from `src/hooks/store.ts` instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
20
src/app/services/api.ts
Normal file
20
src/app/services/api.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createApi, fetchBaseQuery, retry } from "@reduxjs/toolkit/query/react";
|
||||
import type { RootState } from "../store";
|
||||
|
||||
import type { Wrap } from "./base";
|
||||
|
||||
const baseQuery = fetchBaseQuery({
|
||||
baseUrl: import.meta.env.MODE === "production" ? "/api/v1" : "http://127.0.0.1:8000/api/v1",
|
||||
prepareHeaders: (headers, { getState }) => {
|
||||
const token = (getState() as RootState).auth.token;
|
||||
if (token) headers.set("Authorization", `Bearer ${token}`);
|
||||
return headers;
|
||||
},
|
||||
validateStatus: (response, result: Wrap<unknown>) => response.status === 200 && result.code === 0,
|
||||
});
|
||||
|
||||
export const api = createApi({
|
||||
baseQuery: retry(baseQuery, { maxRetries: 6 }),
|
||||
tagTypes: ["User", "Status", "Submission"],
|
||||
endpoints: () => ({}),
|
||||
});
|
17
src/app/services/base.ts
Normal file
17
src/app/services/base.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export interface Wrap<T> {
|
||||
code: number;
|
||||
msg: string;
|
||||
body: T;
|
||||
}
|
||||
|
||||
export interface WithCount<T> {
|
||||
count: number;
|
||||
data: T[];
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
ID: number;
|
||||
CreatedAt: Date;
|
||||
UpdatedAt: Date;
|
||||
DeletedAt: Date;
|
||||
}
|
66
src/app/services/user.ts
Normal file
66
src/app/services/user.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import type { Meta, Wrap } from "./base";
|
||||
import { api } from "./api";
|
||||
|
||||
export enum UserRole {
|
||||
RoleAdmin = 30,
|
||||
RoleUser = 20,
|
||||
RoleGuest = 10,
|
||||
}
|
||||
|
||||
export interface UserRequest {
|
||||
email?: string;
|
||||
password?: string;
|
||||
nickname?: string;
|
||||
uid?: number;
|
||||
}
|
||||
|
||||
export interface UserLoginResponse {
|
||||
nickname: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface UserProfile {
|
||||
meta: Meta;
|
||||
email: string;
|
||||
nick_name: string;
|
||||
role: number;
|
||||
is_enabled: boolean;
|
||||
}
|
||||
|
||||
export const userApi = api.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
create: builder.mutation<Wrap<string>, UserRequest>({
|
||||
query: (data: UserRequest) => ({
|
||||
url: "/user/create",
|
||||
method: "POST",
|
||||
body: data,
|
||||
}),
|
||||
invalidatesTags: ["User", "Status", "Submission"],
|
||||
}),
|
||||
login: builder.mutation<Wrap<UserLoginResponse>, UserRequest>({
|
||||
query: (data: UserRequest) => ({
|
||||
url: "/user/login",
|
||||
method: "POST",
|
||||
body: data,
|
||||
}),
|
||||
invalidatesTags: ["User", "Status", "Submission"],
|
||||
}),
|
||||
logout: builder.mutation<Wrap<void>, void>({
|
||||
query: () => ({
|
||||
url: "/user/logout",
|
||||
method: "POST",
|
||||
}),
|
||||
invalidatesTags: ["User", "Status", "Submission"],
|
||||
}),
|
||||
profile: builder.query<Wrap<UserProfile>, UserRequest>({
|
||||
query: (data: UserRequest) => ({
|
||||
url: "/user/profile",
|
||||
method: "POST",
|
||||
body: data,
|
||||
}),
|
||||
providesTags: ["User"],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useCreateMutation, useLoginMutation, useLogoutMutation, useProfileQuery } = userApi;
|
@ -2,17 +2,23 @@ import type { Action, ThunkAction } from "@reduxjs/toolkit";
|
||||
import { combineSlices, configureStore } from "@reduxjs/toolkit";
|
||||
import { setupListeners } from "@reduxjs/toolkit/query";
|
||||
|
||||
import { userApi } from "./services/user";
|
||||
|
||||
import { counterSlice } from "../features/counter/counterSlice";
|
||||
import { quotesApiSlice } from "../features/quotes/quotesApiSlice";
|
||||
|
||||
const rootReducer = combineSlices(counterSlice, quotesApiSlice);
|
||||
const dataSlices = [counterSlice];
|
||||
const middlewareSlices = [quotesApiSlice, userApi];
|
||||
const slices = [...dataSlices, ...middlewareSlices];
|
||||
|
||||
const rootReducer = combineSlices(...slices);
|
||||
export type RootState = ReturnType<typeof rootReducer>;
|
||||
|
||||
export const makeStore = (preloadedState?: Partial<RootState>) => {
|
||||
const store = configureStore({
|
||||
reducer: rootReducer,
|
||||
middleware: (getDefaultMiddleware) => {
|
||||
return getDefaultMiddleware().concat(quotesApiSlice.middleware);
|
||||
return getDefaultMiddleware().concat(middlewareSlices.map((x) => x.middleware));
|
||||
},
|
||||
preloadedState,
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks/store";
|
||||
import styles from "./Counter.module.css";
|
||||
import {
|
||||
decrement,
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-restricted-imports */
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import type { AppDispatch, RootState } from "./store";
|
||||
import type { AppDispatch, RootState } from "../app/store";
|
||||
|
||||
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
|
||||
export const useAppSelector = useSelector.withTypes<RootState>();
|
Loading…
Reference in New Issue
Block a user