feat: login config
This commit is contained in:
@@ -1,106 +1,100 @@
|
|||||||
import { fetchBaseQuery } from "@reduxjs/toolkit/query";
|
import { fetchBaseQuery } from '@reduxjs/toolkit/query';
|
||||||
import toast from "react-hot-toast";
|
import toast from 'react-hot-toast';
|
||||||
import dayjs from "dayjs";
|
import dayjs from 'dayjs';
|
||||||
import { updateToken, resetAuthData } from "../slices/auth.data";
|
import { updateToken, resetAuthData } from '../slices/auth.data';
|
||||||
import BASE_URL, { tokenHeader } from "../config";
|
import BASE_URL, { tokenHeader } from '../config';
|
||||||
const whiteList = [
|
const whiteList = [
|
||||||
"login",
|
'login',
|
||||||
"register",
|
'register',
|
||||||
"checkInviteTokenValid",
|
'checkInviteTokenValid',
|
||||||
"getServer",
|
'getLoginConfig',
|
||||||
"getOpenid",
|
'getServer',
|
||||||
"getMetamaskNonce",
|
'getOpenid',
|
||||||
"renew",
|
'getMetamaskNonce',
|
||||||
|
'renew'
|
||||||
];
|
];
|
||||||
const baseQuery = fetchBaseQuery({
|
const baseQuery = fetchBaseQuery({
|
||||||
baseUrl: BASE_URL,
|
baseUrl: BASE_URL,
|
||||||
prepareHeaders: (headers, { getState, endpoint }) => {
|
prepareHeaders: (headers, { getState, endpoint }) => {
|
||||||
console.log("req", endpoint);
|
console.log('req', endpoint);
|
||||||
const { token } = getState().authData;
|
const { token } = getState().authData;
|
||||||
if (token && !whiteList.includes(endpoint)) {
|
if (token && !whiteList.includes(endpoint)) {
|
||||||
headers.set(tokenHeader, token);
|
headers.set(tokenHeader, token);
|
||||||
}
|
}
|
||||||
return headers;
|
return headers;
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
let waitingForRenew = null;
|
let waitingForRenew = null;
|
||||||
const baseQueryWithTokenCheck = async (args, api, extraOptions) => {
|
const baseQueryWithTokenCheck = async (args, api, extraOptions) => {
|
||||||
if (waitingForRenew) {
|
if (waitingForRenew) {
|
||||||
await waitingForRenew;
|
await waitingForRenew;
|
||||||
}
|
}
|
||||||
// 先检查token是否过期,过期则renew
|
// 先检查token是否过期,过期则renew
|
||||||
const {
|
const { token, refreshToken, expireTime = new Date().getTime() } = api.getState().authData;
|
||||||
token,
|
let result = null;
|
||||||
refreshToken,
|
if (!whiteList.includes(api.endpoint) && dayjs().isAfter(new Date(expireTime - 20 * 1000))) {
|
||||||
expireTime = new Date().getTime(),
|
// 快过期了,renew
|
||||||
} = api.getState().authData;
|
waitingForRenew = baseQuery(
|
||||||
let result = null;
|
{
|
||||||
if (
|
url: '/token/renew',
|
||||||
!whiteList.includes(api.endpoint) &&
|
method: 'POST',
|
||||||
dayjs().isAfter(new Date(expireTime - 20 * 1000))
|
body: {
|
||||||
) {
|
token,
|
||||||
// 快过期了,renew
|
refresh_token: refreshToken
|
||||||
waitingForRenew = baseQuery(
|
|
||||||
{
|
|
||||||
url: "/token/renew",
|
|
||||||
method: "POST",
|
|
||||||
body: {
|
|
||||||
token,
|
|
||||||
refresh_token: refreshToken,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
api,
|
|
||||||
extraOptions
|
|
||||||
);
|
|
||||||
result = await waitingForRenew;
|
|
||||||
waitingForRenew = null;
|
|
||||||
if (result.data) {
|
|
||||||
// store the new token
|
|
||||||
api.dispatch(updateToken(result.data));
|
|
||||||
// retry the initial query
|
|
||||||
result = await baseQuery(args, api, extraOptions);
|
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
result = await baseQuery(args, api, extraOptions);
|
api,
|
||||||
|
extraOptions
|
||||||
|
);
|
||||||
|
result = await waitingForRenew;
|
||||||
|
waitingForRenew = null;
|
||||||
|
if (result.data) {
|
||||||
|
// store the new token
|
||||||
|
api.dispatch(updateToken(result.data));
|
||||||
|
// retry the initial query
|
||||||
|
result = await baseQuery(args, api, extraOptions);
|
||||||
}
|
}
|
||||||
if (result?.error) {
|
} else {
|
||||||
console.log("api error", result.error, api.endpoint);
|
result = await baseQuery(args, api, extraOptions);
|
||||||
switch (result.error.originalStatus || result.error.status) {
|
}
|
||||||
case "FETCH_ERROR":
|
if (result?.error) {
|
||||||
{
|
console.log('api error', result.error, api.endpoint);
|
||||||
toast.error(`${api.endpoint}: Failed to fetch`);
|
switch (result.error.originalStatus || result.error.status) {
|
||||||
}
|
case 'FETCH_ERROR':
|
||||||
break;
|
{
|
||||||
case 404:
|
toast.error(`${api.endpoint}: Failed to fetch`);
|
||||||
{
|
|
||||||
toast.error("Request Not Found");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 500:
|
|
||||||
{
|
|
||||||
toast.error(result.error.data || "server error");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 401:
|
|
||||||
{
|
|
||||||
// if (api.endpoint === "renew") {
|
|
||||||
// toast.error("token expired, please login again");
|
|
||||||
api.dispatch(resetAuthData());
|
|
||||||
location.href = "/#/login";
|
|
||||||
// } else {
|
|
||||||
toast.error("API Not Authenticated");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
toast.error("Request Not Allowed");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
{
|
||||||
|
toast.error('Request Not Found');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
{
|
||||||
|
toast.error(result.error.data || 'server error');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
{
|
||||||
|
// if (api.endpoint === "renew") {
|
||||||
|
// toast.error("token expired, please login again");
|
||||||
|
api.dispatch(resetAuthData());
|
||||||
|
location.href = '/#/login';
|
||||||
|
// } else {
|
||||||
|
toast.error('API Not Authenticated');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
toast.error('Request Not Allowed');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default baseQueryWithTokenCheck;
|
export default baseQueryWithTokenCheck;
|
||||||
|
|||||||
+149
-140
@@ -1,147 +1,156 @@
|
|||||||
import { createApi } from "@reduxjs/toolkit/query/react";
|
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||||
import BASE_URL from "../config";
|
import BASE_URL from '../config';
|
||||||
import { updateInviteLink, updateInfo } from "../slices/server";
|
import { updateInviteLink, updateInfo } from '../slices/server';
|
||||||
import baseQuery from "./base.query";
|
import baseQuery from './base.query';
|
||||||
const defaultExpireDuration = 7 * 24 * 60 * 60;
|
const defaultExpireDuration = 7 * 24 * 60 * 60;
|
||||||
export const serverApi = createApi({
|
export const serverApi = createApi({
|
||||||
reducerPath: "serverApi",
|
reducerPath: 'serverApi',
|
||||||
baseQuery,
|
baseQuery,
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getServer: builder.query({
|
getServer: builder.query({
|
||||||
query: () => ({ url: `admin/system/organization` }),
|
query: () => ({ url: `admin/system/organization` }),
|
||||||
transformResponse: (data) => {
|
transformResponse: (data) => {
|
||||||
data.logo = `${BASE_URL}/resource/organization/logo?t=${new Date().getTime()}`;
|
data.logo = `${BASE_URL}/resource/organization/logo?t=${new Date().getTime()}`;
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
||||||
try {
|
try {
|
||||||
const { data: server } = await queryFulfilled;
|
const { data: server } = await queryFulfilled;
|
||||||
dispatch(updateInfo(server));
|
dispatch(updateInfo(server));
|
||||||
} catch {
|
} catch {
|
||||||
console.log("get server info error");
|
console.log('get server info error');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
|
||||||
getMetrics: builder.query({
|
|
||||||
query: () => ({ url: `/admin/system/metrics` }),
|
|
||||||
}),
|
|
||||||
getFirebaseConfig: builder.query({
|
|
||||||
query: () => ({ url: `admin/fcm/config` }),
|
|
||||||
}),
|
|
||||||
sendTestEmail: builder.mutation({
|
|
||||||
query: (data) => ({
|
|
||||||
url: `/admin/system/send_mail`,
|
|
||||||
method: "POST",
|
|
||||||
body: data,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
updateFirebaseConfig: builder.mutation({
|
|
||||||
query: (data) => ({
|
|
||||||
url: `admin/fcm/config`,
|
|
||||||
method: "POST",
|
|
||||||
body: data,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
getAgoraConfig: builder.query({
|
|
||||||
query: () => ({ url: `admin/agora/config` }),
|
|
||||||
}),
|
|
||||||
updateAgoraConfig: builder.mutation({
|
|
||||||
query: (data) => ({
|
|
||||||
url: `admin/agora/config`,
|
|
||||||
method: "POST",
|
|
||||||
body: data,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
getSMTPConfig: builder.query({
|
|
||||||
query: () => ({ url: `admin/smtp/config` }),
|
|
||||||
}),
|
|
||||||
updateSMTPConfig: builder.mutation({
|
|
||||||
query: (data) => ({
|
|
||||||
url: `admin/smtp/config`,
|
|
||||||
method: "POST",
|
|
||||||
body: data,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
updateLogo: builder.mutation({
|
|
||||||
query: (data) => ({
|
|
||||||
headers: {
|
|
||||||
"content-type": "image/png",
|
|
||||||
},
|
|
||||||
url: `admin/system/organization/logo`,
|
|
||||||
method: "POST",
|
|
||||||
body: data,
|
|
||||||
}),
|
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
|
||||||
try {
|
|
||||||
await queryFulfilled;
|
|
||||||
dispatch(
|
|
||||||
updateInfo({
|
|
||||||
logo: `${BASE_URL}/resource/organization/logo?t=${new Date().getTime()}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
console.log("update server logo error");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
createInviteLink: builder.query({
|
|
||||||
query: (expired_in = defaultExpireDuration) => ({
|
|
||||||
headers: {
|
|
||||||
"content-type": "text/plain",
|
|
||||||
accept: "text/plain",
|
|
||||||
},
|
|
||||||
url: `/admin/user/create_invite_link?expired_in=${expired_in}`,
|
|
||||||
responseHandler: (response) => response.text(),
|
|
||||||
}),
|
|
||||||
transformResponse: (link) => {
|
|
||||||
// 替换掉域名
|
|
||||||
const invite = new URL(link);
|
|
||||||
return `${location.origin}${invite.pathname}${invite.search}${invite.hash}`;
|
|
||||||
},
|
|
||||||
async onQueryStarted(
|
|
||||||
expire = defaultExpireDuration,
|
|
||||||
{ dispatch, queryFulfilled }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const { data: link } = await queryFulfilled;
|
|
||||||
console.log("link", link);
|
|
||||||
dispatch(updateInviteLink({ expire, link }));
|
|
||||||
} catch {
|
|
||||||
console.log("invite link error");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
updateServer: builder.mutation({
|
|
||||||
query: (data) => ({
|
|
||||||
url: `admin/system/organization`,
|
|
||||||
method: "POST",
|
|
||||||
body: data,
|
|
||||||
}),
|
|
||||||
async onQueryStarted(data, { dispatch, queryFulfilled, getState }) {
|
|
||||||
const { name: prevName, description: prevDesc } = getState().server;
|
|
||||||
dispatch(updateInfo(data));
|
|
||||||
try {
|
|
||||||
await queryFulfilled;
|
|
||||||
} catch {
|
|
||||||
dispatch(updateInfo({ name: prevName, description: prevDesc }));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
|
getMetrics: builder.query({
|
||||||
|
query: () => ({ url: `/admin/system/metrics` })
|
||||||
|
}),
|
||||||
|
getFirebaseConfig: builder.query({
|
||||||
|
query: () => ({ url: `admin/fcm/config` })
|
||||||
|
}),
|
||||||
|
sendTestEmail: builder.mutation({
|
||||||
|
query: (data) => ({
|
||||||
|
url: `/admin/system/send_mail`,
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
updateFirebaseConfig: builder.mutation({
|
||||||
|
query: (data) => ({
|
||||||
|
url: `admin/fcm/config`,
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
getAgoraConfig: builder.query({
|
||||||
|
query: () => ({ url: `admin/agora/config` })
|
||||||
|
}),
|
||||||
|
updateAgoraConfig: builder.mutation({
|
||||||
|
query: (data) => ({
|
||||||
|
url: `admin/agora/config`,
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
getSMTPConfig: builder.query({
|
||||||
|
query: () => ({ url: `admin/smtp/config` })
|
||||||
|
}),
|
||||||
|
updateSMTPConfig: builder.mutation({
|
||||||
|
query: (data) => ({
|
||||||
|
url: `admin/smtp/config`,
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
getLoginConfig: builder.query({
|
||||||
|
query: () => ({ url: `admin/login/config` })
|
||||||
|
}),
|
||||||
|
updateLoginConfig: builder.mutation({
|
||||||
|
query: (data) => ({
|
||||||
|
url: `admin/login/config`,
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
updateLogo: builder.mutation({
|
||||||
|
query: (data) => ({
|
||||||
|
headers: {
|
||||||
|
'content-type': 'image/png'
|
||||||
|
},
|
||||||
|
url: `admin/system/organization/logo`,
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
}),
|
||||||
|
async onQueryStarted(data, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
await queryFulfilled;
|
||||||
|
dispatch(
|
||||||
|
updateInfo({
|
||||||
|
logo: `${BASE_URL}/resource/organization/logo?t=${new Date().getTime()}`
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
console.log('update server logo error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
createInviteLink: builder.query({
|
||||||
|
query: (expired_in = defaultExpireDuration) => ({
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/plain',
|
||||||
|
accept: 'text/plain'
|
||||||
|
},
|
||||||
|
url: `/admin/user/create_invite_link?expired_in=${expired_in}`,
|
||||||
|
responseHandler: (response) => response.text()
|
||||||
|
}),
|
||||||
|
transformResponse: (link) => {
|
||||||
|
// 替换掉域名
|
||||||
|
const invite = new URL(link);
|
||||||
|
return `${location.origin}${invite.pathname}${invite.search}${invite.hash}`;
|
||||||
|
},
|
||||||
|
async onQueryStarted(expire = defaultExpireDuration, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: link } = await queryFulfilled;
|
||||||
|
console.log('link', link);
|
||||||
|
dispatch(updateInviteLink({ expire, link }));
|
||||||
|
} catch {
|
||||||
|
console.log('invite link error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
updateServer: builder.mutation({
|
||||||
|
query: (data) => ({
|
||||||
|
url: `admin/system/organization`,
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
}),
|
||||||
|
async onQueryStarted(data, { dispatch, queryFulfilled, getState }) {
|
||||||
|
const { name: prevName, description: prevDesc } = getState().server;
|
||||||
|
dispatch(updateInfo(data));
|
||||||
|
try {
|
||||||
|
await queryFulfilled;
|
||||||
|
} catch {
|
||||||
|
dispatch(updateInfo({ name: prevName, description: prevDesc }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
useSendTestEmailMutation,
|
useSendTestEmailMutation,
|
||||||
useUpdateFirebaseConfigMutation,
|
useUpdateFirebaseConfigMutation,
|
||||||
useGetFirebaseConfigQuery,
|
useGetFirebaseConfigQuery,
|
||||||
useGetSMTPConfigQuery,
|
useGetLoginConfigQuery,
|
||||||
useUpdateSMTPConfigMutation,
|
useUpdateLoginConfigMutation,
|
||||||
useGetAgoraConfigQuery,
|
useGetSMTPConfigQuery,
|
||||||
useUpdateAgoraConfigMutation,
|
useUpdateSMTPConfigMutation,
|
||||||
useGetServerQuery,
|
useGetAgoraConfigQuery,
|
||||||
useLazyGetMetricsQuery,
|
useUpdateAgoraConfigMutation,
|
||||||
useLazyGetServerQuery,
|
useGetServerQuery,
|
||||||
useUpdateServerMutation,
|
useLazyGetMetricsQuery,
|
||||||
useUpdateLogoMutation,
|
useLazyGetServerQuery,
|
||||||
useLazyCreateInviteLinkQuery,
|
useUpdateServerMutation,
|
||||||
|
useUpdateLogoMutation,
|
||||||
|
useLazyCreateInviteLinkQuery
|
||||||
} = serverApi;
|
} = serverApi;
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// import { useState, useEffect } from "react";
|
||||||
|
import StyledContainer from './StyledContainer';
|
||||||
|
// import Input from "../../styled/Input";
|
||||||
|
import Textarea from '../../styled/Textarea';
|
||||||
|
import Toggle from '../../styled/Toggle';
|
||||||
|
import Label from '../../styled/Label';
|
||||||
|
import SaveTip from '../../SaveTip';
|
||||||
|
import useConfig from './useConfig';
|
||||||
|
|
||||||
|
export default function Logins() {
|
||||||
|
const { values, updateConfig, setValues, reset, changed } = useConfig('login');
|
||||||
|
const handleUpdate = () => {
|
||||||
|
// const { token_url, description } = values;
|
||||||
|
updateConfig(values);
|
||||||
|
};
|
||||||
|
const handleChange = (evt) => {
|
||||||
|
const newValue = evt.target.value;
|
||||||
|
const { type } = evt.target.dataset;
|
||||||
|
const items = newValue ? newValue.split('\n') : [];
|
||||||
|
setValues((prev) => {
|
||||||
|
return { ...prev, [type]: items };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleToggle = (val) => {
|
||||||
|
setValues((prev) => {
|
||||||
|
return { ...prev, ...val };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (!values) return null;
|
||||||
|
const { google, metamask, password, oidc = [] } = values ?? {};
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<div className="inputs">
|
||||||
|
<div className="input row">
|
||||||
|
<Label>Password</Label>
|
||||||
|
<Toggle
|
||||||
|
onClick={handleToggle.bind(null, { password: !password })}
|
||||||
|
data-checked={password}
|
||||||
|
></Toggle>
|
||||||
|
</div>
|
||||||
|
<div className="input row">
|
||||||
|
<Label>Google</Label>
|
||||||
|
<Toggle onClick={handleToggle.bind(null, { google: !google })} data-checked={google}></Toggle>
|
||||||
|
</div>
|
||||||
|
<div className="input row">
|
||||||
|
<Label>Metamask</Label>
|
||||||
|
<Toggle
|
||||||
|
onClick={handleToggle.bind(null, { metamask: !metamask })}
|
||||||
|
data-checked={metamask}
|
||||||
|
></Toggle>
|
||||||
|
</div>
|
||||||
|
<div className="input">
|
||||||
|
<Label htmlFor="desc">OIDC</Label>
|
||||||
|
<Textarea
|
||||||
|
rows={10}
|
||||||
|
data-type="oidc"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={oidc.join('\n')}
|
||||||
|
name="oidc"
|
||||||
|
placeholder="Input issuer list, one line, one issuer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <div className="input">
|
||||||
|
<Label htmlFor="name">Token Url</Label>
|
||||||
|
<Input
|
||||||
|
disabled={!enabled}
|
||||||
|
data-type="token_url"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={token_url || "https://oauth2.googleapis.com/token"}
|
||||||
|
name="token_url"
|
||||||
|
placeholder="Token URL"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="input">
|
||||||
|
<Label htmlFor="desc">Project ID</Label>
|
||||||
|
<Input
|
||||||
|
disabled={!enabled}
|
||||||
|
type={"number"}
|
||||||
|
data-type="project_id"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={project_id}
|
||||||
|
name="project_id"
|
||||||
|
placeholder="Project ID"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="input">
|
||||||
|
<Label htmlFor="desc">Private Key</Label>
|
||||||
|
<Textarea
|
||||||
|
rows={10}
|
||||||
|
disabled={!enabled}
|
||||||
|
data-type="private_key"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={private_key}
|
||||||
|
name="private_key"
|
||||||
|
placeholder="Private key"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="input">
|
||||||
|
<Label htmlFor="desc">Client Email</Label>
|
||||||
|
<Input
|
||||||
|
disabled={!enabled}
|
||||||
|
data-type="client_email"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={client_email}
|
||||||
|
name="client_email"
|
||||||
|
placeholder="Client Email address"
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
{changed && <SaveTip saveHandler={handleUpdate} resetHandler={reset} />}
|
||||||
|
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,96 +1,92 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from 'react';
|
||||||
import { isObjectEqual } from "../../../utils";
|
import { isObjectEqual } from '../../../utils';
|
||||||
import toast from "react-hot-toast";
|
import toast from 'react-hot-toast';
|
||||||
import {
|
import {
|
||||||
useGetAgoraConfigQuery,
|
useGetAgoraConfigQuery,
|
||||||
useGetFirebaseConfigQuery,
|
useGetFirebaseConfigQuery,
|
||||||
useGetSMTPConfigQuery,
|
useGetSMTPConfigQuery,
|
||||||
useUpdateSMTPConfigMutation,
|
useGetLoginConfigQuery,
|
||||||
useUpdateAgoraConfigMutation,
|
useUpdateLoginConfigMutation,
|
||||||
useUpdateFirebaseConfigMutation,
|
useUpdateSMTPConfigMutation,
|
||||||
} from "../../../../app/services/server";
|
useUpdateAgoraConfigMutation,
|
||||||
export default function useConfig(config = "smtp") {
|
useUpdateFirebaseConfigMutation
|
||||||
const [changed, setChanged] = useState(false);
|
} from '../../../../app/services/server';
|
||||||
const [values, setValues] = useState({});
|
export default function useConfig(config = 'smtp') {
|
||||||
const { data: SMTP, refetch: refetchSMTP } = useGetSMTPConfigQuery();
|
const [changed, setChanged] = useState(false);
|
||||||
const [
|
const [values, setValues] = useState({});
|
||||||
updateSMTPConfig,
|
const { data: Login, refetch: refetchLogin } = useGetLoginConfigQuery();
|
||||||
{ isSuccess: SMTPUpdated },
|
const [updateLoginConfig, { isSuccess: LoginUpdated }] = useUpdateLoginConfigMutation();
|
||||||
] = useUpdateSMTPConfigMutation();
|
const { data: SMTP, refetch: refetchSMTP } = useGetSMTPConfigQuery();
|
||||||
const { data: Agora, refetch: refetchAgora } = useGetAgoraConfigQuery();
|
const [updateSMTPConfig, { isSuccess: SMTPUpdated }] = useUpdateSMTPConfigMutation();
|
||||||
const [
|
const { data: Agora, refetch: refetchAgora } = useGetAgoraConfigQuery();
|
||||||
updateAgoraConfig,
|
const [updateAgoraConfig, { isSuccess: AgoraUpdated }] = useUpdateAgoraConfigMutation();
|
||||||
{ isSuccess: AgoraUpdated },
|
const { data: Firebase, refetch: refetchFirebase } = useGetFirebaseConfigQuery();
|
||||||
] = useUpdateAgoraConfigMutation();
|
const [updateFirebaseConfig, { isSuccess: FirebaseUpdated }] = useUpdateFirebaseConfigMutation();
|
||||||
const {
|
|
||||||
data: Firebase,
|
|
||||||
refetch: refetchFirebase,
|
|
||||||
} = useGetFirebaseConfigQuery();
|
|
||||||
const [
|
|
||||||
updateFirebaseConfig,
|
|
||||||
{ isSuccess: FirebaseUpdated },
|
|
||||||
] = useUpdateFirebaseConfigMutation();
|
|
||||||
|
|
||||||
const datas = {
|
const datas = {
|
||||||
smtp: SMTP,
|
login: Login,
|
||||||
agora: Agora,
|
smtp: SMTP,
|
||||||
firebase: Firebase,
|
agora: Agora,
|
||||||
};
|
firebase: Firebase
|
||||||
const updateFns = {
|
};
|
||||||
smtp: updateSMTPConfig,
|
const updateFns = {
|
||||||
agora: updateAgoraConfig,
|
login: updateLoginConfig,
|
||||||
firebase: updateFirebaseConfig,
|
smtp: updateSMTPConfig,
|
||||||
};
|
agora: updateAgoraConfig,
|
||||||
const refetchs = {
|
firebase: updateFirebaseConfig
|
||||||
smtp: refetchSMTP,
|
};
|
||||||
agora: refetchAgora,
|
const refetchs = {
|
||||||
firebase: refetchFirebase,
|
smtp: refetchSMTP,
|
||||||
};
|
agora: refetchAgora,
|
||||||
const updateds = {
|
firebase: refetchFirebase,
|
||||||
smtp: SMTPUpdated,
|
login: refetchLogin
|
||||||
agora: AgoraUpdated,
|
};
|
||||||
firebase: FirebaseUpdated,
|
const updateds = {
|
||||||
};
|
login: LoginUpdated,
|
||||||
const data = datas[config];
|
smtp: SMTPUpdated,
|
||||||
const updateConfig = updateFns[config];
|
agora: AgoraUpdated,
|
||||||
const refetch = refetchs[config];
|
firebase: FirebaseUpdated
|
||||||
const updated = updateds[config];
|
};
|
||||||
const reset = () => {
|
const data = datas[config];
|
||||||
setValues(data ?? {});
|
const updateConfig = updateFns[config];
|
||||||
};
|
const refetch = refetchs[config];
|
||||||
|
const updated = updateds[config];
|
||||||
|
const reset = () => {
|
||||||
|
setValues(data ?? {});
|
||||||
|
};
|
||||||
|
|
||||||
const toggleEnable = () => {
|
const toggleEnable = () => {
|
||||||
setValues((prev) => {
|
setValues((prev) => {
|
||||||
return { ...prev, enabled: !prev.enabled };
|
return { ...prev, enabled: !prev.enabled };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (updated) {
|
if (updated) {
|
||||||
toast.success("Configuration Updated!");
|
toast.success('Configuration Updated!');
|
||||||
refetch();
|
refetch();
|
||||||
}
|
}
|
||||||
}, [updated]);
|
}, [updated]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("wtf", data);
|
console.log('wtf', data);
|
||||||
// if (data) {
|
// if (data) {
|
||||||
setValues(data ?? {});
|
setValues(data ?? {});
|
||||||
// }
|
// }
|
||||||
}, [data]);
|
}, [data]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// if (data && values) {
|
// if (data && values) {
|
||||||
if (!isObjectEqual(data, values)) {
|
if (!isObjectEqual(data, values)) {
|
||||||
setChanged(true);
|
setChanged(true);
|
||||||
} else {
|
} else {
|
||||||
setChanged(false);
|
setChanged(false);
|
||||||
}
|
}
|
||||||
// }
|
// }
|
||||||
}, [data, values]);
|
}, [data, values]);
|
||||||
return {
|
return {
|
||||||
reset,
|
reset,
|
||||||
changed,
|
changed,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
values,
|
values,
|
||||||
setValues,
|
setValues,
|
||||||
toggleEnable,
|
toggleEnable
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +1,104 @@
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from 'react-redux';
|
||||||
import MyAccount from "./MyAccount";
|
import MyAccount from './MyAccount';
|
||||||
import Overview from "./Overview";
|
import Overview from './Overview';
|
||||||
import ConfigFirebase from "./config/Firebase";
|
import Logins from './config/Logins';
|
||||||
import ConfigSMTP from "./config/SMTP";
|
import ConfigFirebase from './config/Firebase';
|
||||||
import Notifications from "./Notifications";
|
import ConfigSMTP from './config/SMTP';
|
||||||
import ManageMembers from "../ManageMembers";
|
import Notifications from './Notifications';
|
||||||
import FAQ from "../FAQ";
|
import ManageMembers from '../ManageMembers';
|
||||||
import ConfigAgora from "./config/Agora";
|
import FAQ from '../FAQ';
|
||||||
|
import ConfigAgora from './config/Agora';
|
||||||
const navs = [
|
const navs = [
|
||||||
{
|
{
|
||||||
title: "General",
|
title: 'General',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
name: "overview",
|
name: 'overview',
|
||||||
title: "Overview",
|
title: 'Overview',
|
||||||
component: <Overview />,
|
component: <Overview />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "members",
|
name: 'members',
|
||||||
title: "Members",
|
title: 'Members',
|
||||||
component: <ManageMembers />,
|
component: <ManageMembers />,
|
||||||
admin: true,
|
admin: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "notification",
|
name: 'notification',
|
||||||
title: "Notification",
|
title: 'Notification',
|
||||||
component: <Notifications />,
|
component: <Notifications />
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "User",
|
title: 'User',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
name: "my_account",
|
name: 'my_account',
|
||||||
title: "My Account",
|
title: 'My Account',
|
||||||
component: <MyAccount />,
|
component: <MyAccount />
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Configuration",
|
title: 'Configuration',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
name: "firebase",
|
name: 'firebase',
|
||||||
title: "Firebase",
|
title: 'Firebase',
|
||||||
component: <ConfigFirebase />,
|
component: <ConfigFirebase />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "agora",
|
name: 'agora',
|
||||||
title: "Agora",
|
title: 'Agora',
|
||||||
component: <ConfigAgora />,
|
component: <ConfigAgora />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "smtp",
|
name: 'smtp',
|
||||||
title: "SMTP",
|
title: 'SMTP',
|
||||||
component: <ConfigSMTP />,
|
component: <ConfigSMTP />
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
admin: true,
|
name: 'social_login',
|
||||||
},
|
title: 'Social Login',
|
||||||
{
|
component: <Logins />
|
||||||
title: "About",
|
}
|
||||||
items: [
|
],
|
||||||
{
|
admin: true
|
||||||
name: "faq",
|
},
|
||||||
title: "FAQ",
|
{
|
||||||
component: <FAQ />,
|
title: 'About',
|
||||||
},
|
items: [
|
||||||
{
|
{
|
||||||
name: "terms",
|
name: 'faq',
|
||||||
title: "Terms & Privacy",
|
title: 'FAQ',
|
||||||
component: "Terms & Privacy",
|
component: <FAQ />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "feedback",
|
name: 'terms',
|
||||||
title: "Feedback",
|
title: 'Terms & Privacy',
|
||||||
component: "feedback",
|
component: 'Terms & Privacy'
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
},
|
name: 'feedback',
|
||||||
|
title: 'Feedback',
|
||||||
|
component: 'feedback'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
];
|
];
|
||||||
const useNavs = () => {
|
const useNavs = () => {
|
||||||
const loginUser = useSelector((store) => {
|
const loginUser = useSelector((store) => {
|
||||||
return store.contacts.byId[store.authData.uid];
|
return store.contacts.byId[store.authData.uid];
|
||||||
});
|
});
|
||||||
const Navs = navs.filter((nav) => {
|
const Navs = navs.filter((nav) => {
|
||||||
if (loginUser.is_admin) {
|
if (loginUser.is_admin) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return !nav.admin;
|
return !nav.admin;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Navs;
|
return Navs;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useNavs;
|
export default useNavs;
|
||||||
|
|||||||
@@ -1,35 +1,31 @@
|
|||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
import { useEffect } from "react";
|
import { useEffect } from 'react';
|
||||||
import { useGetOpenidMutation } from "../../app/services/auth";
|
import { useGetOpenidMutation } from '../../app/services/auth';
|
||||||
import solidSvg from "../../assets/icons/solid.svg?url";
|
import solidSvg from '../../assets/icons/solid.svg?url';
|
||||||
import { StyledSocialButton } from "./styled";
|
import { StyledSocialButton } from './styled';
|
||||||
|
|
||||||
export default function SolidLoginButton() {
|
export default function SolidLoginButton({ issuers }) {
|
||||||
const [getOpenId, { data, isLoading, isSuccess }] = useGetOpenidMutation();
|
const [getOpenId, { data, isLoading, isSuccess }] = useGetOpenidMutation();
|
||||||
|
|
||||||
const handleSolidLogin = () => {
|
const handleSolidLogin = () => {
|
||||||
getOpenId({
|
getOpenId({
|
||||||
// issuer: "solidweb.org",
|
// issuer: "solidweb.org",
|
||||||
issuer: "broker.pod.inrupt.com",
|
issuer: issuers[0],
|
||||||
redirect_uri: `${location.origin}/#/login`,
|
redirect_uri: `${location.origin}/#/login`
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
console.log("wtf", data);
|
console.log('wtf', data);
|
||||||
const { url } = data;
|
const { url } = data;
|
||||||
location.href = url;
|
location.href = url;
|
||||||
}
|
}
|
||||||
}, [data, isSuccess]);
|
}, [data, isSuccess]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledSocialButton
|
<StyledSocialButton disabled={isLoading} onClick={handleSolidLogin} href="#">
|
||||||
disabled={isLoading}
|
<img src={solidSvg} className="icon" alt="solid icon" />
|
||||||
onClick={handleSolidLogin}
|
{isLoading ? `Redirecting...` : `Sign in with Solid`}
|
||||||
href="#"
|
</StyledSocialButton>
|
||||||
>
|
);
|
||||||
<img src={solidSvg} className="icon" alt="solid icon" />
|
|
||||||
{isLoading ? `Redirecting...` : `Sign in with Solid`}
|
|
||||||
</StyledSocialButton>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+123
-123
@@ -1,130 +1,130 @@
|
|||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from 'react';
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from 'react-redux';
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from 'react-router-dom';
|
||||||
import toast from "react-hot-toast";
|
import toast from 'react-hot-toast';
|
||||||
import BASE_URL from "../../app/config";
|
import BASE_URL from '../../app/config';
|
||||||
// import web3 from "web3";
|
// import web3 from "web3";
|
||||||
import StyledWrapper from "./styled";
|
import StyledWrapper from './styled';
|
||||||
import MetamaskLoginButton from "./MetamaskLoginButton";
|
import MetamaskLoginButton from './MetamaskLoginButton';
|
||||||
import SolidLoginButton from "./SolidLoginButton";
|
import SolidLoginButton from './SolidLoginButton';
|
||||||
import Input from "../../common/component/styled/Input";
|
import Input from '../../common/component/styled/Input';
|
||||||
import Button from "../../common/component/styled/Button";
|
import Button from '../../common/component/styled/Button';
|
||||||
import GoogleLoginButton from "./GoogleLoginButton";
|
import GoogleLoginButton from './GoogleLoginButton';
|
||||||
import { useLoginMutation } from "../../app/services/auth";
|
import { useLoginMutation } from '../../app/services/auth';
|
||||||
import { setAuthData } from "../../app/slices/auth.data";
|
import { useGetLoginConfigQuery } from '../../app/services/server';
|
||||||
|
import { setAuthData } from '../../app/slices/auth.data';
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const [login, { data, isSuccess, isLoading, error }] = useLoginMutation();
|
const [login, { data, isSuccess, isLoading, error }] = useLoginMutation();
|
||||||
const navigateTo = useNavigate();
|
const { data: loginConfig, isSuccess: loginConfigSuccess } = useGetLoginConfigQuery();
|
||||||
const dispatch = useDispatch();
|
const navigateTo = useNavigate();
|
||||||
const [input, setInput] = useState({
|
const dispatch = useDispatch();
|
||||||
email: "",
|
const [input, setInput] = useState({
|
||||||
password: "",
|
email: '',
|
||||||
|
password: ''
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
const query = new URLSearchParams(location.search);
|
||||||
|
const code = query.get('code');
|
||||||
|
const state = query.get('state');
|
||||||
|
if (code && state) {
|
||||||
|
login({
|
||||||
|
code,
|
||||||
|
state,
|
||||||
|
type: 'oidc'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
|
switch (error.status) {
|
||||||
|
case 'PARSING_ERROR':
|
||||||
|
toast.error(error.data);
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
toast.error('username or password incorrect');
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
toast.error('account not exsit');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
toast.error('something error');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [error]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuccess && data) {
|
||||||
|
// 更新本地认证信息
|
||||||
|
console.log('login data', data);
|
||||||
|
toast.success('login success');
|
||||||
|
dispatch(setAuthData(data));
|
||||||
|
navigateTo('/');
|
||||||
|
}
|
||||||
|
}, [isSuccess, data]);
|
||||||
|
|
||||||
|
const handleLogin = (evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
console.log('wtf', input);
|
||||||
|
login({
|
||||||
|
...input,
|
||||||
|
type: 'password'
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
};
|
||||||
const query = new URLSearchParams(location.search);
|
|
||||||
const code = query.get("code");
|
|
||||||
const state = query.get("state");
|
|
||||||
if (code && state) {
|
|
||||||
login({
|
|
||||||
code,
|
|
||||||
state,
|
|
||||||
type: "oidc",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const handleInput = (evt) => {
|
||||||
if (error) {
|
const { type } = evt.target.dataset;
|
||||||
console.log(error);
|
const { value } = evt.target;
|
||||||
switch (error.status) {
|
console.log(type, value);
|
||||||
case "PARSING_ERROR":
|
setInput((prev) => {
|
||||||
toast.error(error.data);
|
prev[type] = value;
|
||||||
break;
|
return { ...prev };
|
||||||
case 401:
|
});
|
||||||
toast.error("username or password incorrect");
|
};
|
||||||
break;
|
const { email, password } = input;
|
||||||
case 404:
|
if (!loginConfigSuccess) return null;
|
||||||
toast.error("account not exsit");
|
const { google: enableGoogleLogin, metamask: enableMetamaskLogin, oidc } = loginConfig;
|
||||||
break;
|
return (
|
||||||
default:
|
<StyledWrapper>
|
||||||
toast.error("something error");
|
<div className="form">
|
||||||
break;
|
<div className="tips">
|
||||||
}
|
<img src={`${BASE_URL}/resource/organization/logo`} alt="logo" className="logo" />
|
||||||
return;
|
<h2 className="title">Login to Rustchat</h2>
|
||||||
}
|
<span className="desc">Please enter your details.</span>
|
||||||
}, [error]);
|
</div>
|
||||||
useEffect(() => {
|
<form onSubmit={handleLogin}>
|
||||||
if (isSuccess && data) {
|
<Input
|
||||||
// 更新本地认证信息
|
className="large"
|
||||||
console.log("login data", data);
|
name="email"
|
||||||
toast.success("login success");
|
value={email}
|
||||||
dispatch(setAuthData(data));
|
required
|
||||||
navigateTo("/");
|
placeholder="Enter your email"
|
||||||
}
|
data-type="email"
|
||||||
}, [isSuccess, data]);
|
onChange={handleInput}
|
||||||
|
/>
|
||||||
const handleLogin = (evt) => {
|
<Input
|
||||||
evt.preventDefault();
|
className="large"
|
||||||
console.log("wtf", input);
|
type="password"
|
||||||
login({
|
value={password}
|
||||||
...input,
|
name="password"
|
||||||
type: "password",
|
required
|
||||||
});
|
data-type="password"
|
||||||
};
|
onChange={handleInput}
|
||||||
|
placeholder="Enter your password"
|
||||||
const handleInput = (evt) => {
|
/>
|
||||||
const { type } = evt.target.dataset;
|
<Button type="submit" disabled={isLoading}>
|
||||||
const { value } = evt.target;
|
{isLoading ? 'Signing' : `Sign in`}
|
||||||
console.log(type, value);
|
</Button>
|
||||||
setInput((prev) => {
|
</form>
|
||||||
prev[type] = value;
|
{(enableGoogleLogin || enableMetamaskLogin || oidc.length > 0) && <hr className="or" />}
|
||||||
return { ...prev };
|
{enableGoogleLogin && <GoogleLoginButton login={login} />}
|
||||||
});
|
{enableMetamaskLogin && <MetamaskLoginButton login={login} />}
|
||||||
};
|
{oidc.length > 0 && <SolidLoginButton issuers={oidc} />}
|
||||||
const { email, password } = input;
|
</div>
|
||||||
return (
|
</StyledWrapper>
|
||||||
<StyledWrapper>
|
);
|
||||||
<div className="form">
|
|
||||||
<div className="tips">
|
|
||||||
<img
|
|
||||||
src={`${BASE_URL}/resource/organization/logo`}
|
|
||||||
alt="logo"
|
|
||||||
className="logo"
|
|
||||||
/>
|
|
||||||
<h2 className="title">Login to Rustchat</h2>
|
|
||||||
<span className="desc">Please enter your details.</span>
|
|
||||||
</div>
|
|
||||||
<form onSubmit={handleLogin}>
|
|
||||||
<Input
|
|
||||||
className="large"
|
|
||||||
name="email"
|
|
||||||
value={email}
|
|
||||||
required
|
|
||||||
placeholder="Enter your email"
|
|
||||||
data-type="email"
|
|
||||||
onChange={handleInput}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className="large"
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
name="password"
|
|
||||||
required
|
|
||||||
data-type="password"
|
|
||||||
onChange={handleInput}
|
|
||||||
placeholder="Enter your password"
|
|
||||||
/>
|
|
||||||
<Button type="submit" disabled={isLoading}>
|
|
||||||
{isLoading ? "Signing" : `Sign in`}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
<hr className="or" />
|
|
||||||
<GoogleLoginButton login={login} />
|
|
||||||
<MetamaskLoginButton login={login} />
|
|
||||||
<SolidLoginButton />
|
|
||||||
</div>
|
|
||||||
</StyledWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user