mirror of https://gitee.com/topiam/eiam
⚡ 优化应用
parent
b1a0693eac
commit
ba47bef290
|
@ -1,224 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { history } from '@@/core/history';
|
||||
import { DesktopOutlined, ProfileOutlined, SafetyOutlined } from '@ant-design/icons';
|
||||
import { GridContent, PageContainer } from '@ant-design/pro-components';
|
||||
import { useAsyncEffect } from 'ahooks';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { App, Menu } from 'antd';
|
||||
import React, { useLayoutEffect, useRef, useState } from 'react';
|
||||
import AccessPolicy from './components/AccessPolicy';
|
||||
import AppAccount from './components/AppAccount';
|
||||
import AppBasic from './components/AppBasic';
|
||||
import { ConfigTabs } from './constant';
|
||||
import AppProtocol from './components/AppProtocol';
|
||||
import queryString from 'query-string';
|
||||
import { useIntl, useLocation } from '@umijs/max';
|
||||
import useStyle from './style';
|
||||
import classNames from 'classnames';
|
||||
import { AppProtocolType } from '@/constant';
|
||||
import PermissionResource from './components/PermissionResource';
|
||||
import PermissionRole from './components/PermissionRole';
|
||||
|
||||
const prefixCls = 'app-config';
|
||||
|
||||
export default () => {
|
||||
const [keys, setKeys] = useState<string[]>();
|
||||
const [title, setTitle] = useState<string>();
|
||||
const location = useLocation();
|
||||
const { wrapSSR, hashId } = useStyle(prefixCls);
|
||||
const intl = useIntl();
|
||||
const { message } = App.useApp();
|
||||
const [initConfig, setInitConfig] = useState<{ mode: 'horizontal' | 'vertical' | 'inline' }>({
|
||||
mode: 'inline',
|
||||
});
|
||||
const dom = useRef<HTMLDivElement>();
|
||||
|
||||
const resize = () => {
|
||||
requestAnimationFrame(() => {
|
||||
if (!dom.current) {
|
||||
return;
|
||||
}
|
||||
let mode: 'inline' | 'horizontal' = 'inline';
|
||||
if (window.innerWidth < 1200) {
|
||||
mode = 'horizontal';
|
||||
}
|
||||
setInitConfig({ ...initConfig, mode: mode });
|
||||
});
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (dom.current) {
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener('resize', resize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const query = queryString.parse(location.search) as {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
protocol: string;
|
||||
};
|
||||
const { type, id, name, protocol } = query as {
|
||||
id: string;
|
||||
name: string;
|
||||
type: ConfigTabs;
|
||||
protocol: AppProtocolType;
|
||||
};
|
||||
const items: MenuProps['items'] = [
|
||||
{
|
||||
key: ConfigTabs.basic,
|
||||
label: intl.formatMessage({ id: 'pages.app.config.basic' }),
|
||||
icon: React.createElement(() => {
|
||||
return <ProfileOutlined />;
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: ConfigTabs.login_access,
|
||||
label: intl.formatMessage({ id: 'pages.app.config.items.login_access' }),
|
||||
icon: React.createElement(() => {
|
||||
return <DesktopOutlined />;
|
||||
}),
|
||||
children: [
|
||||
{
|
||||
key: ConfigTabs.protocol_config,
|
||||
label: intl.formatMessage({ id: 'pages.app.config.items.login_access.protocol_config' }),
|
||||
},
|
||||
//OIDC 不展示应用账户
|
||||
...(!protocol || protocol === AppProtocolType.oidc
|
||||
? []
|
||||
: [
|
||||
{
|
||||
key: ConfigTabs.app_account,
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.app_account',
|
||||
}),
|
||||
},
|
||||
]),
|
||||
{
|
||||
key: ConfigTabs.access_policy,
|
||||
label: intl.formatMessage({ id: 'pages.app.config.items.login_access.access_policy' }),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ConfigTabs.app_permission,
|
||||
label: intl.formatMessage({ id: 'pages.app.config.items.app_permission' }),
|
||||
icon: React.createElement(() => {
|
||||
return <SafetyOutlined />;
|
||||
}),
|
||||
children: [
|
||||
{
|
||||
key: ConfigTabs.permission_resource,
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.app.config.items.app_permission.permission_resource',
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: ConfigTabs.permission_role,
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.app.config.items.app_permission.permission_role',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (!id || !protocol) {
|
||||
message.error(intl.formatMessage({ id: 'pages.app.config.error' }));
|
||||
history.push('/app');
|
||||
return;
|
||||
}
|
||||
if (!type || !ConfigTabs[type]) {
|
||||
setKeys([ConfigTabs.basic]);
|
||||
history.replace({
|
||||
pathname: location.pathname,
|
||||
search: queryString.stringify({
|
||||
type: ConfigTabs.basic,
|
||||
id,
|
||||
protocol,
|
||||
name,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
setKeys([type]);
|
||||
}, []);
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
setTitle(query.name);
|
||||
}, [location]);
|
||||
|
||||
const ComponentByKey = ({ key, ...rest }: { key: string; appId: string; protocol: any }) => {
|
||||
const components = {
|
||||
[ConfigTabs.basic]: AppBasic,
|
||||
[ConfigTabs.protocol_config]: AppProtocol,
|
||||
[ConfigTabs.app_account]: AppAccount,
|
||||
[ConfigTabs.access_policy]: AccessPolicy,
|
||||
[ConfigTabs.permission_resource]: PermissionResource,
|
||||
[ConfigTabs.permission_role]: PermissionRole,
|
||||
};
|
||||
const Component = components[key];
|
||||
return <Component {...rest} />;
|
||||
};
|
||||
|
||||
return wrapSSR(
|
||||
<PageContainer
|
||||
title={title}
|
||||
style={{ overflow: 'hidden' }}
|
||||
onBack={() => {
|
||||
history.push('/app');
|
||||
}}
|
||||
>
|
||||
<GridContent>
|
||||
<div
|
||||
className={classNames(`${prefixCls}-main`, hashId)}
|
||||
ref={(ref) => {
|
||||
if (ref) {
|
||||
dom.current = ref;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className={classNames(`${prefixCls}-left`, hashId)}>
|
||||
<Menu
|
||||
mode={initConfig.mode}
|
||||
selectedKeys={keys}
|
||||
className={classNames(`${prefixCls}-left-menu`, hashId)}
|
||||
items={items}
|
||||
onSelect={({ selectedKeys }) => {
|
||||
setKeys(selectedKeys);
|
||||
history.replace({
|
||||
pathname: location.pathname,
|
||||
search: queryString.stringify({ type: selectedKeys?.[0], id, protocol, name }),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames(`${prefixCls}-right`, hashId)}>
|
||||
{keys && ComponentByKey({ key: keys?.[0] || '', appId: id, protocol: protocol })}
|
||||
</div>
|
||||
</div>
|
||||
</GridContent>
|
||||
</PageContainer>,
|
||||
);
|
||||
};
|
|
@ -1,294 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { getAllAppGroupList, updateApp } from '@/services/app';
|
||||
|
||||
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
|
||||
import { useAsyncEffect } from 'ahooks';
|
||||
import { App, Avatar, Skeleton, Upload } from 'antd';
|
||||
import { omit } from 'lodash';
|
||||
import React, { useState } from 'react';
|
||||
import { history, useIntl, useLocation } from '@umijs/max';
|
||||
import queryString from 'query-string';
|
||||
|
||||
import useStyle from './style';
|
||||
import classNames from 'classnames';
|
||||
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { uploadFile } from '@/services/upload';
|
||||
import { GetApp } from '@/pages/app/AppConfig/data';
|
||||
import { getApp } from '../../service';
|
||||
|
||||
const prefixCls = 'app-basic-info';
|
||||
const AppBasic = (props: { appId: string }) => {
|
||||
const { appId } = props;
|
||||
const intl = useIntl();
|
||||
const useApp = App.useApp();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [uploadLoading, setUploadLoading] = useState(false);
|
||||
const [uploadIconUrl, setUploadIconUrl] = useState<string>();
|
||||
const [app, setApp] = useState<GetApp>();
|
||||
const location = useLocation();
|
||||
const query = queryString.parse(location.search) as {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
protocol: string;
|
||||
};
|
||||
const { styles } = useStyle(prefixCls);
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
setLoading(true);
|
||||
const { result, success } = await getApp(appId);
|
||||
if (success && result) {
|
||||
setApp(result);
|
||||
}
|
||||
setLoading(false);
|
||||
}, [appId]);
|
||||
|
||||
/**
|
||||
* onSave
|
||||
*
|
||||
* @param key
|
||||
* @param record
|
||||
*/
|
||||
const onSave = async (key: React.Key | React.Key[], record: GetApp): Promise<any | void> => {
|
||||
//调用接口修改
|
||||
let params: Record<string, any> = {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
remark: record.remark,
|
||||
groupIds: record.groupIds,
|
||||
};
|
||||
if (uploadIconUrl) {
|
||||
params = { ...params, icon: uploadIconUrl };
|
||||
}
|
||||
const { success } = await updateApp(params);
|
||||
if (success) {
|
||||
useApp.message.success(intl.formatMessage({ id: 'app.operation_success' }));
|
||||
if (uploadIconUrl) {
|
||||
setApp({ ...record, icon: uploadIconUrl });
|
||||
} else {
|
||||
setApp((values) => {
|
||||
return { ...record, icon: values?.icon || '' };
|
||||
});
|
||||
}
|
||||
history.replace(
|
||||
`/app/config?id=${query.id}&name=${record.name}&protocol=${query.protocol}&type=${query.type}`,
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
};
|
||||
return (
|
||||
<ProCard
|
||||
title={intl.formatMessage({ id: 'pages.app.config.basic' })}
|
||||
headerBordered
|
||||
className={styles.main}
|
||||
>
|
||||
<Skeleton loading={loading} active={true} paragraph={{ rows: 5 }}>
|
||||
<div className={classNames(`${prefixCls}-descriptions`)}>
|
||||
<ProDescriptions<GetApp>
|
||||
size="small"
|
||||
column={1}
|
||||
dataSource={omit(app, 'config')}
|
||||
editable={{
|
||||
onSave: onSave,
|
||||
}}
|
||||
>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="icon"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.icon' })}
|
||||
copyable={false}
|
||||
fieldProps={{
|
||||
preview: false,
|
||||
width: 85,
|
||||
}}
|
||||
renderText={(text) => {
|
||||
return (
|
||||
<>
|
||||
<Avatar
|
||||
shape="square"
|
||||
size={102}
|
||||
src={text}
|
||||
style={{ border: '1px dashed #d9d9d9' }}
|
||||
/>
|
||||
<div style={{ color: 'rgba(0, 0, 0, 0.45)', marginTop: '8px' }}>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.1' })}
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.2' })}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
renderFormItem={() => {
|
||||
return (
|
||||
<div>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
name="file"
|
||||
showUploadList={false}
|
||||
accept="image/png, image/jpeg"
|
||||
customRequest={async (files) => {
|
||||
setUploadLoading(true);
|
||||
if (!files.file) {
|
||||
return;
|
||||
}
|
||||
const { success, result, message } = await uploadFile(files.file).finally(
|
||||
() => {
|
||||
setUploadLoading(false);
|
||||
},
|
||||
);
|
||||
if (success && result) {
|
||||
setUploadIconUrl(result);
|
||||
return;
|
||||
}
|
||||
useApp.message.error(message);
|
||||
}}
|
||||
>
|
||||
{uploadIconUrl ? (
|
||||
<img src={uploadIconUrl} alt="avatar" style={{ width: '100%' }} />
|
||||
) : (
|
||||
<div>
|
||||
{uploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
|
||||
<div style={{ marginTop: 8 }}>上传</div>
|
||||
</div>
|
||||
)}
|
||||
</Upload>
|
||||
<div style={{ color: 'rgba(0, 0, 0, 0.45)' }}>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.1' })}
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.2' })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ProDescriptions>
|
||||
<ProDescriptions<GetApp>
|
||||
size="small"
|
||||
column={2}
|
||||
dataSource={omit(app, 'config')}
|
||||
editable={{
|
||||
onSave: onSave,
|
||||
}}
|
||||
>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="name"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.name' })}
|
||||
copyable={false}
|
||||
formItemProps={{
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.basic.name.rule.0.message',
|
||||
}),
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="groupIds"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.group' })}
|
||||
valueType={'select'}
|
||||
request={async () => {
|
||||
const { success, data } = await getAllAppGroupList({}, {}, {});
|
||||
if (success && data) {
|
||||
return data.map((i) => {
|
||||
return { label: i.name, value: i.id };
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}}
|
||||
fieldProps={{ rows: 2, maxLength: 20, maxTagCount: 'responsive', mode: 'multiple' }}
|
||||
copyable={false}
|
||||
/>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="enabled"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.enabled' })}
|
||||
editable={false}
|
||||
valueEnum={{
|
||||
true: { text: intl.formatMessage({ id: 'app.normal' }), status: 'Success' },
|
||||
false: { text: intl.formatMessage({ id: 'app.disable' }), status: 'Error' },
|
||||
}}
|
||||
/>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="type"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.type' })}
|
||||
editable={false}
|
||||
valueEnum={{
|
||||
custom_made: {
|
||||
text: intl.formatMessage({
|
||||
id: 'pages.app.config.basic.type.value_enum.custom_made',
|
||||
}),
|
||||
},
|
||||
standard: {
|
||||
text: intl.formatMessage({
|
||||
id: 'pages.app.config.basic.type.value_enum.standard',
|
||||
}),
|
||||
},
|
||||
self_developed: {
|
||||
text: intl.formatMessage({
|
||||
id: 'pages.app.config.basic.type.value_enum.self_developed',
|
||||
}),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="clientId"
|
||||
ellipsis
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.client_id' })}
|
||||
valueType={'text'}
|
||||
editable={false}
|
||||
copyable={true}
|
||||
/>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="clientSecret"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.client_secret' })}
|
||||
valueType={'password'}
|
||||
editable={false}
|
||||
copyable={true}
|
||||
/>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="createTime"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.create_time' })}
|
||||
valueType={'dateTime'}
|
||||
copyable={false}
|
||||
editable={false}
|
||||
/>
|
||||
<ProDescriptions.Item
|
||||
dataIndex="remark"
|
||||
label={intl.formatMessage({ id: 'pages.app.config.basic.remark' })}
|
||||
valueType={'textarea'}
|
||||
fieldProps={{ rows: 2, maxLength: 200 }}
|
||||
copyable={false}
|
||||
/>
|
||||
</ProDescriptions>
|
||||
</div>
|
||||
</Skeleton>
|
||||
</ProCard>
|
||||
);
|
||||
};
|
||||
export default AppBasic;
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
const useStyle = createStyles(({ prefixCls, token }, props) => {
|
||||
const antCls = `.${prefixCls}`;
|
||||
const prefix = `${props}`;
|
||||
return {
|
||||
main: {
|
||||
height: 'calc(100vh - 178px)',
|
||||
[`${antCls}-pro-card-body`]: {
|
||||
overflow: 'auto !important',
|
||||
},
|
||||
[`.${prefix}-descriptions`]: {
|
||||
[`${antCls}-descriptions-item-container ${antCls}-space-item`]: {
|
||||
span: {
|
||||
padding: '0 !important',
|
||||
},
|
||||
},
|
||||
[`${antCls}-descriptions-small ${antCls}-descriptions-row > th, ${antCls}-descriptions-small ${antCls}-descriptions-row > td`]:
|
||||
{
|
||||
paddingBottom: '16px',
|
||||
},
|
||||
},
|
||||
[`@media screen and (max-width: ${token.screenXL}px)`]: {
|
||||
height: 'calc(100vh - 240px)',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
export default useStyle;
|
|
@ -1,401 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { getAppConfig, saveAppConfig } from '@/services/app';
|
||||
import { useAsyncEffect } from 'ahooks';
|
||||
import { Alert, App, Divider, Form, Spin } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
EditableProTable,
|
||||
FooterToolbar,
|
||||
ProColumns,
|
||||
ProForm,
|
||||
ProFormDependency,
|
||||
ProFormRadio,
|
||||
ProFormSelect,
|
||||
ProFormText,
|
||||
} from '@ant-design/pro-components';
|
||||
import { FormEncryptType } from '../../../constant';
|
||||
import { omit } from 'lodash';
|
||||
import ConfigAbout from './ConfigAbout';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { AuthorizationType } from '../CommonConfig';
|
||||
import { GetApp } from '../../../data.d';
|
||||
import { generateUUID } from '@/utils/utils';
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
span: 6,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 12,
|
||||
},
|
||||
};
|
||||
export default (props: { app: GetApp | Record<string, any> }) => {
|
||||
const { app } = props;
|
||||
const intl = useIntl();
|
||||
const { message } = App.useApp();
|
||||
const { id, template } = app;
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [otherFieldTableForm] = Form.useForm();
|
||||
const [otherFieldEditableKeys, setOtherFieldEditableKeys] = useState<React.Key[]>(() => []);
|
||||
const [protocolEndpoint, setProtocolEndpoint] = useState<Record<string, string>>({});
|
||||
|
||||
const getConfig = async () => {
|
||||
setLoading(true);
|
||||
const { result, success } = await getAppConfig(id);
|
||||
if (success && result) {
|
||||
form.setFieldsValue({ ...omit(result, 'protocolEndpoint', 'otherField'), appId: id });
|
||||
//设置Endpoint相关
|
||||
setProtocolEndpoint(result.protocolEndpoint);
|
||||
//其他字段
|
||||
if (result?.otherField) {
|
||||
const otherField = result?.otherField.map((i: Record<string, string>) => {
|
||||
return { key: generateUUID(), fieldValue: i.fieldValue, fieldName: i.fieldName };
|
||||
});
|
||||
form.setFieldsValue({ otherField: otherField });
|
||||
setOtherFieldEditableKeys(
|
||||
otherField.map((i: Record<string, string>) => {
|
||||
return i.key;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
await getConfig();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Spin spinning={loading}>
|
||||
<Alert
|
||||
showIcon={true}
|
||||
message={
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'app.issue' })}
|
||||
{intl.formatMessage({ id: 'app.disposition' })}{' '}
|
||||
<a
|
||||
target={'_blank'}
|
||||
href={'https://eiam.topiam.cn/docs/application/form/overview'}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form',
|
||||
})}
|
||||
</a>{' '}
|
||||
。
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
<ProForm
|
||||
form={form}
|
||||
requiredMark={true}
|
||||
layout={'horizontal'}
|
||||
{...formItemLayout}
|
||||
scrollToFirstError
|
||||
onFinish={async (values) => {
|
||||
const validate = await otherFieldTableForm.validateFields();
|
||||
setLoading(true);
|
||||
if (validate) {
|
||||
const { success } = await saveAppConfig({
|
||||
id,
|
||||
template,
|
||||
config: omit(values, 'id', 'template'),
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
if (success) {
|
||||
message.success(intl.formatMessage({ id: 'app.save_success' }));
|
||||
await getConfig();
|
||||
return true;
|
||||
}
|
||||
message.error(intl.formatMessage({ id: 'app.save_fail' }));
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
submitter={{
|
||||
render: (_, dom) => {
|
||||
return <FooterToolbar>{dom}</FooterToolbar>;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ProFormText name={'appId'} hidden />
|
||||
<ProFormText
|
||||
name={'loginUrl'}
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.login_url',
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.placeholder',
|
||||
})}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.rule.0.message',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'url',
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.rule.1.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name={'usernameField'}
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_field',
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_field.placeholder',
|
||||
})}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_field.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_field.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name={'passwordField'}
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_field',
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_field.placeholder',
|
||||
})}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_field.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_field.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormSelect
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type',
|
||||
})}
|
||||
name={'passwordEncryptType'}
|
||||
options={[
|
||||
{ value: FormEncryptType.aes, label: 'AES' },
|
||||
{ value: FormEncryptType.base64, label: 'BASE64' },
|
||||
{ value: FormEncryptType.md5, label: 'MD5' },
|
||||
]}
|
||||
fieldProps={{ allowClear: true }}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type.extra',
|
||||
})}
|
||||
/>
|
||||
<ProFormDependency name={['passwordEncryptType']}>
|
||||
{({ passwordEncryptType }) => {
|
||||
return passwordEncryptType === FormEncryptType.aes ? (
|
||||
<ProFormText
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key',
|
||||
})}
|
||||
name={'passwordEncryptKey'}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}}
|
||||
</ProFormDependency>
|
||||
<ProFormSelect
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type',
|
||||
})}
|
||||
name={'usernameEncryptType'}
|
||||
options={[
|
||||
{ value: FormEncryptType.aes, label: 'AES' },
|
||||
{ value: FormEncryptType.base64, label: 'BASE64' },
|
||||
]}
|
||||
fieldProps={{ allowClear: true }}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type.extra',
|
||||
})}
|
||||
/>
|
||||
<ProFormDependency name={['usernameEncryptType']}>
|
||||
{({ usernameEncryptType }) => {
|
||||
return usernameEncryptType === FormEncryptType.aes ? (
|
||||
<ProFormText
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key',
|
||||
})}
|
||||
name={'usernameEncryptKey'}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}}
|
||||
</ProFormDependency>
|
||||
<ProFormRadio.Group
|
||||
name={'submitType'}
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.submit_type',
|
||||
})}
|
||||
initialValue={['post']}
|
||||
options={[
|
||||
{ value: 'post', label: 'POST' },
|
||||
{ value: 'get', label: 'GET' },
|
||||
]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.submit_type.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Divider />
|
||||
{/*授权类型*/}
|
||||
<AuthorizationType />
|
||||
<Divider />
|
||||
<ProForm.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field',
|
||||
})}
|
||||
name="otherField"
|
||||
trigger="onValuesChange"
|
||||
>
|
||||
<EditableProTable
|
||||
rowKey={'key'}
|
||||
toolBarRender={false}
|
||||
scroll={{
|
||||
x: 500,
|
||||
}}
|
||||
columns={
|
||||
[
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name',
|
||||
}),
|
||||
dataIndex: 'fieldName',
|
||||
fieldProps: {
|
||||
allowClear: false,
|
||||
},
|
||||
formItemProps: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name.rule.0',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value',
|
||||
}),
|
||||
key: 'fieldValue',
|
||||
dataIndex: 'fieldValue',
|
||||
ellipsis: true,
|
||||
formItemProps: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value.rule.0',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.option',
|
||||
}),
|
||||
valueType: 'option',
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
width: 50,
|
||||
},
|
||||
] as ProColumns[]
|
||||
}
|
||||
recordCreatorProps={{
|
||||
creatorButtonText: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.record_creator_props',
|
||||
}),
|
||||
newRecordType: 'dataSource',
|
||||
position: 'bottom',
|
||||
record: () => ({
|
||||
key: Date.now(),
|
||||
}),
|
||||
}}
|
||||
editable={{
|
||||
form: otherFieldTableForm,
|
||||
type: 'multiple',
|
||||
editableKeys: otherFieldEditableKeys,
|
||||
onChange: setOtherFieldEditableKeys,
|
||||
deletePopconfirmMessage: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.editable',
|
||||
}),
|
||||
actionRender: (row, _, dom) => {
|
||||
return [dom.delete];
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</ProForm>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<ConfigAbout appId={id} protocolEndpoint={protocolEndpoint} collapsed={true} />
|
||||
</Spin>
|
||||
);
|
||||
};
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { getAppConfig, saveAppConfig } from '@/services/app';
|
||||
import { useAsyncEffect } from 'ahooks';
|
||||
import { Alert, App, Divider, Form, Spin } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
FooterToolbar,
|
||||
ProForm,
|
||||
ProFormDigit,
|
||||
ProFormRadio,
|
||||
ProFormSelect,
|
||||
ProFormText,
|
||||
} from '@ant-design/pro-components';
|
||||
import ConfigAbout from './ConfigAbout';
|
||||
import { omit } from 'lodash';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { AuthorizationType } from '../CommonConfig';
|
||||
import { GetApp } from '../../../data.d';
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 7 },
|
||||
md: { span: 6 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 13 },
|
||||
md: { span: 14 },
|
||||
},
|
||||
};
|
||||
export default (props: { app: GetApp | Record<string, any> }) => {
|
||||
const { app } = props;
|
||||
const { id, template } = app;
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [protocolEndpoint, setProtocolEndpoint] = useState<Record<string, string>>({});
|
||||
const intl = useIntl();
|
||||
const { message } = App.useApp();
|
||||
|
||||
const getConfig = async () => {
|
||||
setLoading(true);
|
||||
const { result, success } = await getAppConfig(id);
|
||||
if (success && result) {
|
||||
form.setFieldsValue({ ...omit(result, 'protocolEndpoint'), appId: id });
|
||||
//设置Endpoint相关
|
||||
setProtocolEndpoint(result.protocolEndpoint);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
await getConfig();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Spin spinning={loading}>
|
||||
<Alert
|
||||
showIcon={true}
|
||||
message={
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'app.issue' })}
|
||||
{intl.formatMessage({ id: 'app.disposition' })}{' '}
|
||||
<a
|
||||
target={'_blank'}
|
||||
href={'https://eiam.topiam.cn/docs/application/jwt/overview'}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt',
|
||||
})}
|
||||
</a>{' '}
|
||||
。
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
<ProForm
|
||||
layout={'horizontal'}
|
||||
{...layout}
|
||||
form={form}
|
||||
scrollToFirstError
|
||||
onFinish={async (values) => {
|
||||
setLoading(true);
|
||||
const { success } = await saveAppConfig({
|
||||
id,
|
||||
template,
|
||||
config: omit(values, 'id', 'template'),
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
if (success) {
|
||||
message.success(intl.formatMessage({ id: 'app.save_success' }));
|
||||
await getConfig();
|
||||
return true;
|
||||
}
|
||||
message.error(intl.formatMessage({ id: 'app.save_fail' }));
|
||||
return false;
|
||||
}}
|
||||
submitter={{
|
||||
render: (_, dom) => {
|
||||
return <FooterToolbar>{dom}</FooterToolbar>;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ProFormText
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url',
|
||||
})}
|
||||
name={'redirectUrl'}
|
||||
fieldProps={{ allowClear: false }}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.0.message',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'url',
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.1.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
label={'target link url'}
|
||||
name={'targetLinkUrl'}
|
||||
fieldProps={{ allowClear: false }}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
type: 'url',
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<ProFormRadio.Group
|
||||
name={'bindingType'}
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.binding_type',
|
||||
})}
|
||||
initialValue={['post']}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.binding_type.extra',
|
||||
})}
|
||||
options={[
|
||||
{ value: 'post', label: 'POST' },
|
||||
{ value: 'get', label: 'GET' },
|
||||
]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.binding_type.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormSelect
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type',
|
||||
})}
|
||||
name={'idTokenSubjectType'}
|
||||
options={[
|
||||
{
|
||||
value: 'user_id',
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.0',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: 'app_user',
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.1',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
fieldProps={{ allowClear: false }}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormDigit
|
||||
name={'idTokenTimeToLive'}
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live',
|
||||
})}
|
||||
addonAfter={'秒'}
|
||||
max={84600}
|
||||
min={1}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Divider />
|
||||
{/*授权类型*/}
|
||||
<AuthorizationType />
|
||||
</ProForm>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<ConfigAbout appId={app.id} protocolEndpoint={protocolEndpoint} collapsed={true} />
|
||||
</Spin>
|
||||
);
|
||||
};
|
|
@ -1,637 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { SsoInitiator } from '@/pages/app/AppConfig/constant';
|
||||
import { getAppConfig, saveAppConfig } from '@/services/app';
|
||||
import {
|
||||
FooterToolbar,
|
||||
ProForm,
|
||||
ProFormCheckbox,
|
||||
ProFormDependency,
|
||||
ProFormDigit,
|
||||
ProFormSelect,
|
||||
ProFormSwitch,
|
||||
ProFormText,
|
||||
} from '@ant-design/pro-components';
|
||||
import { useMount } from 'ahooks';
|
||||
import { Alert, App, Button, Collapse, Divider, Form, Input, Spin } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import ConfigAbout from './ConfigAbout';
|
||||
import { omit } from 'lodash';
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { AuthorizationType } from '../CommonConfig';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { GetApp } from '../../../data.d';
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
span: 6,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 12,
|
||||
},
|
||||
};
|
||||
const formItemLayoutWithOutLabel = {
|
||||
wrapperCol: {
|
||||
span: 12,
|
||||
offset: 6,
|
||||
},
|
||||
};
|
||||
|
||||
const useStyle = createStyles(({ prefixCls, css }) => {
|
||||
return css`
|
||||
.${prefixCls}-checkbox-group-item {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.${prefixCls}-checkbox {
|
||||
align-self: flex-start;
|
||||
top: 2px;
|
||||
}
|
||||
`;
|
||||
});
|
||||
|
||||
export default (props: { app: GetApp | Record<string, any> }) => {
|
||||
const intl = useIntl();
|
||||
const { styles } = useStyle();
|
||||
const { message } = App.useApp();
|
||||
const { app } = props;
|
||||
const { id, template } = app;
|
||||
const [form] = Form.useForm();
|
||||
const [protocolEndpoint, setProtocolEndpoint] = useState<Record<string, string>>({});
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
useMount(async () => {
|
||||
setLoading(true);
|
||||
const { result, success } = await getAppConfig(id);
|
||||
if (success && result) {
|
||||
form.setFieldsValue({
|
||||
appId: id,
|
||||
...result,
|
||||
});
|
||||
//设置Endpoint相关
|
||||
setProtocolEndpoint(result.protocolEndpoint);
|
||||
}
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
return (
|
||||
<Spin spinning={loading}>
|
||||
<Alert
|
||||
showIcon={true}
|
||||
message={
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'app.issue' })}
|
||||
{intl.formatMessage({ id: 'app.disposition' })}{' '}
|
||||
<a
|
||||
target={'_blank'}
|
||||
href={'https://eiam.topiam.cn/docs/application/oidc/overview'}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc',
|
||||
})}
|
||||
</a>{' '}
|
||||
。
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
<ProForm
|
||||
className={styles}
|
||||
requiredMark={true}
|
||||
layout={'horizontal'}
|
||||
{...formItemLayout}
|
||||
form={form}
|
||||
scrollToFirstError
|
||||
onFinish={async (values) => {
|
||||
const { success } = await saveAppConfig({
|
||||
id,
|
||||
template,
|
||||
config: omit(values, 'id', 'template'),
|
||||
});
|
||||
if (success) {
|
||||
message.success(intl.formatMessage({ id: 'app.operation_success' }));
|
||||
return true;
|
||||
}
|
||||
message.success(intl.formatMessage({ id: 'app.operation_fail' }));
|
||||
return false;
|
||||
}}
|
||||
submitter={{
|
||||
render: (_, dom) => {
|
||||
return <FooterToolbar>{dom}</FooterToolbar>;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ProFormText name={'appId'} hidden />
|
||||
<ProFormCheckbox.Group
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types',
|
||||
})}
|
||||
layout={'vertical'}
|
||||
name={'authGrantTypes'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
options={[
|
||||
{
|
||||
value: 'authorization_code',
|
||||
label: (
|
||||
<>
|
||||
<span style={{ marginRight: '12px' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.0',
|
||||
})}
|
||||
</span>
|
||||
<span style={{ color: '#999' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.1',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'refresh_token',
|
||||
label: (
|
||||
<>
|
||||
<span style={{ marginRight: '12px' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.0',
|
||||
})}
|
||||
</span>
|
||||
<span style={{ color: '#999' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.1',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'implicit',
|
||||
label: (
|
||||
<>
|
||||
<span style={{ marginRight: '12px' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.0',
|
||||
})}
|
||||
</span>
|
||||
<span style={{ color: '#999' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.1',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'password',
|
||||
label: (
|
||||
<>
|
||||
<span style={{ marginRight: '12px' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.0',
|
||||
})}
|
||||
</span>
|
||||
<span style={{ color: '#999' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.1',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormDependency name={['authGrantTypes']}>
|
||||
{({ authGrantTypes }) => {
|
||||
return (
|
||||
authGrantTypes?.includes('authorization_code') && (
|
||||
<ProFormSwitch
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key',
|
||||
})}
|
||||
name={'requireProofKey'}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key.extra',
|
||||
})}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}}
|
||||
</ProFormDependency>
|
||||
<Form.List
|
||||
name="redirectUris"
|
||||
rules={[
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
if (value && value.length > 0) {
|
||||
return null;
|
||||
}
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message',
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
{(fields, { add, remove }, { errors }) => (
|
||||
<>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item
|
||||
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
|
||||
required={true}
|
||||
key={field.key}
|
||||
label={
|
||||
index === 0
|
||||
? intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris',
|
||||
})
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
{...field}
|
||||
validateTrigger={['onChange', 'onBlur']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'url',
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.1.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.placeholder',
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<DeleteOutlined onClick={() => remove(field.name)} />
|
||||
</div>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item
|
||||
{...(fields.length === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
|
||||
required={true}
|
||||
label={
|
||||
fields.length === 0
|
||||
? intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris',
|
||||
})
|
||||
: ''
|
||||
}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.extra',
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
icon={<PlusOutlined />}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{intl.formatMessage({ id: 'app.add' })}
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
<Form.List name="postLogoutRedirectUris">
|
||||
{(fields, { add, remove }, {}) => (
|
||||
<>
|
||||
{fields.map((field, index) => {
|
||||
return (
|
||||
<Form.Item
|
||||
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
|
||||
key={field.key}
|
||||
label={
|
||||
index === 0
|
||||
? intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
|
||||
})
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
{...field}
|
||||
validateTrigger={['onChange', 'onBlur']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'url',
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.1.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.placeholder',
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<DeleteOutlined onClick={() => remove(field.name)} />
|
||||
</div>
|
||||
</Form.Item>
|
||||
);
|
||||
})}
|
||||
<Form.Item
|
||||
{...(fields.length === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
|
||||
label={
|
||||
fields.length === 0
|
||||
? intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
|
||||
})
|
||||
: ''
|
||||
}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.extra',
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
icon={<PlusOutlined />}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{intl.formatMessage({ id: 'app.add' })}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
{/*授权类型*/}
|
||||
<AuthorizationType />
|
||||
<ProFormSelect
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type',
|
||||
})}
|
||||
name={'initLoginType'}
|
||||
allowClear={false}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
options={[
|
||||
{
|
||||
value: SsoInitiator.APP,
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.0',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: SsoInitiator.PORTAL_OR_APP,
|
||||
label: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.1',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
extra={
|
||||
<>
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.0',
|
||||
})}
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.1',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<ProFormDependency name={['initLoginType']}>
|
||||
{(values) => {
|
||||
return (
|
||||
values?.initLoginType === SsoInitiator.PORTAL_OR_APP && (
|
||||
<ProFormText
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url',
|
||||
})}
|
||||
name={'initLoginUrl'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
fieldProps={{
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.field_props',
|
||||
}),
|
||||
}}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.extra',
|
||||
})}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}}
|
||||
</ProFormDependency>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Collapse
|
||||
ghost
|
||||
expandIconPosition={'start'}
|
||||
defaultActiveKey={'advanced'}
|
||||
items={[
|
||||
{
|
||||
key: 'advanced',
|
||||
label: (
|
||||
<a>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced',
|
||||
})}
|
||||
</a>
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<ProFormCheckbox.Group
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes',
|
||||
})}
|
||||
layout={'vertical'}
|
||||
name={'grantScopes'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.extra',
|
||||
})}
|
||||
options={[
|
||||
{
|
||||
value: 'openid',
|
||||
label: 'openid',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
value: 'email',
|
||||
label: (
|
||||
<>
|
||||
<span style={{ marginRight: '12px' }}>email</span>
|
||||
<span style={{ color: '#999' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.1.label',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'phone',
|
||||
label: (
|
||||
<>
|
||||
<span style={{ marginRight: '12px' }}>phone</span>
|
||||
<span style={{ color: '#999' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.2.label',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'profile',
|
||||
label: (
|
||||
<>
|
||||
<span style={{ marginRight: '12px' }}>profile</span>
|
||||
<span style={{ color: '#999' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.3.label',
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormDigit
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live',
|
||||
})}
|
||||
name={'accessTokenTimeToLive'}
|
||||
addonAfter={intl.formatMessage({ id: 'app.minute' })}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live.extra',
|
||||
})}
|
||||
/>
|
||||
<ProFormDigit
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live',
|
||||
})}
|
||||
name={'refreshTokenTimeToLive'}
|
||||
addonAfter={intl.formatMessage({ id: 'app.minute' })}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live.extra',
|
||||
})}
|
||||
/>
|
||||
<ProFormDigit
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live',
|
||||
})}
|
||||
readonly
|
||||
name={'idTokenTimeToLive'}
|
||||
addonAfter={intl.formatMessage({ id: 'app.minute.not_update' })}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live.extra',
|
||||
})}
|
||||
/>
|
||||
<ProFormSelect
|
||||
options={[
|
||||
{ value: 'ES256', label: 'ES256' },
|
||||
{ value: 'RS256', label: 'RS256' },
|
||||
]}
|
||||
label={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm',
|
||||
})}
|
||||
name={'idTokenSignatureAlgorithm'}
|
||||
allowClear={false}
|
||||
extra={intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.extra',
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</ProForm>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<ConfigAbout appId={id} protocolEndpoint={protocolEndpoint} collapsed={true} />
|
||||
</Spin>
|
||||
);
|
||||
};
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { getPermissionResource, permissionResourceParamCheck } from '../../service';
|
||||
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
|
||||
import { Form, Spin } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useAsyncEffect } from 'ahooks';
|
||||
import Paragraph from 'antd/es/typography/Paragraph';
|
||||
|
||||
type UpdateFormProps = {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* SYSTEM
|
||||
*/
|
||||
appId: string;
|
||||
/**
|
||||
* 是否显示
|
||||
*/
|
||||
open: boolean;
|
||||
/**
|
||||
* 取消方法
|
||||
*/
|
||||
onCancel: (e?: React.MouseEvent | React.KeyboardEvent) => void;
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
onFinish?: (formData: Record<string, string>) => Promise<boolean | void>;
|
||||
};
|
||||
const UpdateResource: React.FC<UpdateFormProps> = (props) => {
|
||||
const { open, onCancel, onFinish, id, appId } = props;
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (open && id) {
|
||||
setLoading(true);
|
||||
const { success, result } = await getPermissionResource(id);
|
||||
if (success && result) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, [id, onCancel, open]);
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
title="修改资源"
|
||||
width={'500px'}
|
||||
open={open}
|
||||
labelCol={{ span: 4 }}
|
||||
wrapperCol={{ span: 20 }}
|
||||
layout={'horizontal'}
|
||||
labelAlign={'right'}
|
||||
modalProps={{
|
||||
maskClosable: true,
|
||||
destroyOnClose: true,
|
||||
onCancel: onCancel,
|
||||
}}
|
||||
form={form}
|
||||
onFinish={async (values) => {
|
||||
setLoading(true);
|
||||
const result = await onFinish?.(values);
|
||||
setLoading(false);
|
||||
return result;
|
||||
}}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
<ProFormText name={'id'} hidden />
|
||||
<ProFormText
|
||||
name="name"
|
||||
label="资源名称"
|
||||
placeholder="请输入资源名称"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入资源名称',
|
||||
},
|
||||
{
|
||||
validator: async (rule, value) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
setLoading(true);
|
||||
const { success, result } = await permissionResourceParamCheck(
|
||||
appId,
|
||||
'NAME',
|
||||
value,
|
||||
id,
|
||||
);
|
||||
setLoading(false);
|
||||
if (!success) {
|
||||
return Promise.reject<any>();
|
||||
}
|
||||
if (!result) {
|
||||
return Promise.reject<any>(new Error('资源名称已存在'));
|
||||
}
|
||||
},
|
||||
validateTrigger: ['onBlur'],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
name="code"
|
||||
label="资源编码"
|
||||
placeholder="请输入资源编码"
|
||||
proFieldProps={{
|
||||
render: (value: string) => {
|
||||
return (
|
||||
value && (
|
||||
<Paragraph copyable={{ text: value }} style={{ marginBottom: '0' }}>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<span>${value}</span>`,
|
||||
}}
|
||||
/>
|
||||
</Paragraph>
|
||||
)
|
||||
);
|
||||
},
|
||||
}}
|
||||
readonly
|
||||
extra="资源编码在当前应用中的唯一标识,不能重复,仅支持英文、数字、下划线,创建后不可修改。"
|
||||
/>
|
||||
<ProFormTextArea
|
||||
name="desc"
|
||||
fieldProps={{ rows: 2, maxLength: 20, showCount: false }}
|
||||
label="资源描述"
|
||||
placeholder="请输入资源描述"
|
||||
/>
|
||||
</Spin>
|
||||
</ModalForm>
|
||||
);
|
||||
};
|
||||
export default UpdateResource;
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { getPermissionRole, permissionRoleParamCheck } from '../../service';
|
||||
import type { ProFormInstance } from '@ant-design/pro-components';
|
||||
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
|
||||
import { Spin } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { useAsyncEffect } from 'ahooks';
|
||||
import Paragraph from 'antd/es/typography/Paragraph';
|
||||
|
||||
type UpdateFormProps = {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id: string | undefined;
|
||||
/**
|
||||
* 是否显示
|
||||
*/
|
||||
open: boolean;
|
||||
/**
|
||||
* 取消方法
|
||||
*/
|
||||
onCancel: () => void;
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
onFinish?: (formData: Record<string, string>) => Promise<boolean | void>;
|
||||
};
|
||||
const UpdateRole: React.FC<UpdateFormProps> = (props) => {
|
||||
const { open, onCancel, onFinish, id } = props;
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const formRef = useRef<ProFormInstance>();
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (open && id) {
|
||||
setLoading(true);
|
||||
const { success, result } = await getPermissionRole(id);
|
||||
if (success && result) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, [id, open]);
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
title="修改角色"
|
||||
width={600}
|
||||
modalProps={{
|
||||
onCancel: onCancel,
|
||||
destroyOnClose: true,
|
||||
}}
|
||||
layout={'horizontal'}
|
||||
labelCol={{ span: 4 }}
|
||||
wrapperCol={{ span: 19 }}
|
||||
labelAlign={'right'}
|
||||
open={open}
|
||||
key={'update'}
|
||||
onFinish={async (values) => {
|
||||
setLoading(true);
|
||||
const result = await onFinish?.(values);
|
||||
setLoading(false);
|
||||
return result;
|
||||
}}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
<ProFormText name="id" hidden />
|
||||
<ProFormText name="appId" hidden />
|
||||
<ProFormText
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入角色名称',
|
||||
},
|
||||
{
|
||||
validator: async (rule, value) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
setLoading(true);
|
||||
const { success, result } = await permissionRoleParamCheck(
|
||||
formRef.current?.getFieldValue('appId'),
|
||||
'NAME',
|
||||
value,
|
||||
id,
|
||||
);
|
||||
setLoading(false);
|
||||
if (!success) {
|
||||
return Promise.reject<any>();
|
||||
}
|
||||
if (!result) {
|
||||
return Promise.reject<any>(new Error('手机号已存在'));
|
||||
}
|
||||
},
|
||||
validateTrigger: ['onBlur'],
|
||||
},
|
||||
]}
|
||||
placeholder="请输入角色名称"
|
||||
/>
|
||||
<ProFormText
|
||||
name="code"
|
||||
label="标识"
|
||||
placeholder="请输入角色标识"
|
||||
readonly
|
||||
proFieldProps={{
|
||||
render: (value: string) => {
|
||||
return (
|
||||
value && (
|
||||
<Paragraph copyable={{ text: value }} style={{ marginBottom: '0' }}>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<span>${value}</span>`,
|
||||
}}
|
||||
/>
|
||||
</Paragraph>
|
||||
)
|
||||
);
|
||||
},
|
||||
}}
|
||||
extra="角色编码在当前应用中的唯一标识,不能重复,仅支持英文、数字、下划线,创建后不可修改。"
|
||||
/>
|
||||
<ProFormTextArea
|
||||
name="remark"
|
||||
fieldProps={{ rows: 2, maxLength: 20, showCount: false }}
|
||||
label="描述"
|
||||
placeholder="请输入角色描述"
|
||||
/>
|
||||
</Spin>
|
||||
</ModalForm>
|
||||
);
|
||||
};
|
||||
export default UpdateRole;
|
|
@ -1,603 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
export default {
|
||||
'pages.app.config.basic': '基本信息',
|
||||
'pages.app.config.basic.name': '应用名称',
|
||||
'pages.app.config.basic.name.rule.0.message': '应用名称为必填项',
|
||||
'pages.app.config.basic.icon': '应用图标',
|
||||
'pages.app.config.basic.icon.rule.0.message': '应用图标为必填项',
|
||||
'pages.app.config.basic.icon.rule.1.message': '应用图标上传失败',
|
||||
'pages.app.config.basic.icon.desc.1': '必须为 PNG/JPG 格式',
|
||||
'pages.app.config.basic.icon.desc.2': '建议使用 256 * 256 像素方形图标',
|
||||
'pages.app.config.basic.enabled': '应用状态',
|
||||
'pages.app.config.basic.type': '应用类型',
|
||||
'pages.app.config.basic.group': '应用分组',
|
||||
'pages.app.config.basic.type.value_enum.custom_made': '定制应用',
|
||||
'pages.app.config.basic.type.value_enum.standard': '标准应用',
|
||||
'pages.app.config.basic.type.value_enum.self_developed': '自研应用',
|
||||
'pages.app.config.basic.protocol_name': '应用协议',
|
||||
'pages.app.config.basic.client_id': '客户端 ID',
|
||||
'pages.app.config.basic.client_secret': '客户端秘钥',
|
||||
'pages.app.config.basic.create_time': '创建时间',
|
||||
'pages.app.config.basic.remark': '备注',
|
||||
'pages.app.config.items.login_access': '登录访问',
|
||||
'pages.app.config.items.login_access.protocol_config': '单点登录',
|
||||
|
||||
'pages.app.config.items.login_access.protocol_config.common.authorization_type': '授权范围',
|
||||
'pages.app.config.items.login_access.protocol_config.common.authorization_type.extra':
|
||||
'若选择手动授权,需要在 访问授权 中进行权限分配。',
|
||||
'pages.app.config.items.login_access.protocol_config.common.authorization_type.rule.0.message':
|
||||
'请选择授权范围',
|
||||
'pages.app.config.items.login_access.protocol_config.common.authorization_type.option.0':
|
||||
'手动授权',
|
||||
'pages.app.config.items.login_access.protocol_config.common.authorization_type.option.1':
|
||||
'全员可访问',
|
||||
|
||||
'pages.app.config.items.login_access.protocol_config.cas.client_service_url': 'CAS 客户端的地址',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.client_service_url.extra':
|
||||
'单点登录时,将选中项作为 CAS 用户标识,传递给业务系统。',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.client_service_url.rule.0.message':
|
||||
'请输入 CAS 客户端的地址',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.client_service_url.rule.1.message':
|
||||
'CAS 客户端地址格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type': 'CAS 用户标识',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.0': '用户名称',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.1': '用户姓名',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.2': '用户昵称',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.3': '邮箱地址',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.4': '应用账户',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.extra':
|
||||
'单点登录时,将选中项作为 CAS 用户标识,传递给业务系统。',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.rule.0.message':
|
||||
'请选择 CAS 用户标识',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.service_ticket_expire_time':
|
||||
'ST 过期时间',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.service_ticket_expire_time.extra':
|
||||
'ServiceTicket 过期时间',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.service_ticket_expire_time.rule.0.message':
|
||||
'请配置 ServiceTicket 过期时间',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_type': 'SSO 发起方',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.rule.0.message':
|
||||
'请配置 SSO 发起方',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.option.0':
|
||||
'只允许应用发起',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.option.1':
|
||||
'支持门户和应用发起',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.extra.0':
|
||||
'门户发起:由 IAM 门户页点击进行 SSO 。',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.extra.1':
|
||||
'应用发起:由应用登录主动发起。',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_url': '登录发起地址',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_url.placeholder':
|
||||
'请输入登录链接',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.init_login_url.extra':
|
||||
'若您希望由 TopIAM 门户页访问应用,请填写 TopIAM 发起 SSO 请求访问的应用地址。该地址接收到请求,应即刻转向 TopIAM / login 授权端点。 ',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about': '应用配置信息',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_server_url_prefix':
|
||||
'CAS 服务地址前缀',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_sso_endpoint':
|
||||
'IdP SSO 地址',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_sso_endpoint.extra':
|
||||
'应用发起单点登录的地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_slo_endpoint':
|
||||
'登出端点',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_slo_endpoint.extra':
|
||||
'应用发起单点登出的地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_validate_endpoint':
|
||||
'service ticket 检验端点(CAS 1.0)',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_validate_v2_endpoint':
|
||||
'service ticket 检验端点(CAS 2.0)',
|
||||
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_validate_v3_endpoint':
|
||||
'service ticket 检验端点(CAS 3.0)',
|
||||
'pages.app.config.items.login_access.protocol_config.form': '表单代填单点登录',
|
||||
'pages.app.config.items.login_access.protocol_config.form.login_url': '登录提交URL',
|
||||
'pages.app.config.items.login_access.protocol_config.form.login_url.placeholder':
|
||||
'请输入登录提交URL',
|
||||
'pages.app.config.items.login_access.protocol_config.form.login_url.extra':
|
||||
'登录表单提交完整URL,以http://或https://开头,如:https://oa.xxxx.com/login',
|
||||
'pages.app.config.items.login_access.protocol_config.form.login_url.rule.0.message':
|
||||
'应用登录URL为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.login_url.rule.1.message':
|
||||
'URL格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_field': '登录名属性名称',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_field.placeholder':
|
||||
'请输入登录名属性名称',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_field.extra':
|
||||
'username标签的name属性',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_field.rule.0.message':
|
||||
'登录名属性名称为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_field': '登录密码属性名称',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_field.placeholder':
|
||||
'请输入登录密码属性名称',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_field.extra':
|
||||
'password标签的name属性',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_field.rule.0.message':
|
||||
'登录密码属性名称为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type':
|
||||
'登录密码加密算法',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type.extra':
|
||||
'登录密码加密算法类型。',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key':
|
||||
'登录密码加密秘钥',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.extra':
|
||||
'登录密码加密秘钥。',
|
||||
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.rule.0.message':
|
||||
'登录密码加密秘钥为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type':
|
||||
'用户名加密算法',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type.extra':
|
||||
'用户名加密算法类型。',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key': '用户名加密秘钥',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.extra':
|
||||
'用户名加密秘钥。',
|
||||
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.rule.0.message':
|
||||
'用户名加密秘钥为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.submit_type': '登录提交方式',
|
||||
'pages.app.config.items.login_access.protocol_config.form.submit_type.rule.0.message':
|
||||
'登录提交方式为必选项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field': '登录其他字段',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name':
|
||||
'属性名称',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name.rule.0':
|
||||
'此项为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value':
|
||||
'属性值',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value.rule.0':
|
||||
'此项为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.option': '操作',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field.record_creator_props':
|
||||
'添加其他字段',
|
||||
'pages.app.config.items.login_access.protocol_config.form.other_field.editable':
|
||||
'您确定要删除此属性吗?',
|
||||
'pages.app.config.items.login_access.protocol_config.form.config_about': '应用配置信息',
|
||||
'pages.app.config.items.login_access.protocol_config.form.config_about.idp_sso_endpoint':
|
||||
'IdP SSO 地址',
|
||||
'pages.app.config.items.login_access.protocol_config.form.config_about.idp_sso_endpoint.extra':
|
||||
'应用发起单点登录的地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt': 'JWT单点登录',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url': 'SSO 地址',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.extra':
|
||||
'业务系统中的JWT SSO地址,在单点登录时本系统将向该地址发送id_token信息,参数名为id_token,业务系统通过id_token与Public Key可获取业务系统中的用户信息,如果在业务系统(SP)发起登录,请求SP登录地址时如果携带redirect_uri参数,系统会检验合法性,成功后会将浏览器重定向到该地址,并携带id_token身份令牌。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.0.message':
|
||||
'请输入JWT应用SSO地址',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.1.message':
|
||||
'JWT应用SSO地址格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.extra':
|
||||
'业务系统中在JWT SSO成功后重定向的URL,一般用于跳转到二级菜单等,若设置了该URL,在JWT SSO时会以参数target_link_url优先传递该值,若未设置该值,此时若SSO中有请求参数target_link_url,则会按照请求参数传递该值。此项可选。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.rule.0.message':
|
||||
'地址格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.binding_type': 'SSO 绑定类型',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.binding_type.extra':
|
||||
'指定向JWT应用发送 id_token 的请求方式。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.binding_type.rule.0.message':
|
||||
'登录提交方式为必选项',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type':
|
||||
'id_token 主体类型',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.0': '用户ID',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.1':
|
||||
'应用账户',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.extra':
|
||||
'id_token 中 sub 主体(用户) 类型',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.rule.0.message':
|
||||
'请选择 CAS 用户标识',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live':
|
||||
'id_token 过期时间',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.extra':
|
||||
'id_token 的有效期,单位为:秒。可设置范围为1-84600。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.rule.0.message':
|
||||
'请配置 id_token 的有效期',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about': '应用配置信息',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint':
|
||||
'IdP SSO 地址',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint.extra':
|
||||
'应用发起单点登录的地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_slo_endpoint':
|
||||
'登出端点',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_slo_endpoint.extra':
|
||||
'应用发起单点登出的地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert':
|
||||
'JWT 验签公钥',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.0':
|
||||
'下载或复制证书,并导入或粘贴到应用中。',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.1':
|
||||
'复制证书内容',
|
||||
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.2':
|
||||
'下载证书 .cer 文件',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc': 'OIDC 单点登录',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types': '授权模式',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.rule.0.message':
|
||||
'请勾选授权模式',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.0':
|
||||
'授权码模式',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.1':
|
||||
' authorization_code 模式,用于账户的登录认证、授权。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.0':
|
||||
'令牌刷新模式',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.1':
|
||||
'refresh_token 模式,用于既有 token 的延期。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.0':
|
||||
'隐式模式',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.1':
|
||||
' implicit\n' +
|
||||
' 模式,由于协议本身的安全性,通常不推荐使用。如果有特殊需求,可以使用变体的' +
|
||||
' PKCE 的授权码模式。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.0':
|
||||
'密码模式',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.1':
|
||||
'password 模式,由于协议本身的安全性,通常不推荐使用。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.device.label.0':
|
||||
'设备模式',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.device.label.1':
|
||||
'device 模式,兼容设备发起的登录流程。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key':
|
||||
'PKCE(用于授权码模式)',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key.extra':
|
||||
'PKCE (Proof Key for Code Exchange)是 OAuth 2.0 的安全性扩展模式,用于防护 CSRF、中间人进攻等恶意攻击。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris': '登录 Redirect URI',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.extra':
|
||||
'Redirect URI 白名单,应用在请求登录时携带 redirect_uri 参数,该值需要在白名单中,IAM 才会在认证完成后发起跳转。若有多条,请点击添加进行扩展',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message':
|
||||
'请配置登录 Redirect URI',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.1.message':
|
||||
'Redirect URI 格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.placeholder':
|
||||
'请输入登录 Redirect URI',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris':
|
||||
'登出 Redirect URI',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.extra':
|
||||
'登出 Redirect URI 白名单,应用在请求登录时携带 post_logout_redirect_uri 参数,该值需要在白名单中,IAM 才会在认证完成后发起跳转。若有多条,请点击添加进行扩展',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.0.message':
|
||||
'请配置登出 Redirect URI',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.1.message':
|
||||
'登出 Redirect URI 格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.placeholder':
|
||||
'请输入登出 Redirect URI',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type': 'SSO 发起方',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.rule.0.message':
|
||||
'请配置 SSO 发起方',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.0':
|
||||
'只允许应用发起',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.1':
|
||||
'支持门户和应用发起',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.0':
|
||||
'门户发起:由 IAM 门户页点击进行 SSO 。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.1':
|
||||
'应用发起:由应用登录主动发起。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url': '登录发起地址',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.rule.0.message':
|
||||
'登录发起地址不能为空',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.field_props':
|
||||
'请输入登录链接',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.extra':
|
||||
'若您希望由 TopIAM 门户页访问应用,请填写 TopIAM 发起 SSO 请求访问的应用地址。该地址接收到请求,应即刻转向 TopIAM / authorize 授权端点。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced': '显示高级配置',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes': '用户信息范围',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.rule.0.message':
|
||||
'请勾选用户信息范围',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.extra':
|
||||
'用户登录后,使用用户信息端点或解析 id_token 可以获取到的已登录用户信息 。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.1.label':
|
||||
'应用可获取登录用户邮箱信息。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.2.label':
|
||||
'应用可获取登录用户手机信息。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.3.label':
|
||||
'应用可获取登录用户详情信息。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live':
|
||||
'access_token 有效期',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live.extra':
|
||||
'access_token 用于请求 IAM 接口,过期后需要使用 refresh_token 刷新,或重新登录。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live':
|
||||
'refresh_token 有效期',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live.extra':
|
||||
'用于获取新的 access_token 和 id_token,refresh_token 过期后,用户需要重新登录。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live':
|
||||
'id_token 有效期',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live.extra':
|
||||
'id_token 用于鉴别用户身份,JWT格式,允许应用使用公钥自行验证用户身份。最小5分钟,最大24小时,过期后需要使用refresh_token 刷新,或重新登录。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm':
|
||||
'id_token 签名算法',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.extra':
|
||||
'id_token 签名使用的非对称算法。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.rule.0.message':
|
||||
'请配置 id_token 签名算法',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.config_about': '应用配置信息',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.config_about.issuer.extra':
|
||||
'用于标识 token 发放来源的字段。同时是下述接口的 baseUrl。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.discovery_endpoint': '发现端点',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.discovery_endpoint.extra':
|
||||
'用于获取当前 IAM 支持的各端点信息和支持的模式、参数信息,可公开访问。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.authorization_endpoint': '授权端点',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.authorization_endpoint.extra':
|
||||
'应用发起单点登录的地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.token_endpoint': '令牌端点',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.token_endpoint.extra':
|
||||
'应用在单点登录过程中,拿到 code 后,从后端发起换取 token 的接口地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.revoke_endpoint': '令牌吊销端点',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.jwks_endpoint': '验签公钥端点',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.jwks_endpoint.extra':
|
||||
'用于验证 id_token、完成 SSO 流程的公钥端点。公钥可能会轮转。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.userinfo_endpoint': '用户信息端点',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.userinfo_endpoint.extra':
|
||||
'在账户登录后,使用 access_token 调用用户信息端点,获取账户基本信息。',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.end_session_endpoint': '结束会话端点',
|
||||
'pages.app.config.items.login_access.protocol_config.oidc.end_session_endpoint.extra':
|
||||
'结束会话端点可用于触发单点注销。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard': 'SAML 单点登录',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.meta_data_url.extra':
|
||||
'SP 元数据为应用侧生成的 SSO 配置文件。若已有元数据,直接导入,可自动填充下方 SSO 参数,简单快捷。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.meta_data_url.placeholder':
|
||||
'请输入应用 metadata 地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.meta_data_url.label':
|
||||
'上传 SP 元数据 ',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_acs_url.placeholder':
|
||||
'请输入单点登录地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_acs_url.extra':
|
||||
'应用的 SAML SSO 核心地址,与 IAM 交互处理单点登录请求。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_acs_url.rule.0.message':
|
||||
'请输入单点登录地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_entity_id.placeholder':
|
||||
'请输入应用唯一标识',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_entity_id.extra':
|
||||
'应用在 IAM 中的标识,通常在应用侧获取,格式通常为应用 URI。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_entity_id.rule.0':
|
||||
'请输入应用唯一标识。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.name_id_value_type':
|
||||
'Name ID 类型',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.name_id_value_type.extra':
|
||||
'单点登录时,将选中项作为账户标识,传递给业务系统。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.name_id_value_type.rule.0.message':
|
||||
'请选择 Name ID',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type':
|
||||
'SSO 发起方',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.rule.0.message':
|
||||
'请配置 SSO 发起方',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.option.0':
|
||||
'只允许应用发起',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.option.1':
|
||||
'支持门户和应用发起',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.extra.0':
|
||||
'门户发起:由 IAM 门户页点击进行 SSO 。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.extra.1':
|
||||
'应用发起:由应用登录主动发起。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url':
|
||||
'登录发起地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url.placeholder':
|
||||
'请输入登录链接',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url.extra.0':
|
||||
'若应用支持 IDP 发起 SAMLResponse SSO 请求,可不填写。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url.extra.1':
|
||||
'若应用仅支持 SP 发起 SSO,您又希望实现由 IAM 门户页发起登录,请填写此项。门户页访问应用时,IAM 会跳转到本地址,应即刻自动向 IAM 发起 SAMLRequest 登录请求。地址可填写为任意受保护资源访问地址,在未登录时会自动触发 SAMLRequest 即可。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced':
|
||||
'显示高级配置',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state':
|
||||
'默认RelayState',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state.placeholder':
|
||||
'请输入默认跳转地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state.rule.0.message':
|
||||
'RelayState 格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state.extra':
|
||||
'DP 发起 SSO 登录成功后,应用应自动跳转的地址。在 SAML Response 中会在 RelayState 参数中传递,应用读取后实现跳转。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion':
|
||||
'断言',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.audience.extra':
|
||||
'SAML断言的目标受众,默认和SP Entity ID相同。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.audience.placeholder':
|
||||
'请输入SAML断言的目标受众',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.name_id_format':
|
||||
'NameID 格式',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.name_id_format.rule.0.message':
|
||||
'请选择 NameID 字段格式',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.name_id_format.extra':
|
||||
'SAML Response 中指定账户标识 NameID 字段格式。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.acs_binding':
|
||||
'ACS 绑定类型',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.acs_binding.rule.0.message':
|
||||
'请选择 ACS 绑定类型',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.acs_binding.extra':
|
||||
'指定IDP向SP发送断言请求的方式。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypted':
|
||||
'是否加密断言',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypted.rule.0.message':
|
||||
'请选择是否对断言加密。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypt_algorithm':
|
||||
'断言加密算法。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypt_algorithm.extra':
|
||||
'断言加密使用的算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypt_algorithm.rule.0.message':
|
||||
'请选择断言加密使用的算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_signed':
|
||||
'是否签名断言',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_signed.rule.0.message':
|
||||
'请选择是否对断言签名',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_signed.extra':
|
||||
'是否对断言使用IdP的证书签名,对应SP元数据文件中“WantAssertionsSigned”值。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_sign_algorithm':
|
||||
'断言签名算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_sign_algorithm.extra':
|
||||
'断言签名使用的算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_sign_algorithm.rule.0.message':
|
||||
'请选择签名使用的算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.authn_context_classref':
|
||||
'身份认证上下文',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.authn_context_classref.extra':
|
||||
'SAML 身份认证上下文',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.authn_context_classref.rule.0.message':
|
||||
'请选择身份认证上下文',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements':
|
||||
'断言属性',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name':
|
||||
'属性名称',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name.rule.0':
|
||||
'此项为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name_format':
|
||||
'名称格式',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name_format.rule.0':
|
||||
'此项为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression':
|
||||
'属性值',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression.tooltip':
|
||||
'支持的变量:',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression.placeholder':
|
||||
'变量值',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression.rule.0':
|
||||
'此项为必填项',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.option':
|
||||
'操作',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.recordCreatorProps':
|
||||
'添加断言属性',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.editable':
|
||||
'您确定要删除此断言属性吗?',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed':
|
||||
'响应签名',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.label':
|
||||
'是否签名响应',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.rule.0.message':
|
||||
'请选择是否对响应签名',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.response_sign_algorithm':
|
||||
'响应签名算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.response_sign_algorithm.extra':
|
||||
'断言响应使用的算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.response_sign_algorithm.rule.0.message':
|
||||
'请选择响应签名使用的算法',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests':
|
||||
'验证请求',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests.sp_requests_signed':
|
||||
'验证请求签名',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests.sp_requests_signed.rule.0.message':
|
||||
'请选择是否验证请求签名',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests.sp_requests_signed.extra':
|
||||
'用来对SAML Request签名进行验证,对应SP元数据文件中“AuthnRequestsSigned”值。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout':
|
||||
'单点注销',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_slo_enabled':
|
||||
'启用 SLO',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_slo_enabled.rule.0.message':
|
||||
'请选择是否启用 SLO',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_slo_enabled.extra':
|
||||
'SP 是否支持单点注销 (SLO) 。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url':
|
||||
'单点注销 URL',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url.rule.0.message':
|
||||
'单点注销地址格式不正确',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url.rule.1.message':
|
||||
'请输入单点注销地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url.extra':
|
||||
'会话注销功能,用户在 TopIAM 注销会话后通知该地址。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_binding':
|
||||
'单点注销绑定类型',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_binding.rule.0.message':
|
||||
'请选择单点注销绑定类型',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_binding.extra':
|
||||
'指定了 IDP 向 SP 发送 SLO 请求的方式。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_sign_cert':
|
||||
'签名证书',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_sign_cert.rule.0.message':
|
||||
'请填写验证签名证书',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_sign_cert.extra':
|
||||
'SP公钥证书,用来验证SAML request的签名,对应SP元数据文件中use="signing"证书内容。',
|
||||
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about': '应用配置信息',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_meta_endpoint':
|
||||
'IdP 元数据',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_meta_endpoint.extra':
|
||||
'若应用支持 metadata 配置信息上传/拉取,可以节省大量配置步骤。请在应用 SSO 配置中寻找是否有 metadata 上传能力。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_entity_id_endpoint':
|
||||
'IdP 唯一标识',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_entity_id_endpoint.extra':
|
||||
'AM 在应用中的标识。需要将值填写在应用单点登录配置中。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sso_endpoint':
|
||||
'IdP SSO 地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sso_endpoint.extra':
|
||||
'SAML 协议支持 SP 发起单点登录,可能需要填写此地址在应用配置中。由 IAM 提供。可以直接访问该地址,进行应用登录。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_slo_endpoint':
|
||||
'单点退出地址',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_slo_endpoint.extra':
|
||||
'SAML 协议支持单点退出,可能需要填写此地址在应用配置中。由 IAM 提供。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert':
|
||||
'签名公钥证书',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert.extra.0':
|
||||
'下载或复制证书,并导入或粘贴到应用中。',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert.extra.1':
|
||||
'复制证书内容',
|
||||
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert.extra.2':
|
||||
'下载证书 .cer 文件',
|
||||
'pages.app.config.items.login_access.app_account': '应用账户',
|
||||
'pages.app.config.items.login_access.app_account.alert.message':
|
||||
'如果您配置使用【系统账户名】,应用账户属性配置后不会生效;如需变更,请在 协议配置 更改配置。',
|
||||
'pages.app.config.items.login_access.app_account.columns.username': '系统用户',
|
||||
'pages.app.config.items.login_access.app_account.columns.account': '应用账户',
|
||||
'pages.app.config.items.login_access.app_account.columns.create_time': '添加时间',
|
||||
'pages.app.config.items.login_access.app_account.columns.option': '操作',
|
||||
'pages.app.config.items.login_access.app_account.columns.option.popconfirm.title':
|
||||
'您确定要删除此应用账户?',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account': '添加应用账户',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.user_id.rule.0.message':
|
||||
'请选择系统用户',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.user_id.placeholder':
|
||||
'请输入用户名、手机或邮箱搜索用户',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.account':
|
||||
'应用用户名',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.account.rule.0.message':
|
||||
'请输入账户访问应用时所使用户名',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.password':
|
||||
'应用用户密码',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.password.rule.0.message':
|
||||
'请输入账户访问应用时所使密码',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.app_identity':
|
||||
'应用身份',
|
||||
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.app_identity.rule.0.message':
|
||||
'请输入账户访问应用时所使身份',
|
||||
'pages.app.config.items.login_access.access_policy': '访问授权',
|
||||
'pages.app.config.items.login_access.access_policy.columns.subject_name': '授权主体',
|
||||
'pages.app.config.items.login_access.access_policy.columns.subject_type': '主体类型',
|
||||
'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user': '用户',
|
||||
'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user_group':
|
||||
'用户组',
|
||||
'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.organization':
|
||||
'组织机构',
|
||||
'pages.app.config.items.login_access.access_policy.columns.create_time': '授权时间',
|
||||
'pages.app.config.items.login_access.access_policy.columns.option': '操作',
|
||||
'pages.app.config.items.login_access.access_policy.columns.option.popconfirm.title':
|
||||
'您确定要取消主体授权?取消授权后不可自动恢复。',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy': '添加授权',
|
||||
'pages.app.config.items.login_access.access_policy.cancel_policy': '取消授权',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type':
|
||||
'授权类型',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.rule.0.message':
|
||||
'请选择授权类型',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user':
|
||||
'授权用户',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.rule.0.message':
|
||||
'请选择授权用户',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.placeholder':
|
||||
'请输入用户名搜索用户',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group':
|
||||
'授权分组',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group.rule.0.message':
|
||||
'请选择授权分组',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization':
|
||||
'授权组织',
|
||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message':
|
||||
'请选择组织节点',
|
||||
'pages.app.config.items.app_permission': '权限管理',
|
||||
'pages.app.config.items.app_permission.permission_resource': '资源管理',
|
||||
'pages.app.config.items.app_permission.permission_role': '角色管理',
|
||||
'pages.app.config.items.app_permission.permission_audit': '权限审计',
|
||||
'pages.app.config.items.account_sync': '账户同步',
|
||||
'pages.app.config.error': '未指定应用',
|
||||
};
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import type { GenerateStyle, ProAliasToken } from '@ant-design/pro-components';
|
||||
import { useStyle as useAntdStyle } from '@ant-design/pro-components';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { useContext } from 'react';
|
||||
|
||||
const { ConfigContext } = ConfigProvider;
|
||||
|
||||
interface AppConfigToken extends ProAliasToken {
|
||||
antCls: string;
|
||||
prefixCls: string;
|
||||
}
|
||||
|
||||
const genActionsStyle: GenerateStyle<AppConfigToken> = (token) => {
|
||||
const { prefixCls, antCls } = token;
|
||||
|
||||
return {
|
||||
[`${prefixCls}`]: {
|
||||
[`&-main`]: {
|
||||
display: 'flex',
|
||||
},
|
||||
[`&-left`]: {
|
||||
minHeight: '100%',
|
||||
overflow: 'auto',
|
||||
marginRight: token.margin,
|
||||
width: '200px',
|
||||
[`&-menu`]: {
|
||||
height: 'calc(100vh - 178px)',
|
||||
[`&${antCls}-menu-light${antCls}-menu-root${antCls}-menu-inline`]: {
|
||||
'border-inline-end': 'none',
|
||||
},
|
||||
[`&${antCls}-menu-light:not(${antCls}-menu-horizontal) ${antCls}-menu-item-selected`]: {
|
||||
'background-color': token.layout?.sider?.colorBgMenuItemSelected,
|
||||
color: token.layout?.sider?.colorTextMenuSelected,
|
||||
},
|
||||
[`&${antCls}-menu-light:not(${antCls}-menu-horizontal) ${antCls}-menu-item:not(${antCls}-menu-item-selected):active`]:
|
||||
{
|
||||
'background-color': token.layout?.sider?.colorBgMenuItemSelected,
|
||||
color: token.layout?.sider?.colorTextMenuActive,
|
||||
},
|
||||
[`&${antCls}-menu-light ${antCls}-menu-submenu-selected >${antCls}-menu-submenu-title`]: {
|
||||
color: token.layout?.sider?.colorTextMenuSelected,
|
||||
},
|
||||
},
|
||||
},
|
||||
[`&-right`]: {
|
||||
flex: 1,
|
||||
minHeight: '100%',
|
||||
overflow: 'auto',
|
||||
},
|
||||
},
|
||||
|
||||
[`@media screen and (max-width: ${token.screenXL}px)`]: {
|
||||
[`${prefixCls}`]: {
|
||||
['&-main']: {
|
||||
flexDirection: 'column',
|
||||
},
|
||||
['&-left']: {
|
||||
width: '100%',
|
||||
border: 'none',
|
||||
marginRight: 0,
|
||||
marginBottom: token.margin,
|
||||
[`&-menu`]: {
|
||||
height: 'auto',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function useStyle(prefixCls?: string) {
|
||||
const { getPrefixCls } = useContext(ConfigContext || ConfigProvider.ConfigContext);
|
||||
const antCls = `.${getPrefixCls()}`;
|
||||
|
||||
return useAntdStyle('AppConfig', (token) => {
|
||||
const appConfigToken: AppConfigToken = {
|
||||
...token,
|
||||
prefixCls: `.${prefixCls}`,
|
||||
antCls,
|
||||
};
|
||||
|
||||
return [genActionsStyle(appConfigToken)];
|
||||
});
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
|
||||
import { Form, Spin } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useIntl } from '@@/exports';
|
||||
import { random } from '@/utils/utils';
|
||||
|
||||
export default (props: {
|
||||
open: boolean;
|
||||
onFinish: (formData: Record<string, string>) => Promise<boolean | void>;
|
||||
onCancel: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
}) => {
|
||||
const { open, onCancel, onFinish } = props;
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
title={intl.formatMessage({ id: 'pages.app_group.create.modal_form.title' })}
|
||||
form={form}
|
||||
open={open}
|
||||
labelCol={{ span: 4 }}
|
||||
wrapperCol={{ span: 20 }}
|
||||
width={'500px'}
|
||||
layout={'horizontal'}
|
||||
labelAlign={'right'}
|
||||
preserve={false}
|
||||
autoFocusFirstInput
|
||||
onOpenChange={(visible) => {
|
||||
if (visible) {
|
||||
form.setFieldsValue({ code: random(9) });
|
||||
}
|
||||
}}
|
||||
modalProps={{
|
||||
maskClosable: true,
|
||||
destroyOnClose: true,
|
||||
onCancel: onCancel,
|
||||
}}
|
||||
onFinish={async (values: Record<string, string>) => {
|
||||
setLoading(true);
|
||||
await onFinish(values).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
<ProFormText
|
||||
label={intl.formatMessage({ id: 'pages.app_group.modal_form.name' })}
|
||||
name="name"
|
||||
placeholder={intl.formatMessage({ id: 'pages.app_group.modal_form.name.placeholder' })}
|
||||
fieldProps={{
|
||||
maxLength: 8,
|
||||
}}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app_group.modal_form.name.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText
|
||||
label={intl.formatMessage({ id: 'pages.app_group.modal_form.code' })}
|
||||
placeholder={intl.formatMessage({ id: 'pages.app_group.modal_form.code.placeholder' })}
|
||||
name="code"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'pages.app_group.modal_form.code.rule.0.message',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormTextArea
|
||||
label={intl.formatMessage({ id: 'pages.app_group.modal_form.remark' })}
|
||||
name="remark"
|
||||
fieldProps={{
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'pages.app_group.modal_form.remark.placeholder',
|
||||
}),
|
||||
rows: 2,
|
||||
maxLength: 20,
|
||||
autoComplete: 'off',
|
||||
showCount: true,
|
||||
}}
|
||||
/>
|
||||
</Spin>
|
||||
</ModalForm>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import CreateModal from './CreateModal';
|
||||
export default CreateModal;
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import UpdateModal from './UpdateModal';
|
||||
export default UpdateModal;
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { getAllAppGroupList, getAppList } from '@/services/app';
|
||||
import { getAllAppGroupList, getAppList, removeApp } from '@/services/app';
|
||||
import { PlusOutlined, QuestionCircleOutlined, GroupOutlined } from '@ant-design/icons';
|
||||
import type { ActionType } from '@ant-design/pro-components';
|
||||
import { PageContainer, ProList } from '@ant-design/pro-components';
|
||||
|
@ -25,7 +25,7 @@ import { history, useIntl } from '@umijs/max';
|
|||
import useStyle from './style';
|
||||
import classnames from 'classnames';
|
||||
import { AppList } from './data.d';
|
||||
import { disableApp, enableApp, removeApp } from './service';
|
||||
import { disableApp, enableApp } from './service';
|
||||
|
||||
const prefixCls = 'app-list';
|
||||
|
||||
|
@ -111,7 +111,7 @@ export default () => {
|
|||
<span
|
||||
onClick={() => {
|
||||
history.push(
|
||||
`/app/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
|
||||
`/app/detail?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
@ -183,14 +183,12 @@ export default () => {
|
|||
</Popconfirm>
|
||||
),
|
||||
<a
|
||||
key="config"
|
||||
key="details"
|
||||
onClick={() => {
|
||||
history.push(
|
||||
`/app/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
|
||||
);
|
||||
history.push(`/app/detail?id=${row.id}`);
|
||||
}}
|
||||
>
|
||||
{intl.formatMessage({ id: 'app.manage' })}
|
||||
{intl.formatMessage({ id: 'pages.app.list.actions.detail' })}
|
||||
</a>,
|
||||
<Popconfirm
|
||||
title={intl.formatMessage({
|
||||
|
|
|
@ -31,11 +31,4 @@ export async function disableApp(id: string): Promise<API.ApiResult<boolean>> {
|
|||
return request(`/api/v1/app/disable/${id}`, { method: 'PUT' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Application
|
||||
*/
|
||||
export async function removeApp(id: string): Promise<API.ApiResult<boolean>> {
|
||||
return request<API.ApiResult<boolean>>(`/api/v1/app/delete/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue