mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-02 06:43:23 +08:00
feat: oauth 添加字段
This commit is contained in:
232
ui/src/api/Admin.ts
Normal file
232
ui/src/api/Admin.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||
* ## ##
|
||||
* ## AUTHOR: acacode ##
|
||||
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import request, { ContentType, RequestParams } from "./httpClient";
|
||||
import {
|
||||
DeleteDeleteAdminParams,
|
||||
DomainAdminUser,
|
||||
DomainCreateAdminReq,
|
||||
DomainListAdminLoginHistoryResp,
|
||||
DomainListAdminUserResp,
|
||||
DomainLoginReq,
|
||||
DomainSetting,
|
||||
DomainUpdateSettingReq,
|
||||
GetAdminLoginHistoryParams,
|
||||
GetListAdminUserParams,
|
||||
WebResp,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
* @description 创建管理员
|
||||
*
|
||||
* @tags Admin
|
||||
* @name PostCreateAdmin
|
||||
* @summary 创建管理员
|
||||
* @request POST:/api/v1/admin/create
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainAdminUser,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const postCreateAdmin = (
|
||||
param: DomainCreateAdminReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainAdminUser;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/create`,
|
||||
method: "POST",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 删除管理员
|
||||
*
|
||||
* @tags Admin
|
||||
* @name DeleteDeleteAdmin
|
||||
* @summary 删除管理员
|
||||
* @request DELETE:/api/v1/admin/delete
|
||||
* @response `200` `(WebResp & {
|
||||
data?: Record<string, any>,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const deleteDeleteAdmin = (
|
||||
query: DeleteDeleteAdminParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/delete`,
|
||||
method: "DELETE",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取管理员用户列表
|
||||
*
|
||||
* @tags Admin
|
||||
* @name GetListAdminUser
|
||||
* @summary 获取管理员用户列表
|
||||
* @request GET:/api/v1/admin/list
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainListAdminUserResp,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getListAdminUser = (
|
||||
query: GetListAdminUserParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainListAdminUserResp;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/list`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 管理员登录
|
||||
*
|
||||
* @tags Admin
|
||||
* @name PostAdminLogin
|
||||
* @summary 管理员登录
|
||||
* @request POST:/api/v1/admin/login
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainAdminUser,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const postAdminLogin = (
|
||||
param: DomainLoginReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainAdminUser;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/login`,
|
||||
method: "POST",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取管理员登录历史
|
||||
*
|
||||
* @tags Admin
|
||||
* @name GetAdminLoginHistory
|
||||
* @summary 获取管理员登录历史
|
||||
* @request GET:/api/v1/admin/login-history
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainListAdminLoginHistoryResp,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getAdminLoginHistory = (
|
||||
query: GetAdminLoginHistoryParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainListAdminLoginHistoryResp;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/login-history`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取系统设置
|
||||
*
|
||||
* @tags Admin
|
||||
* @name GetGetSetting
|
||||
* @summary 获取系统设置
|
||||
* @request GET:/api/v1/admin/setting
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainSetting,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getGetSetting = (params: RequestParams = {}) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainSetting;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/setting`,
|
||||
method: "GET",
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 更新系统设置
|
||||
*
|
||||
* @tags Admin
|
||||
* @name PutUpdateSetting
|
||||
* @summary 更新系统设置
|
||||
* @request PUT:/api/v1/admin/setting
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainSetting,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const putUpdateSetting = (
|
||||
param: DomainUpdateSettingReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainSetting;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/setting`,
|
||||
method: "PUT",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
@@ -12,25 +12,16 @@
|
||||
|
||||
import request, { ContentType, RequestParams } from "./httpClient";
|
||||
import {
|
||||
DeleteDeleteAdminParams,
|
||||
DeleteDeleteUserParams,
|
||||
DomainAdminUser,
|
||||
DomainCreateAdminReq,
|
||||
DomainInviteResp,
|
||||
DomainListAdminLoginHistoryResp,
|
||||
DomainListAdminUserResp,
|
||||
DomainListLoginHistoryResp,
|
||||
DomainListUserResp,
|
||||
DomainLoginReq,
|
||||
DomainLoginResp,
|
||||
DomainOAuthURLResp,
|
||||
DomainRegisterReq,
|
||||
DomainSetting,
|
||||
DomainUpdateSettingReq,
|
||||
DomainUpdateUserReq,
|
||||
DomainUser,
|
||||
GetAdminLoginHistoryParams,
|
||||
GetListAdminUserParams,
|
||||
GetListUserParams,
|
||||
GetLoginHistoryParams,
|
||||
GetUserOauthCallbackParams,
|
||||
@@ -38,212 +29,6 @@ import {
|
||||
WebResp,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
* @description 创建管理员
|
||||
*
|
||||
* @tags User
|
||||
* @name PostCreateAdmin
|
||||
* @summary 创建管理员
|
||||
* @request POST:/api/v1/admin/create
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainAdminUser,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const postCreateAdmin = (
|
||||
param: DomainCreateAdminReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainAdminUser;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/create`,
|
||||
method: "POST",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 删除管理员
|
||||
*
|
||||
* @tags User
|
||||
* @name DeleteDeleteAdmin
|
||||
* @summary 删除管理员
|
||||
* @request DELETE:/api/v1/admin/delete
|
||||
* @response `200` `(WebResp & {
|
||||
data?: Record<string, any>,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const deleteDeleteAdmin = (
|
||||
query: DeleteDeleteAdminParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/delete`,
|
||||
method: "DELETE",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取管理员用户列表
|
||||
*
|
||||
* @tags User
|
||||
* @name GetListAdminUser
|
||||
* @summary 获取管理员用户列表
|
||||
* @request GET:/api/v1/admin/list
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainListAdminUserResp,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getListAdminUser = (
|
||||
query: GetListAdminUserParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainListAdminUserResp;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/list`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 管理员登录
|
||||
*
|
||||
* @tags User
|
||||
* @name PostAdminLogin
|
||||
* @summary 管理员登录
|
||||
* @request POST:/api/v1/admin/login
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainAdminUser,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const postAdminLogin = (
|
||||
param: DomainLoginReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainAdminUser;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/login`,
|
||||
method: "POST",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取管理员登录历史
|
||||
*
|
||||
* @tags User
|
||||
* @name GetAdminLoginHistory
|
||||
* @summary 获取管理员登录历史
|
||||
* @request GET:/api/v1/admin/login-history
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainListAdminLoginHistoryResp,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getAdminLoginHistory = (
|
||||
query: GetAdminLoginHistoryParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainListAdminLoginHistoryResp;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/login-history`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取系统设置
|
||||
*
|
||||
* @tags User
|
||||
* @name GetGetSetting
|
||||
* @summary 获取系统设置
|
||||
* @request GET:/api/v1/admin/setting
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainSetting,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getGetSetting = (params: RequestParams = {}) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainSetting;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/setting`,
|
||||
method: "GET",
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 更新系统设置
|
||||
*
|
||||
* @tags User
|
||||
* @name PutUpdateSetting
|
||||
* @summary 更新系统设置
|
||||
* @request PUT:/api/v1/admin/setting
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainSetting,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const putUpdateSetting = (
|
||||
param: DomainUpdateSettingReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainSetting;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/admin/setting`,
|
||||
method: "PUT",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 下载VSCode插件
|
||||
*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './Admin'
|
||||
export * from './Billing'
|
||||
export * from './Dashboard'
|
||||
export * from './Model'
|
||||
|
||||
@@ -185,6 +185,40 @@ export interface DomainCreateModelReq {
|
||||
provider?: string;
|
||||
}
|
||||
|
||||
export interface DomainCustomOAuth {
|
||||
/** 自定义OAuth访问令牌URL */
|
||||
access_token_url?: string;
|
||||
/** 自定义OAuth授权URL */
|
||||
authorize_url?: string;
|
||||
/** 用户信息回包中的头像URL字段名` */
|
||||
avatar_field?: string;
|
||||
/** 自定义客户端ID */
|
||||
client_id?: string;
|
||||
/** 自定义客户端密钥 */
|
||||
client_secret?: string;
|
||||
/** 用户信息回包中的邮箱字段名 */
|
||||
email_field?: string;
|
||||
/** 自定义OAuth开关 */
|
||||
enable?: boolean;
|
||||
/** 用户信息回包中的ID字段名 */
|
||||
id_field?: string;
|
||||
/** 用户信息回包中的用户名字段名` */
|
||||
name_field?: string;
|
||||
/** 自定义OAuth Scope列表 */
|
||||
scopes?: string[];
|
||||
/** 自定义OAuth用户信息URL */
|
||||
userinfo_url?: string;
|
||||
}
|
||||
|
||||
export interface DomainDingtalkOAuth {
|
||||
/** 钉钉客户端ID */
|
||||
client_id?: string;
|
||||
/** 钉钉客户端密钥 */
|
||||
client_secret?: string;
|
||||
/** 钉钉OAuth开关 */
|
||||
enable?: boolean;
|
||||
}
|
||||
|
||||
export interface DomainIPInfo {
|
||||
/** ASN */
|
||||
asn?: string;
|
||||
@@ -357,24 +391,12 @@ export interface DomainRegisterReq {
|
||||
export interface DomainSetting {
|
||||
/** 创建时间 */
|
||||
created_at?: number;
|
||||
/** 自定义OAuth访问令牌URL */
|
||||
custom_oauth_access_token_url?: string;
|
||||
/** 自定义OAuth授权URL */
|
||||
custom_oauth_authorize_url?: string;
|
||||
/** 自定义OAuth客户端ID */
|
||||
custom_oauth_client_id?: string;
|
||||
/** 自定义OAuth Scope列表 */
|
||||
custom_oauth_scopes?: string[];
|
||||
/** 自定义OAuth用户信息URL */
|
||||
custom_oauth_userinfo_url?: string;
|
||||
/** 钉钉客户端ID */
|
||||
dingtalk_client_id?: string;
|
||||
/** 自定义OAuth接入 */
|
||||
custom_oauth?: DomainCustomOAuth;
|
||||
/** 钉钉OAuth接入 */
|
||||
dingtalk_oauth?: DomainDingtalkOAuth;
|
||||
/** 是否禁用密码登录 */
|
||||
disable_password_login?: boolean;
|
||||
/** 是否开启自定义OAuth */
|
||||
enable_custom_oauth?: boolean;
|
||||
/** 是否开启钉钉OAuth */
|
||||
enable_dingtalk_oauth?: boolean;
|
||||
/** 是否开启SSO */
|
||||
enable_sso?: boolean;
|
||||
/** 是否强制两步验证 */
|
||||
@@ -461,28 +483,12 @@ export interface DomainUpdateModelReq {
|
||||
}
|
||||
|
||||
export interface DomainUpdateSettingReq {
|
||||
/** 自定义OAuth访问令牌URL */
|
||||
custom_oauth_access_token_url?: string;
|
||||
/** 自定义OAuth授权URL */
|
||||
custom_oauth_authorize_url?: string;
|
||||
/** 自定义OAuth客户端ID */
|
||||
custom_oauth_client_id?: string;
|
||||
/** 自定义OAuth客户端密钥 */
|
||||
custom_oauth_client_secret?: string;
|
||||
/** 自定义OAuth Scope列表 */
|
||||
custom_oauth_scopes?: string[];
|
||||
/** 自定义OAuth用户信息URL */
|
||||
custom_oauth_userinfo_url?: string;
|
||||
/** 钉钉客户端ID */
|
||||
dingtalk_client_id?: string;
|
||||
/** 钉钉客户端密钥 */
|
||||
dingtalk_client_secret?: string;
|
||||
/** 自定义OAuth配置 */
|
||||
custom_oauth?: DomainCustomOAuth;
|
||||
/** 钉钉OAuth配置 */
|
||||
dingtalk_oauth?: DomainDingtalkOAuth;
|
||||
/** 是否禁用密码登录 */
|
||||
disable_password_login?: boolean;
|
||||
/** 是否开启自定义OAuth */
|
||||
enable_custom_oauth?: boolean;
|
||||
/** 是否开启钉钉OAuth */
|
||||
enable_dingtalk_oauth?: boolean;
|
||||
/** 是否开启SSO */
|
||||
enable_sso?: boolean;
|
||||
/** 是否强制两步验证 */
|
||||
@@ -499,6 +505,8 @@ export interface DomainUpdateUserReq {
|
||||
}
|
||||
|
||||
export interface DomainUser {
|
||||
/** 头像URL */
|
||||
avatar_url?: string;
|
||||
/** 创建时间 */
|
||||
created_at?: number;
|
||||
/** 邮箱 */
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
TextField,
|
||||
Paper,
|
||||
} from '@mui/material';
|
||||
import { postCreateAdmin } from '@/api/User';
|
||||
import { postCreateAdmin } from '@/api/Admin';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { deleteDeleteAdmin, getListAdminUser } from '@/api/User';
|
||||
import { deleteDeleteAdmin, getListAdminUser } from '@/api/Admin';
|
||||
import { Table, Modal, message } from '@c-x/ui';
|
||||
import { ColumnsType } from '@c-x/ui/dist/Table';
|
||||
import { useRequest } from 'ahooks';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Stack, Box } from '@mui/material';
|
||||
import { Table } from '@c-x/ui';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { getAdminLoginHistory } from '@/api/User';
|
||||
import { getAdminLoginHistory } from '@/api/Admin';
|
||||
import { ColumnsType } from '@c-x/ui/dist/Table';
|
||||
import { DomainListAdminLoginHistoryResp } from '@/api/types';
|
||||
import User from '@/components/user';
|
||||
|
||||
@@ -21,11 +21,13 @@ import { Icon, message } from '@c-x/ui';
|
||||
import { AestheticFluidBg } from '@/assets/jsm/AestheticFluidBg.module.js';
|
||||
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { postLogin, getUserOauthSignupOrIn, getGetSetting } from '@/api/User';
|
||||
import { postLogin, getUserOauthSignupOrIn } from '@/api/User';
|
||||
import { getGetSetting } from '@/api/Admin';
|
||||
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { DomainSetting } from '@/api/types';
|
||||
|
||||
// 样式化组件
|
||||
const StyledContainer = styled(Container)(({ theme }) => ({
|
||||
@@ -114,8 +116,9 @@ const AuthPage = () => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const { data: loginSetting = {} } = useRequest(getGetSetting);
|
||||
|
||||
const { data: loginSetting = {} as DomainSetting } =
|
||||
useRequest(getGetSetting);
|
||||
const { custom_oauth = {}, dingtalk_oauth = {} } = loginSetting;
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -175,10 +178,8 @@ const AuthPage = () => {
|
||||
}, []);
|
||||
|
||||
const oauthEnable = useMemo(() => {
|
||||
return (
|
||||
loginSetting.enable_custom_oauth || loginSetting.enable_dingtalk_oauth
|
||||
);
|
||||
}, [loginSetting]);
|
||||
return custom_oauth.enable || dingtalk_oauth.enable;
|
||||
}, [custom_oauth, dingtalk_oauth]);
|
||||
|
||||
// 渲染用户名输入框
|
||||
const renderUsernameField = () => (
|
||||
@@ -290,7 +291,7 @@ const AuthPage = () => {
|
||||
<Divider sx={{ my: 3, fontSize: 12, borderColor: 'divider' }}>
|
||||
使用其他方式登录
|
||||
</Divider>
|
||||
{loginSetting.enable_dingtalk_oauth && (
|
||||
{dingtalk_oauth.enable && (
|
||||
<IconButton
|
||||
sx={{ alignSelf: 'center' }}
|
||||
onClick={() => onOauthLogin('dingtalk')}
|
||||
@@ -298,7 +299,7 @@ const AuthPage = () => {
|
||||
<Icon type='icon-dingding' sx={{ fontSize: 30 }} />
|
||||
</IconButton>
|
||||
)}
|
||||
{loginSetting.enable_custom_oauth && (
|
||||
{custom_oauth.enable && (
|
||||
<IconButton
|
||||
sx={{ alignSelf: 'center' }}
|
||||
onClick={() => onOauthLogin('custom')}
|
||||
|
||||
@@ -23,12 +23,10 @@ import {
|
||||
Divider,
|
||||
} from '@mui/material';
|
||||
import { useRequest } from 'ahooks';
|
||||
import {
|
||||
postRegister,
|
||||
getUserOauthSignupOrIn,
|
||||
getGetSetting,
|
||||
} from '@/api/User';
|
||||
import { postRegister, getUserOauthSignupOrIn } from '@/api/User';
|
||||
import { getGetSetting } from '@/api/Admin';
|
||||
import { Icon } from '@c-x/ui';
|
||||
import { DomainSetting } from '@/api/types';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import MenuBookIcon from '@mui/icons-material/MenuBook';
|
||||
@@ -92,7 +90,9 @@ const StyledTextField = styled(TextField)(({ theme }) => ({
|
||||
const Invite = () => {
|
||||
const { id, step } = useParams();
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const { data: loginSetting = {} } = useRequest(getGetSetting);
|
||||
const { data: loginSetting = {} as DomainSetting } =
|
||||
useRequest(getGetSetting);
|
||||
const { custom_oauth = {}, dingtalk_oauth = {} } = loginSetting;
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -148,10 +148,8 @@ const Invite = () => {
|
||||
};
|
||||
|
||||
const oauthEnable = useMemo(() => {
|
||||
return (
|
||||
loginSetting.enable_custom_oauth || loginSetting.enable_dingtalk_oauth
|
||||
);
|
||||
}, [loginSetting]);
|
||||
return custom_oauth.enable || dingtalk_oauth.enable;
|
||||
}, [custom_oauth, dingtalk_oauth]);
|
||||
|
||||
const oauthLogin = () => {
|
||||
return (
|
||||
@@ -159,7 +157,7 @@ const Invite = () => {
|
||||
<Divider sx={{ my: 2, fontSize: 12, borderColor: 'divider' }}>
|
||||
使用以下方式注册
|
||||
</Divider>
|
||||
{loginSetting.enable_dingtalk_oauth && (
|
||||
{dingtalk_oauth.enable && (
|
||||
<Button
|
||||
sx={{ alignSelf: 'center' }}
|
||||
onClick={() => onOauthLogin('dingtalk')}
|
||||
@@ -167,7 +165,7 @@ const Invite = () => {
|
||||
<Icon type='icon-dingding' sx={{ fontSize: 30 }} />
|
||||
</Button>
|
||||
)}
|
||||
{loginSetting.enable_custom_oauth && (
|
||||
{custom_oauth.enable && (
|
||||
<IconButton
|
||||
sx={{ alignSelf: 'center' }}
|
||||
onClick={() => onOauthLogin('custom')}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
InputAdornment,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
import { postAdminLogin } from '@/api/User';
|
||||
import { postAdminLogin } from '@/api/Admin';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
@@ -8,9 +8,8 @@ import {
|
||||
Button,
|
||||
Box,
|
||||
} from '@mui/material';
|
||||
import { Icon, Modal } from '@c-x/ui';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { getGetSetting, putUpdateSetting } from '@/api/User';
|
||||
import { getGetSetting, putUpdateSetting } from '@/api/Admin';
|
||||
import MemberManage from './memberManage';
|
||||
import LoginHistory from './loginHistory';
|
||||
import { message } from '@c-x/ui';
|
||||
@@ -30,11 +29,11 @@ const StyledLabel = styled('div')(({ theme }) => ({
|
||||
color: theme.vars.palette.text.primary,
|
||||
}));
|
||||
|
||||
const OAUTH_LOGIN_TYPE_KEYS = ['enable_custom_oauth', 'enable_dingtalk_oauth'];
|
||||
const OAUTH_LOGIN_TYPE_KEYS = ['dingtalk_oauth', 'custom_oauth'];
|
||||
|
||||
const OAUTH_LOGIN_TYPE_LABELS = {
|
||||
enable_custom_oauth: '已开启 OAuth 登录',
|
||||
enable_dingtalk_oauth: '已开启钉钉登录',
|
||||
custom_oauth: '已开启 OAuth 登录',
|
||||
dingtalk_oauth: '已开启钉钉登录',
|
||||
};
|
||||
|
||||
type OAUTH_LOGIN_TYPE_KEYS = keyof typeof OAUTH_LOGIN_TYPE_LABELS;
|
||||
@@ -42,16 +41,7 @@ type OAUTH_LOGIN_TYPE_KEYS = keyof typeof OAUTH_LOGIN_TYPE_LABELS;
|
||||
const User = () => {
|
||||
const [thirdPartyLoginSettingModalOpen, setThirdPartyLoginSettingModalOpen] =
|
||||
useState(false);
|
||||
const {
|
||||
data = {
|
||||
enable_sso: false,
|
||||
force_two_factor_auth: false,
|
||||
disable_password_login: false,
|
||||
enable_dingtalk_oauth: false,
|
||||
enable_custom_oauth: false,
|
||||
},
|
||||
refresh,
|
||||
} = useRequest(getGetSetting);
|
||||
const { data, refresh } = useRequest(getGetSetting);
|
||||
|
||||
const { runAsync: updateSetting } = useRequest(putUpdateSetting, {
|
||||
manual: true,
|
||||
@@ -62,8 +52,9 @@ const User = () => {
|
||||
});
|
||||
|
||||
const oauthLabel = useMemo(() => {
|
||||
if (!data) return '未开启';
|
||||
const key = OAUTH_LOGIN_TYPE_KEYS.find(
|
||||
(key) => data[key as OAUTH_LOGIN_TYPE_KEYS]
|
||||
(key) => data[key as OAUTH_LOGIN_TYPE_KEYS]?.enable
|
||||
);
|
||||
return key
|
||||
? OAUTH_LOGIN_TYPE_LABELS[key as OAUTH_LOGIN_TYPE_KEYS]
|
||||
@@ -127,7 +118,7 @@ const User = () => {
|
||||
<ThirdPartyLoginSettingModal
|
||||
open={thirdPartyLoginSettingModalOpen}
|
||||
onCancel={() => setThirdPartyLoginSettingModalOpen(false)}
|
||||
settingData={data}
|
||||
settingData={data || {}}
|
||||
onOk={() => {
|
||||
refresh();
|
||||
}}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { Button, Radio, Stack, Box, TextField } from '@mui/material';
|
||||
import { Modal, Icon, message } from '@c-x/ui';
|
||||
import {
|
||||
Button,
|
||||
Radio,
|
||||
Stack,
|
||||
TextField,
|
||||
Autocomplete,
|
||||
Chip,
|
||||
} from '@mui/material';
|
||||
import { Modal, message } from '@c-x/ui';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { FormItem } from '@/components/form';
|
||||
import { putUpdateSetting } from '@/api/User';
|
||||
import { putUpdateSetting } from '@/api/Admin';
|
||||
import { DomainSetting, DomainUpdateSettingReq } from '@/api/types';
|
||||
|
||||
type LoginType = 'dingding' | 'wechat' | 'feishu' | 'oauth' | 'none';
|
||||
@@ -59,22 +66,33 @@ const ThirdPartyLoginSettingModal = ({
|
||||
control,
|
||||
handleSubmit,
|
||||
reset,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
dingtalk_client_id: '',
|
||||
dingtalk_client_secret: '',
|
||||
custom_oauth_access_token_url: '',
|
||||
custom_oauth_authorize_url: '',
|
||||
custom_oauth_client_id: '',
|
||||
custom_oauth_client_secret: '',
|
||||
access_token_url: '',
|
||||
authorize_url: '',
|
||||
client_id: '',
|
||||
client_secret: '',
|
||||
id_field: '',
|
||||
name_field: '',
|
||||
scopes: [] as string[],
|
||||
avatar_field: '',
|
||||
userinfo_url: '',
|
||||
email_field: '',
|
||||
},
|
||||
});
|
||||
|
||||
const [loginType, setLoginType] = useState<LoginType>(
|
||||
settingData?.enable_dingtalk_oauth ? 'dingding' : 'none'
|
||||
settingData?.dingtalk_oauth?.enable ? 'dingding' : 'none'
|
||||
);
|
||||
|
||||
const [scopeInputValue, setScopeInputValue] = useState('');
|
||||
|
||||
const userInfoUrl = watch('userinfo_url');
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
reset();
|
||||
@@ -82,25 +100,31 @@ const ThirdPartyLoginSettingModal = ({
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (settingData?.enable_dingtalk_oauth) {
|
||||
if (settingData?.dingtalk_oauth?.enable) {
|
||||
setLoginType('dingding');
|
||||
reset(
|
||||
{
|
||||
dingtalk_client_id: settingData.dingtalk_client_id,
|
||||
dingtalk_client_id: settingData.dingtalk_oauth.client_id,
|
||||
dingtalk_client_secret: settingData.dingtalk_oauth.client_secret,
|
||||
},
|
||||
{
|
||||
keepValues: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
if (settingData?.enable_custom_oauth) {
|
||||
if (settingData?.custom_oauth?.enable) {
|
||||
setLoginType('oauth');
|
||||
reset(
|
||||
{
|
||||
custom_oauth_access_token_url:
|
||||
settingData.custom_oauth_access_token_url,
|
||||
custom_oauth_authorize_url: settingData.custom_oauth_authorize_url,
|
||||
custom_oauth_client_id: settingData.custom_oauth_client_id,
|
||||
access_token_url: settingData.custom_oauth.access_token_url,
|
||||
authorize_url: settingData.custom_oauth.authorize_url,
|
||||
client_id: settingData.custom_oauth.client_id,
|
||||
id_field: settingData.custom_oauth.id_field,
|
||||
name_field: settingData.custom_oauth.name_field,
|
||||
scopes: settingData.custom_oauth.scopes || [],
|
||||
avatar_field: settingData.custom_oauth.avatar_field,
|
||||
userinfo_url: settingData.custom_oauth.userinfo_url,
|
||||
email_field: settingData.custom_oauth.email_field,
|
||||
},
|
||||
{
|
||||
keepValues: true,
|
||||
@@ -113,24 +137,42 @@ const ThirdPartyLoginSettingModal = ({
|
||||
let params: DomainUpdateSettingReq = {};
|
||||
if (loginType === 'none') {
|
||||
params = {
|
||||
enable_dingtalk_oauth: false,
|
||||
enable_custom_oauth: false,
|
||||
dingtalk_oauth: {
|
||||
enable: false,
|
||||
},
|
||||
custom_oauth: {
|
||||
enable: false,
|
||||
},
|
||||
};
|
||||
} else if (loginType === 'dingding') {
|
||||
params = {
|
||||
enable_dingtalk_oauth: true,
|
||||
enable_custom_oauth: false,
|
||||
dingtalk_client_id: data.dingtalk_client_id,
|
||||
dingtalk_client_secret: data.dingtalk_client_secret,
|
||||
dingtalk_oauth: {
|
||||
enable: true,
|
||||
client_id: data.dingtalk_client_id,
|
||||
client_secret: data.dingtalk_client_secret,
|
||||
},
|
||||
custom_oauth: {
|
||||
enable: false,
|
||||
},
|
||||
};
|
||||
} else if (loginType === 'oauth') {
|
||||
params = {
|
||||
enable_custom_oauth: true,
|
||||
enable_dingtalk_oauth: false,
|
||||
custom_oauth_access_token_url: data.custom_oauth_access_token_url,
|
||||
custom_oauth_authorize_url: data.custom_oauth_authorize_url,
|
||||
custom_oauth_client_id: data.custom_oauth_client_id,
|
||||
custom_oauth_client_secret: data.custom_oauth_client_secret,
|
||||
dingtalk_oauth: {
|
||||
enable: false,
|
||||
},
|
||||
custom_oauth: {
|
||||
enable: true,
|
||||
access_token_url: data.access_token_url,
|
||||
authorize_url: data.authorize_url,
|
||||
client_id: data.client_id,
|
||||
client_secret: data.client_secret,
|
||||
id_field: data.id_field,
|
||||
name_field: data.name_field,
|
||||
scopes: data.scopes,
|
||||
avatar_field: data.avatar_field,
|
||||
userinfo_url: data.userinfo_url,
|
||||
email_field: data.email_field,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -201,15 +243,15 @@ const ThirdPartyLoginSettingModal = ({
|
||||
rules={{
|
||||
required: 'Access Token URL 不能为空',
|
||||
}}
|
||||
name='custom_oauth_access_token_url'
|
||||
name='access_token_url'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.custom_oauth_access_token_url}
|
||||
helperText={errors.custom_oauth_access_token_url?.message}
|
||||
error={!!errors.access_token_url}
|
||||
helperText={errors.access_token_url?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@@ -217,7 +259,7 @@ const ThirdPartyLoginSettingModal = ({
|
||||
<FormItem label='Authorize URL' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='custom_oauth_authorize_url'
|
||||
name='authorize_url'
|
||||
rules={{
|
||||
required: 'Authorize URL 不能为空',
|
||||
}}
|
||||
@@ -227,8 +269,8 @@ const ThirdPartyLoginSettingModal = ({
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.custom_oauth_authorize_url}
|
||||
helperText={errors.custom_oauth_authorize_url?.message}
|
||||
error={!!errors.authorize_url}
|
||||
helperText={errors.authorize_url?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@@ -236,7 +278,7 @@ const ThirdPartyLoginSettingModal = ({
|
||||
<FormItem label='Client ID' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='custom_oauth_client_id'
|
||||
name='client_id'
|
||||
rules={{
|
||||
required: 'Client ID 不能为空',
|
||||
}}
|
||||
@@ -246,8 +288,8 @@ const ThirdPartyLoginSettingModal = ({
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.custom_oauth_client_id}
|
||||
helperText={errors.custom_oauth_client_id?.message}
|
||||
error={!!errors.client_id}
|
||||
helperText={errors.client_id?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@@ -255,7 +297,7 @@ const ThirdPartyLoginSettingModal = ({
|
||||
<FormItem label='Client Secret' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='custom_oauth_client_secret'
|
||||
name='client_secret'
|
||||
rules={{
|
||||
required: 'Client Secret 不能为空',
|
||||
}}
|
||||
@@ -265,12 +307,175 @@ const ThirdPartyLoginSettingModal = ({
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.custom_oauth_client_secret}
|
||||
helperText={errors.custom_oauth_client_secret?.message}
|
||||
error={!!errors.client_secret}
|
||||
helperText={errors.client_secret?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label='Scope' required>
|
||||
<Controller
|
||||
name='scopes'
|
||||
control={control}
|
||||
rules={{
|
||||
validate: (value) => {
|
||||
if (value.length === 0) {
|
||||
return 'Scope 不能为空';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Autocomplete
|
||||
multiple
|
||||
id='tags-filled'
|
||||
options={[]}
|
||||
value={field.value}
|
||||
inputValue={scopeInputValue}
|
||||
onChange={(_, value) => {
|
||||
field.onChange(value);
|
||||
}}
|
||||
onInputChange={(_, value) => {
|
||||
setScopeInputValue(value);
|
||||
}}
|
||||
size='small'
|
||||
freeSolo
|
||||
renderTags={(value: readonly string[], getTagProps) =>
|
||||
value.map((option: string, index: number) => {
|
||||
const { key, ...tagProps } = getTagProps({ index });
|
||||
const label = `${option}`;
|
||||
return (
|
||||
<Chip
|
||||
key={key}
|
||||
label={label}
|
||||
size='small'
|
||||
{...tagProps}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
required
|
||||
placeholder='请输入(可多个, 回车键确认)'
|
||||
error={Boolean(errors.scopes)}
|
||||
helperText={errors.scopes?.message as string}
|
||||
onBlur={() => {
|
||||
// 失去焦点时自动添加当前输入的值
|
||||
const trimmedValue = scopeInputValue.trim();
|
||||
if (trimmedValue && !field.value.includes(trimmedValue)) {
|
||||
field.onChange([...field.value, trimmedValue]);
|
||||
// 清空输入框
|
||||
setScopeInputValue('');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label='用户信息 URL' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='userinfo_url'
|
||||
rules={{
|
||||
required: '用户信息 URL 不能为空',
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.userinfo_url}
|
||||
helperText={errors.userinfo_url?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
{userInfoUrl && (
|
||||
<>
|
||||
<FormItem label='ID 字段' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='id_field'
|
||||
rules={{
|
||||
required: 'ID 字段 不能为空',
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.id_field}
|
||||
helperText={errors.id_field?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label='用户名字段' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='name_field'
|
||||
rules={{
|
||||
required: '用户名字段不能为空',
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.name_field}
|
||||
helperText={errors.name_field?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label='头像字段' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='avatar_field'
|
||||
rules={{
|
||||
required: '头像字段不能为空',
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.avatar_field}
|
||||
helperText={errors.avatar_field?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label='邮箱字段' required>
|
||||
<Controller
|
||||
control={control}
|
||||
name='email_field'
|
||||
rules={{
|
||||
required: '邮箱字段不能为空',
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.email_field}
|
||||
helperText={errors.email_field?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -64,12 +64,7 @@ const Dashboard = () => {
|
||||
</Select>
|
||||
</Stack>
|
||||
|
||||
<MemberStatistic
|
||||
memberData={memberData}
|
||||
userList={userList}
|
||||
onMemberChange={onMemberChange}
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
<MemberStatistic memberData={memberData} timeRange={timeRange} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,11 +22,13 @@ import { getRedirectUrl } from '@/utils';
|
||||
import { AestheticFluidBg } from '@/assets/jsm/AestheticFluidBg.module.js';
|
||||
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { postLogin, getUserOauthSignupOrIn, getGetSetting } from '@/api/User';
|
||||
import { postLogin, getUserOauthSignupOrIn } from '@/api/User';
|
||||
import { getGetSetting } from '@/api/Admin';
|
||||
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { DomainSetting } from '@/api/types';
|
||||
|
||||
// 样式化组件
|
||||
const StyledContainer = styled(Container)(({ theme }) => ({
|
||||
@@ -115,8 +117,9 @@ const UserLogin = () => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const { data: loginSetting = {} } = useRequest(getGetSetting);
|
||||
|
||||
const { data: loginSetting = {} as DomainSetting } =
|
||||
useRequest(getGetSetting);
|
||||
const { custom_oauth = {}, dingtalk_oauth = {} } = loginSetting;
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -166,10 +169,8 @@ const UserLogin = () => {
|
||||
}, []);
|
||||
|
||||
const oauthEnable = useMemo(() => {
|
||||
return (
|
||||
loginSetting.enable_custom_oauth || loginSetting.enable_dingtalk_oauth
|
||||
);
|
||||
}, [loginSetting]);
|
||||
return custom_oauth.enable || dingtalk_oauth.enable;
|
||||
}, [custom_oauth, dingtalk_oauth]);
|
||||
|
||||
// 渲染用户名输入框
|
||||
const renderUsernameField = () => (
|
||||
@@ -280,7 +281,7 @@ const UserLogin = () => {
|
||||
<Divider sx={{ my: 3, fontSize: 12, borderColor: 'divider' }}>
|
||||
使用其他方式登录
|
||||
</Divider>
|
||||
{loginSetting.enable_dingtalk_oauth && (
|
||||
{dingtalk_oauth.enable && (
|
||||
<IconButton
|
||||
sx={{ alignSelf: 'center' }}
|
||||
onClick={() => onOauthLogin('dingtalk')}
|
||||
@@ -288,7 +289,7 @@ const UserLogin = () => {
|
||||
<Icon type='icon-dingding' sx={{ fontSize: 30 }} />
|
||||
</IconButton>
|
||||
)}
|
||||
{loginSetting.enable_custom_oauth && (
|
||||
{custom_oauth.enable && (
|
||||
<IconButton
|
||||
sx={{ alignSelf: 'center' }}
|
||||
onClick={() => onOauthLogin('custom')}
|
||||
|
||||
Reference in New Issue
Block a user