add v2 login page
This commit is contained in:
1 parent
659162d60c
commit
cb1e10f0a9
14 files changed
+445
-5
No files matched your search
Generated
+64
-1
@@ -11,6 +11,7 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@hello-pangea/dnd": "^18.0.0",
|
||||
"@hookform/resolvers": "5.2.2",
|
||||
"@mui/icons-material": "6.4.11",
|
||||
"@mui/lab": "6.0.0-dev.240424162023-9968b4889d",
|
||||
"@mui/material": "6.4.11",
|
||||
@@ -26,6 +27,7 @@
|
||||
"mui-color-input": "^6.0.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "7.63.0",
|
||||
"react-i18next": "^15.4.0",
|
||||
"react-icons": "5.5.0",
|
||||
"react-redux": "9.2.0",
|
||||
@@ -34,7 +36,9 @@
|
||||
"react-toastify": "^10.0.5",
|
||||
"recharts": "2.15.2",
|
||||
"redux-persist": "6.0.0",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
"swr": "2.3.6",
|
||||
"vite-plugin-svgr": "^4.2.0",
|
||||
"zod": "4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.66",
|
||||
@@ -653,6 +657,18 @@
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hookform/resolvers": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz",
|
||||
"integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/utils": "^0.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-hook-form": "^7.55.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
@@ -2991,6 +3007,15 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@@ -5343,6 +5368,22 @@
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.63.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.63.0.tgz",
|
||||
"integrity": "sha512-ZwueDMvUeucovM2VjkCf7zIHcs1aAlDimZu2Hvel5C5907gUzMpm4xCrQXtRzCvsBqFjonB4m3x4LzCFI1ZKWA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/react-hook-form"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "15.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.1.tgz",
|
||||
@@ -6141,6 +6182,19 @@
|
||||
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/swr": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
|
||||
"integrity": "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
@@ -6639,6 +6693,15 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz",
|
||||
"integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -16,6 +16,7 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@hello-pangea/dnd": "^18.0.0",
|
||||
"@hookform/resolvers": "5.2.2",
|
||||
"@mui/icons-material": "6.4.11",
|
||||
"@mui/lab": "6.0.0-dev.240424162023-9968b4889d",
|
||||
"@mui/material": "6.4.11",
|
||||
@@ -31,6 +32,7 @@
|
||||
"mui-color-input": "^6.0.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "7.63.0",
|
||||
"react-i18next": "^15.4.0",
|
||||
"react-icons": "5.5.0",
|
||||
"react-redux": "9.2.0",
|
||||
@@ -39,7 +41,9 @@
|
||||
"react-toastify": "^10.0.5",
|
||||
"recharts": "2.15.2",
|
||||
"redux-persist": "6.0.0",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
"swr": "2.3.6",
|
||||
"vite-plugin-svgr": "^4.2.0",
|
||||
"zod": "4.1.11"
|
||||
},
|
||||
"unusedDepencies": {
|
||||
"@solana/wallet-adapter-base": "0.9.25",
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Box from "@mui/material/Box";
|
||||
import type { TextFieldProps } from "@mui/material";
|
||||
export const TextInput = (props: TextFieldProps) => {
|
||||
return <TextField {...props} />;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useState } from "react";
|
||||
import useSWR from "swr";
|
||||
import type { SWRConfiguration } from "swr";
|
||||
import type { AxiosRequestConfig } from "axios";
|
||||
import { get, post } from "@/Utils/ApiClient"; // your axios wrapper
|
||||
|
||||
// Generic fetcher for GET requests
|
||||
const fetcher = async <T,>(url: string, config?: AxiosRequestConfig) => {
|
||||
const res = await get<T>(url, config);
|
||||
return res.data;
|
||||
};
|
||||
|
||||
export const useGet = <T,>(
|
||||
url: string,
|
||||
axiosConfig?: AxiosRequestConfig,
|
||||
swrConfig?: SWRConfiguration<T, Error>
|
||||
) => {
|
||||
const { data, error, isLoading, mutate } = useSWR<T>(
|
||||
url,
|
||||
(url) => fetcher<T>(url, axiosConfig),
|
||||
swrConfig
|
||||
);
|
||||
|
||||
return {
|
||||
response: data ?? null,
|
||||
loading: isLoading,
|
||||
error: error?.message ?? null,
|
||||
refetch: mutate,
|
||||
};
|
||||
};
|
||||
|
||||
export const usePost = <B = any, R = any>(endpoint: string) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const postFn = async (body: B, config?: AxiosRequestConfig): Promise<R | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const res = await post<R>(endpoint, body, config);
|
||||
return res.data;
|
||||
} catch (err: any) {
|
||||
setError(err?.message ?? "Unknown error");
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { post: postFn, loading, error };
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
import { TextInput } from "@/Components/Inputs/TextInput/indexV2.tsx";
|
||||
import Button from "@mui/material/Button";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { usePost } from "@/Hooks/v2/UseApi";
|
||||
const schema = z.object({
|
||||
email: z.email("Invalid email address"),
|
||||
password: z.string().min(6, "Password must be at least 6 characters"),
|
||||
});
|
||||
|
||||
type FormData = z.infer<typeof schema>;
|
||||
type LoginData = {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
const Login = () => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { post, loading, error } = usePost<FormData, LoginData>("/auth/login");
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(schema), // ⬅️ connect Zod
|
||||
defaultValues: {
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (data: FormData) => {
|
||||
const result = await post(data);
|
||||
if (result) {
|
||||
console.log("Login successful:", result);
|
||||
} else {
|
||||
console.error("Login failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
alignItems={"center"}
|
||||
justifyContent={"center"}
|
||||
minHeight="100vh"
|
||||
>
|
||||
<Stack
|
||||
component="form"
|
||||
padding={theme.spacing(8)}
|
||||
gap={theme.spacing(12)}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
maxWidth={400}
|
||||
sx={{
|
||||
width: {
|
||||
sm: "80%",
|
||||
md: "70%",
|
||||
lg: "65%",
|
||||
xl: "65%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.email.placeholder")}
|
||||
error={!!errors.email}
|
||||
helperText={errors.email ? errors.email.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
type="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
fullWidth
|
||||
placeholder="••••••••••"
|
||||
error={!!errors.password}
|
||||
helperText={errors.password ? errors.password.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
type="submit"
|
||||
sx={{ width: "100%", alignSelf: "center", fontWeight: 700 }}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { lightTheme, darkTheme } from "@/Utils/Theme/v2/theme";
|
||||
import { Navigate, Route, Routes as LibRoutes } from "react-router";
|
||||
import { ThemeProvider } from "@emotion/react";
|
||||
import HomeLayout from "../Components/Layouts/HomeLayout";
|
||||
import NotFound from "../Pages/v1/NotFound";
|
||||
|
||||
// Auth
|
||||
import AuthLogin from "../Pages/v1/Auth/Login";
|
||||
import AuthLoginV2 from "@/Pages/v2/Auth/Login";
|
||||
import AuthRegister from "../Pages/v1/Auth/Register/";
|
||||
import AuthForgotPassword from "../Pages/v1/Auth/ForgotPassword";
|
||||
import AuthCheckEmail from "../Pages/v1/Auth/CheckEmail";
|
||||
@@ -54,6 +57,8 @@ import BulkImport from "../Pages/v1/Uptime/BulkImport";
|
||||
import Logs from "../Pages/v1/Logs";
|
||||
|
||||
const Routes = () => {
|
||||
const mode = useSelector((state) => state.ui.mode);
|
||||
const v2Theme = mode === "light" ? lightTheme : darkTheme;
|
||||
const AdminCheckedRegister = withAdminCheck(AuthRegister);
|
||||
return (
|
||||
<LibRoutes>
|
||||
@@ -214,6 +219,14 @@ const Routes = () => {
|
||||
path="/login"
|
||||
element={<AuthLogin />}
|
||||
/>
|
||||
<Route
|
||||
path="/v2/login"
|
||||
element={
|
||||
<ThemeProvider theme={v2Theme}>
|
||||
<AuthLoginV2 />
|
||||
</ThemeProvider>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/register"
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import axios from "axios";
|
||||
import type { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
const BASE_URL = import.meta.env.VITE_APP_API_V2_BASE_URL;
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: BASE_URL || "http://localhost:55555/api/v2",
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
export const get = <T>(
|
||||
url: string,
|
||||
config: AxiosRequestConfig = {}
|
||||
): Promise<AxiosResponse<T>> => api.get<T>(url, config);
|
||||
|
||||
export const post = <T>(
|
||||
url: string,
|
||||
data: any,
|
||||
config: AxiosRequestConfig = {}
|
||||
): Promise<AxiosResponse<T>> => api.post<T>(url, data, config);
|
||||
|
||||
export default api;
|
||||
@@ -100,7 +100,6 @@ const newColors = {
|
||||
blueGray900: "#515151",
|
||||
blueBlueWave: "#1570EF",
|
||||
lightBlueWave: "#CDE2FF",
|
||||
/* I changed green 100 and green 700. Need to change red and warning as well, and refactor the object following the structure */
|
||||
green100: "#67cd78",
|
||||
green200: "#4B9B77",
|
||||
green400: "#079455",
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { lighten, darken } from "@mui/material/styles";
|
||||
|
||||
const typographyBase = 13;
|
||||
|
||||
export const typographyLevels = {
|
||||
base: typographyBase,
|
||||
xs: `${(typographyBase - 4) / 16}rem`,
|
||||
s: `${(typographyBase - 2) / 16}rem`,
|
||||
m: `${typographyBase / 16}rem`,
|
||||
l: `${(typographyBase + 2) / 16}rem`,
|
||||
xl: `${(typographyBase + 10) / 16}rem`,
|
||||
};
|
||||
|
||||
const colors = {
|
||||
offWhite: "#FEFEFE",
|
||||
offBlack: "#131315",
|
||||
gray0: "#FDFDFD",
|
||||
gray10: "#F4F4FF",
|
||||
gray50: "#F9F9F9",
|
||||
gray100: "#F3F3F3",
|
||||
gray200: "#EFEFEF",
|
||||
gray250: "#DADADA",
|
||||
gray500: "#A2A3A3",
|
||||
gray900: "#1c1c1c",
|
||||
blueGray50: "#E8F0FE",
|
||||
blueGray500: "#475467",
|
||||
blueGray600: "#344054",
|
||||
blueGray800: "#1C2130",
|
||||
blueGray900: "#515151",
|
||||
blueBlueWave: "#1570EF",
|
||||
lightBlueWave: "#CDE2FF",
|
||||
green100: "#67cd78",
|
||||
green200: "#4B9B77",
|
||||
green400: "#079455",
|
||||
green700: "#026513",
|
||||
orange100: "#FD8F22",
|
||||
orange200: "#D69A5D",
|
||||
orange600: "#9B734B",
|
||||
orange700: "#884605",
|
||||
red100: "#F27C7C",
|
||||
red400: "#D92020",
|
||||
red600: "#9B4B4B",
|
||||
red700: "#980303",
|
||||
};
|
||||
|
||||
export const lightPalette = {
|
||||
accent: {
|
||||
main: colors.blueBlueWave,
|
||||
light: lighten(colors.blueBlueWave, 0.2),
|
||||
dark: darken(colors.blueBlueWave, 0.2),
|
||||
contrastText: colors.offWhite,
|
||||
},
|
||||
primary: {
|
||||
main: colors.offWhite,
|
||||
contrastText: colors.blueGray800,
|
||||
contrastTextSecondary: colors.blueGray600,
|
||||
},
|
||||
secondary: {
|
||||
main: colors.gray200,
|
||||
light: colors.lightBlueWave,
|
||||
contrastText: colors.blueGray600,
|
||||
},
|
||||
};
|
||||
|
||||
export const darkPalette = {
|
||||
accent: {
|
||||
main: colors.blueBlueWave,
|
||||
light: lighten(colors.blueBlueWave, 0.2),
|
||||
dark: darken(colors.blueBlueWave, 0.2),
|
||||
contrastText: colors.offWhite,
|
||||
},
|
||||
primary: {
|
||||
main: colors.offBlack,
|
||||
contrastText: colors.blueGray50,
|
||||
contrastTextSecondary: colors.gray200,
|
||||
},
|
||||
secondary: {
|
||||
main: "#313131",
|
||||
light: colors.lightBlueWave,
|
||||
contrastText: colors.gray200,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import { createTheme } from "@mui/material";
|
||||
import { lightPalette, darkPalette, typographyLevels } from "./palette";
|
||||
const fontFamilyPrimary = '"Inter" , sans-serif';
|
||||
|
||||
export const theme = (mode: string, palette: any) =>
|
||||
createTheme({
|
||||
spacing: 2,
|
||||
palette: {
|
||||
mode: mode,
|
||||
...palette,
|
||||
},
|
||||
typography: {
|
||||
fontFamily: fontFamilyPrimary,
|
||||
fontSize: typographyLevels.base,
|
||||
},
|
||||
|
||||
components: {
|
||||
MuiFormLabel: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
fontSize: typographyLevels.base,
|
||||
"&.Mui-focused": {
|
||||
color: theme.palette.secondary.contrastText,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiInputLabel: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
top: `-${theme.spacing(4)}`,
|
||||
"&.MuiInputLabel-shrink": {
|
||||
top: 0,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiTextField: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
"& .MuiOutlinedInput-root": {
|
||||
height: 34,
|
||||
fontSize: typographyLevels.base,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const lightTheme = createTheme(theme("light", lightPalette));
|
||||
export const darkTheme = createTheme(theme("dark", darkPalette));
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_APP_API_V2_BASE_URL?: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
import "@mui/material/Button";
|
||||
|
||||
declare module "@mui/material/styles" {
|
||||
interface Palette {
|
||||
accent: Palette["primary"];
|
||||
}
|
||||
interface PaletteOptions {
|
||||
accent?: PaletteOptions["primary"];
|
||||
}
|
||||
|
||||
interface PaletteColor {
|
||||
contrastTextSecondary?: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module "@mui/material/Button" {
|
||||
interface ButtonPropsColorOverrides {
|
||||
accent: true;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import svgr from "vite-plugin-svgr";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig(({}) => {
|
||||
let version = "3.2.0";
|
||||
@@ -8,6 +9,11 @@ export default defineConfig(({}) => {
|
||||
return {
|
||||
base: "/",
|
||||
plugins: [svgr(), react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ["@mui/material/Tooltip", "@emotion/styled"],
|
||||
},
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ export const createApp = ({ services, controllers, envSettings, frontendPath, op
|
||||
cors({
|
||||
origin: allowedOrigin,
|
||||
methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
|
||||
allowedHeaders: "*",
|
||||
allowedHeaders: ["Content-Type", "Authorization"],
|
||||
credentials: true,
|
||||
})
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user